pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / java / org / apache / lucene / queryParser / QueryParser.java
1 /* Generated By:JavaCC: Do not edit this line. QueryParser.java */
2 package org.apache.lucene.queryParser;
3
4 import java.io.IOException;
5 import java.io.StringReader;
6 import java.text.Collator;
7 import java.text.DateFormat;
8 import java.util.ArrayList;
9 import java.util.Calendar;
10 import java.util.Date;
11 import java.util.HashMap;
12 import java.util.List;
13 import java.util.Locale;
14 import java.util.Map;
15
16 import org.apache.lucene.analysis.Analyzer;
17 import org.apache.lucene.analysis.CachingTokenFilter;
18 import org.apache.lucene.analysis.TokenStream;
19 import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
20 import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
21 import org.apache.lucene.document.DateField;
22 import org.apache.lucene.document.DateTools;
23 import org.apache.lucene.index.Term;
24 import org.apache.lucene.search.BooleanClause;
25 import org.apache.lucene.search.BooleanQuery;
26 import org.apache.lucene.search.FuzzyQuery;
27 import org.apache.lucene.search.MultiTermQuery;
28 import org.apache.lucene.search.MatchAllDocsQuery;
29 import org.apache.lucene.search.MultiPhraseQuery;
30 import org.apache.lucene.search.PhraseQuery;
31 import org.apache.lucene.search.PrefixQuery;
32 import org.apache.lucene.search.Query;
33 import org.apache.lucene.search.TermRangeQuery;
34 import org.apache.lucene.search.TermQuery;
35 import org.apache.lucene.search.WildcardQuery;
36 import org.apache.lucene.util.Version;
37 import org.apache.lucene.util.VirtualMethod;
38
39 /**
40  * This class is generated by JavaCC.  The most important method is
41  * {@link #parse(String)}.
42  *
43  * The syntax for query strings is as follows:
44  * A Query is a series of clauses.
45  * A clause may be prefixed by:
46  * <ul>
47  * <li> a plus (<code>+</code>) or a minus (<code>-</code>) sign, indicating
48  * that the clause is required or prohibited respectively; or
49  * <li> a term followed by a colon, indicating the field to be searched.
50  * This enables one to construct queries which search multiple fields.
51  * </ul>
52  *
53  * A clause may be either:
54  * <ul>
55  * <li> a term, indicating all the documents that contain this term; or
56  * <li> a nested query, enclosed in parentheses.  Note that this may be used
57  * with a <code>+</code>/<code>-</code> prefix to require any of a set of
58  * terms.
59  * </ul>
60  *
61  * Thus, in BNF, the query grammar is:
62  * <pre>
63  *   Query  ::= ( Clause )*
64  *   Clause ::= ["+", "-"] [&lt;TERM&gt; ":"] ( &lt;TERM&gt; | "(" Query ")" )
65  * </pre>
66  *
67  * <p>
68  * Examples of appropriately formatted queries can be found in the <a
69  * href="../../../../../../queryparsersyntax.html">query syntax
70  * documentation</a>.
71  * </p>
72  *
73  * <p>
74  * In {@link TermRangeQuery}s, QueryParser tries to detect date values, e.g.
75  * <tt>date:[6/1/2005 TO 6/4/2005]</tt> produces a range query that searches
76  * for "date" fields between 2005-06-01 and 2005-06-04. Note that the format
77  * of the accepted input depends on {@link #setLocale(Locale) the locale}.
78  * By default a date is converted into a search term using the deprecated
79  * {@link DateField} for compatibility reasons.
80  * To use the new {@link DateTools} to convert dates, a
81  * {@link org.apache.lucene.document.DateTools.Resolution} has to be set.
82  * </p>
83  * <p>
84  * The date resolution that shall be used for RangeQueries can be set
85  * using {@link #setDateResolution(DateTools.Resolution)}
86  * or {@link #setDateResolution(String, DateTools.Resolution)}. The former
87  * sets the default date resolution for all fields, whereas the latter can
88  * be used to set field specific date resolutions. Field specific date
89  * resolutions take, if set, precedence over the default date resolution.
90  * </p>
91  * <p>
92  * If you use neither {@link DateField} nor {@link DateTools} in your
93  * index, you can create your own
94  * query parser that inherits QueryParser and overwrites
95  * {@link #getRangeQuery(String, String, String, boolean)} to
96  * use a different method for date conversion.
97  * </p>
98  *
99  * <p>Note that QueryParser is <em>not</em> thread-safe.</p> 
100  * 
101  * <p><b>NOTE</b>: there is a new QueryParser in contrib, which matches
102  * the same syntax as this class, but is more modular,
103  * enabling substantial customization to how a query is created.
104  *
105  * <a name="version"/>
106  * <p><b>NOTE</b>: You must specify the required {@link Version}
107  * compatibility when creating QueryParser:
108  * <ul>
109  *    <li> As of 2.9, {@link #setEnablePositionIncrements} is true by
110  *         default.
111  *    <li> As of 3.1, {@link #setAutoGeneratePhraseQueries} is false by
112  *         default.
113  * </ul>
114  */
115 public class QueryParser implements QueryParserConstants {
116
117   private static final int CONJ_NONE   = 0;
118   private static final int CONJ_AND    = 1;
119   private static final int CONJ_OR     = 2;
120
121   private static final int MOD_NONE    = 0;
122   private static final int MOD_NOT     = 10;
123   private static final int MOD_REQ     = 11;
124
125   // make it possible to call setDefaultOperator() without accessing 
126   // the nested class:
127   /** Alternative form of QueryParser.Operator.AND */
128   public static final Operator AND_OPERATOR = Operator.AND;
129   /** Alternative form of QueryParser.Operator.OR */
130   public static final Operator OR_OPERATOR = Operator.OR;
131
132   /** The actual operator that parser uses to combine query terms */
133   private Operator operator = OR_OPERATOR;
134
135   boolean lowercaseExpandedTerms = true;
136   MultiTermQuery.RewriteMethod multiTermRewriteMethod = MultiTermQuery.CONSTANT_SCORE_AUTO_REWRITE_DEFAULT;
137   boolean allowLeadingWildcard = false;
138   boolean enablePositionIncrements = true;
139
140   Analyzer analyzer;
141   String field;
142   int phraseSlop = 0;
143   float fuzzyMinSim = FuzzyQuery.defaultMinSimilarity;
144   int fuzzyPrefixLength = FuzzyQuery.defaultPrefixLength;
145   Locale locale = Locale.getDefault();
146
147   // the default date resolution
148   DateTools.Resolution dateResolution = null;
149   // maps field names to date resolutions
150   Map<String,DateTools.Resolution> fieldToDateResolution = null;
151
152   // The collator to use when determining range inclusion,
153   // for use when constructing RangeQuerys.
154   Collator rangeCollator = null;
155
156   /** @deprecated remove when getFieldQuery is removed */
157   @Deprecated
158   private static final VirtualMethod<QueryParser> getFieldQueryMethod =
159     new VirtualMethod<QueryParser>(QueryParser.class, "getFieldQuery", String.class, String.class);
160   /** @deprecated remove when getFieldQuery is removed */
161   @Deprecated
162   private static final VirtualMethod<QueryParser> getFieldQueryWithQuotedMethod =
163     new VirtualMethod<QueryParser>(QueryParser.class, "getFieldQuery", String.class, String.class, boolean.class);
164   /** @deprecated remove when getFieldQuery is removed */
165   @Deprecated
166   private final boolean hasNewAPI =
167     VirtualMethod.compareImplementationDistance(getClass(),
168         getFieldQueryWithQuotedMethod, getFieldQueryMethod) >= 0; // its ok for both to be overridden
169
170   private boolean autoGeneratePhraseQueries;
171
172   /** The default operator for parsing queries. 
173    * Use {@link QueryParser#setDefaultOperator} to change it.
174    */
175   static public enum Operator { OR, AND }
176
177   /** Constructs a query parser.
178    *  @param matchVersion  Lucene version to match. See <a href="#version">above</a>.
179    *  @param f  the default field for query terms.
180    *  @param a   used to find terms in the query text.
181    */
182   public QueryParser(Version matchVersion, String f, Analyzer a) {
183     this(new FastCharStream(new StringReader("")));
184     analyzer = a;
185     field = f;
186     if (matchVersion.onOrAfter(Version.LUCENE_29)) {
187       enablePositionIncrements = true;
188     } else {
189       enablePositionIncrements = false;
190     }
191     if (matchVersion.onOrAfter(Version.LUCENE_31)) {
192       setAutoGeneratePhraseQueries(false);
193     } else {
194       setAutoGeneratePhraseQueries(true);
195     }
196   }
197
198   /** Parses a query string, returning a {@link org.apache.lucene.search.Query}.
199    *  @param query  the query string to be parsed.
200    *  @throws ParseException if the parsing fails
201    */
202   public Query parse(String query) throws ParseException {
203     ReInit(new FastCharStream(new StringReader(query)));
204     try {
205       // TopLevelQuery is a Query followed by the end-of-input (EOF)
206       Query res = TopLevelQuery(field);
207       return res!=null ? res : newBooleanQuery(false);
208     }
209     catch (ParseException tme) {
210       // rethrow to include the original query:
211       ParseException e = new ParseException("Cannot parse '" +query+ "': " + tme.getMessage());
212       e.initCause(tme);
213       throw e;
214     }
215     catch (TokenMgrError tme) {
216       ParseException e = new ParseException("Cannot parse '" +query+ "': " + tme.getMessage());
217       e.initCause(tme);
218       throw e;
219     }
220     catch (BooleanQuery.TooManyClauses tmc) {
221       ParseException e = new ParseException("Cannot parse '" +query+ "': too many boolean clauses");
222       e.initCause(tmc);
223       throw e;
224     }
225   }
226
227    /**
228    * @return Returns the analyzer.
229    */
230   public Analyzer getAnalyzer() {
231     return analyzer;
232   }
233
234   /**
235    * @return Returns the field.
236    */
237   public String getField() {
238     return field;
239   }
240
241   /**
242    * @see #setAutoGeneratePhraseQueries(boolean)
243    */
244   public final boolean getAutoGeneratePhraseQueries() {
245     return autoGeneratePhraseQueries;
246   }
247
248   /**
249    * Set to true if phrase queries will be automatically generated
250    * when the analyzer returns more than one term from whitespace
251    * delimited text.
252    * NOTE: this behavior may not be suitable for all languages.
253    * <p>
254    * Set to false if phrase queries should only be generated when
255    * surrounded by double quotes.
256    */
257   public final void setAutoGeneratePhraseQueries(boolean value) {
258     if (value == false && !hasNewAPI)
259       throw new IllegalArgumentException("You must implement the new API: getFieldQuery(String,String,boolean)"
260        + " to use setAutoGeneratePhraseQueries(false)");
261     this.autoGeneratePhraseQueries = value;
262   }
263
264    /**
265    * Get the minimal similarity for fuzzy queries.
266    */
267   public float getFuzzyMinSim() {
268       return fuzzyMinSim;
269   }
270
271   /**
272    * Set the minimum similarity for fuzzy queries.
273    * Default is 0.5f.
274    */
275   public void setFuzzyMinSim(float fuzzyMinSim) {
276       this.fuzzyMinSim = fuzzyMinSim;
277   }
278
279    /**
280    * Get the prefix length for fuzzy queries. 
281    * @return Returns the fuzzyPrefixLength.
282    */
283   public int getFuzzyPrefixLength() {
284     return fuzzyPrefixLength;
285   }
286
287   /**
288    * Set the prefix length for fuzzy queries. Default is 0.
289    * @param fuzzyPrefixLength The fuzzyPrefixLength to set.
290    */
291   public void setFuzzyPrefixLength(int fuzzyPrefixLength) {
292     this.fuzzyPrefixLength = fuzzyPrefixLength;
293   }
294
295   /**
296    * Sets the default slop for phrases.  If zero, then exact phrase matches
297    * are required.  Default value is zero.
298    */
299   public void setPhraseSlop(int phraseSlop) {
300     this.phraseSlop = phraseSlop;
301   }
302
303   /**
304    * Gets the default slop for phrases.
305    */
306   public int getPhraseSlop() {
307     return phraseSlop;
308   }
309
310
311   /**
312    * Set to <code>true</code> to allow leading wildcard characters.
313    * <p>
314    * When set, <code>*</code> or <code>?</code> are allowed as 
315    * the first character of a PrefixQuery and WildcardQuery.
316    * Note that this can produce very slow
317    * queries on big indexes. 
318    * <p>
319    * Default: false.
320    */
321   public void setAllowLeadingWildcard(boolean allowLeadingWildcard) {
322     this.allowLeadingWildcard = allowLeadingWildcard;
323   }
324
325   /**
326    * @see #setAllowLeadingWildcard(boolean)
327    */
328   public boolean getAllowLeadingWildcard() {
329     return allowLeadingWildcard;
330   }
331
332   /**
333    * Set to <code>true</code> to enable position increments in result query.
334    * <p>
335    * When set, result phrase and multi-phrase queries will
336    * be aware of position increments.
337    * Useful when e.g. a StopFilter increases the position increment of
338    * the token that follows an omitted token.
339    * <p>
340    * Default: false.
341    */
342   public void setEnablePositionIncrements(boolean enable) {
343     this.enablePositionIncrements = enable;
344   }
345
346   /**
347    * @see #setEnablePositionIncrements(boolean)
348    */
349   public boolean getEnablePositionIncrements() {
350     return enablePositionIncrements;
351   }
352
353   /**
354    * Sets the boolean operator of the QueryParser.
355    * In default mode (<code>OR_OPERATOR</code>) terms without any modifiers
356    * are considered optional: for example <code>capital of Hungary</code> is equal to
357    * <code>capital OR of OR Hungary</code>.<br/>
358    * In <code>AND_OPERATOR</code> mode terms are considered to be in conjunction: the
359    * above mentioned query is parsed as <code>capital AND of AND Hungary</code>
360    */
361   public void setDefaultOperator(Operator op) {
362     this.operator = op;
363   }
364
365
366   /**
367    * Gets implicit operator setting, which will be either AND_OPERATOR
368    * or OR_OPERATOR.
369    */
370   public Operator getDefaultOperator() {
371     return operator;
372   }
373
374
375   /**
376    * Whether terms of wildcard, prefix, fuzzy and range queries are to be automatically
377    * lower-cased or not.  Default is <code>true</code>.
378    */
379   public void setLowercaseExpandedTerms(boolean lowercaseExpandedTerms) {
380     this.lowercaseExpandedTerms = lowercaseExpandedTerms;
381   }
382
383
384   /**
385    * @see #setLowercaseExpandedTerms(boolean)
386    */
387   public boolean getLowercaseExpandedTerms() {
388     return lowercaseExpandedTerms;
389   }
390
391   /**
392    * By default QueryParser uses {@link MultiTermQuery#CONSTANT_SCORE_AUTO_REWRITE_DEFAULT}
393    * when creating a PrefixQuery, WildcardQuery or RangeQuery. This implementation is generally preferable because it 
394    * a) Runs faster b) Does not have the scarcity of terms unduly influence score 
395    * c) avoids any "TooManyBooleanClauses" exception.
396    * However, if your application really needs to use the
397    * old-fashioned BooleanQuery expansion rewriting and the above
398    * points are not relevant then use this to change
399    * the rewrite method.
400    */
401   public void setMultiTermRewriteMethod(MultiTermQuery.RewriteMethod method) {
402     multiTermRewriteMethod = method;
403   }
404
405
406   /**
407    * @see #setMultiTermRewriteMethod
408    */
409   public MultiTermQuery.RewriteMethod getMultiTermRewriteMethod() {
410     return multiTermRewriteMethod;
411   }
412
413   /**
414    * Set locale used by date range parsing.
415    */
416   public void setLocale(Locale locale) {
417     this.locale = locale;
418   }
419
420   /**
421    * Returns current locale, allowing access by subclasses.
422    */
423   public Locale getLocale() {
424     return locale;
425   }
426
427   /**
428    * Sets the default date resolution used by RangeQueries for fields for which no
429    * specific date resolutions has been set. Field specific resolutions can be set
430    * with {@link #setDateResolution(String, DateTools.Resolution)}.
431    *  
432    * @param dateResolution the default date resolution to set
433    */
434   public void setDateResolution(DateTools.Resolution dateResolution) {
435     this.dateResolution = dateResolution;
436   }
437
438   /**
439    * Sets the date resolution used by RangeQueries for a specific field.
440    *  
441    * @param fieldName field for which the date resolution is to be set 
442    * @param dateResolution date resolution to set
443    */
444   public void setDateResolution(String fieldName, DateTools.Resolution dateResolution) {
445     if (fieldName == null) {
446       throw new IllegalArgumentException("Field cannot be null.");
447     }
448
449     if (fieldToDateResolution == null) {
450       // lazily initialize HashMap
451       fieldToDateResolution = new HashMap<String,DateTools.Resolution>();
452     }
453
454     fieldToDateResolution.put(fieldName, dateResolution);
455   }
456
457   /**
458    * Returns the date resolution that is used by RangeQueries for the given field. 
459    * Returns null, if no default or field specific date resolution has been set
460    * for the given field.
461    *
462    */
463   public DateTools.Resolution getDateResolution(String fieldName) {
464     if (fieldName == null) {
465       throw new IllegalArgumentException("Field cannot be null.");
466     }
467
468     if (fieldToDateResolution == null) {
469       // no field specific date resolutions set; return default date resolution instead
470       return this.dateResolution;
471     }
472
473     DateTools.Resolution resolution = fieldToDateResolution.get(fieldName);
474     if (resolution == null) {
475       // no date resolutions set for the given field; return default date resolution instead
476       resolution = this.dateResolution;
477     }
478
479     return resolution;
480   }
481
482   /** 
483    * Sets the collator used to determine index term inclusion in ranges
484    * for RangeQuerys.
485    * <p/>
486    * <strong>WARNING:</strong> Setting the rangeCollator to a non-null
487    * collator using this method will cause every single index Term in the
488    * Field referenced by lowerTerm and/or upperTerm to be examined.
489    * Depending on the number of index Terms in this Field, the operation could
490    * be very slow.
491    *
492    *  @param rc  the collator to use when constructing RangeQuerys
493    */
494   public void setRangeCollator(Collator rc) {
495     rangeCollator = rc;
496   }
497
498   /**
499    * @return the collator used to determine index term inclusion in ranges
500    * for RangeQuerys.
501    */
502   public Collator getRangeCollator() {
503     return rangeCollator;
504   }
505
506   protected void addClause(List<BooleanClause> clauses, int conj, int mods, Query q) {
507     boolean required, prohibited;
508
509     // If this term is introduced by AND, make the preceding term required,
510     // unless it's already prohibited
511     if (clauses.size() > 0 && conj == CONJ_AND) {
512       BooleanClause c = clauses.get(clauses.size()-1);
513       if (!c.isProhibited())
514         c.setOccur(BooleanClause.Occur.MUST);
515     }
516
517     if (clauses.size() > 0 && operator == AND_OPERATOR && conj == CONJ_OR) {
518       // If this term is introduced by OR, make the preceding term optional,
519       // unless it's prohibited (that means we leave -a OR b but +a OR b-->a OR b)
520       // notice if the input is a OR b, first term is parsed as required; without
521       // this modification a OR b would parsed as +a OR b
522       BooleanClause c = clauses.get(clauses.size()-1);
523       if (!c.isProhibited())
524         c.setOccur(BooleanClause.Occur.SHOULD);
525     }
526
527     // We might have been passed a null query; the term might have been
528     // filtered away by the analyzer.
529     if (q == null)
530       return;
531
532     if (operator == OR_OPERATOR) {
533       // We set REQUIRED if we're introduced by AND or +; PROHIBITED if
534       // introduced by NOT or -; make sure not to set both.
535       prohibited = (mods == MOD_NOT);
536       required = (mods == MOD_REQ);
537       if (conj == CONJ_AND && !prohibited) {
538         required = true;
539       }
540     } else {
541       // We set PROHIBITED if we're introduced by NOT or -; We set REQUIRED
542       // if not PROHIBITED and not introduced by OR
543       prohibited = (mods == MOD_NOT);
544       required   = (!prohibited && conj != CONJ_OR);
545     }
546     if (required && !prohibited)
547       clauses.add(newBooleanClause(q, BooleanClause.Occur.MUST));
548     else if (!required && !prohibited)
549       clauses.add(newBooleanClause(q, BooleanClause.Occur.SHOULD));
550     else if (!required && prohibited)
551       clauses.add(newBooleanClause(q, BooleanClause.Occur.MUST_NOT));
552     else
553       throw new RuntimeException("Clause cannot be both required and prohibited");
554   }
555
556   /**
557    * @deprecated Use {@link #getFieldQuery(String,String,boolean)} instead.
558    */
559   @Deprecated
560   protected Query getFieldQuery(String field, String queryText) throws ParseException {
561     // treat the text as if it was quoted, to drive phrase logic with old versions.
562     return getFieldQuery(field, queryText, true);
563   }
564
565   /**
566    * @exception ParseException throw in overridden method to disallow
567    */
568   protected Query getFieldQuery(String field, String queryText, boolean quoted)  throws ParseException {
569     // Use the analyzer to get all the tokens, and then build a TermQuery,
570     // PhraseQuery, or nothing based on the term count
571
572     TokenStream source;
573     try {
574       source = analyzer.reusableTokenStream(field, new StringReader(queryText));
575       source.reset();
576     } catch (IOException e) {
577       source = analyzer.tokenStream(field, new StringReader(queryText));
578     }
579     CachingTokenFilter buffer = new CachingTokenFilter(source);
580     CharTermAttribute termAtt = null;
581     PositionIncrementAttribute posIncrAtt = null;
582     int numTokens = 0;
583
584     boolean success = false;
585     try {
586       buffer.reset();
587       success = true;
588     } catch (IOException e) {
589       // success==false if we hit an exception
590     }
591     if (success) {
592       if (buffer.hasAttribute(CharTermAttribute.class)) {
593         termAtt = buffer.getAttribute(CharTermAttribute.class);
594       }
595       if (buffer.hasAttribute(PositionIncrementAttribute.class)) {
596         posIncrAtt = buffer.getAttribute(PositionIncrementAttribute.class);
597       }
598     }
599
600     int positionCount = 0;
601     boolean severalTokensAtSamePosition = false;
602
603     boolean hasMoreTokens = false;
604     if (termAtt != null) {
605       try {
606         hasMoreTokens = buffer.incrementToken();
607         while (hasMoreTokens) {
608           numTokens++;
609           int positionIncrement = (posIncrAtt != null) ? posIncrAtt.getPositionIncrement() : 1;
610           if (positionIncrement != 0) {
611             positionCount += positionIncrement;
612           } else {
613             severalTokensAtSamePosition = true;
614           }
615           hasMoreTokens = buffer.incrementToken();
616         }
617       } catch (IOException e) {
618         // ignore
619       }
620     }
621     try {
622       // rewind the buffer stream
623       buffer.reset();
624
625       // close original stream - all tokens buffered
626       source.close();
627     }
628     catch (IOException e) {
629       // ignore
630     }
631
632     if (numTokens == 0)
633       return null;
634     else if (numTokens == 1) {
635       String term = null;
636       try {
637         boolean hasNext = buffer.incrementToken();
638         assert hasNext == true;
639         term = termAtt.toString();
640       } catch (IOException e) {
641         // safe to ignore, because we know the number of tokens
642       }
643       return newTermQuery(new Term(field, term));
644     } else {
645       if (severalTokensAtSamePosition || (!quoted && !autoGeneratePhraseQueries)) {
646         if (positionCount == 1 || (!quoted && !autoGeneratePhraseQueries)) {
647           // no phrase query:
648           BooleanQuery q = newBooleanQuery(positionCount == 1);
649
650           BooleanClause.Occur occur = positionCount > 1 && operator == AND_OPERATOR ?
651             BooleanClause.Occur.MUST : BooleanClause.Occur.SHOULD;
652
653           for (int i = 0; i < numTokens; i++) {
654             String term = null;
655             try {
656               boolean hasNext = buffer.incrementToken();
657               assert hasNext == true;
658               term = termAtt.toString();
659             } catch (IOException e) {
660               // safe to ignore, because we know the number of tokens
661             }
662
663             Query currentQuery = newTermQuery(
664                 new Term(field, term));
665             q.add(currentQuery, occur);
666           }
667           return q;
668         }
669         else {
670           // phrase query:
671           MultiPhraseQuery mpq = newMultiPhraseQuery();
672           mpq.setSlop(phraseSlop);
673           List<Term> multiTerms = new ArrayList<Term>();
674           int position = -1;
675           for (int i = 0; i < numTokens; i++) {
676             String term = null;
677             int positionIncrement = 1;
678             try {
679               boolean hasNext = buffer.incrementToken();
680               assert hasNext == true;
681               term = termAtt.toString();
682               if (posIncrAtt != null) {
683                 positionIncrement = posIncrAtt.getPositionIncrement();
684               }
685             } catch (IOException e) {
686               // safe to ignore, because we know the number of tokens
687             }
688
689             if (positionIncrement > 0 && multiTerms.size() > 0) {
690               if (enablePositionIncrements) {
691                 mpq.add(multiTerms.toArray(new Term[0]),position);
692               } else {
693                 mpq.add(multiTerms.toArray(new Term[0]));
694               }
695               multiTerms.clear();
696             }
697             position += positionIncrement;
698             multiTerms.add(new Term(field, term));
699           }
700           if (enablePositionIncrements) {
701             mpq.add(multiTerms.toArray(new Term[0]),position);
702           } else {
703             mpq.add(multiTerms.toArray(new Term[0]));
704           }
705           return mpq;
706         }
707       }
708       else {
709         PhraseQuery pq = newPhraseQuery();
710         pq.setSlop(phraseSlop);
711         int position = -1;
712
713
714         for (int i = 0; i < numTokens; i++) {
715           String term = null;
716           int positionIncrement = 1;
717
718           try {
719             boolean hasNext = buffer.incrementToken();
720             assert hasNext == true;
721             term = termAtt.toString();
722             if (posIncrAtt != null) {
723               positionIncrement = posIncrAtt.getPositionIncrement();
724             }
725           } catch (IOException e) {
726             // safe to ignore, because we know the number of tokens
727           }
728
729           if (enablePositionIncrements) {
730             position += positionIncrement;
731             pq.add(new Term(field, term),position);
732           } else {
733             pq.add(new Term(field, term));
734           }
735         }
736         return pq;
737       }
738     }
739   }
740
741
742
743   /**
744    * Base implementation delegates to {@link #getFieldQuery(String,String,boolean)}.
745    * This method may be overridden, for example, to return
746    * a SpanNearQuery instead of a PhraseQuery.
747    *
748    * @exception ParseException throw in overridden method to disallow
749    */
750   protected Query getFieldQuery(String field, String queryText, int slop)
751         throws ParseException {
752     Query query = hasNewAPI ? getFieldQuery(field, queryText, true) : getFieldQuery(field, queryText);
753
754     if (query instanceof PhraseQuery) {
755       ((PhraseQuery) query).setSlop(slop);
756     }
757     if (query instanceof MultiPhraseQuery) {
758       ((MultiPhraseQuery) query).setSlop(slop);
759     }
760
761     return query;
762   }
763
764
765   /**
766    * @exception ParseException throw in overridden method to disallow
767    */
768   protected Query getRangeQuery(String field,
769                                 String part1,
770                                 String part2,
771                                 boolean inclusive) throws ParseException
772   {
773     if (lowercaseExpandedTerms) {
774       part1 = part1.toLowerCase();
775       part2 = part2.toLowerCase();
776     }
777     try {
778       DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
779       df.setLenient(true);
780       Date d1 = df.parse(part1);
781       Date d2 = df.parse(part2);
782       if (inclusive) {
783         // The user can only specify the date, not the time, so make sure
784         // the time is set to the latest possible time of that date to really
785         // include all documents:
786         Calendar cal = Calendar.getInstance(locale);
787         cal.setTime(d2);
788         cal.set(Calendar.HOUR_OF_DAY, 23);
789         cal.set(Calendar.MINUTE, 59);
790         cal.set(Calendar.SECOND, 59);
791         cal.set(Calendar.MILLISECOND, 999);
792         d2 = cal.getTime();
793       }
794       DateTools.Resolution resolution = getDateResolution(field);
795       if (resolution == null) {
796         // no default or field specific date resolution has been set,
797         // use deprecated DateField to maintain compatibility with
798         // pre-1.9 Lucene versions.
799         part1 = DateField.dateToString(d1);
800         part2 = DateField.dateToString(d2);
801       } else {
802         part1 = DateTools.dateToString(d1, resolution);
803         part2 = DateTools.dateToString(d2, resolution);
804       }
805     }
806     catch (Exception e) { }
807
808     return newRangeQuery(field, part1, part2, inclusive);
809   }
810
811  /**
812   * Builds a new BooleanQuery instance
813   * @param disableCoord disable coord
814   * @return new BooleanQuery instance
815   */
816   protected BooleanQuery newBooleanQuery(boolean disableCoord) {
817     return new BooleanQuery(disableCoord);
818   }
819
820  /**
821   * Builds a new BooleanClause instance
822   * @param q sub query
823   * @param occur how this clause should occur when matching documents
824   * @return new BooleanClause instance
825   */
826   protected BooleanClause newBooleanClause(Query q, BooleanClause.Occur occur) {
827     return new BooleanClause(q, occur);
828   }
829
830   /**
831    * Builds a new TermQuery instance
832    * @param term term
833    * @return new TermQuery instance
834    */
835   protected Query newTermQuery(Term term){
836     return new TermQuery(term);
837   }
838
839   /**
840    * Builds a new PhraseQuery instance
841    * @return new PhraseQuery instance
842    */
843   protected PhraseQuery newPhraseQuery(){
844     return new PhraseQuery();
845   }
846
847   /**
848    * Builds a new MultiPhraseQuery instance
849    * @return new MultiPhraseQuery instance
850    */
851   protected MultiPhraseQuery newMultiPhraseQuery(){
852     return new MultiPhraseQuery();
853   }
854
855   /**
856    * Builds a new PrefixQuery instance
857    * @param prefix Prefix term
858    * @return new PrefixQuery instance
859    */
860   protected Query newPrefixQuery(Term prefix){
861     PrefixQuery query = new PrefixQuery(prefix);
862     query.setRewriteMethod(multiTermRewriteMethod);
863     return query;
864   }
865
866   /**
867    * Builds a new FuzzyQuery instance
868    * @param term Term
869    * @param minimumSimilarity minimum similarity
870    * @param prefixLength prefix length
871    * @return new FuzzyQuery Instance
872    */
873   protected Query newFuzzyQuery(Term term, float minimumSimilarity, int prefixLength) {
874     // FuzzyQuery doesn't yet allow constant score rewrite
875     return new FuzzyQuery(term,minimumSimilarity,prefixLength);
876   }
877
878   /**
879    * Builds a new TermRangeQuery instance
880    * @param field Field
881    * @param part1 min
882    * @param part2 max
883    * @param inclusive true if range is inclusive
884    * @return new TermRangeQuery instance
885    */
886   protected Query newRangeQuery(String field, String part1, String part2, boolean inclusive) {
887     final TermRangeQuery query = new TermRangeQuery(field, part1, part2, inclusive, inclusive, rangeCollator);
888     query.setRewriteMethod(multiTermRewriteMethod);
889     return query;
890   }
891
892   /**
893    * Builds a new MatchAllDocsQuery instance
894    * @return new MatchAllDocsQuery instance
895    */
896   protected Query newMatchAllDocsQuery() {
897     return new MatchAllDocsQuery();
898   }
899
900   /**
901    * Builds a new WildcardQuery instance
902    * @param t wildcard term
903    * @return new WildcardQuery instance
904    */
905   protected Query newWildcardQuery(Term t) {
906     WildcardQuery query = new WildcardQuery(t);
907     query.setRewriteMethod(multiTermRewriteMethod);
908     return query;
909   }
910
911   /**
912    * Factory method for generating query, given a set of clauses.
913    * By default creates a boolean query composed of clauses passed in.
914    *
915    * Can be overridden by extending classes, to modify query being
916    * returned.
917    *
918    * @param clauses List that contains {@link BooleanClause} instances
919    *    to join.
920    *
921    * @return Resulting {@link Query} object.
922    * @exception ParseException throw in overridden method to disallow
923    */
924   protected Query getBooleanQuery(List<BooleanClause> clauses) throws ParseException {
925     return getBooleanQuery(clauses, false);
926   }
927
928   /**
929    * Factory method for generating query, given a set of clauses.
930    * By default creates a boolean query composed of clauses passed in.
931    *
932    * Can be overridden by extending classes, to modify query being
933    * returned.
934    *
935    * @param clauses List that contains {@link BooleanClause} instances
936    *    to join.
937    * @param disableCoord true if coord scoring should be disabled.
938    *
939    * @return Resulting {@link Query} object.
940    * @exception ParseException throw in overridden method to disallow
941    */
942   protected Query getBooleanQuery(List<BooleanClause> clauses, boolean disableCoord)
943     throws ParseException
944   {
945     if (clauses.size()==0) {
946       return null; // all clause words were filtered away by the analyzer.
947     }
948     BooleanQuery query = newBooleanQuery(disableCoord);
949     for(final BooleanClause clause: clauses) {
950       query.add(clause);
951     }
952     return query;
953   }
954
955   /**
956    * Factory method for generating a query. Called when parser
957    * parses an input term token that contains one or more wildcard
958    * characters (? and *), but is not a prefix term token (one
959    * that has just a single * character at the end)
960    *<p>
961    * Depending on settings, prefix term may be lower-cased
962    * automatically. It will not go through the default Analyzer,
963    * however, since normal Analyzers are unlikely to work properly
964    * with wildcard templates.
965    *<p>
966    * Can be overridden by extending classes, to provide custom handling for
967    * wildcard queries, which may be necessary due to missing analyzer calls.
968    *
969    * @param field Name of the field query will use.
970    * @param termStr Term token that contains one or more wild card
971    *   characters (? or *), but is not simple prefix term
972    *
973    * @return Resulting {@link Query} built for the term
974    * @exception ParseException throw in overridden method to disallow
975    */
976   protected Query getWildcardQuery(String field, String termStr) throws ParseException
977   {
978     if ("*".equals(field)) {
979       if ("*".equals(termStr)) return newMatchAllDocsQuery();
980     }
981     if (!allowLeadingWildcard && (termStr.startsWith("*") || termStr.startsWith("?")))
982       throw new ParseException("'*' or '?' not allowed as first character in WildcardQuery");
983     if (lowercaseExpandedTerms) {
984       termStr = termStr.toLowerCase();
985     }
986     Term t = new Term(field, termStr);
987     return newWildcardQuery(t);
988   }
989
990   /**
991    * Factory method for generating a query (similar to
992    * {@link #getWildcardQuery}). Called when parser parses an input term
993    * token that uses prefix notation; that is, contains a single '*' wildcard
994    * character as its last character. Since this is a special case
995    * of generic wildcard term, and such a query can be optimized easily,
996    * this usually results in a different query object.
997    *<p>
998    * Depending on settings, a prefix term may be lower-cased
999    * automatically. It will not go through the default Analyzer,
1000    * however, since normal Analyzers are unlikely to work properly
1001    * with wildcard templates.
1002    *<p>
1003    * Can be overridden by extending classes, to provide custom handling for
1004    * wild card queries, which may be necessary due to missing analyzer calls.
1005    *
1006    * @param field Name of the field query will use.
1007    * @param termStr Term token to use for building term for the query
1008    *    (<b>without</b> trailing '*' character!)
1009    *
1010    * @return Resulting {@link Query} built for the term
1011    * @exception ParseException throw in overridden method to disallow
1012    */
1013   protected Query getPrefixQuery(String field, String termStr) throws ParseException
1014   {
1015     if (!allowLeadingWildcard && termStr.startsWith("*"))
1016       throw new ParseException("'*' not allowed as first character in PrefixQuery");
1017     if (lowercaseExpandedTerms) {
1018       termStr = termStr.toLowerCase();
1019     }
1020     Term t = new Term(field, termStr);
1021     return newPrefixQuery(t);
1022   }
1023
1024    /**
1025    * Factory method for generating a query (similar to
1026    * {@link #getWildcardQuery}). Called when parser parses
1027    * an input term token that has the fuzzy suffix (~) appended.
1028    *
1029    * @param field Name of the field query will use.
1030    * @param termStr Term token to use for building term for the query
1031    *
1032    * @return Resulting {@link Query} built for the term
1033    * @exception ParseException throw in overridden method to disallow
1034    */
1035   protected Query getFuzzyQuery(String field, String termStr, float minSimilarity) throws ParseException
1036   {
1037     if (lowercaseExpandedTerms) {
1038       termStr = termStr.toLowerCase();
1039     }
1040     Term t = new Term(field, termStr);
1041     return newFuzzyQuery(t, minSimilarity, fuzzyPrefixLength);
1042   }
1043
1044   /**
1045    * Returns a String where the escape char has been
1046    * removed, or kept only once if there was a double escape.
1047    * 
1048    * Supports escaped unicode characters, e. g. translates
1049    * <code>\\u0041</code> to <code>A</code>.
1050    * 
1051    */
1052   private String discardEscapeChar(String input) throws ParseException {
1053     // Create char array to hold unescaped char sequence
1054     char[] output = new char[input.length()];
1055
1056     // The length of the output can be less than the input
1057     // due to discarded escape chars. This variable holds
1058     // the actual length of the output
1059     int length = 0;
1060
1061     // We remember whether the last processed character was 
1062     // an escape character
1063     boolean lastCharWasEscapeChar = false;
1064
1065     // The multiplier the current unicode digit must be multiplied with.
1066     // E. g. the first digit must be multiplied with 16^3, the second with 16^2...
1067     int codePointMultiplier = 0;
1068
1069     // Used to calculate the codepoint of the escaped unicode character
1070     int codePoint = 0;
1071
1072     for (int i = 0; i < input.length(); i++) {
1073       char curChar = input.charAt(i);
1074       if (codePointMultiplier > 0) {
1075         codePoint += hexToInt(curChar) * codePointMultiplier;
1076         codePointMultiplier >>>= 4;
1077         if (codePointMultiplier == 0) {
1078           output[length++] = (char)codePoint;
1079           codePoint = 0;
1080         }
1081       } else if (lastCharWasEscapeChar) {
1082         if (curChar == 'u') {
1083           // found an escaped unicode character
1084           codePointMultiplier = 16 * 16 * 16;
1085         } else {
1086           // this character was escaped
1087           output[length] = curChar;
1088           length++;
1089         }
1090         lastCharWasEscapeChar = false;
1091       } else {
1092         if (curChar == '\\') {
1093           lastCharWasEscapeChar = true;
1094         } else {
1095           output[length] = curChar;
1096           length++;
1097         }
1098       }
1099     }
1100
1101     if (codePointMultiplier > 0) {
1102       throw new ParseException("Truncated unicode escape sequence.");
1103     }
1104
1105     if (lastCharWasEscapeChar) {
1106       throw new ParseException("Term can not end with escape character.");
1107     }
1108
1109     return new String(output, 0, length);
1110   }
1111
1112   /** Returns the numeric value of the hexadecimal character */
1113   private static final int hexToInt(char c) throws ParseException {
1114     if ('0' <= c && c <= '9') {
1115       return c - '0';
1116     } else if ('a' <= c && c <= 'f'){
1117       return c - 'a' + 10;
1118     } else if ('A' <= c && c <= 'F') {
1119       return c - 'A' + 10;
1120     } else {
1121       throw new ParseException("None-hex character in unicode escape sequence: " + c);
1122     }
1123   }
1124
1125   /**
1126    * Returns a String where those characters that QueryParser
1127    * expects to be escaped are escaped by a preceding <code>\</code>.
1128    */
1129   public static String escape(String s) {
1130     StringBuilder sb = new StringBuilder();
1131     for (int i = 0; i < s.length(); i++) {
1132       char c = s.charAt(i);
1133       // These characters are part of the query syntax and must be escaped
1134       if (c == '\\' || c == '+' || c == '-' || c == '!' || c == '(' || c == ')' || c == ':'
1135         || c == '^' || c == '[' || c == ']' || c == '\"' || c == '{' || c == '}' || c == '~'
1136         || c == '*' || c == '?' || c == '|' || c == '&') {
1137         sb.append('\\');
1138       }
1139       sb.append(c);
1140     }
1141     return sb.toString();
1142   }
1143
1144   /**
1145    * Command line tool to test QueryParser, using {@link org.apache.lucene.analysis.SimpleAnalyzer}.
1146    * Usage:<br>
1147    * <code>java org.apache.lucene.queryParser.QueryParser &lt;input&gt;</code>
1148    */
1149   public static void main(String[] args) throws Exception {
1150     if (args.length == 0) {
1151       System.out.println("Usage: java org.apache.lucene.queryParser.QueryParser <input>");
1152       System.exit(0);
1153     }
1154     QueryParser qp = new QueryParser(Version.LUCENE_CURRENT, "field",
1155                            new org.apache.lucene.analysis.SimpleAnalyzer());
1156     Query q = qp.parse(args[0]);
1157     System.out.println(q.toString("field"));
1158   }
1159
1160 // *   Query  ::= ( Clause )*
1161 // *   Clause ::= ["+", "-"] [<TERM> ":"] ( <TERM> | "(" Query ")" )
1162   final public int Conjunction() throws ParseException {
1163   int ret = CONJ_NONE;
1164     switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1165     case AND:
1166     case OR:
1167       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1168       case AND:
1169         jj_consume_token(AND);
1170             ret = CONJ_AND;
1171         break;
1172       case OR:
1173         jj_consume_token(OR);
1174               ret = CONJ_OR;
1175         break;
1176       default:
1177         jj_la1[0] = jj_gen;
1178         jj_consume_token(-1);
1179         throw new ParseException();
1180       }
1181       break;
1182     default:
1183       jj_la1[1] = jj_gen;
1184       ;
1185     }
1186     {if (true) return ret;}
1187     throw new Error("Missing return statement in function");
1188   }
1189
1190   final public int Modifiers() throws ParseException {
1191   int ret = MOD_NONE;
1192     switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1193     case NOT:
1194     case PLUS:
1195     case MINUS:
1196       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1197       case PLUS:
1198         jj_consume_token(PLUS);
1199               ret = MOD_REQ;
1200         break;
1201       case MINUS:
1202         jj_consume_token(MINUS);
1203                  ret = MOD_NOT;
1204         break;
1205       case NOT:
1206         jj_consume_token(NOT);
1207                ret = MOD_NOT;
1208         break;
1209       default:
1210         jj_la1[2] = jj_gen;
1211         jj_consume_token(-1);
1212         throw new ParseException();
1213       }
1214       break;
1215     default:
1216       jj_la1[3] = jj_gen;
1217       ;
1218     }
1219     {if (true) return ret;}
1220     throw new Error("Missing return statement in function");
1221   }
1222
1223 // This makes sure that there is no garbage after the query string
1224   final public Query TopLevelQuery(String field) throws ParseException {
1225         Query q;
1226     q = Query(field);
1227     jj_consume_token(0);
1228                 {if (true) return q;}
1229     throw new Error("Missing return statement in function");
1230   }
1231
1232   final public Query Query(String field) throws ParseException {
1233   List<BooleanClause> clauses = new ArrayList<BooleanClause>();
1234   Query q, firstQuery=null;
1235   int conj, mods;
1236     mods = Modifiers();
1237     q = Clause(field);
1238     addClause(clauses, CONJ_NONE, mods, q);
1239     if (mods == MOD_NONE)
1240         firstQuery=q;
1241     label_1:
1242     while (true) {
1243       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1244       case AND:
1245       case OR:
1246       case NOT:
1247       case PLUS:
1248       case MINUS:
1249       case LPAREN:
1250       case STAR:
1251       case QUOTED:
1252       case TERM:
1253       case PREFIXTERM:
1254       case WILDTERM:
1255       case RANGEIN_START:
1256       case RANGEEX_START:
1257       case NUMBER:
1258         ;
1259         break;
1260       default:
1261         jj_la1[4] = jj_gen;
1262         break label_1;
1263       }
1264       conj = Conjunction();
1265       mods = Modifiers();
1266       q = Clause(field);
1267       addClause(clauses, conj, mods, q);
1268     }
1269       if (clauses.size() == 1 && firstQuery != null)
1270         {if (true) return firstQuery;}
1271       else {
1272   {if (true) return getBooleanQuery(clauses);}
1273       }
1274     throw new Error("Missing return statement in function");
1275   }
1276
1277   final public Query Clause(String field) throws ParseException {
1278   Query q;
1279   Token fieldToken=null, boost=null;
1280     if (jj_2_1(2)) {
1281       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1282       case TERM:
1283         fieldToken = jj_consume_token(TERM);
1284         jj_consume_token(COLON);
1285                                field=discardEscapeChar(fieldToken.image);
1286         break;
1287       case STAR:
1288         jj_consume_token(STAR);
1289         jj_consume_token(COLON);
1290                       field="*";
1291         break;
1292       default:
1293         jj_la1[5] = jj_gen;
1294         jj_consume_token(-1);
1295         throw new ParseException();
1296       }
1297     } else {
1298       ;
1299     }
1300     switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1301     case STAR:
1302     case QUOTED:
1303     case TERM:
1304     case PREFIXTERM:
1305     case WILDTERM:
1306     case RANGEIN_START:
1307     case RANGEEX_START:
1308     case NUMBER:
1309       q = Term(field);
1310       break;
1311     case LPAREN:
1312       jj_consume_token(LPAREN);
1313       q = Query(field);
1314       jj_consume_token(RPAREN);
1315       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1316       case CARAT:
1317         jj_consume_token(CARAT);
1318         boost = jj_consume_token(NUMBER);
1319         break;
1320       default:
1321         jj_la1[6] = jj_gen;
1322         ;
1323       }
1324       break;
1325     default:
1326       jj_la1[7] = jj_gen;
1327       jj_consume_token(-1);
1328       throw new ParseException();
1329     }
1330       if (boost != null) {
1331         float f = (float)1.0;
1332   try {
1333     f = Float.valueOf(boost.image).floatValue();
1334           q.setBoost(f);
1335   } catch (Exception ignored) { }
1336       }
1337       {if (true) return q;}
1338     throw new Error("Missing return statement in function");
1339   }
1340
1341   final public Query Term(String field) throws ParseException {
1342   Token term, boost=null, fuzzySlop=null, goop1, goop2;
1343   boolean prefix = false;
1344   boolean wildcard = false;
1345   boolean fuzzy = false;
1346   Query q;
1347     switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1348     case STAR:
1349     case TERM:
1350     case PREFIXTERM:
1351     case WILDTERM:
1352     case NUMBER:
1353       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1354       case TERM:
1355         term = jj_consume_token(TERM);
1356         break;
1357       case STAR:
1358         term = jj_consume_token(STAR);
1359                        wildcard=true;
1360         break;
1361       case PREFIXTERM:
1362         term = jj_consume_token(PREFIXTERM);
1363                              prefix=true;
1364         break;
1365       case WILDTERM:
1366         term = jj_consume_token(WILDTERM);
1367                            wildcard=true;
1368         break;
1369       case NUMBER:
1370         term = jj_consume_token(NUMBER);
1371         break;
1372       default:
1373         jj_la1[8] = jj_gen;
1374         jj_consume_token(-1);
1375         throw new ParseException();
1376       }
1377       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1378       case FUZZY_SLOP:
1379         fuzzySlop = jj_consume_token(FUZZY_SLOP);
1380                                 fuzzy=true;
1381         break;
1382       default:
1383         jj_la1[9] = jj_gen;
1384         ;
1385       }
1386       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1387       case CARAT:
1388         jj_consume_token(CARAT);
1389         boost = jj_consume_token(NUMBER);
1390         switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1391         case FUZZY_SLOP:
1392           fuzzySlop = jj_consume_token(FUZZY_SLOP);
1393                                                          fuzzy=true;
1394           break;
1395         default:
1396           jj_la1[10] = jj_gen;
1397           ;
1398         }
1399         break;
1400       default:
1401         jj_la1[11] = jj_gen;
1402         ;
1403       }
1404        String termImage=discardEscapeChar(term.image);
1405        if (wildcard) {
1406        q = getWildcardQuery(field, termImage);
1407        } else if (prefix) {
1408          q = getPrefixQuery(field,
1409            discardEscapeChar(term.image.substring
1410           (0, term.image.length()-1)));
1411        } else if (fuzzy) {
1412           float fms = fuzzyMinSim;
1413           try {
1414             fms = Float.valueOf(fuzzySlop.image.substring(1)).floatValue();
1415           } catch (Exception ignored) { }
1416          if(fms < 0.0f || fms > 1.0f){
1417            {if (true) throw new ParseException("Minimum similarity for a FuzzyQuery has to be between 0.0f and 1.0f !");}
1418          }
1419          q = getFuzzyQuery(field, termImage,fms);
1420        } else {
1421          q = hasNewAPI ? getFieldQuery(field, termImage, false) : getFieldQuery(field, termImage);
1422        }
1423       break;
1424     case RANGEIN_START:
1425       jj_consume_token(RANGEIN_START);
1426       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1427       case RANGEIN_GOOP:
1428         goop1 = jj_consume_token(RANGEIN_GOOP);
1429         break;
1430       case RANGEIN_QUOTED:
1431         goop1 = jj_consume_token(RANGEIN_QUOTED);
1432         break;
1433       default:
1434         jj_la1[12] = jj_gen;
1435         jj_consume_token(-1);
1436         throw new ParseException();
1437       }
1438       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1439       case RANGEIN_TO:
1440         jj_consume_token(RANGEIN_TO);
1441         break;
1442       default:
1443         jj_la1[13] = jj_gen;
1444         ;
1445       }
1446       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1447       case RANGEIN_GOOP:
1448         goop2 = jj_consume_token(RANGEIN_GOOP);
1449         break;
1450       case RANGEIN_QUOTED:
1451         goop2 = jj_consume_token(RANGEIN_QUOTED);
1452         break;
1453       default:
1454         jj_la1[14] = jj_gen;
1455         jj_consume_token(-1);
1456         throw new ParseException();
1457       }
1458       jj_consume_token(RANGEIN_END);
1459       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1460       case CARAT:
1461         jj_consume_token(CARAT);
1462         boost = jj_consume_token(NUMBER);
1463         break;
1464       default:
1465         jj_la1[15] = jj_gen;
1466         ;
1467       }
1468           if (goop1.kind == RANGEIN_QUOTED) {
1469             goop1.image = goop1.image.substring(1, goop1.image.length()-1);
1470           }
1471           if (goop2.kind == RANGEIN_QUOTED) {
1472             goop2.image = goop2.image.substring(1, goop2.image.length()-1);
1473           }
1474           q = getRangeQuery(field, discardEscapeChar(goop1.image), discardEscapeChar(goop2.image), true);
1475       break;
1476     case RANGEEX_START:
1477       jj_consume_token(RANGEEX_START);
1478       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1479       case RANGEEX_GOOP:
1480         goop1 = jj_consume_token(RANGEEX_GOOP);
1481         break;
1482       case RANGEEX_QUOTED:
1483         goop1 = jj_consume_token(RANGEEX_QUOTED);
1484         break;
1485       default:
1486         jj_la1[16] = jj_gen;
1487         jj_consume_token(-1);
1488         throw new ParseException();
1489       }
1490       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1491       case RANGEEX_TO:
1492         jj_consume_token(RANGEEX_TO);
1493         break;
1494       default:
1495         jj_la1[17] = jj_gen;
1496         ;
1497       }
1498       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1499       case RANGEEX_GOOP:
1500         goop2 = jj_consume_token(RANGEEX_GOOP);
1501         break;
1502       case RANGEEX_QUOTED:
1503         goop2 = jj_consume_token(RANGEEX_QUOTED);
1504         break;
1505       default:
1506         jj_la1[18] = jj_gen;
1507         jj_consume_token(-1);
1508         throw new ParseException();
1509       }
1510       jj_consume_token(RANGEEX_END);
1511       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1512       case CARAT:
1513         jj_consume_token(CARAT);
1514         boost = jj_consume_token(NUMBER);
1515         break;
1516       default:
1517         jj_la1[19] = jj_gen;
1518         ;
1519       }
1520           if (goop1.kind == RANGEEX_QUOTED) {
1521             goop1.image = goop1.image.substring(1, goop1.image.length()-1);
1522           }
1523           if (goop2.kind == RANGEEX_QUOTED) {
1524             goop2.image = goop2.image.substring(1, goop2.image.length()-1);
1525           }
1526
1527           q = getRangeQuery(field, discardEscapeChar(goop1.image), discardEscapeChar(goop2.image), false);
1528       break;
1529     case QUOTED:
1530       term = jj_consume_token(QUOTED);
1531       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1532       case FUZZY_SLOP:
1533         fuzzySlop = jj_consume_token(FUZZY_SLOP);
1534         break;
1535       default:
1536         jj_la1[20] = jj_gen;
1537         ;
1538       }
1539       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1540       case CARAT:
1541         jj_consume_token(CARAT);
1542         boost = jj_consume_token(NUMBER);
1543         break;
1544       default:
1545         jj_la1[21] = jj_gen;
1546         ;
1547       }
1548          int s = phraseSlop;
1549
1550          if (fuzzySlop != null) {
1551            try {
1552              s = Float.valueOf(fuzzySlop.image.substring(1)).intValue();
1553            }
1554            catch (Exception ignored) { }
1555          }
1556          q = getFieldQuery(field, discardEscapeChar(term.image.substring(1, term.image.length()-1)), s);
1557       break;
1558     default:
1559       jj_la1[22] = jj_gen;
1560       jj_consume_token(-1);
1561       throw new ParseException();
1562     }
1563     if (boost != null) {
1564       float f = (float) 1.0;
1565       try {
1566         f = Float.valueOf(boost.image).floatValue();
1567       }
1568       catch (Exception ignored) {
1569     /* Should this be handled somehow? (defaults to "no boost", if
1570      * boost number is invalid)
1571      */
1572       }
1573
1574       // avoid boosting null queries, such as those caused by stop words
1575       if (q != null) {
1576         q.setBoost(f);
1577       }
1578     }
1579     {if (true) return q;}
1580     throw new Error("Missing return statement in function");
1581   }
1582
1583   private boolean jj_2_1(int xla) {
1584     jj_la = xla; jj_lastpos = jj_scanpos = token;
1585     try { return !jj_3_1(); }
1586     catch(LookaheadSuccess ls) { return true; }
1587     finally { jj_save(0, xla); }
1588   }
1589
1590   private boolean jj_3R_3() {
1591     if (jj_scan_token(STAR)) return true;
1592     if (jj_scan_token(COLON)) return true;
1593     return false;
1594   }
1595
1596   private boolean jj_3R_2() {
1597     if (jj_scan_token(TERM)) return true;
1598     if (jj_scan_token(COLON)) return true;
1599     return false;
1600   }
1601
1602   private boolean jj_3_1() {
1603     Token xsp;
1604     xsp = jj_scanpos;
1605     if (jj_3R_2()) {
1606     jj_scanpos = xsp;
1607     if (jj_3R_3()) return true;
1608     }
1609     return false;
1610   }
1611
1612   /** Generated Token Manager. */
1613   public QueryParserTokenManager token_source;
1614   /** Current token. */
1615   public Token token;
1616   /** Next token. */
1617   public Token jj_nt;
1618   private int jj_ntk;
1619   private Token jj_scanpos, jj_lastpos;
1620   private int jj_la;
1621   private int jj_gen;
1622   final private int[] jj_la1 = new int[23];
1623   static private int[] jj_la1_0;
1624   static private int[] jj_la1_1;
1625   static {
1626       jj_la1_init_0();
1627       jj_la1_init_1();
1628    }
1629    private static void jj_la1_init_0() {
1630       jj_la1_0 = new int[] {0x300,0x300,0x1c00,0x1c00,0x3ed3f00,0x90000,0x20000,0x3ed2000,0x2690000,0x100000,0x100000,0x20000,0x30000000,0x4000000,0x30000000,0x20000,0x0,0x40000000,0x0,0x20000,0x100000,0x20000,0x3ed0000,};
1631    }
1632    private static void jj_la1_init_1() {
1633       jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x3,0x0,0x0,0x0,0x0,};
1634    }
1635   final private JJCalls[] jj_2_rtns = new JJCalls[1];
1636   private boolean jj_rescan = false;
1637   private int jj_gc = 0;
1638
1639   /** Constructor with user supplied CharStream. */
1640   protected QueryParser(CharStream stream) {
1641     token_source = new QueryParserTokenManager(stream);
1642     token = new Token();
1643     jj_ntk = -1;
1644     jj_gen = 0;
1645     for (int i = 0; i < 23; i++) jj_la1[i] = -1;
1646     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
1647   }
1648
1649   /** Reinitialise. */
1650   public void ReInit(CharStream stream) {
1651     token_source.ReInit(stream);
1652     token = new Token();
1653     jj_ntk = -1;
1654     jj_gen = 0;
1655     for (int i = 0; i < 23; i++) jj_la1[i] = -1;
1656     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
1657   }
1658
1659   /** Constructor with generated Token Manager. */
1660   protected QueryParser(QueryParserTokenManager tm) {
1661     token_source = tm;
1662     token = new Token();
1663     jj_ntk = -1;
1664     jj_gen = 0;
1665     for (int i = 0; i < 23; i++) jj_la1[i] = -1;
1666     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
1667   }
1668
1669   /** Reinitialise. */
1670   public void ReInit(QueryParserTokenManager tm) {
1671     token_source = tm;
1672     token = new Token();
1673     jj_ntk = -1;
1674     jj_gen = 0;
1675     for (int i = 0; i < 23; i++) jj_la1[i] = -1;
1676     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
1677   }
1678
1679   private Token jj_consume_token(int kind) throws ParseException {
1680     Token oldToken;
1681     if ((oldToken = token).next != null) token = token.next;
1682     else token = token.next = token_source.getNextToken();
1683     jj_ntk = -1;
1684     if (token.kind == kind) {
1685       jj_gen++;
1686       if (++jj_gc > 100) {
1687         jj_gc = 0;
1688         for (int i = 0; i < jj_2_rtns.length; i++) {
1689           JJCalls c = jj_2_rtns[i];
1690           while (c != null) {
1691             if (c.gen < jj_gen) c.first = null;
1692             c = c.next;
1693           }
1694         }
1695       }
1696       return token;
1697     }
1698     token = oldToken;
1699     jj_kind = kind;
1700     throw generateParseException();
1701   }
1702
1703   static private final class LookaheadSuccess extends java.lang.Error { }
1704   final private LookaheadSuccess jj_ls = new LookaheadSuccess();
1705   private boolean jj_scan_token(int kind) {
1706     if (jj_scanpos == jj_lastpos) {
1707       jj_la--;
1708       if (jj_scanpos.next == null) {
1709         jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken();
1710       } else {
1711         jj_lastpos = jj_scanpos = jj_scanpos.next;
1712       }
1713     } else {
1714       jj_scanpos = jj_scanpos.next;
1715     }
1716     if (jj_rescan) {
1717       int i = 0; Token tok = token;
1718       while (tok != null && tok != jj_scanpos) { i++; tok = tok.next; }
1719       if (tok != null) jj_add_error_token(kind, i);
1720     }
1721     if (jj_scanpos.kind != kind) return true;
1722     if (jj_la == 0 && jj_scanpos == jj_lastpos) throw jj_ls;
1723     return false;
1724   }
1725
1726
1727 /** Get the next Token. */
1728   final public Token getNextToken() {
1729     if (token.next != null) token = token.next;
1730     else token = token.next = token_source.getNextToken();
1731     jj_ntk = -1;
1732     jj_gen++;
1733     return token;
1734   }
1735
1736 /** Get the specific Token. */
1737   final public Token getToken(int index) {
1738     Token t = token;
1739     for (int i = 0; i < index; i++) {
1740       if (t.next != null) t = t.next;
1741       else t = t.next = token_source.getNextToken();
1742     }
1743     return t;
1744   }
1745
1746   private int jj_ntk() {
1747     if ((jj_nt=token.next) == null)
1748       return (jj_ntk = (token.next=token_source.getNextToken()).kind);
1749     else
1750       return (jj_ntk = jj_nt.kind);
1751   }
1752
1753   private java.util.List<int[]> jj_expentries = new java.util.ArrayList<int[]>();
1754   private int[] jj_expentry;
1755   private int jj_kind = -1;
1756   private int[] jj_lasttokens = new int[100];
1757   private int jj_endpos;
1758
1759   private void jj_add_error_token(int kind, int pos) {
1760     if (pos >= 100) return;
1761     if (pos == jj_endpos + 1) {
1762       jj_lasttokens[jj_endpos++] = kind;
1763     } else if (jj_endpos != 0) {
1764       jj_expentry = new int[jj_endpos];
1765       for (int i = 0; i < jj_endpos; i++) {
1766         jj_expentry[i] = jj_lasttokens[i];
1767       }
1768       jj_entries_loop: for (java.util.Iterator it = jj_expentries.iterator(); it.hasNext();) {
1769         int[] oldentry = (int[])(it.next());
1770         if (oldentry.length == jj_expentry.length) {
1771           for (int i = 0; i < jj_expentry.length; i++) {
1772             if (oldentry[i] != jj_expentry[i]) {
1773               continue jj_entries_loop;
1774             }
1775           }
1776           jj_expentries.add(jj_expentry);
1777           break jj_entries_loop;
1778         }
1779       }
1780       if (pos != 0) jj_lasttokens[(jj_endpos = pos) - 1] = kind;
1781     }
1782   }
1783
1784   /** Generate ParseException. */
1785   public ParseException generateParseException() {
1786     jj_expentries.clear();
1787     boolean[] la1tokens = new boolean[34];
1788     if (jj_kind >= 0) {
1789       la1tokens[jj_kind] = true;
1790       jj_kind = -1;
1791     }
1792     for (int i = 0; i < 23; i++) {
1793       if (jj_la1[i] == jj_gen) {
1794         for (int j = 0; j < 32; j++) {
1795           if ((jj_la1_0[i] & (1<<j)) != 0) {
1796             la1tokens[j] = true;
1797           }
1798           if ((jj_la1_1[i] & (1<<j)) != 0) {
1799             la1tokens[32+j] = true;
1800           }
1801         }
1802       }
1803     }
1804     for (int i = 0; i < 34; i++) {
1805       if (la1tokens[i]) {
1806         jj_expentry = new int[1];
1807         jj_expentry[0] = i;
1808         jj_expentries.add(jj_expentry);
1809       }
1810     }
1811     jj_endpos = 0;
1812     jj_rescan_token();
1813     jj_add_error_token(0, 0);
1814     int[][] exptokseq = new int[jj_expentries.size()][];
1815     for (int i = 0; i < jj_expentries.size(); i++) {
1816       exptokseq[i] = jj_expentries.get(i);
1817     }
1818     return new ParseException(token, exptokseq, tokenImage);
1819   }
1820
1821   /** Enable tracing. */
1822   final public void enable_tracing() {
1823   }
1824
1825   /** Disable tracing. */
1826   final public void disable_tracing() {
1827   }
1828
1829   private void jj_rescan_token() {
1830     jj_rescan = true;
1831     for (int i = 0; i < 1; i++) {
1832     try {
1833       JJCalls p = jj_2_rtns[i];
1834       do {
1835         if (p.gen > jj_gen) {
1836           jj_la = p.arg; jj_lastpos = jj_scanpos = p.first;
1837           switch (i) {
1838             case 0: jj_3_1(); break;
1839           }
1840         }
1841         p = p.next;
1842       } while (p != null);
1843       } catch(LookaheadSuccess ls) { }
1844     }
1845     jj_rescan = false;
1846   }
1847
1848   private void jj_save(int index, int xla) {
1849     JJCalls p = jj_2_rtns[index];
1850     while (p.gen > jj_gen) {
1851       if (p.next == null) { p = p.next = new JJCalls(); break; }
1852       p = p.next;
1853     }
1854     p.gen = jj_gen + xla - jj_la; p.first = token; p.arg = xla;
1855   }
1856
1857   static final class JJCalls {
1858     int gen;
1859     Token first;
1860     int arg;
1861     JJCalls next;
1862   }
1863
1864 }