001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.search.lucene2.query;
020
021import java.io.IOException;
022import java.io.StringReader;
023import java.util.Map;
024import java.util.Vector;
025import java.util.WeakHashMap;
026
027import lucee.commons.lang.ParserString;
028
029import org.apache.lucene.analysis.Analyzer;
030import org.apache.lucene.analysis.TokenStream;
031import org.apache.lucene.index.Term;
032import org.apache.lucene.search.BooleanQuery;
033import org.apache.lucene.search.FuzzyQuery;
034import org.apache.lucene.search.PhraseQuery;
035import org.apache.lucene.search.PrefixQuery;
036import org.apache.lucene.search.Query;
037import org.apache.lucene.search.TermQuery;
038import org.apache.lucene.search.WildcardQuery;
039
040/**
041 * @deprecated no longer in use
042 * The simple query is the default query type and is appropriate for the vast majority of searches. 
043 * When entering text on a search form, you perform a simple query by entering a word or comma-delimited strings, 
044 * with optional wildcard characters. 
045 * Verity treats each comma as a logical OR. If you omit the commas, Verity treats the expression as a phrase.
046 */
047public final class Simple {
048    private String OR="or";
049    private String AND="and";
050    private String NOT="not";
051    private char QUOTER='"';
052    private String FIELD="contents";
053    
054    private static final short TYPE_TERMAL=0;
055    private static final short TYPE_WILDCARD=1;
056    private static final short TYPE_PREFIX=2;
057    private static final short TYPE_FUZZY=3;
058    private static final short TYPE_PHRASE=4;
059    
060    private Analyzer analyzer;
061    
062    private Map results=new WeakHashMap();
063    
064    /**
065     * constructor of the class
066     * @param analyzer
067     */
068    public Simple(Analyzer analyzer) {
069        this.analyzer=analyzer;
070        
071    }
072    /**
073     * parse given string query
074     * @param criteria
075     * @return matching Query
076     */
077    public Query parse(String criteria) {
078        Query qry=(Query) results.get(criteria);
079        if(qry!=null) return qry;
080        
081        // remove operators at start
082        if(criteria.length()>0) {
083            char first=criteria.charAt(0);
084            // start with operator
085            while(first=='*' || first=='~' || first=='?') {
086                criteria=criteria.substring(1);
087                if(criteria.length()==0) break;
088                first=criteria.charAt(0);
089            } 
090        }
091        
092        // make never foud query if quey is empty
093        if(criteria.length()==0) {
094            BooleanQuery bool = new BooleanQuery();
095            bool.add(new TermQuery(new Term(FIELD, "dshnuiaslfspfhsadhfisd")), OccurUtil.toOccur(false, true));
096            results.put(criteria,bool);
097            return bool;
098        }
099        
100        ParserString ps=new ParserString(criteria);
101        qry= orOp(ps);
102        results.put(criteria,qry);
103        return qry;
104    }
105    
106    
107
108    private Query orOp(ParserString ps) {
109        Query query=andOp(ps);
110        ps.removeSpace();
111               
112        // OR
113        while(ps.isValidIndex() && ps.forwardIfCurrent(OR) || ps.forwardIfCurrent(',')) {
114            ps.removeSpace();
115            BooleanQuery bool = new BooleanQuery();
116            
117            bool.add(query, OccurUtil.toOccur(false, false));
118            //bool.add(query, false, false);
119            bool.add(andOp(ps), OccurUtil.toOccur(false, false));
120            query = bool;
121        }
122        return query;
123    }
124
125    private Query andOp(ParserString ps) {
126        Query query = notOp(ps);
127        ps.removeSpace();
128        
129        // AND
130        while(ps.isValidIndex() && ps.forwardIfCurrent(AND)) {
131            ps.removeSpace();
132            BooleanQuery bool = new BooleanQuery();
133            bool.add(query, OccurUtil.toOccur(true, false));
134            bool.add(notOp(ps), OccurUtil.toOccur(true, false));
135            query = bool;
136        }
137        return query;
138    }
139    private Query notOp(ParserString ps) {
140        // NOT
141        if(ps.isValidIndex() && ps.forwardIfCurrent(NOT)) {
142            ps.removeSpace();
143            BooleanQuery bool = new BooleanQuery();
144            bool.add(clip(ps), OccurUtil.toOccur(false, true));
145            return bool;
146        }
147        return clip(ps);
148    }
149
150    private Query clip(ParserString ps) {
151        // ()
152        if(ps.isValidIndex() && ps.forwardIfCurrent('(')) {
153            Query query=orOp(ps);
154            ps.removeSpace();
155            ps.forwardIfCurrent(')');
156            ps.removeSpace();
157            return query;
158        }
159        return literal(ps);
160    }
161
162    private Query literal(ParserString ps) {
163        _Term term=term(ps);
164        ps.removeSpace();
165        while(ps.isValidIndex() && !ps.isCurrent(',') && !ps.isCurrent(OR) && !ps.isCurrent(AND) && !ps.isCurrent(')')) {
166            term.append(term(ps));
167            ps.removeSpace();
168        }
169        return term.toQuery();
170    }
171
172    private _Term term(ParserString ps) {
173        short type=TYPE_TERMAL;
174        ps.removeSpace();
175        StringBuffer sb=new StringBuffer();
176        boolean inside=false;
177        char c=0;
178        while(ps.isValidIndex() && ((c=ps.getCurrentLower())!=' ' && c!=',' && c!=')' || inside)) {
179            ps.next();
180            if(c==QUOTER) {
181                inside=!inside;
182                type=TYPE_PHRASE;
183                continue;
184            }
185            sb.append(c);
186            if(!inside) {
187                    if(type==TYPE_PREFIX)type=TYPE_WILDCARD;
188                    if(type==TYPE_TERMAL && c=='*')type=TYPE_PREFIX;
189                    if(c=='?')type=TYPE_WILDCARD;
190                    if(type==TYPE_TERMAL && c=='~') {
191                        type=TYPE_FUZZY;
192                        break;
193                    }
194            }
195        }
196        return new _Term(type,sb.toString());
197
198    }
199    
200    class _Term {
201        private short type;
202        private String content;
203        
204        private _Term(short type, String content) {
205            this.type = type;
206            this.content=content;
207        }
208        
209        private void append(_Term term) {
210            content+=' '+term.content;
211            type=TYPE_PHRASE;
212        }
213
214        private Query toQuery() {
215            if(type==TYPE_FUZZY) return toFuzzyQuery();
216            else if(type==TYPE_WILDCARD) return new WildcardQuery(toTerm());
217            else if(type==TYPE_PREFIX)return toPrefixQuery();
218            else if(type==TYPE_PHRASE) return toPhraseQuery();
219            return new TermQuery(toTerm());
220        }
221        
222        private FuzzyQuery toFuzzyQuery() {
223            String c=toContent();
224            return new FuzzyQuery(new Term(FIELD,c.substring(0,c.length()-1)));
225        }
226        
227        private PrefixQuery toPrefixQuery() {
228            String c=toContent();
229            return new PrefixQuery(new Term(FIELD,c.substring(0,c.length()-1)));
230        }
231        
232        private PhraseQuery toPhraseQuery() {
233            
234
235            TokenStream source = analyzer.tokenStream(FIELD,new StringReader(content));
236            Vector v = new Vector();
237            org.apache.lucene.analysis.Token t;
238
239            while (true) {
240              try {
241                t = source.next();
242              }
243              catch (IOException e) {
244                t = null;
245              }
246              if (t == null)
247                break;
248              v.addElement(t.termText());
249            }
250            try {
251              source.close();
252            }
253            catch (IOException e) {
254              // ignore
255            }
256
257            PhraseQuery q = new PhraseQuery();
258             q.setSlop(0);
259             
260             for (int i=0; i<v.size(); i++) {
261                q.add(new Term(FIELD, (String) v.elementAt(i)));
262              }
263              return q;
264            
265        }
266
267        private String toContent() {
268            return content;
269        }
270        private Term toTerm() {
271            return new Term(FIELD, toContent());
272        }
273        @Override
274        public String toString() {
275            return toContent();
276        }
277    }   
278}