1 /* Generated By:JavaCC: Do not edit this line. QueryParser.java */
2 package org.apache.lucene.queryParser;
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;
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;
40 * This class is generated by JavaCC. The most important method is
41 * {@link #parse(String)}.
43 * The syntax for query strings is as follows:
44 * A Query is a series of clauses.
45 * A clause may be prefixed by:
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.
53 * A clause may be either:
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
61 * Thus, in BNF, the query grammar is:
63 * Query ::= ( Clause )*
64 * Clause ::= ["+", "-"] [<TERM> ":"] ( <TERM> | "(" Query ")" )
68 * Examples of appropriately formatted queries can be found in the <a
69 * href="../../../../../../queryparsersyntax.html">query syntax
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.
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.
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.
99 * <p>Note that QueryParser is <em>not</em> thread-safe.</p>
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.
105 * <a name="version"/>
106 * <p><b>NOTE</b>: You must specify the required {@link Version}
107 * compatibility when creating QueryParser:
109 * <li> As of 2.9, {@link #setEnablePositionIncrements} is true by
111 * <li> As of 3.1, {@link #setAutoGeneratePhraseQueries} is false by
115 public class QueryParser implements QueryParserConstants {
117 private static final int CONJ_NONE = 0;
118 private static final int CONJ_AND = 1;
119 private static final int CONJ_OR = 2;
121 private static final int MOD_NONE = 0;
122 private static final int MOD_NOT = 10;
123 private static final int MOD_REQ = 11;
125 // make it possible to call setDefaultOperator() without accessing
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;
132 /** The actual operator that parser uses to combine query terms */
133 private Operator operator = OR_OPERATOR;
135 boolean lowercaseExpandedTerms = true;
136 MultiTermQuery.RewriteMethod multiTermRewriteMethod = MultiTermQuery.CONSTANT_SCORE_AUTO_REWRITE_DEFAULT;
137 boolean allowLeadingWildcard = false;
138 boolean enablePositionIncrements = true;
143 float fuzzyMinSim = FuzzyQuery.defaultMinSimilarity;
144 int fuzzyPrefixLength = FuzzyQuery.defaultPrefixLength;
145 Locale locale = Locale.getDefault();
147 // the default date resolution
148 DateTools.Resolution dateResolution = null;
149 // maps field names to date resolutions
150 Map<String,DateTools.Resolution> fieldToDateResolution = null;
152 // The collator to use when determining range inclusion,
153 // for use when constructing RangeQuerys.
154 Collator rangeCollator = null;
156 /** @deprecated remove when getFieldQuery is removed */
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 */
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 */
166 private final boolean hasNewAPI =
167 VirtualMethod.compareImplementationDistance(getClass(),
168 getFieldQueryWithQuotedMethod, getFieldQueryMethod) >= 0; // its ok for both to be overridden
170 private boolean autoGeneratePhraseQueries;
172 /** The default operator for parsing queries.
173 * Use {@link QueryParser#setDefaultOperator} to change it.
175 static public enum Operator { OR, AND }
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.
182 public QueryParser(Version matchVersion, String f, Analyzer a) {
183 this(new FastCharStream(new StringReader("")));
186 if (matchVersion.onOrAfter(Version.LUCENE_29)) {
187 enablePositionIncrements = true;
189 enablePositionIncrements = false;
191 if (matchVersion.onOrAfter(Version.LUCENE_31)) {
192 setAutoGeneratePhraseQueries(false);
194 setAutoGeneratePhraseQueries(true);
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
202 public Query parse(String query) throws ParseException {
203 ReInit(new FastCharStream(new StringReader(query)));
205 // TopLevelQuery is a Query followed by the end-of-input (EOF)
206 Query res = TopLevelQuery(field);
207 return res!=null ? res : newBooleanQuery(false);
209 catch (ParseException tme) {
210 // rethrow to include the original query:
211 ParseException e = new ParseException("Cannot parse '" +query+ "': " + tme.getMessage());
215 catch (TokenMgrError tme) {
216 ParseException e = new ParseException("Cannot parse '" +query+ "': " + tme.getMessage());
220 catch (BooleanQuery.TooManyClauses tmc) {
221 ParseException e = new ParseException("Cannot parse '" +query+ "': too many boolean clauses");
228 * @return Returns the analyzer.
230 public Analyzer getAnalyzer() {
235 * @return Returns the field.
237 public String getField() {
242 * @see #setAutoGeneratePhraseQueries(boolean)
244 public final boolean getAutoGeneratePhraseQueries() {
245 return autoGeneratePhraseQueries;
249 * Set to true if phrase queries will be automatically generated
250 * when the analyzer returns more than one term from whitespace
252 * NOTE: this behavior may not be suitable for all languages.
254 * Set to false if phrase queries should only be generated when
255 * surrounded by double quotes.
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;
265 * Get the minimal similarity for fuzzy queries.
267 public float getFuzzyMinSim() {
272 * Set the minimum similarity for fuzzy queries.
275 public void setFuzzyMinSim(float fuzzyMinSim) {
276 this.fuzzyMinSim = fuzzyMinSim;
280 * Get the prefix length for fuzzy queries.
281 * @return Returns the fuzzyPrefixLength.
283 public int getFuzzyPrefixLength() {
284 return fuzzyPrefixLength;
288 * Set the prefix length for fuzzy queries. Default is 0.
289 * @param fuzzyPrefixLength The fuzzyPrefixLength to set.
291 public void setFuzzyPrefixLength(int fuzzyPrefixLength) {
292 this.fuzzyPrefixLength = fuzzyPrefixLength;
296 * Sets the default slop for phrases. If zero, then exact phrase matches
297 * are required. Default value is zero.
299 public void setPhraseSlop(int phraseSlop) {
300 this.phraseSlop = phraseSlop;
304 * Gets the default slop for phrases.
306 public int getPhraseSlop() {
312 * Set to <code>true</code> to allow leading wildcard characters.
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.
321 public void setAllowLeadingWildcard(boolean allowLeadingWildcard) {
322 this.allowLeadingWildcard = allowLeadingWildcard;
326 * @see #setAllowLeadingWildcard(boolean)
328 public boolean getAllowLeadingWildcard() {
329 return allowLeadingWildcard;
333 * Set to <code>true</code> to enable position increments in result query.
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.
342 public void setEnablePositionIncrements(boolean enable) {
343 this.enablePositionIncrements = enable;
347 * @see #setEnablePositionIncrements(boolean)
349 public boolean getEnablePositionIncrements() {
350 return enablePositionIncrements;
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>
361 public void setDefaultOperator(Operator op) {
367 * Gets implicit operator setting, which will be either AND_OPERATOR
370 public Operator getDefaultOperator() {
376 * Whether terms of wildcard, prefix, fuzzy and range queries are to be automatically
377 * lower-cased or not. Default is <code>true</code>.
379 public void setLowercaseExpandedTerms(boolean lowercaseExpandedTerms) {
380 this.lowercaseExpandedTerms = lowercaseExpandedTerms;
385 * @see #setLowercaseExpandedTerms(boolean)
387 public boolean getLowercaseExpandedTerms() {
388 return lowercaseExpandedTerms;
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.
401 public void setMultiTermRewriteMethod(MultiTermQuery.RewriteMethod method) {
402 multiTermRewriteMethod = method;
407 * @see #setMultiTermRewriteMethod
409 public MultiTermQuery.RewriteMethod getMultiTermRewriteMethod() {
410 return multiTermRewriteMethod;
414 * Set locale used by date range parsing.
416 public void setLocale(Locale locale) {
417 this.locale = locale;
421 * Returns current locale, allowing access by subclasses.
423 public Locale getLocale() {
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)}.
432 * @param dateResolution the default date resolution to set
434 public void setDateResolution(DateTools.Resolution dateResolution) {
435 this.dateResolution = dateResolution;
439 * Sets the date resolution used by RangeQueries for a specific field.
441 * @param fieldName field for which the date resolution is to be set
442 * @param dateResolution date resolution to set
444 public void setDateResolution(String fieldName, DateTools.Resolution dateResolution) {
445 if (fieldName == null) {
446 throw new IllegalArgumentException("Field cannot be null.");
449 if (fieldToDateResolution == null) {
450 // lazily initialize HashMap
451 fieldToDateResolution = new HashMap<String,DateTools.Resolution>();
454 fieldToDateResolution.put(fieldName, dateResolution);
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.
463 public DateTools.Resolution getDateResolution(String fieldName) {
464 if (fieldName == null) {
465 throw new IllegalArgumentException("Field cannot be null.");
468 if (fieldToDateResolution == null) {
469 // no field specific date resolutions set; return default date resolution instead
470 return this.dateResolution;
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;
483 * Sets the collator used to determine index term inclusion in ranges
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
492 * @param rc the collator to use when constructing RangeQuerys
494 public void setRangeCollator(Collator rc) {
499 * @return the collator used to determine index term inclusion in ranges
502 public Collator getRangeCollator() {
503 return rangeCollator;
506 protected void addClause(List<BooleanClause> clauses, int conj, int mods, Query q) {
507 boolean required, prohibited;
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);
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);
527 // We might have been passed a null query; the term might have been
528 // filtered away by the analyzer.
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) {
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);
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));
553 throw new RuntimeException("Clause cannot be both required and prohibited");
557 * @deprecated Use {@link #getFieldQuery(String,String,boolean)} instead.
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);
566 * @exception ParseException throw in overridden method to disallow
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
574 source = analyzer.reusableTokenStream(field, new StringReader(queryText));
576 } catch (IOException e) {
577 source = analyzer.tokenStream(field, new StringReader(queryText));
579 CachingTokenFilter buffer = new CachingTokenFilter(source);
580 CharTermAttribute termAtt = null;
581 PositionIncrementAttribute posIncrAtt = null;
584 boolean success = false;
588 } catch (IOException e) {
589 // success==false if we hit an exception
592 if (buffer.hasAttribute(CharTermAttribute.class)) {
593 termAtt = buffer.getAttribute(CharTermAttribute.class);
595 if (buffer.hasAttribute(PositionIncrementAttribute.class)) {
596 posIncrAtt = buffer.getAttribute(PositionIncrementAttribute.class);
600 int positionCount = 0;
601 boolean severalTokensAtSamePosition = false;
603 boolean hasMoreTokens = false;
604 if (termAtt != null) {
606 hasMoreTokens = buffer.incrementToken();
607 while (hasMoreTokens) {
609 int positionIncrement = (posIncrAtt != null) ? posIncrAtt.getPositionIncrement() : 1;
610 if (positionIncrement != 0) {
611 positionCount += positionIncrement;
613 severalTokensAtSamePosition = true;
615 hasMoreTokens = buffer.incrementToken();
617 } catch (IOException e) {
622 // rewind the buffer stream
625 // close original stream - all tokens buffered
628 catch (IOException e) {
634 else if (numTokens == 1) {
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
643 return newTermQuery(new Term(field, term));
645 if (severalTokensAtSamePosition || (!quoted && !autoGeneratePhraseQueries)) {
646 if (positionCount == 1 || (!quoted && !autoGeneratePhraseQueries)) {
648 BooleanQuery q = newBooleanQuery(positionCount == 1);
650 BooleanClause.Occur occur = positionCount > 1 && operator == AND_OPERATOR ?
651 BooleanClause.Occur.MUST : BooleanClause.Occur.SHOULD;
653 for (int i = 0; i < numTokens; i++) {
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
663 Query currentQuery = newTermQuery(
664 new Term(field, term));
665 q.add(currentQuery, occur);
671 MultiPhraseQuery mpq = newMultiPhraseQuery();
672 mpq.setSlop(phraseSlop);
673 List<Term> multiTerms = new ArrayList<Term>();
675 for (int i = 0; i < numTokens; i++) {
677 int positionIncrement = 1;
679 boolean hasNext = buffer.incrementToken();
680 assert hasNext == true;
681 term = termAtt.toString();
682 if (posIncrAtt != null) {
683 positionIncrement = posIncrAtt.getPositionIncrement();
685 } catch (IOException e) {
686 // safe to ignore, because we know the number of tokens
689 if (positionIncrement > 0 && multiTerms.size() > 0) {
690 if (enablePositionIncrements) {
691 mpq.add(multiTerms.toArray(new Term[0]),position);
693 mpq.add(multiTerms.toArray(new Term[0]));
697 position += positionIncrement;
698 multiTerms.add(new Term(field, term));
700 if (enablePositionIncrements) {
701 mpq.add(multiTerms.toArray(new Term[0]),position);
703 mpq.add(multiTerms.toArray(new Term[0]));
709 PhraseQuery pq = newPhraseQuery();
710 pq.setSlop(phraseSlop);
714 for (int i = 0; i < numTokens; i++) {
716 int positionIncrement = 1;
719 boolean hasNext = buffer.incrementToken();
720 assert hasNext == true;
721 term = termAtt.toString();
722 if (posIncrAtt != null) {
723 positionIncrement = posIncrAtt.getPositionIncrement();
725 } catch (IOException e) {
726 // safe to ignore, because we know the number of tokens
729 if (enablePositionIncrements) {
730 position += positionIncrement;
731 pq.add(new Term(field, term),position);
733 pq.add(new Term(field, term));
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.
748 * @exception ParseException throw in overridden method to disallow
750 protected Query getFieldQuery(String field, String queryText, int slop)
751 throws ParseException {
752 Query query = hasNewAPI ? getFieldQuery(field, queryText, true) : getFieldQuery(field, queryText);
754 if (query instanceof PhraseQuery) {
755 ((PhraseQuery) query).setSlop(slop);
757 if (query instanceof MultiPhraseQuery) {
758 ((MultiPhraseQuery) query).setSlop(slop);
766 * @exception ParseException throw in overridden method to disallow
768 protected Query getRangeQuery(String field,
771 boolean inclusive) throws ParseException
773 if (lowercaseExpandedTerms) {
774 part1 = part1.toLowerCase();
775 part2 = part2.toLowerCase();
778 DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
780 Date d1 = df.parse(part1);
781 Date d2 = df.parse(part2);
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);
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);
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);
802 part1 = DateTools.dateToString(d1, resolution);
803 part2 = DateTools.dateToString(d2, resolution);
806 catch (Exception e) { }
808 return newRangeQuery(field, part1, part2, inclusive);
812 * Builds a new BooleanQuery instance
813 * @param disableCoord disable coord
814 * @return new BooleanQuery instance
816 protected BooleanQuery newBooleanQuery(boolean disableCoord) {
817 return new BooleanQuery(disableCoord);
821 * Builds a new BooleanClause instance
823 * @param occur how this clause should occur when matching documents
824 * @return new BooleanClause instance
826 protected BooleanClause newBooleanClause(Query q, BooleanClause.Occur occur) {
827 return new BooleanClause(q, occur);
831 * Builds a new TermQuery instance
833 * @return new TermQuery instance
835 protected Query newTermQuery(Term term){
836 return new TermQuery(term);
840 * Builds a new PhraseQuery instance
841 * @return new PhraseQuery instance
843 protected PhraseQuery newPhraseQuery(){
844 return new PhraseQuery();
848 * Builds a new MultiPhraseQuery instance
849 * @return new MultiPhraseQuery instance
851 protected MultiPhraseQuery newMultiPhraseQuery(){
852 return new MultiPhraseQuery();
856 * Builds a new PrefixQuery instance
857 * @param prefix Prefix term
858 * @return new PrefixQuery instance
860 protected Query newPrefixQuery(Term prefix){
861 PrefixQuery query = new PrefixQuery(prefix);
862 query.setRewriteMethod(multiTermRewriteMethod);
867 * Builds a new FuzzyQuery instance
869 * @param minimumSimilarity minimum similarity
870 * @param prefixLength prefix length
871 * @return new FuzzyQuery Instance
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);
879 * Builds a new TermRangeQuery instance
883 * @param inclusive true if range is inclusive
884 * @return new TermRangeQuery instance
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);
893 * Builds a new MatchAllDocsQuery instance
894 * @return new MatchAllDocsQuery instance
896 protected Query newMatchAllDocsQuery() {
897 return new MatchAllDocsQuery();
901 * Builds a new WildcardQuery instance
902 * @param t wildcard term
903 * @return new WildcardQuery instance
905 protected Query newWildcardQuery(Term t) {
906 WildcardQuery query = new WildcardQuery(t);
907 query.setRewriteMethod(multiTermRewriteMethod);
912 * Factory method for generating query, given a set of clauses.
913 * By default creates a boolean query composed of clauses passed in.
915 * Can be overridden by extending classes, to modify query being
918 * @param clauses List that contains {@link BooleanClause} instances
921 * @return Resulting {@link Query} object.
922 * @exception ParseException throw in overridden method to disallow
924 protected Query getBooleanQuery(List<BooleanClause> clauses) throws ParseException {
925 return getBooleanQuery(clauses, false);
929 * Factory method for generating query, given a set of clauses.
930 * By default creates a boolean query composed of clauses passed in.
932 * Can be overridden by extending classes, to modify query being
935 * @param clauses List that contains {@link BooleanClause} instances
937 * @param disableCoord true if coord scoring should be disabled.
939 * @return Resulting {@link Query} object.
940 * @exception ParseException throw in overridden method to disallow
942 protected Query getBooleanQuery(List<BooleanClause> clauses, boolean disableCoord)
943 throws ParseException
945 if (clauses.size()==0) {
946 return null; // all clause words were filtered away by the analyzer.
948 BooleanQuery query = newBooleanQuery(disableCoord);
949 for(final BooleanClause clause: clauses) {
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)
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.
966 * Can be overridden by extending classes, to provide custom handling for
967 * wildcard queries, which may be necessary due to missing analyzer calls.
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
973 * @return Resulting {@link Query} built for the term
974 * @exception ParseException throw in overridden method to disallow
976 protected Query getWildcardQuery(String field, String termStr) throws ParseException
978 if ("*".equals(field)) {
979 if ("*".equals(termStr)) return newMatchAllDocsQuery();
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();
986 Term t = new Term(field, termStr);
987 return newWildcardQuery(t);
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.
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.
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.
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!)
1010 * @return Resulting {@link Query} built for the term
1011 * @exception ParseException throw in overridden method to disallow
1013 protected Query getPrefixQuery(String field, String termStr) throws ParseException
1015 if (!allowLeadingWildcard && termStr.startsWith("*"))
1016 throw new ParseException("'*' not allowed as first character in PrefixQuery");
1017 if (lowercaseExpandedTerms) {
1018 termStr = termStr.toLowerCase();
1020 Term t = new Term(field, termStr);
1021 return newPrefixQuery(t);
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.
1029 * @param field Name of the field query will use.
1030 * @param termStr Term token to use for building term for the query
1032 * @return Resulting {@link Query} built for the term
1033 * @exception ParseException throw in overridden method to disallow
1035 protected Query getFuzzyQuery(String field, String termStr, float minSimilarity) throws ParseException
1037 if (lowercaseExpandedTerms) {
1038 termStr = termStr.toLowerCase();
1040 Term t = new Term(field, termStr);
1041 return newFuzzyQuery(t, minSimilarity, fuzzyPrefixLength);
1045 * Returns a String where the escape char has been
1046 * removed, or kept only once if there was a double escape.
1048 * Supports escaped unicode characters, e. g. translates
1049 * <code>\\u0041</code> to <code>A</code>.
1052 private String discardEscapeChar(String input) throws ParseException {
1053 // Create char array to hold unescaped char sequence
1054 char[] output = new char[input.length()];
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
1061 // We remember whether the last processed character was
1062 // an escape character
1063 boolean lastCharWasEscapeChar = false;
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;
1069 // Used to calculate the codepoint of the escaped unicode character
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;
1081 } else if (lastCharWasEscapeChar) {
1082 if (curChar == 'u') {
1083 // found an escaped unicode character
1084 codePointMultiplier = 16 * 16 * 16;
1086 // this character was escaped
1087 output[length] = curChar;
1090 lastCharWasEscapeChar = false;
1092 if (curChar == '\\') {
1093 lastCharWasEscapeChar = true;
1095 output[length] = curChar;
1101 if (codePointMultiplier > 0) {
1102 throw new ParseException("Truncated unicode escape sequence.");
1105 if (lastCharWasEscapeChar) {
1106 throw new ParseException("Term can not end with escape character.");
1109 return new String(output, 0, length);
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') {
1116 } else if ('a' <= c && c <= 'f'){
1117 return c - 'a' + 10;
1118 } else if ('A' <= c && c <= 'F') {
1119 return c - 'A' + 10;
1121 throw new ParseException("None-hex character in unicode escape sequence: " + c);
1126 * Returns a String where those characters that QueryParser
1127 * expects to be escaped are escaped by a preceding <code>\</code>.
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 == '&') {
1141 return sb.toString();
1145 * Command line tool to test QueryParser, using {@link org.apache.lucene.analysis.SimpleAnalyzer}.
1147 * <code>java org.apache.lucene.queryParser.QueryParser <input></code>
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>");
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"));
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) {
1167 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1169 jj_consume_token(AND);
1173 jj_consume_token(OR);
1178 jj_consume_token(-1);
1179 throw new ParseException();
1186 {if (true) return ret;}
1187 throw new Error("Missing return statement in function");
1190 final public int Modifiers() throws ParseException {
1192 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1196 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1198 jj_consume_token(PLUS);
1202 jj_consume_token(MINUS);
1206 jj_consume_token(NOT);
1211 jj_consume_token(-1);
1212 throw new ParseException();
1219 {if (true) return ret;}
1220 throw new Error("Missing return statement in function");
1223 // This makes sure that there is no garbage after the query string
1224 final public Query TopLevelQuery(String field) throws ParseException {
1227 jj_consume_token(0);
1228 {if (true) return q;}
1229 throw new Error("Missing return statement in function");
1232 final public Query Query(String field) throws ParseException {
1233 List<BooleanClause> clauses = new ArrayList<BooleanClause>();
1234 Query q, firstQuery=null;
1238 addClause(clauses, CONJ_NONE, mods, q);
1239 if (mods == MOD_NONE)
1243 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1264 conj = Conjunction();
1267 addClause(clauses, conj, mods, q);
1269 if (clauses.size() == 1 && firstQuery != null)
1270 {if (true) return firstQuery;}
1272 {if (true) return getBooleanQuery(clauses);}
1274 throw new Error("Missing return statement in function");
1277 final public Query Clause(String field) throws ParseException {
1279 Token fieldToken=null, boost=null;
1281 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1283 fieldToken = jj_consume_token(TERM);
1284 jj_consume_token(COLON);
1285 field=discardEscapeChar(fieldToken.image);
1288 jj_consume_token(STAR);
1289 jj_consume_token(COLON);
1294 jj_consume_token(-1);
1295 throw new ParseException();
1300 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1312 jj_consume_token(LPAREN);
1314 jj_consume_token(RPAREN);
1315 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1317 jj_consume_token(CARAT);
1318 boost = jj_consume_token(NUMBER);
1327 jj_consume_token(-1);
1328 throw new ParseException();
1330 if (boost != null) {
1331 float f = (float)1.0;
1333 f = Float.valueOf(boost.image).floatValue();
1335 } catch (Exception ignored) { }
1337 {if (true) return q;}
1338 throw new Error("Missing return statement in function");
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;
1347 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1353 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1355 term = jj_consume_token(TERM);
1358 term = jj_consume_token(STAR);
1362 term = jj_consume_token(PREFIXTERM);
1366 term = jj_consume_token(WILDTERM);
1370 term = jj_consume_token(NUMBER);
1374 jj_consume_token(-1);
1375 throw new ParseException();
1377 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1379 fuzzySlop = jj_consume_token(FUZZY_SLOP);
1386 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1388 jj_consume_token(CARAT);
1389 boost = jj_consume_token(NUMBER);
1390 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1392 fuzzySlop = jj_consume_token(FUZZY_SLOP);
1396 jj_la1[10] = jj_gen;
1401 jj_la1[11] = jj_gen;
1404 String termImage=discardEscapeChar(term.image);
1406 q = getWildcardQuery(field, termImage);
1407 } else if (prefix) {
1408 q = getPrefixQuery(field,
1409 discardEscapeChar(term.image.substring
1410 (0, term.image.length()-1)));
1412 float fms = fuzzyMinSim;
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 !");}
1419 q = getFuzzyQuery(field, termImage,fms);
1421 q = hasNewAPI ? getFieldQuery(field, termImage, false) : getFieldQuery(field, termImage);
1425 jj_consume_token(RANGEIN_START);
1426 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1428 goop1 = jj_consume_token(RANGEIN_GOOP);
1430 case RANGEIN_QUOTED:
1431 goop1 = jj_consume_token(RANGEIN_QUOTED);
1434 jj_la1[12] = jj_gen;
1435 jj_consume_token(-1);
1436 throw new ParseException();
1438 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1440 jj_consume_token(RANGEIN_TO);
1443 jj_la1[13] = jj_gen;
1446 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1448 goop2 = jj_consume_token(RANGEIN_GOOP);
1450 case RANGEIN_QUOTED:
1451 goop2 = jj_consume_token(RANGEIN_QUOTED);
1454 jj_la1[14] = jj_gen;
1455 jj_consume_token(-1);
1456 throw new ParseException();
1458 jj_consume_token(RANGEIN_END);
1459 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1461 jj_consume_token(CARAT);
1462 boost = jj_consume_token(NUMBER);
1465 jj_la1[15] = jj_gen;
1468 if (goop1.kind == RANGEIN_QUOTED) {
1469 goop1.image = goop1.image.substring(1, goop1.image.length()-1);
1471 if (goop2.kind == RANGEIN_QUOTED) {
1472 goop2.image = goop2.image.substring(1, goop2.image.length()-1);
1474 q = getRangeQuery(field, discardEscapeChar(goop1.image), discardEscapeChar(goop2.image), true);
1477 jj_consume_token(RANGEEX_START);
1478 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1480 goop1 = jj_consume_token(RANGEEX_GOOP);
1482 case RANGEEX_QUOTED:
1483 goop1 = jj_consume_token(RANGEEX_QUOTED);
1486 jj_la1[16] = jj_gen;
1487 jj_consume_token(-1);
1488 throw new ParseException();
1490 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1492 jj_consume_token(RANGEEX_TO);
1495 jj_la1[17] = jj_gen;
1498 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1500 goop2 = jj_consume_token(RANGEEX_GOOP);
1502 case RANGEEX_QUOTED:
1503 goop2 = jj_consume_token(RANGEEX_QUOTED);
1506 jj_la1[18] = jj_gen;
1507 jj_consume_token(-1);
1508 throw new ParseException();
1510 jj_consume_token(RANGEEX_END);
1511 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1513 jj_consume_token(CARAT);
1514 boost = jj_consume_token(NUMBER);
1517 jj_la1[19] = jj_gen;
1520 if (goop1.kind == RANGEEX_QUOTED) {
1521 goop1.image = goop1.image.substring(1, goop1.image.length()-1);
1523 if (goop2.kind == RANGEEX_QUOTED) {
1524 goop2.image = goop2.image.substring(1, goop2.image.length()-1);
1527 q = getRangeQuery(field, discardEscapeChar(goop1.image), discardEscapeChar(goop2.image), false);
1530 term = jj_consume_token(QUOTED);
1531 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1533 fuzzySlop = jj_consume_token(FUZZY_SLOP);
1536 jj_la1[20] = jj_gen;
1539 switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
1541 jj_consume_token(CARAT);
1542 boost = jj_consume_token(NUMBER);
1545 jj_la1[21] = jj_gen;
1550 if (fuzzySlop != null) {
1552 s = Float.valueOf(fuzzySlop.image.substring(1)).intValue();
1554 catch (Exception ignored) { }
1556 q = getFieldQuery(field, discardEscapeChar(term.image.substring(1, term.image.length()-1)), s);
1559 jj_la1[22] = jj_gen;
1560 jj_consume_token(-1);
1561 throw new ParseException();
1563 if (boost != null) {
1564 float f = (float) 1.0;
1566 f = Float.valueOf(boost.image).floatValue();
1568 catch (Exception ignored) {
1569 /* Should this be handled somehow? (defaults to "no boost", if
1570 * boost number is invalid)
1574 // avoid boosting null queries, such as those caused by stop words
1579 {if (true) return q;}
1580 throw new Error("Missing return statement in function");
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); }
1590 private boolean jj_3R_3() {
1591 if (jj_scan_token(STAR)) return true;
1592 if (jj_scan_token(COLON)) return true;
1596 private boolean jj_3R_2() {
1597 if (jj_scan_token(TERM)) return true;
1598 if (jj_scan_token(COLON)) return true;
1602 private boolean jj_3_1() {
1607 if (jj_3R_3()) return true;
1612 /** Generated Token Manager. */
1613 public QueryParserTokenManager token_source;
1614 /** Current token. */
1619 private Token jj_scanpos, jj_lastpos;
1622 final private int[] jj_la1 = new int[23];
1623 static private int[] jj_la1_0;
1624 static private int[] jj_la1_1;
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,};
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,};
1635 final private JJCalls[] jj_2_rtns = new JJCalls[1];
1636 private boolean jj_rescan = false;
1637 private int jj_gc = 0;
1639 /** Constructor with user supplied CharStream. */
1640 protected QueryParser(CharStream stream) {
1641 token_source = new QueryParserTokenManager(stream);
1642 token = new Token();
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();
1649 /** Reinitialise. */
1650 public void ReInit(CharStream stream) {
1651 token_source.ReInit(stream);
1652 token = new Token();
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();
1659 /** Constructor with generated Token Manager. */
1660 protected QueryParser(QueryParserTokenManager tm) {
1662 token = new Token();
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();
1669 /** Reinitialise. */
1670 public void ReInit(QueryParserTokenManager tm) {
1672 token = new Token();
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();
1679 private Token jj_consume_token(int kind) throws ParseException {
1681 if ((oldToken = token).next != null) token = token.next;
1682 else token = token.next = token_source.getNextToken();
1684 if (token.kind == kind) {
1686 if (++jj_gc > 100) {
1688 for (int i = 0; i < jj_2_rtns.length; i++) {
1689 JJCalls c = jj_2_rtns[i];
1691 if (c.gen < jj_gen) c.first = null;
1700 throw generateParseException();
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) {
1708 if (jj_scanpos.next == null) {
1709 jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken();
1711 jj_lastpos = jj_scanpos = jj_scanpos.next;
1714 jj_scanpos = jj_scanpos.next;
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);
1721 if (jj_scanpos.kind != kind) return true;
1722 if (jj_la == 0 && jj_scanpos == jj_lastpos) throw jj_ls;
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();
1736 /** Get the specific Token. */
1737 final public Token getToken(int index) {
1739 for (int i = 0; i < index; i++) {
1740 if (t.next != null) t = t.next;
1741 else t = t.next = token_source.getNextToken();
1746 private int jj_ntk() {
1747 if ((jj_nt=token.next) == null)
1748 return (jj_ntk = (token.next=token_source.getNextToken()).kind);
1750 return (jj_ntk = jj_nt.kind);
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;
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];
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;
1776 jj_expentries.add(jj_expentry);
1777 break jj_entries_loop;
1780 if (pos != 0) jj_lasttokens[(jj_endpos = pos) - 1] = kind;
1784 /** Generate ParseException. */
1785 public ParseException generateParseException() {
1786 jj_expentries.clear();
1787 boolean[] la1tokens = new boolean[34];
1789 la1tokens[jj_kind] = true;
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;
1798 if ((jj_la1_1[i] & (1<<j)) != 0) {
1799 la1tokens[32+j] = true;
1804 for (int i = 0; i < 34; i++) {
1806 jj_expentry = new int[1];
1808 jj_expentries.add(jj_expentry);
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);
1818 return new ParseException(token, exptokseq, tokenImage);
1821 /** Enable tracing. */
1822 final public void enable_tracing() {
1825 /** Disable tracing. */
1826 final public void disable_tracing() {
1829 private void jj_rescan_token() {
1831 for (int i = 0; i < 1; i++) {
1833 JJCalls p = jj_2_rtns[i];
1835 if (p.gen > jj_gen) {
1836 jj_la = p.arg; jj_lastpos = jj_scanpos = p.first;
1838 case 0: jj_3_1(); break;
1842 } while (p != null);
1843 } catch(LookaheadSuccess ls) { }
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; }
1854 p.gen = jj_gen + xla - jj_la; p.first = token; p.arg = xla;
1857 static final class JJCalls {