001    /*
002     * Copyright 2001,2004 The Apache Software Foundation.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.apache.commons.scaffold.lucene;
018    
019    
020    import java.io.IOException;
021    
022    import org.apache.commons.scaffold.lang.ParameterException;
023    import org.apache.commons.scaffold.lang.ResourceException;
024    
025    import org.apache.lucene.analysis.Analyzer;
026    import org.apache.lucene.analysis.StopAnalyzer;
027    import org.apache.lucene.index.IndexReader;
028    import org.apache.lucene.index.IndexWriter;
029    import org.apache.lucene.index.Term;
030    import org.apache.lucene.queryParser.ParseException;
031    import org.apache.lucene.queryParser.QueryParser;
032    import org.apache.lucene.search.Hits;
033    import org.apache.lucene.search.IndexSearcher;
034    import org.apache.lucene.search.Query;
035    import org.apache.lucene.search.Searcher;
036    
037    
038    /**
039     * Search engine methods for Lucene.
040     *
041     * @author Ted Husted
042     * @version $Revision: 155464 $ $Date: 2005-02-26 13:26:54 +0000 (Sat, 26 Feb 2005) $
043     * @todo Make singleton instead of static
044     */
045    public final class Engine {
046    
047    
048        /**
049         * Default path for index ["/var/lucene/scaffold"].
050         * Use the <code>setIndexPath</code> method on startup
051         * to change.
052         * Each application needs its own index path.
053         */
054        private static String indexPath;
055    
056    
057        /**
058         * Return path for index.
059         */
060        public static String getIndexPath() {
061            return indexPath;
062        }
063    
064    
065        /**
066         * Set path for index.
067         * MUST be set at startup before using other methods.
068         */
069        public static void init(String _indexPath) {
070            indexPath = _indexPath;
071        }
072    
073    
074        /**
075         * Performance optimization to ensure JVM doesn't
076         * create a new empty string whenever blankNull
077         * needs one.
078         */
079        private static final String BLANK_STRING = "";
080    
081    
082        /**
083         * Convenience method to use an empty string in place
084         * of a null for indexing.
085         *
086         * @param string The string to test for null
087         * @return A blank String [BLANK_STRING] if string is null
088         */
089        public static final String blankNull(String string) {
090            if (string==null) return BLANK_STRING;
091            else return string;
092        }
093    
094    
095        /**
096         * Return default analyzer for application
097         * A non-English site should use a different analyzer
098         *
099         * @return The default analyzer
100         * @exception Throws ResourceException on IO error
101         */
102        public static final Analyzer getAnalyzer() {
103            return new StopAnalyzer();
104        }
105    
106    
107        /**
108         * Return default searcher for application.
109         *
110         * @exception Throws ResourceException on IO error
111         * @return default searcher
112         */
113        public static final Searcher getSearcher()
114                throws ResourceException {
115    
116            try {
117    
118                return new IndexSearcher(getIndexPath());
119    
120            }
121            catch (IOException e) {
122                throw new ResourceException(e);
123            }
124    
125        } // end getSearcher()
126    
127    
128        /**
129         * Return default writer for application.
130         *
131         * @param Set to true to create a new index; usually false.
132         * @return default writer
133         * @exception Throws ResourceException on IO error
134         */
135        public static final IndexWriter getIndexWriter(boolean create)
136            throws ResourceException {
137    
138            try {
139    
140                return (new IndexWriter(getIndexPath(),
141                    getAnalyzer(),create));
142    
143            }
144            catch (IOException e) {
145                throw new ResourceException(e);
146            }
147    
148        } // end getIndexWriter()
149    
150    
151        /**
152         * Return default reader for application.
153         *
154         * @return default reader
155         * @exception Throws ResourceException on IO error
156         */
157        public static final IndexReader getIndexReader()
158            throws ResourceException {
159    
160            try {
161    
162                return (IndexReader.open(getIndexPath()));
163    
164            }
165            catch (IOException e) {
166                throw new ResourceException(e);
167            }
168    
169        } // end getIndexReader()
170    
171    
172        /**
173         * Return hits for model using default searcher.
174         *
175         * @return hits for model
176         * @param query The query we are processing
177         */
178        public static final Hits getHits(Query query)
179            throws ResourceException {
180    
181            try {
182    
183                return getSearcher().search(query);
184    
185            }
186            catch (IOException e) {
187                throw new ResourceException(e);
188            }
189    
190        } // end getHits()
191    
192    
193        /**
194         * Return hits for value and field
195         *
196         * @param value The term to match ("Smith")
197         * @param field The field to patch ("name")
198         * @return hits for model
199         */
200        public static final Query getQuery(
201                String value,
202                String field) throws ParameterException {
203    
204            try {
205    
206                return QueryParser.parse(value,field, getAnalyzer());
207    
208            }
209            catch (ParseException e) {
210                throw new ParameterException(e);
211            }
212    
213        } // end getQuery()
214    
215    
216        /**
217         * Object for synchronizing deletions to index
218         */
219        private static final Object INDEX_LOCK = new Object();
220    
221    
222        /**
223         * Delete record matching term from default index.
224         * Access to the index is synchronized.
225         *
226         * @param term The term matching the entry to delete
227         * @exception Throws ResourceException on IO error
228         */
229        public static final int deleteTerm(Term term)
230            throws ResourceException {
231    
232            int result = 0;
233            synchronized (INDEX_LOCK) {
234                IndexReader reader = null;
235                try {
236                    reader = getIndexReader();
237                    result = reader.delete(term);
238                }
239                catch (IOException e) {
240                    throw new ResourceException(e);
241                }
242                finally {
243                    try {
244                        if (reader!=null) reader.close();
245                    }
246                    catch (IOException e) {
247                    // do nothing
248                    }
249                }
250            }
251            return result;
252    
253        } // end deleteTerm()
254    
255    
256    } // end Engine
257