001/* 002 Licensed to the Apache Software Foundation (ASF) under one 003 or more contributor license agreements. See the NOTICE file 004 distributed with this work for additional information 005 regarding copyright ownership. The ASF licenses this file 006 to you under the Apache License, Version 2.0 (the 007 "License"); you may not use this file except in compliance 008 with the License. You may obtain a copy of the License at 009 010 http://www.apache.org/licenses/LICENSE-2.0 011 012 Unless required by applicable law or agreed to in writing, 013 software distributed under the License is distributed on an 014 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 KIND, either express or implied. See the License for the 016 specific language governing permissions and limitations 017 under the License. 018 */ 019package org.apache.wiki.search; 020 021import org.apache.logging.log4j.LogManager; 022import org.apache.logging.log4j.Logger; 023import org.apache.wiki.api.core.Attachment; 024import org.apache.wiki.api.core.Context; 025import org.apache.wiki.api.core.Engine; 026import org.apache.wiki.api.core.Page; 027import org.apache.wiki.api.exceptions.NoRequiredPropertyException; 028import org.apache.wiki.api.exceptions.ProviderException; 029import org.apache.wiki.api.providers.PageProvider; 030import org.apache.wiki.api.search.QueryItem; 031import org.apache.wiki.api.search.SearchResult; 032import org.apache.wiki.attachment.AttachmentManager; 033import org.apache.wiki.auth.AuthorizationManager; 034import org.apache.wiki.auth.permissions.PagePermission; 035import org.apache.wiki.pages.PageManager; 036 037import java.io.IOException; 038import java.util.Collection; 039import java.util.Iterator; 040import java.util.List; 041import java.util.Properties; 042import java.util.StringTokenizer; 043import java.util.TreeSet; 044 045/** 046 * Interface for the search providers that handle searching the Wiki 047 * 048 * @since 2.2.21. 049 */ 050public class BasicSearchProvider implements SearchProvider { 051 052 private static final Logger log = LogManager.getLogger( BasicSearchProvider.class ); 053 private Engine m_engine; 054 055 /** 056 * {@inheritDoc} 057 */ 058 @Override 059 public void initialize( final Engine engine, final Properties props ) throws NoRequiredPropertyException, IOException { 060 m_engine = engine; 061 } 062 063 /** 064 * {@inheritDoc} 065 */ 066 @Override 067 public void pageRemoved( final Page page ) {} 068 069 /** 070 * {@inheritDoc} 071 */ 072 @Override 073 public void reindexPage( final Page page ) {} 074 075 /** 076 * Parses a query into something that we can use. 077 * 078 * @param query A query string. 079 * @return A parsed array. 080 */ 081 public QueryItem[] parseQuery( final String query) { 082 final StringTokenizer st = new StringTokenizer( query, " \t," ); 083 final QueryItem[] items = new QueryItem[st.countTokens()]; 084 int word = 0; 085 086 log.debug("Expecting "+items.length+" items"); 087 088 // Parse incoming search string 089 while( st.hasMoreTokens() ) { 090 log.debug( "Item " + word ); 091 String token = st.nextToken().toLowerCase(); 092 093 items[ word ] = new QueryItem(); 094 095 switch( token.charAt( 0 ) ) { 096 case '+': 097 items[ word ].type = QueryItem.REQUIRED; 098 token = token.substring( 1 ); 099 log.debug( "Required word: " + token ); 100 break; 101 102 case '-': 103 items[ word ].type = QueryItem.FORBIDDEN; 104 token = token.substring( 1 ); 105 log.debug( "Forbidden word: " + token ); 106 break; 107 108 default: 109 items[ word ].type = QueryItem.REQUESTED; 110 log.debug( "Requested word: " + token ); 111 break; 112 } 113 114 items[ word++ ].word = token; 115 } 116 117 return items; 118 } 119 120 private String attachmentNames( final Page page ) { 121 if( m_engine.getManager( AttachmentManager.class ).hasAttachments( page ) ) { 122 final List< Attachment > attachments; 123 try { 124 attachments = m_engine.getManager( AttachmentManager.class ).listAttachments( page ); 125 } catch( final ProviderException e ) { 126 log.error( "Unable to get attachments for page", e ); 127 return ""; 128 } 129 130 final StringBuilder attachmentNames = new StringBuilder(); 131 for( final Iterator< Attachment > it = attachments.iterator(); it.hasNext(); ) { 132 final Attachment att = it.next(); 133 attachmentNames.append( att.getName() ); 134 if( it.hasNext() ) { 135 attachmentNames.append( " " ); 136 } 137 } 138 return attachmentNames.toString(); 139 } 140 141 return ""; 142 } 143 144 private Collection< SearchResult > findPages( final QueryItem[] query, final Context wikiContext ) { 145 final TreeSet< SearchResult > res = new TreeSet<>( new SearchResultComparator() ); 146 final SearchMatcher matcher = new SearchMatcher( m_engine, query ); 147 final Collection< Page > allPages; 148 try { 149 allPages = m_engine.getManager( PageManager.class ).getAllPages(); 150 } catch( final ProviderException pe ) { 151 log.error( "Unable to retrieve page list", pe ); 152 return null; 153 } 154 155 final AuthorizationManager mgr = m_engine.getManager( AuthorizationManager.class ); 156 157 for( final Page page : allPages ) { 158 try { 159 if( page != null ) { 160 final PagePermission pp = new PagePermission( page, PagePermission.VIEW_ACTION ); 161 if( wikiContext == null || mgr.checkPermission( wikiContext.getWikiSession(), pp ) ) { 162 final String pageName = page.getName(); 163 final String pageContent = 164 m_engine.getManager( PageManager.class ).getPageText( pageName, PageProvider.LATEST_VERSION ) + attachmentNames( page ); 165 final SearchResult comparison = matcher.matchPageContent( pageName, pageContent ); 166 if( comparison != null ) { 167 res.add( comparison ); 168 } 169 } 170 } 171 } catch( final ProviderException pe ) { 172 log.error( "Unable to retrieve page from cache", pe ); 173 } catch( final IOException ioe ) { 174 log.error( "Failed to search page", ioe ); 175 } 176 } 177 178 return res; 179 } 180 181 /** 182 * {@inheritDoc} 183 */ 184 @Override 185 public Collection< SearchResult > findPages( final String query, final Context wikiContext ) { 186 return findPages( parseQuery( query ), wikiContext ); 187 } 188 189 /** 190 * {@inheritDoc} 191 */ 192 @Override 193 public String getProviderInfo() { 194 return "BasicSearchProvider"; 195 } 196 197}