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