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 java.io.IOException;
022import java.util.Collection;
023import java.util.Iterator;
024import java.util.List;
025import java.util.Properties;
026import java.util.StringTokenizer;
027import java.util.TreeSet;
028
029import org.apache.log4j.Logger;
030import org.apache.wiki.WikiContext;
031import org.apache.wiki.WikiEngine;
032import org.apache.wiki.WikiPage;
033import org.apache.wiki.api.exceptions.NoRequiredPropertyException;
034import org.apache.wiki.api.exceptions.ProviderException;
035import org.apache.wiki.attachment.Attachment;
036import org.apache.wiki.auth.AuthorizationManager;
037import org.apache.wiki.auth.permissions.PagePermission;
038import org.apache.wiki.providers.WikiPageProvider;
039
040/**
041 *  Interface for the search providers that handle searching the Wiki
042 *
043 *  @since 2.2.21.
044 */
045public class BasicSearchProvider implements SearchProvider
046{
047    private static final Logger log = Logger.getLogger(BasicSearchProvider.class);
048
049    private WikiEngine m_engine;
050
051    /**
052     *  {@inheritDoc}
053     */
054    public void initialize(WikiEngine engine, Properties props)
055            throws NoRequiredPropertyException, IOException
056    {
057        m_engine = engine;
058    }
059
060    /**
061     *  {@inheritDoc}
062     */
063    public void pageRemoved(WikiPage page) {}
064
065    /**
066     *  {@inheritDoc}
067     */
068    public void reindexPage(WikiPage page) {}
069
070    /**
071     *  Parses a query into something that we can use.
072     *  
073     *  @param query A query string.
074     *  @return A parsed array.
075     */
076    public  QueryItem[] parseQuery(String query)
077    {
078        StringTokenizer st = new StringTokenizer( query, " \t," );
079
080        QueryItem[] items = new QueryItem[st.countTokens()];
081        int word = 0;
082
083        log.debug("Expecting "+items.length+" items");
084
085        //
086        //  Parse incoming search string
087        //
088
089        while( st.hasMoreTokens() )
090        {
091            log.debug("Item "+word);
092            String token = st.nextToken().toLowerCase();
093
094            items[word] = new QueryItem();
095
096            switch( token.charAt(0) )
097            {
098              case '+':
099                items[word].type = QueryItem.REQUIRED;
100                token = token.substring(1);
101                log.debug("Required word: "+token);
102                break;
103
104              case '-':
105                items[word].type = QueryItem.FORBIDDEN;
106                token = token.substring(1);
107                log.debug("Forbidden word: "+token);
108                break;
109
110              default:
111                items[word].type = QueryItem.REQUESTED;
112                log.debug("Requested word: "+token);
113                break;
114            }
115
116            items[word++].word = token;
117        }
118
119        return items;
120    }
121
122    private String attachmentNames(WikiPage page, String separator)
123    {
124        if(m_engine.getAttachmentManager().hasAttachments(page))
125        {
126            List< Attachment > attachments;
127            try
128            {
129                attachments = m_engine.getAttachmentManager().listAttachments(page);
130            }
131            catch (ProviderException e)
132            {
133                log.error("Unable to get attachments for page", e);
134                return "";
135            }
136
137            StringBuilder attachmentNames = new StringBuilder();
138            for( Iterator< Attachment > it = attachments.iterator(); it.hasNext(); )
139            {
140                Attachment att = it.next();
141                attachmentNames.append(att.getName());
142                if(it.hasNext()) {
143                    attachmentNames.append(separator);
144                }
145            }
146            return attachmentNames.toString();
147        }
148
149        return "";
150    }
151
152    private Collection< SearchResult > findPages( QueryItem[] query, WikiContext wikiContext )
153    {
154        TreeSet<SearchResult> res = new TreeSet<>( new SearchResultComparator() );
155        SearchMatcher matcher = new SearchMatcher( m_engine, query );
156
157        Collection< WikiPage > allPages = null;
158        try {
159            allPages = m_engine.getPageManager().getAllPages();
160        } catch( ProviderException pe ) {
161            log.error( "Unable to retrieve page list", pe );
162            return null;
163        }
164
165        AuthorizationManager mgr = m_engine.getAuthorizationManager();
166
167        Iterator< WikiPage > it = allPages.iterator();
168        while( it.hasNext() ) {
169            try {
170                WikiPage page = (WikiPage) it.next();
171                if (page != null) {
172                    
173                    PagePermission pp = new PagePermission( page, PagePermission.VIEW_ACTION );
174                    if( wikiContext==null || mgr.checkPermission( wikiContext.getWikiSession(), pp ) ) {
175                        String pageName = page.getName();
176                        String pageContent = m_engine.getPageManager().getPageText(pageName, WikiPageProvider.LATEST_VERSION) +
177                                             attachmentNames(page, " ");
178                        SearchResult comparison = matcher.matchPageContent( pageName, pageContent );
179    
180                        if( comparison != null ) {
181                            res.add( comparison );
182                        }
183                    }
184                }
185            } catch( ProviderException pe ) {
186                log.error( "Unable to retrieve page from cache", pe );
187            } catch( IOException ioe ) {
188                log.error( "Failed to search page", ioe );
189            }
190        }
191
192        return res;
193    }
194
195    /**
196     *  {@inheritDoc}
197     */
198    public Collection< SearchResult > findPages(String query, WikiContext wikiContext) {
199        return findPages(parseQuery(query), wikiContext);
200    }
201
202    /**
203     *  {@inheritDoc}
204     */
205    public String getProviderInfo() {
206        return "BasicSearchProvider";
207    }
208
209}