You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucenenet.apache.org by mh...@apache.org on 2013/09/24 20:33:19 UTC

[43/50] [abbrv] git commit: Initial port of classic QueryParser. Broken.

Initial port of classic QueryParser. Broken.

Gets stuck in infinite loop if you search with the default field. If
searching with a field specified or the *:* query, it errors out.


Project: http://git-wip-us.apache.org/repos/asf/lucenenet/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucenenet/commit/0e6eb14a
Tree: http://git-wip-us.apache.org/repos/asf/lucenenet/tree/0e6eb14a
Diff: http://git-wip-us.apache.org/repos/asf/lucenenet/diff/0e6eb14a

Branch: refs/heads/branch_4x
Commit: 0e6eb14ac34d87812d5f49a59278a67eaafae531
Parents: 7a4b442
Author: Paul Irwin <pa...@gmail.com>
Authored: Thu Aug 8 17:46:19 2013 -0400
Committer: Paul Irwin <pa...@gmail.com>
Committed: Thu Aug 8 17:46:19 2013 -0400

----------------------------------------------------------------------
 build/vs2012/Lucene.Net.All/Lucene.Net.All.sln  |   11 +
 src/contrib/Core/Analysis/Ext/Analysis.Ext.cs   |    1 +
 src/contrib/Core/Contrib.Core.csproj            |    7 +-
 .../QueryParsers/Classic/FastCharStream.cs      |  134 ++
 src/contrib/QueryParsers/Classic/ICharStream.cs |   37 +
 .../QueryParsers/Classic/ParseException.cs      |  153 +++
 src/contrib/QueryParsers/Classic/QueryParser.cs |  785 ++++++++++++
 .../QueryParsers/Classic/QueryParserBase.cs     | 1033 +++++++++++++++
 .../Classic/QueryParserConstants.cs             |  126 ++
 .../Classic/QueryParserTokenManager.cs          | 1188 ++++++++++++++++++
 src/contrib/QueryParsers/Classic/Token.cs       |  104 ++
 .../QueryParsers/Classic/TokenMgrError.cs       |  105 ++
 .../QueryParsers/Contrib.QueryParsers.csproj    |   69 +
 .../Standard/ICommonQueryParserConfiguration.cs |   37 +
 .../QueryParsers/Properties/AssemblyInfo.cs     |   36 +
 15 files changed, 3825 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucenenet/blob/0e6eb14a/build/vs2012/Lucene.Net.All/Lucene.Net.All.sln
----------------------------------------------------------------------
diff --git a/build/vs2012/Lucene.Net.All/Lucene.Net.All.sln b/build/vs2012/Lucene.Net.All/Lucene.Net.All.sln
index d8c2cc2..0b9121d 100644
--- a/build/vs2012/Lucene.Net.All/Lucene.Net.All.sln
+++ b/build/vs2012/Lucene.Net.All/Lucene.Net.All.sln
@@ -37,6 +37,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Contrib.WordNet.SynLookup",
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Contrib.WordNet.Syns2Index", "..\..\..\src\contrib\WordNet\Syns2Index\Contrib.WordNet.Syns2Index.csproj", "{7563D4D9-AE91-42BA-A270-1D264660F6DF}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Contrib.QueryParsers", "..\..\..\src\contrib\QueryParsers\Contrib.QueryParsers.csproj", "{56438272-B00E-40DE-9C9A-0785E705E7D9}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -173,6 +175,14 @@ Global
 		{7563D4D9-AE91-42BA-A270-1D264660F6DF}.Release|Any CPU.Build.0 = Release|Any CPU
 		{7563D4D9-AE91-42BA-A270-1D264660F6DF}.Release35|Any CPU.ActiveCfg = Release35|Any CPU
 		{7563D4D9-AE91-42BA-A270-1D264660F6DF}.Release35|Any CPU.Build.0 = Release35|Any CPU
+		{56438272-B00E-40DE-9C9A-0785E705E7D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{56438272-B00E-40DE-9C9A-0785E705E7D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{56438272-B00E-40DE-9C9A-0785E705E7D9}.Debug35|Any CPU.ActiveCfg = Debug|Any CPU
+		{56438272-B00E-40DE-9C9A-0785E705E7D9}.Debug35|Any CPU.Build.0 = Debug|Any CPU
+		{56438272-B00E-40DE-9C9A-0785E705E7D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{56438272-B00E-40DE-9C9A-0785E705E7D9}.Release|Any CPU.Build.0 = Release|Any CPU
+		{56438272-B00E-40DE-9C9A-0785E705E7D9}.Release35|Any CPU.ActiveCfg = Release|Any CPU
+		{56438272-B00E-40DE-9C9A-0785E705E7D9}.Release35|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -194,5 +204,6 @@ Global
 		{1407C9BA-337C-4C6C-B065-68328D3871B3} = {7E19085A-545B-4DE8-BBF5-B1DBC370FD37}
 		{2CA12E3F-76E1-4FA6-9E87-37079A7B7C69} = {7E19085A-545B-4DE8-BBF5-B1DBC370FD37}
 		{7563D4D9-AE91-42BA-A270-1D264660F6DF} = {7E19085A-545B-4DE8-BBF5-B1DBC370FD37}
+		{56438272-B00E-40DE-9C9A-0785E705E7D9} = {7E19085A-545B-4DE8-BBF5-B1DBC370FD37}
 	EndGlobalSection
 EndGlobal

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/0e6eb14a/src/contrib/Core/Analysis/Ext/Analysis.Ext.cs
----------------------------------------------------------------------
diff --git a/src/contrib/Core/Analysis/Ext/Analysis.Ext.cs b/src/contrib/Core/Analysis/Ext/Analysis.Ext.cs
index beec3fd..0903cfb 100644
--- a/src/contrib/Core/Analysis/Ext/Analysis.Ext.cs
+++ b/src/contrib/Core/Analysis/Ext/Analysis.Ext.cs
@@ -24,6 +24,7 @@ using System.IO;
 using Lucene.Net.Analysis;
 using Lucene.Net.Analysis.Tokenattributes;
 using Lucene.Net.Util;
+using Lucene.Net.Analysis.Core;
 
 
 namespace Lucene.Net.Analysis.Ext

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/0e6eb14a/src/contrib/Core/Contrib.Core.csproj
----------------------------------------------------------------------
diff --git a/src/contrib/Core/Contrib.Core.csproj b/src/contrib/Core/Contrib.Core.csproj
index bced134..6e1bdb9 100644
--- a/src/contrib/Core/Contrib.Core.csproj
+++ b/src/contrib/Core/Contrib.Core.csproj
@@ -30,7 +30,8 @@
     <RootNamespace>Lucene.Net</RootNamespace>
     <AssemblyName>Lucene.Net.Contrib.Core</AssemblyName>
     <FileAlignment>512</FileAlignment>
-    <FileUpgradeFlags></FileUpgradeFlags>
+    <FileUpgradeFlags>
+    </FileUpgradeFlags>
     <OldToolsVersion>3.5</OldToolsVersion>
     <UpgradeBackupLocation />
     <PublishUrl>publish\</PublishUrl>
@@ -126,6 +127,10 @@
       <Project>{5D4AD9BE-1FFB-41AB-9943-25737971BF57}</Project>
       <Name>Lucene.Net</Name>
     </ProjectReference>
+    <ProjectReference Include="..\Analyzers\Contrib.Analyzers.csproj">
+      <Project>{4286e961-9143-4821-b46d-3d39d3736386}</Project>
+      <Name>Contrib.Analyzers</Name>
+    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <BootstrapperPackage Include=".NETFramework,Version=v4.0">

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/0e6eb14a/src/contrib/QueryParsers/Classic/FastCharStream.cs
----------------------------------------------------------------------
diff --git a/src/contrib/QueryParsers/Classic/FastCharStream.cs b/src/contrib/QueryParsers/Classic/FastCharStream.cs
new file mode 100644
index 0000000..6e3a39e
--- /dev/null
+++ b/src/contrib/QueryParsers/Classic/FastCharStream.cs
@@ -0,0 +1,134 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Lucene.Net.QueryParsers.Classic
+{
+    public sealed class FastCharStream : ICharStream
+    {
+        internal char[] buffer = null;
+
+        internal int bufferLength = 0;          // end of valid chars
+        internal int bufferPosition = 0;        // next char to read
+
+        internal int tokenStart = 0;          // offset in buffer
+        internal int bufferStart = 0;          // position in file of buffer
+
+        internal TextReader input;            // source of chars
+
+        public FastCharStream(TextReader r)
+        {
+            input = r;
+        }
+
+        public char ReadChar()
+        {
+            if (bufferPosition >= bufferLength)
+                Refill();
+            return buffer[bufferPosition++];
+        }
+
+        private void Refill()
+        {
+            int newPosition = bufferLength - tokenStart;
+
+            if (tokenStart == 0)
+            {        // token won't fit in buffer
+                if (buffer == null)
+                {        // first time: alloc buffer
+                    buffer = new char[2048];
+                }
+                else if (bufferLength == buffer.Length)
+                { // grow buffer
+                    char[] newBuffer = new char[buffer.Length * 2];
+                    Array.Copy(buffer, 0, newBuffer, 0, bufferLength);
+                    buffer = newBuffer;
+                }
+            }
+            else
+            {            // shift token to front
+                Array.Copy(buffer, tokenStart, buffer, 0, newPosition);
+            }
+
+            bufferLength = newPosition;        // update state
+            bufferPosition = newPosition;
+            bufferStart += tokenStart;
+            tokenStart = 0;
+
+            int charsRead =          // fill space in buffer
+              input.Read(buffer, newPosition, buffer.Length - newPosition);
+            if (charsRead == -1)
+                throw new IOException("read past eof");
+            else
+                bufferLength += charsRead;
+        }
+        
+        public char BeginToken()
+        {
+            tokenStart = bufferPosition;
+            return ReadChar();
+        }
+        
+        public void Backup(int amount)
+        {
+            bufferPosition -= amount;
+        }
+
+        public string GetImage()
+        {
+            return new String(buffer, tokenStart, bufferPosition - tokenStart);
+        }
+
+        public char[] GetSuffix(int len)
+        {
+            char[] value = new char[len];
+            Array.Copy(buffer, bufferPosition - len, value, 0, len);
+            return value;
+        }
+
+        public void Done()
+        {
+            try
+            {
+                input.Close();
+            }
+            catch (IOException)
+            {
+            }
+        }
+
+        public int Column
+        {
+            get { return bufferStart + bufferPosition; }
+        }
+
+        public int Line
+        {
+            get { return 1; }
+        }
+
+        public int EndColumn
+        {
+            get { return bufferStart + bufferPosition; }
+        }
+
+        public int EndLine
+        {
+            get { return 1; }
+        }
+
+        public int BeginColumn
+        {
+            get { return bufferStart + tokenStart; }
+        }
+
+        public int BeginLine
+        {
+            get { return 1; }
+        }
+        
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/0e6eb14a/src/contrib/QueryParsers/Classic/ICharStream.cs
----------------------------------------------------------------------
diff --git a/src/contrib/QueryParsers/Classic/ICharStream.cs b/src/contrib/QueryParsers/Classic/ICharStream.cs
new file mode 100644
index 0000000..d68cfba
--- /dev/null
+++ b/src/contrib/QueryParsers/Classic/ICharStream.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Lucene.Net.QueryParsers.Classic
+{
+    public interface ICharStream
+    {
+        char ReadChar();
+
+        [Obsolete]
+        int Column { get; }
+
+        [Obsolete]
+        int Line { get; }
+
+        int EndColumn { get; }
+
+        int EndLine { get; }
+
+        int BeginColumn { get; }
+
+        int BeginLine { get; }
+
+        void Backup(int amount);
+
+        char BeginToken();
+
+        string GetImage();
+
+        char[] GetSuffix(int len);
+
+        void Done();
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/0e6eb14a/src/contrib/QueryParsers/Classic/ParseException.cs
----------------------------------------------------------------------
diff --git a/src/contrib/QueryParsers/Classic/ParseException.cs b/src/contrib/QueryParsers/Classic/ParseException.cs
new file mode 100644
index 0000000..253c0cb
--- /dev/null
+++ b/src/contrib/QueryParsers/Classic/ParseException.cs
@@ -0,0 +1,153 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Lucene.Net.QueryParsers.Classic
+{
+    public class ParseException : Exception
+    {
+        private const long serialVersionUID = 1L;
+
+        public ParseException(Token currentTokenVal,
+                        int[][] expectedTokenSequencesVal,
+                        String[] tokenImageVal
+                       )
+            : base(Initialise(currentTokenVal, expectedTokenSequencesVal, tokenImageVal))
+        {
+            currentToken = currentTokenVal;
+            expectedTokenSequences = expectedTokenSequencesVal;
+            tokenImage = tokenImageVal;
+        }
+
+        public ParseException()
+            : base()
+        {
+        }
+
+        public ParseException(String message)
+            : base(message)
+        {
+        }
+
+        // .NET Port: not present in Java version but needed for inner exception
+        public ParseException(String message, Exception innerException)
+            : base(message, innerException)
+        {
+        }
+
+
+        public Token currentToken;
+
+        public int[][] expectedTokenSequences;
+
+        public String[] tokenImage;
+
+        private static String Initialise(Token currentToken,
+                           int[][] expectedTokenSequences,
+                           String[] tokenImage)
+        {
+            String eol = ConfigurationManager.AppSettings["line.separator"] ?? "\n";
+            StringBuilder expected = new StringBuilder();
+            int maxSize = 0;
+            for (int i = 0; i < expectedTokenSequences.Length; i++)
+            {
+                if (maxSize < expectedTokenSequences[i].Length)
+                {
+                    maxSize = expectedTokenSequences[i].Length;
+                }
+                for (int j = 0; j < expectedTokenSequences[i].Length; j++)
+                {
+                    expected.Append(tokenImage[expectedTokenSequences[i][j]]).Append(' ');
+                }
+                if (expectedTokenSequences[i][expectedTokenSequences[i].Length - 1] != 0)
+                {
+                    expected.Append("...");
+                }
+                expected.Append(eol).Append("    ");
+            }
+            String retval = "Encountered \"";
+            Token tok = currentToken.next;
+            for (int i = 0; i < maxSize; i++)
+            {
+                if (i != 0) retval += " ";
+                if (tok.kind == 0)
+                {
+                    retval += tokenImage[0];
+                    break;
+                }
+                retval += " " + tokenImage[tok.kind];
+                retval += " \"";
+                retval += Add_escapes(tok.image);
+                retval += " \"";
+                tok = tok.next;
+            }
+            retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
+            retval += "." + eol;
+            if (expectedTokenSequences.Length == 1)
+            {
+                retval += "Was expecting:" + eol + "    ";
+            }
+            else
+            {
+                retval += "Was expecting one of:" + eol + "    ";
+            }
+            retval += expected.ToString();
+            return retval;
+        }
+
+        protected String eol = ConfigurationManager.AppSettings["line.separator"] ?? "\n";
+
+        static String Add_escapes(String str)
+        {
+            StringBuilder retval = new StringBuilder();
+            char ch;
+            for (int i = 0; i < str.Length; i++)
+            {
+                switch (str[i])
+                {
+                    case (char)0:
+                        continue;
+                    case '\b':
+                        retval.Append("\\b");
+                        continue;
+                    case '\t':
+                        retval.Append("\\t");
+                        continue;
+                    case '\n':
+                        retval.Append("\\n");
+                        continue;
+                    case '\f':
+                        retval.Append("\\f");
+                        continue;
+                    case '\r':
+                        retval.Append("\\r");
+                        continue;
+                    case '\"':
+                        retval.Append("\\\"");
+                        continue;
+                    case '\'':
+                        retval.Append("\\\'");
+                        continue;
+                    case '\\':
+                        retval.Append("\\\\");
+                        continue;
+                    default:
+                        if ((ch = str[i]) < 0x20 || ch > 0x7e)
+                        {
+                            String s = "0000" + Convert.ToString(ch, 16);
+                            retval.Append("\\u" + s.Substring(s.Length - 4, s.Length));
+                        }
+                        else
+                        {
+                            retval.Append(ch);
+                        }
+                        continue;
+                }
+            }
+            return retval.ToString();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/0e6eb14a/src/contrib/QueryParsers/Classic/QueryParser.cs
----------------------------------------------------------------------
diff --git a/src/contrib/QueryParsers/Classic/QueryParser.cs b/src/contrib/QueryParsers/Classic/QueryParser.cs
new file mode 100644
index 0000000..ca76ac5
--- /dev/null
+++ b/src/contrib/QueryParsers/Classic/QueryParser.cs
@@ -0,0 +1,785 @@
+using Lucene.Net.Analysis;
+using Lucene.Net.Search;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Version = Lucene.Net.Util.Version;
+
+namespace Lucene.Net.QueryParsers.Classic
+{
+    public class QueryParser : QueryParserBase, IQueryParserConstants
+    {
+        public enum Operator
+        {
+            OR,
+            AND
+        }
+
+        public QueryParser(Version matchVersion, String f, Analyzer a)
+            : this(new FastCharStream(new StringReader("")))
+        {
+            Init(matchVersion, f, a);
+        }
+
+        public int Conjunction()
+        {
+            int ret = CONJ_NONE;
+            switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+            {
+                case QueryParserConstants.AND:
+                case QueryParserConstants.OR:
+                    switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+                    {
+                        case QueryParserConstants.AND:
+                            jj_consume_token(QueryParserConstants.AND);
+                            ret = CONJ_AND;
+                            break;
+                        case QueryParserConstants.OR:
+                            jj_consume_token(QueryParserConstants.OR);
+                            ret = CONJ_OR;
+                            break;
+                        default:
+                            jj_la1[0] = jj_gen;
+                            jj_consume_token(-1);
+                            throw new ParseException();
+                    }
+                    break;
+                default:
+                    jj_la1[1] = jj_gen;
+                    break;
+            }
+            { if (true) return ret; }
+            throw new Exception("Missing return statement in function");
+        }
+
+        public int Modifiers()
+        {
+            int ret = MOD_NONE;
+            switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+            {
+                case QueryParserConstants.NOT:
+                case QueryParserConstants.PLUS:
+                case QueryParserConstants.MINUS:
+                    switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+                    {
+                        case QueryParserConstants.PLUS:
+                            jj_consume_token(QueryParserConstants.PLUS);
+                            ret = MOD_REQ;
+                            break;
+                        case QueryParserConstants.MINUS:
+                            jj_consume_token(QueryParserConstants.MINUS);
+                            ret = MOD_NOT;
+                            break;
+                        case QueryParserConstants.NOT:
+                            jj_consume_token(QueryParserConstants.NOT);
+                            ret = MOD_NOT;
+                            break;
+                        default:
+                            jj_la1[2] = jj_gen;
+                            jj_consume_token(-1);
+                            throw new ParseException();
+                    }
+                    break;
+                default:
+                    jj_la1[3] = jj_gen;
+                    break;
+            }
+            { if (true) return ret; }
+            throw new Exception("Missing return statement in function");
+        }
+
+        public override Query TopLevelQuery(String field)
+        {
+            Query q;
+            q = Query(field);
+            jj_consume_token(0);
+            { if (true) return q; }
+            throw new Exception("Missing return statement in function");
+        }
+
+        public Query Query(String field)
+        {
+            IList<BooleanClause> clauses = new List<BooleanClause>();
+            Query q, firstQuery = null;
+            int conj, mods;
+            mods = Modifiers();
+            q = Clause(field);
+            AddClause(clauses, CONJ_NONE, mods, q);
+            if (mods == MOD_NONE)
+                firstQuery = q;
+
+            while (true)
+            {
+                bool shouldBreakOuter = false;
+                switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+                {
+                    case QueryParserConstants.AND:
+                    case QueryParserConstants.OR:
+                    case QueryParserConstants.NOT:
+                    case QueryParserConstants.PLUS:
+                    case QueryParserConstants.MINUS:
+                    case QueryParserConstants.BAREOPER:
+                    case QueryParserConstants.LPAREN:
+                    case QueryParserConstants.STAR:
+                    case QueryParserConstants.QUOTED:
+                    case QueryParserConstants.TERM:
+                    case QueryParserConstants.PREFIXTERM:
+                    case QueryParserConstants.WILDTERM:
+                    case QueryParserConstants.REGEXPTERM:
+                    case QueryParserConstants.RANGEIN_START:
+                    case QueryParserConstants.RANGEEX_START:
+                    case QueryParserConstants.NUMBER:
+                        ;
+                        break;
+                    default:
+                        jj_la1[4] = jj_gen;
+                        shouldBreakOuter = true;
+                        break;
+                }
+
+                if (shouldBreakOuter) break;
+                conj = Conjunction();
+                mods = Modifiers();
+                q = Clause(field);
+                AddClause(clauses, conj, mods, q);
+            }
+            if (clauses.Count == 1 && firstQuery != null)
+            { if (true) return firstQuery; }
+            else
+            {
+                { if (true) return GetBooleanQuery(clauses); }
+            }
+            throw new Exception("Missing return statement in function");
+        }
+
+        public Query Clause(String field)
+        {
+            Query q;
+            Token fieldToken = null, boost = null;
+            if (jj_2_1(2))
+            {
+                switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+                {
+                    case QueryParserConstants.TERM:
+                        fieldToken = jj_consume_token(QueryParserConstants.TERM);
+                        jj_consume_token(QueryParserConstants.COLON);
+                        field = DiscardEscapeChar(fieldToken.image);
+                        break;
+                    case QueryParserConstants.STAR:
+                        jj_consume_token(QueryParserConstants.STAR);
+                        jj_consume_token(QueryParserConstants.COLON);
+                        field = "*";
+                        break;
+                    default:
+                        jj_la1[5] = jj_gen;
+                        jj_consume_token(-1);
+                        throw new ParseException();
+                }
+            }
+            else
+            {
+                ;
+            }
+            switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+            {
+                case QueryParserConstants.BAREOPER:
+                case QueryParserConstants.STAR:
+                case QueryParserConstants.QUOTED:
+                case QueryParserConstants.TERM:
+                case QueryParserConstants.PREFIXTERM:
+                case QueryParserConstants.WILDTERM:
+                case QueryParserConstants.REGEXPTERM:
+                case QueryParserConstants.RANGEIN_START:
+                case QueryParserConstants.RANGEEX_START:
+                case QueryParserConstants.NUMBER:
+                    q = Term(field);
+                    break;
+                case QueryParserConstants.LPAREN:
+                    jj_consume_token(QueryParserConstants.LPAREN);
+                    q = Query(field);
+                    jj_consume_token(QueryParserConstants.RPAREN);
+                    switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+                    {
+                        case QueryParserConstants.CARAT:
+                            jj_consume_token(QueryParserConstants.CARAT);
+                            boost = jj_consume_token(QueryParserConstants.NUMBER);
+                            break;
+                        default:
+                            jj_la1[6] = jj_gen;
+                            break;
+                    }
+                    break;
+                default:
+                    jj_la1[7] = jj_gen;
+                    jj_consume_token(-1);
+                    throw new ParseException();
+            }
+            { if (true) return HandleBoost(q, boost); }
+            throw new Exception("Missing return statement in function");
+        }
+
+        public Query Term(String field)
+        {
+            Token term, boost = null, fuzzySlop = null, goop1, goop2;
+            bool prefix = false;
+            bool wildcard = false;
+            bool fuzzy = false;
+            bool regexp = false;
+            bool startInc = false;
+            bool endInc = false;
+            Query q;
+            switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+            {
+                case QueryParserConstants.BAREOPER:
+                case QueryParserConstants.STAR:
+                case QueryParserConstants.TERM:
+                case QueryParserConstants.PREFIXTERM:
+                case QueryParserConstants.WILDTERM:
+                case QueryParserConstants.REGEXPTERM:
+                case QueryParserConstants.NUMBER:
+                    switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+                    {
+                        case QueryParserConstants.TERM:
+                            term = jj_consume_token(QueryParserConstants.TERM);
+                            break;
+                        case QueryParserConstants.STAR:
+                            term = jj_consume_token(QueryParserConstants.STAR);
+                            wildcard = true;
+                            break;
+                        case QueryParserConstants.PREFIXTERM:
+                            term = jj_consume_token(QueryParserConstants.PREFIXTERM);
+                            prefix = true;
+                            break;
+                        case QueryParserConstants.WILDTERM:
+                            term = jj_consume_token(QueryParserConstants.WILDTERM);
+                            wildcard = true;
+                            break;
+                        case QueryParserConstants.REGEXPTERM:
+                            term = jj_consume_token(QueryParserConstants.REGEXPTERM);
+                            regexp = true;
+                            break;
+                        case QueryParserConstants.NUMBER:
+                            term = jj_consume_token(QueryParserConstants.NUMBER);
+                            break;
+                        case QueryParserConstants.BAREOPER:
+                            term = jj_consume_token(QueryParserConstants.BAREOPER);
+                            term.image = term.image.Substring(0, 1);
+                            break;
+                        default:
+                            jj_la1[8] = jj_gen;
+                            jj_consume_token(-1);
+                            throw new ParseException();
+                    }
+                    switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+                    {
+                        case QueryParserConstants.FUZZY_SLOP:
+                            fuzzySlop = jj_consume_token(QueryParserConstants.FUZZY_SLOP);
+                            fuzzy = true;
+                            break;
+                        default:
+                            jj_la1[9] = jj_gen;
+                            break;
+                    }
+                    switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+                    {
+                        case QueryParserConstants.CARAT:
+                            jj_consume_token(QueryParserConstants.CARAT);
+                            boost = jj_consume_token(QueryParserConstants.NUMBER);
+                            switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+                            {
+                                case QueryParserConstants.FUZZY_SLOP:
+                                    fuzzySlop = jj_consume_token(QueryParserConstants.FUZZY_SLOP);
+                                    fuzzy = true;
+                                    break;
+                                default:
+                                    jj_la1[10] = jj_gen;
+                                    break;
+                            }
+                            break;
+                        default:
+                            jj_la1[11] = jj_gen;
+                            break;
+                    }
+                    q = HandleBareTokenQuery(field, term, fuzzySlop, prefix, wildcard, fuzzy, regexp);
+                    break;
+                case QueryParserConstants.RANGEIN_START:
+                case QueryParserConstants.RANGEEX_START:
+                    switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+                    {
+                        case QueryParserConstants.RANGEIN_START:
+                            jj_consume_token(QueryParserConstants.RANGEIN_START);
+                            startInc = true;
+                            break;
+                        case QueryParserConstants.RANGEEX_START:
+                            jj_consume_token(QueryParserConstants.RANGEEX_START);
+                            break;
+                        default:
+                            jj_la1[12] = jj_gen;
+                            jj_consume_token(-1);
+                            throw new ParseException();
+                    }
+                    switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+                    {
+                        case QueryParserConstants.RANGE_GOOP:
+                            goop1 = jj_consume_token(QueryParserConstants.RANGE_GOOP);
+                            break;
+                        case QueryParserConstants.RANGE_QUOTED:
+                            goop1 = jj_consume_token(QueryParserConstants.RANGE_QUOTED);
+                            break;
+                        default:
+                            jj_la1[13] = jj_gen;
+                            jj_consume_token(-1);
+                            throw new ParseException();
+                    }
+                    switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+                    {
+                        case QueryParserConstants.RANGE_TO:
+                            jj_consume_token(QueryParserConstants.RANGE_TO);
+                            break;
+                        default:
+                            jj_la1[14] = jj_gen;
+                            break;
+                    }
+                    switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+                    {
+                        case QueryParserConstants.RANGE_GOOP:
+                            goop2 = jj_consume_token(QueryParserConstants.RANGE_GOOP);
+                            break;
+                        case QueryParserConstants.RANGE_QUOTED:
+                            goop2 = jj_consume_token(QueryParserConstants.RANGE_QUOTED);
+                            break;
+                        default:
+                            jj_la1[15] = jj_gen;
+                            jj_consume_token(-1);
+                            throw new ParseException();
+                    }
+                    switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+                    {
+                        case QueryParserConstants.RANGEIN_END:
+                            jj_consume_token(QueryParserConstants.RANGEIN_END);
+                            endInc = true;
+                            break;
+                        case QueryParserConstants.RANGEEX_END:
+                            jj_consume_token(QueryParserConstants.RANGEEX_END);
+                            break;
+                        default:
+                            jj_la1[16] = jj_gen;
+                            jj_consume_token(-1);
+                            throw new ParseException();
+                    }
+                    switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+                    {
+                        case QueryParserConstants.CARAT:
+                            jj_consume_token(QueryParserConstants.CARAT);
+                            boost = jj_consume_token(QueryParserConstants.NUMBER);
+                            break;
+                        default:
+                            jj_la1[17] = jj_gen;
+                            break;
+                    }
+                    bool startOpen = false;
+                    bool endOpen = false;
+                    if (goop1.kind == QueryParserConstants.RANGE_QUOTED)
+                    {
+                        goop1.image = goop1.image.Substring(1, goop1.image.Length - 1);
+                    }
+                    else if ("*".Equals(goop1.image))
+                    {
+                        startOpen = true;
+                    }
+                    if (goop2.kind == QueryParserConstants.RANGE_QUOTED)
+                    {
+                        goop2.image = goop2.image.Substring(1, goop2.image.Length - 1);
+                    }
+                    else if ("*".Equals(goop2.image))
+                    {
+                        endOpen = true;
+                    }
+                    q = GetRangeQuery(field, startOpen ? null : DiscardEscapeChar(goop1.image), endOpen ? null : DiscardEscapeChar(goop2.image), startInc, endInc);
+                    break;
+                case QueryParserConstants.QUOTED:
+                    term = jj_consume_token(QueryParserConstants.QUOTED);
+                    switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+                    {
+                        case QueryParserConstants.FUZZY_SLOP:
+                            fuzzySlop = jj_consume_token(QueryParserConstants.FUZZY_SLOP);
+                            break;
+                        default:
+                            jj_la1[18] = jj_gen;
+                            break;
+                    }
+                    switch ((_jj_ntk == -1) ? jj_ntk() : _jj_ntk)
+                    {
+                        case QueryParserConstants.CARAT:
+                            jj_consume_token(QueryParserConstants.CARAT);
+                            boost = jj_consume_token(QueryParserConstants.NUMBER);
+                            break;
+                        default:
+                            jj_la1[19] = jj_gen;
+                            break;
+                    }
+                    q = HandleQuotedTerm(field, term, fuzzySlop);
+                    break;
+                default:
+                    jj_la1[20] = jj_gen;
+                    jj_consume_token(-1);
+                    throw new ParseException();
+            }
+            { if (true) return HandleBoost(q, boost); }
+            throw new Exception("Missing return statement in function");
+        }
+
+        private bool jj_2_1(int xla)
+        {
+            jj_la = xla; jj_lastpos = jj_scanpos = token;
+            try { return !jj_3_1(); }
+            catch (LookaheadSuccess ls) { return true; }
+            finally { jj_save(0, xla); }
+        }
+
+        private bool jj_3R_2()
+        {
+            if (jj_scan_token(QueryParserConstants.TERM)) return true;
+            if (jj_scan_token(QueryParserConstants.COLON)) return true;
+            return false;
+        }
+
+        private bool jj_3_1()
+        {
+            Token xsp;
+            xsp = jj_scanpos;
+            if (jj_3R_2())
+            {
+                jj_scanpos = xsp;
+                if (jj_3R_3()) return true;
+            }
+            return false;
+        }
+
+        private bool jj_3R_3()
+        {
+            if (jj_scan_token(QueryParserConstants.STAR)) return true;
+            if (jj_scan_token(QueryParserConstants.COLON)) return true;
+            return false;
+        }
+
+        /** Generated Token Manager. */
+        public QueryParserTokenManager token_source;
+        /** Current token. */
+        public Token token;
+        /** Next token. */
+        public Token jj_nt;
+        private int _jj_ntk;
+        private Token jj_scanpos, jj_lastpos;
+        private int jj_la;
+        private int jj_gen;
+        private readonly int[] jj_la1 = new int[21];
+        static private int[] jj_la1_0;
+        static private int[] jj_la1_1;
+
+        static QueryParser()
+        {
+            jj_la1_init_0();
+            jj_la1_init_1();
+        }
+
+        private static void jj_la1_init_0()
+        {
+            jj_la1_0 = new int[] { 0x300, 0x300, 0x1c00, 0x1c00, 0xfda7f00, 0x120000, 0x40000, 0xfda6000, 0x9d22000, 0x200000, 0x200000, 0x40000, 0x6000000, unchecked((int)0x80000000), 0x10000000, unchecked((int)0x80000000), 0x60000000, 0x40000, 0x200000, 0x40000, 0xfda2000, };
+        }
+        private static void jj_la1_init_1()
+        {
+            jj_la1_1 = new int[] { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, };
+        }
+
+        private readonly JJCalls[] jj_2_rtns = new JJCalls[1];
+        private bool jj_rescan = false;
+        private int jj_gc = 0;
+
+        /** Constructor with user supplied CharStream. */
+        protected QueryParser(ICharStream stream)
+        {
+            token_source = new QueryParserTokenManager(stream);
+            token = new Token();
+            _jj_ntk = -1;
+            jj_gen = 0;
+            for (int i = 0; i < 21; i++) jj_la1[i] = -1;
+            for (int i = 0; i < jj_2_rtns.Length; i++) jj_2_rtns[i] = new JJCalls();
+        }
+
+        /** Reinitialise. */
+        public override void ReInit(ICharStream stream)
+        {
+            token_source.ReInit(stream);
+            token = new Token();
+            _jj_ntk = -1;
+            jj_gen = 0;
+            for (int i = 0; i < 21; i++) jj_la1[i] = -1;
+            for (int i = 0; i < jj_2_rtns.Length; i++) jj_2_rtns[i] = new JJCalls();
+        }
+
+        /** Constructor with generated Token Manager. */
+        protected QueryParser(QueryParserTokenManager tm)
+        {
+            token_source = tm;
+            token = new Token();
+            _jj_ntk = -1;
+            jj_gen = 0;
+            for (int i = 0; i < 21; i++) jj_la1[i] = -1;
+            for (int i = 0; i < jj_2_rtns.Length; i++) jj_2_rtns[i] = new JJCalls();
+        }
+
+        /** Reinitialise. */
+        public void ReInit(QueryParserTokenManager tm)
+        {
+            token_source = tm;
+            token = new Token();
+            _jj_ntk = -1;
+            jj_gen = 0;
+            for (int i = 0; i < 21; i++) jj_la1[i] = -1;
+            for (int i = 0; i < jj_2_rtns.Length; i++) jj_2_rtns[i] = new JJCalls();
+        }
+
+        private Token jj_consume_token(int kind)
+        {
+            Token oldToken;
+            if ((oldToken = token).next != null) token = token.next;
+            else token = token.next = token_source.GetNextToken();
+            _jj_ntk = -1;
+            if (token.kind == kind)
+            {
+                jj_gen++;
+                if (++jj_gc > 100)
+                {
+                    jj_gc = 0;
+                    for (int i = 0; i < jj_2_rtns.Length; i++)
+                    {
+                        JJCalls c = jj_2_rtns[i];
+                        while (c != null)
+                        {
+                            if (c.gen < jj_gen) c.first = null;
+                            c = c.next;
+                        }
+                    }
+                }
+                return token;
+            }
+            token = oldToken;
+            jj_kind = kind;
+            throw GenerateParseException();
+        }
+
+        private sealed class LookaheadSuccess : Exception { }
+
+        private readonly LookaheadSuccess jj_ls = new LookaheadSuccess();
+
+        private bool jj_scan_token(int kind)
+        {
+            if (jj_scanpos == jj_lastpos)
+            {
+                jj_la--;
+                if (jj_scanpos.next == null)
+                {
+                    jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.GetNextToken();
+                }
+                else
+                {
+                    jj_lastpos = jj_scanpos = jj_scanpos.next;
+                }
+            }
+            else
+            {
+                jj_scanpos = jj_scanpos.next;
+            }
+            if (jj_rescan)
+            {
+                int i = 0; Token tok = token;
+                while (tok != null && tok != jj_scanpos) { i++; tok = tok.next; }
+                if (tok != null) jj_add_error_token(kind, i);
+            }
+            if (jj_scanpos.kind != kind) return true;
+            if (jj_la == 0 && jj_scanpos == jj_lastpos) throw jj_ls;
+            return false;
+        }
+
+        /** Get the next Token. */
+        public Token GetNextToken()
+        {
+            if (token.next != null) token = token.next;
+            else token = token.next = token_source.GetNextToken();
+            _jj_ntk = -1;
+            jj_gen++;
+            return token;
+        }
+
+        /** Get the specific Token. */
+        public Token GetToken(int index)
+        {
+            Token t = token;
+            for (int i = 0; i < index; i++)
+            {
+                if (t.next != null) t = t.next;
+                else t = t.next = token_source.GetNextToken();
+            }
+            return t;
+        }
+
+        private int jj_ntk()
+        {
+            if ((jj_nt = token.next) == null)
+                return (_jj_ntk = (token.next = token_source.GetNextToken()).kind);
+            else
+                return (_jj_ntk = jj_nt.kind);
+        }
+
+        private IList<int[]> jj_expentries = new List<int[]>();
+        private int[] jj_expentry;
+        private int jj_kind = -1;
+        private int[] jj_lasttokens = new int[100];
+        private int jj_endpos;
+
+        private void jj_add_error_token(int kind, int pos)
+        {
+            if (pos >= 100) return;
+            if (pos == jj_endpos + 1)
+            {
+                jj_lasttokens[jj_endpos++] = kind;
+            }
+            else if (jj_endpos != 0)
+            {
+                jj_expentry = new int[jj_endpos];
+                for (int i = 0; i < jj_endpos; i++)
+                {
+                    jj_expentry[i] = jj_lasttokens[i];
+                }
+
+                foreach (int[] oldentry in jj_expentries)
+                {
+                    bool shouldContinueOuter = false;
+                    if (oldentry.Length == jj_expentry.Length)
+                    {
+                        for (int i = 0; i < jj_expentry.Length; i++)
+                        {
+                            if (oldentry[i] != jj_expentry[i])
+                            {
+                                shouldContinueOuter = true;
+                                break;
+                            }
+                        }
+
+                        if (shouldContinueOuter)
+                            continue;
+                        jj_expentries.Add(jj_expentry);
+                        break;
+                    }
+                }
+                if (pos != 0) jj_lasttokens[(jj_endpos = pos) - 1] = kind;
+            }
+        }
+
+        /** Generate ParseException. */
+        public ParseException GenerateParseException()
+        {
+            jj_expentries.Clear();
+            bool[] la1tokens = new bool[33];
+            if (jj_kind >= 0)
+            {
+                la1tokens[jj_kind] = true;
+                jj_kind = -1;
+            }
+            for (int i = 0; i < 21; i++)
+            {
+                if (jj_la1[i] == jj_gen)
+                {
+                    for (int j = 0; j < 32; j++)
+                    {
+                        if ((jj_la1_0[i] & (1 << j)) != 0)
+                        {
+                            la1tokens[j] = true;
+                        }
+                        if ((jj_la1_1[i] & (1 << j)) != 0)
+                        {
+                            la1tokens[32 + j] = true;
+                        }
+                    }
+                }
+            }
+            for (int i = 0; i < 33; i++)
+            {
+                if (la1tokens[i])
+                {
+                    jj_expentry = new int[1];
+                    jj_expentry[0] = i;
+                    jj_expentries.Add(jj_expentry);
+                }
+            }
+            jj_endpos = 0;
+            jj_rescan_token();
+            jj_add_error_token(0, 0);
+            int[][] exptokseq = new int[jj_expentries.Count][];
+            for (int i = 0; i < jj_expentries.Count; i++)
+            {
+                exptokseq[i] = jj_expentries[i];
+            }
+            return new ParseException(token, exptokseq, QueryParserConstants.tokenImage);
+        }
+
+        /** Enable tracing. */
+        public virtual void EnableTracing()
+        {
+        }
+
+        /** Disable tracing. */
+        public virtual void DisableTracing()
+        {
+        }
+
+        private void jj_rescan_token()
+        {
+            jj_rescan = true;
+            for (int i = 0; i < 1; i++)
+            {
+                try
+                {
+                    JJCalls p = jj_2_rtns[i];
+                    do
+                    {
+                        if (p.gen > jj_gen)
+                        {
+                            jj_la = p.arg; jj_lastpos = jj_scanpos = p.first;
+                            switch (i)
+                            {
+                                case 0: jj_3_1(); break;
+                            }
+                        }
+                        p = p.next;
+                    } while (p != null);
+                }
+                catch (LookaheadSuccess ls) { }
+            }
+            jj_rescan = false;
+        }
+
+        private void jj_save(int index, int xla)
+        {
+            JJCalls p = jj_2_rtns[index];
+            while (p.gen > jj_gen)
+            {
+                if (p.next == null) { p = p.next = new JJCalls(); break; }
+                p = p.next;
+            }
+            p.gen = jj_gen + xla - jj_la; p.first = token; p.arg = xla;
+        }
+
+        internal sealed class JJCalls
+        {
+            public int gen;
+            public Token first;
+            public int arg;
+            public JJCalls next;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/0e6eb14a/src/contrib/QueryParsers/Classic/QueryParserBase.cs
----------------------------------------------------------------------
diff --git a/src/contrib/QueryParsers/Classic/QueryParserBase.cs b/src/contrib/QueryParsers/Classic/QueryParserBase.cs
new file mode 100644
index 0000000..5425f0c
--- /dev/null
+++ b/src/contrib/QueryParsers/Classic/QueryParserBase.cs
@@ -0,0 +1,1033 @@
+using Lucene.Net.Analysis;
+using Lucene.Net.Analysis.Tokenattributes;
+using Lucene.Net.Documents;
+using Lucene.Net.Index;
+using Lucene.Net.QueryParsers.Flexible.Standard;
+using Lucene.Net.Search;
+using Lucene.Net.Support;
+using Lucene.Net.Util;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Operator = Lucene.Net.QueryParsers.Classic.QueryParser.Operator;
+using Version = Lucene.Net.Util.Version;
+
+namespace Lucene.Net.QueryParsers.Classic
+{
+    public abstract class QueryParserBase : ICommonQueryParserConfiguration
+    {
+        /** Do not catch this exception in your code, it means you are using methods that you should no longer use. */
+        public class MethodRemovedUseAnother : Exception { }
+
+        internal const int CONJ_NONE = 0;
+        internal const int CONJ_AND = 1;
+        internal const int CONJ_OR = 2;
+
+        internal const int MOD_NONE = 0;
+        internal const int MOD_NOT = 10;
+        internal const int MOD_REQ = 11;
+
+        // make it possible to call setDefaultOperator() without accessing
+        // the nested class:
+        /** Alternative form of QueryParser.Operator.AND */
+        public static readonly Operator AND_OPERATOR = Operator.AND;
+        /** Alternative form of QueryParser.Operator.OR */
+        public static readonly Operator OR_OPERATOR = Operator.OR;
+
+        /** The actual operator that parser uses to combine query terms */
+        internal Operator operator_renamed = OR_OPERATOR;
+
+        internal bool lowercaseExpandedTerms = true;
+        internal MultiTermQuery.RewriteMethod multiTermRewriteMethod = MultiTermQuery.CONSTANT_SCORE_AUTO_REWRITE_DEFAULT;
+        internal bool allowLeadingWildcard = false;
+        internal bool enablePositionIncrements = true;
+
+        internal Analyzer analyzer;
+        internal String field;
+        internal int phraseSlop = 0;
+        internal float fuzzyMinSim = FuzzyQuery.defaultMinSimilarity;
+        internal int fuzzyPrefixLength = FuzzyQuery.defaultPrefixLength;
+        internal CultureInfo locale = CultureInfo.InvariantCulture;
+        internal TimeZone timeZone = TimeZone.CurrentTimeZone;
+
+        // the default date resolution
+        internal DateTools.Resolution dateResolution = null;
+        // maps field names to date resolutions
+        internal IDictionary<String, DateTools.Resolution> fieldToDateResolution = null;
+
+        //Whether or not to analyze range terms when constructing RangeQuerys
+        // (For example, analyzing terms into collation keys for locale-sensitive RangeQuery)
+        internal bool analyzeRangeTerms = false;
+
+        internal bool autoGeneratePhraseQueries;
+
+        // So the generated QueryParser(CharStream) won't error out
+        protected QueryParserBase()
+        {
+        }
+
+        public void Init(Version matchVersion, String f, Analyzer a)
+        {
+            analyzer = a;
+            field = f;
+            if (matchVersion.OnOrAfter(Version.LUCENE_31))
+            {
+                AutoGeneratePhraseQueries = false;
+            }
+            else
+            {
+                AutoGeneratePhraseQueries = true;
+            }
+        }
+
+        // the generated parser will create these in QueryParser
+        public abstract void ReInit(ICharStream stream);
+        public abstract Query TopLevelQuery(String field);
+
+        public Query Parse(String query)
+        {
+            ReInit(new FastCharStream(new StringReader(query)));
+            try
+            {
+                // TopLevelQuery is a Query followed by the end-of-input (EOF)
+                Query res = TopLevelQuery(field);
+                return res != null ? res : NewBooleanQuery(false);
+            }
+            catch (ParseException tme)
+            {
+                // rethrow to include the original query:
+                ParseException e = new ParseException("Cannot parse '" + query + "': " + tme.Message, tme);
+                throw e;
+            }
+            catch (TokenMgrError tme)
+            {
+                ParseException e = new ParseException("Cannot parse '" + query + "': " + tme.Message, tme);
+                throw e;
+            }
+            catch (BooleanQuery.TooManyClauses tmc)
+            {
+                ParseException e = new ParseException("Cannot parse '" + query + "': too many boolean clauses", tmc);
+                throw e;
+            }
+        }
+
+        public Analyzer Analyzer
+        {
+            get { return analyzer; }
+        }
+
+        public string Field
+        {
+            get { return field; }
+        }
+
+        public bool AutoGeneratePhraseQueries
+        {
+            get { return autoGeneratePhraseQueries; }
+            set { autoGeneratePhraseQueries = value; }
+        }
+
+        public float FuzzyMinSim
+        {
+            get { return fuzzyMinSim; }
+            set { fuzzyMinSim = value; }
+        }
+
+        public int FuzzyPrefixLength
+        {
+            get { return fuzzyPrefixLength; }
+            set { fuzzyPrefixLength = value; }
+        }
+
+        public int PhraseSlop
+        {
+            get { return phraseSlop; }
+            set { phraseSlop = value; }
+        }
+
+        public bool AllowLeadingWildcard
+        {
+            get { return allowLeadingWildcard; }
+            set { allowLeadingWildcard = value; }
+        }
+
+        public bool EnablePositionIncrements
+        {
+            get { return enablePositionIncrements; }
+            set { enablePositionIncrements = value; }
+        }
+
+        public Operator DefaultOperator
+        {
+            get { return operator_renamed; }
+            set { operator_renamed = value; }
+        }
+
+        public bool LowercaseExpandedTerms
+        {
+            get { return lowercaseExpandedTerms; }
+            set { lowercaseExpandedTerms = value; }
+        }
+
+        public MultiTermQuery.RewriteMethod MultiTermRewriteMethod
+        {
+            get { return multiTermRewriteMethod; }
+            set { multiTermRewriteMethod = value; }
+        }
+
+        public CultureInfo Locale
+        {
+            get { return locale; }
+            set { locale = value; }
+        }
+
+        public TimeZone TimeZone
+        {
+            get { return timeZone; }
+            set { timeZone = value; }
+        }
+
+        public DateTools.Resolution DateResolution
+        {
+            get { return dateResolution; }
+            set { dateResolution = value; }
+        }
+
+        public void SetDateResolution(string fieldName, DateTools.Resolution dateResolution)
+        {
+            if (fieldName == null)
+            {
+                throw new ArgumentException("Field cannot be null.");
+            }
+
+            if (fieldToDateResolution == null)
+            {
+                // lazily initialize HashMap
+                fieldToDateResolution = new HashMap<String, DateTools.Resolution>();
+            }
+
+            fieldToDateResolution[fieldName] = dateResolution;
+        }
+
+        public DateTools.Resolution GetDateResolution(string fieldName)
+        {
+            if (fieldName == null)
+            {
+                throw new ArgumentException("Field cannot be null.");
+            }
+
+            if (fieldToDateResolution == null)
+            {
+                // no field specific date resolutions set; return default date resolution instead
+                return this.dateResolution;
+            }
+
+            DateTools.Resolution resolution = fieldToDateResolution[fieldName];
+            if (resolution == null)
+            {
+                // no date resolutions set for the given field; return default date resolution instead
+                resolution = this.dateResolution;
+            }
+
+            return resolution;
+        }
+
+        public bool AnalyzeRangeTerms
+        {
+            get { return analyzeRangeTerms; }
+            set { analyzeRangeTerms = value; }
+        }
+
+        protected void AddClause(IList<BooleanClause> clauses, int conj, int mods, Query q)
+        {
+            bool required, prohibited;
+
+            // If this term is introduced by AND, make the preceding term required,
+            // unless it's already prohibited
+            if (clauses.Count > 0 && conj == CONJ_AND)
+            {
+                BooleanClause c = clauses[clauses.Count - 1];
+                if (!c.IsProhibited)
+                    c.Occur = Occur.MUST;
+            }
+
+            if (clauses.Count > 0 && operator_renamed == AND_OPERATOR && conj == CONJ_OR)
+            {
+                // If this term is introduced by OR, make the preceding term optional,
+                // unless it's prohibited (that means we leave -a OR b but +a OR b-->a OR b)
+                // notice if the input is a OR b, first term is parsed as required; without
+                // this modification a OR b would parsed as +a OR b
+                BooleanClause c = clauses[clauses.Count - 1];
+                if (!c.IsProhibited)
+                    c.Occur = Occur.SHOULD;
+            }
+
+            // We might have been passed a null query; the term might have been
+            // filtered away by the analyzer.
+            if (q == null)
+                return;
+
+            if (operator_renamed == OR_OPERATOR)
+            {
+                // We set REQUIRED if we're introduced by AND or +; PROHIBITED if
+                // introduced by NOT or -; make sure not to set both.
+                prohibited = (mods == MOD_NOT);
+                required = (mods == MOD_REQ);
+                if (conj == CONJ_AND && !prohibited)
+                {
+                    required = true;
+                }
+            }
+            else
+            {
+                // We set PROHIBITED if we're introduced by NOT or -; We set REQUIRED
+                // if not PROHIBITED and not introduced by OR
+                prohibited = (mods == MOD_NOT);
+                required = (!prohibited && conj != CONJ_OR);
+            }
+            if (required && !prohibited)
+                clauses.Add(NewBooleanClause(q, Occur.MUST));
+            else if (!required && !prohibited)
+                clauses.Add(NewBooleanClause(q, Occur.SHOULD));
+            else if (!required && prohibited)
+                clauses.Add(NewBooleanClause(q, Occur.MUST_NOT));
+            else
+                throw new SystemException("Clause cannot be both required and prohibited");
+        }
+
+        protected Query GetFieldQuery(String field, String queryText, bool quoted)
+        {
+            return NewFieldQuery(analyzer, field, queryText, quoted);
+        }
+
+        protected Query NewFieldQuery(Analyzer analyzer, String field, String queryText, bool quoted)
+        {
+            // Use the analyzer to get all the tokens, and then build a TermQuery,
+            // PhraseQuery, or nothing based on the term count
+
+            TokenStream source;
+            try
+            {
+                source = analyzer.TokenStream(field, new StringReader(queryText));
+                source.Reset();
+            }
+            catch (IOException e)
+            {
+                ParseException p = new ParseException("Unable to initialize TokenStream to analyze query text", e);
+                throw p;
+            }
+            CachingTokenFilter buffer = new CachingTokenFilter(source);
+            ITermToBytesRefAttribute termAtt = null;
+            IPositionIncrementAttribute posIncrAtt = null;
+            int numTokens = 0;
+
+            buffer.Reset();
+
+            if (buffer.HasAttribute<ITermToBytesRefAttribute>())
+            {
+                termAtt = buffer.GetAttribute<ITermToBytesRefAttribute>();
+            }
+            if (buffer.HasAttribute<IPositionIncrementAttribute>())
+            {
+                posIncrAtt = buffer.GetAttribute<IPositionIncrementAttribute>();
+            }
+
+            int positionCount = 0;
+            bool severalTokensAtSamePosition = false;
+
+            bool hasMoreTokens = false;
+            if (termAtt != null)
+            {
+                try
+                {
+                    hasMoreTokens = buffer.IncrementToken();
+                    while (hasMoreTokens)
+                    {
+                        numTokens++;
+                        int positionIncrement = (posIncrAtt != null) ? posIncrAtt.PositionIncrement : 1;
+                        if (positionIncrement != 0)
+                        {
+                            positionCount += positionIncrement;
+                        }
+                        else
+                        {
+                            severalTokensAtSamePosition = true;
+                        }
+                        hasMoreTokens = buffer.IncrementToken();
+                    }
+                }
+                catch (IOException e)
+                {
+                    // ignore
+                }
+            }
+            try
+            {
+                // rewind the buffer stream
+                buffer.Reset();
+
+                // close original stream - all tokens buffered
+                source.Dispose();
+            }
+            catch (IOException e)
+            {
+                ParseException p = new ParseException("Cannot close TokenStream analyzing query text", e);
+                throw p;
+            }
+
+            BytesRef bytes = termAtt == null ? null : termAtt.BytesRef;
+
+            if (numTokens == 0)
+                return null;
+            else if (numTokens == 1)
+            {
+                try
+                {
+                    bool hasNext = buffer.IncrementToken();
+                    //assert hasNext == true;
+                    termAtt.FillBytesRef();
+                }
+                catch (IOException e)
+                {
+                    // safe to ignore, because we know the number of tokens
+                }
+                return NewTermQuery(new Term(field, BytesRef.DeepCopyOf(bytes)));
+            }
+            else
+            {
+                if (severalTokensAtSamePosition || (!quoted && !autoGeneratePhraseQueries))
+                {
+                    if (positionCount == 1 || (!quoted && !autoGeneratePhraseQueries))
+                    {
+                        // no phrase query:
+
+                        if (positionCount == 1)
+                        {
+                            // simple case: only one position, with synonyms
+                            BooleanQuery q = NewBooleanQuery(true);
+                            for (int i = 0; i < numTokens; i++)
+                            {
+                                try
+                                {
+                                    bool hasNext = buffer.IncrementToken();
+                                    //assert hasNext == true;
+                                    termAtt.FillBytesRef();
+                                }
+                                catch (IOException e)
+                                {
+                                    // safe to ignore, because we know the number of tokens
+                                }
+                                Query currentQuery = NewTermQuery(
+                                    new Term(field, BytesRef.DeepCopyOf(bytes)));
+                                q.Add(currentQuery, Occur.SHOULD);
+                            }
+                            return q;
+                        }
+                        else
+                        {
+                            // multiple positions
+                            BooleanQuery q = NewBooleanQuery(false);
+                            Occur occur = operator_renamed == Operator.AND ? Occur.MUST : Occur.SHOULD;
+                            Query currentQuery = null;
+                            for (int i = 0; i < numTokens; i++)
+                            {
+                                try
+                                {
+                                    bool hasNext = buffer.IncrementToken();
+                                    //assert hasNext == true;
+                                    termAtt.FillBytesRef();
+                                }
+                                catch (IOException e)
+                                {
+                                    // safe to ignore, because we know the number of tokens
+                                }
+                                if (posIncrAtt != null && posIncrAtt.PositionIncrement == 0)
+                                {
+                                    if (!(currentQuery is BooleanQuery))
+                                    {
+                                        Query t = currentQuery;
+                                        currentQuery = NewBooleanQuery(true);
+                                        ((BooleanQuery)currentQuery).Add(t, Occur.SHOULD);
+                                    }
+                                    ((BooleanQuery)currentQuery).Add(NewTermQuery(new Term(field, BytesRef.DeepCopyOf(bytes))), Occur.SHOULD);
+                                }
+                                else
+                                {
+                                    if (currentQuery != null)
+                                    {
+                                        q.Add(currentQuery, occur);
+                                    }
+                                    currentQuery = NewTermQuery(new Term(field, BytesRef.DeepCopyOf(bytes)));
+                                }
+                            }
+                            q.Add(currentQuery, occur);
+                            return q;
+                        }
+                    }
+                    else
+                    {
+                        // phrase query:
+                        MultiPhraseQuery mpq = NewMultiPhraseQuery();
+                        mpq.Slop = phraseSlop;
+                        List<Term> multiTerms = new List<Term>();
+                        int position = -1;
+                        for (int i = 0; i < numTokens; i++)
+                        {
+                            int positionIncrement = 1;
+                            try
+                            {
+                                bool hasNext = buffer.IncrementToken();
+                                //assert hasNext == true;
+                                termAtt.FillBytesRef();
+                                if (posIncrAtt != null)
+                                {
+                                    positionIncrement = posIncrAtt.PositionIncrement;
+                                }
+                            }
+                            catch (IOException e)
+                            {
+                                // safe to ignore, because we know the number of tokens
+                            }
+
+                            if (positionIncrement > 0 && multiTerms.Count > 0)
+                            {
+                                if (enablePositionIncrements)
+                                {
+                                    mpq.Add(multiTerms.ToArray(), position);
+                                }
+                                else
+                                {
+                                    mpq.Add(multiTerms.ToArray());
+                                }
+                                multiTerms.Clear();
+                            }
+                            position += positionIncrement;
+                            multiTerms.Add(new Term(field, BytesRef.DeepCopyOf(bytes)));
+                        }
+                        if (enablePositionIncrements)
+                        {
+                            mpq.Add(multiTerms.ToArray(), position);
+                        }
+                        else
+                        {
+                            mpq.Add(multiTerms.ToArray());
+                        }
+                        return mpq;
+                    }
+                }
+                else
+                {
+                    PhraseQuery pq = NewPhraseQuery();
+                    pq.Slop = phraseSlop;
+                    int position = -1;
+
+                    for (int i = 0; i < numTokens; i++)
+                    {
+                        int positionIncrement = 1;
+
+                        try
+                        {
+                            bool hasNext = buffer.IncrementToken();
+                            //assert hasNext == true;
+                            termAtt.FillBytesRef();
+                            if (posIncrAtt != null)
+                            {
+                                positionIncrement = posIncrAtt.PositionIncrement;
+                            }
+                        }
+                        catch (IOException e)
+                        {
+                            // safe to ignore, because we know the number of tokens
+                        }
+
+                        if (enablePositionIncrements)
+                        {
+                            position += positionIncrement;
+                            pq.Add(new Term(field, BytesRef.DeepCopyOf(bytes)), position);
+                        }
+                        else
+                        {
+                            pq.Add(new Term(field, BytesRef.DeepCopyOf(bytes)));
+                        }
+                    }
+                    return pq;
+                }
+            }
+        }
+
+        protected Query GetFieldQuery(String field, String queryText, int slop)
+        {
+            Query query = GetFieldQuery(field, queryText, true);
+
+            if (query is PhraseQuery)
+            {
+                ((PhraseQuery)query).Slop = slop;
+            }
+            if (query is MultiPhraseQuery)
+            {
+                ((MultiPhraseQuery)query).Slop = slop;
+            }
+
+            return query;
+        }
+
+        protected Query GetRangeQuery(String field,
+                                String part1,
+                                String part2,
+                                bool startInclusive,
+                                bool endInclusive)
+        {
+            if (lowercaseExpandedTerms)
+            {
+                part1 = part1 == null ? null : part1.ToLower(locale);
+                part2 = part2 == null ? null : part2.ToLower(locale);
+            }
+
+
+            //DateTimeFormatInfo df = DateTimeFormatInfo.GetInstance(locale);
+            //df.setLenient(true);
+            DateTools.Resolution resolution = GetDateResolution(field);
+
+            try
+            {
+                part1 = DateTools.DateToString(DateTime.Parse(part1, locale), resolution);
+            }
+            catch (Exception e) { }
+
+            try
+            {
+                DateTime d2 = DateTime.Parse(part2, locale);
+                if (endInclusive)
+                {
+                    // The user can only specify the date, not the time, so make sure
+                    // the time is set to the latest possible time of that date to really
+                    // include all documents:
+                    d2 = d2.AddHours(23);
+                    d2 = d2.AddMinutes(59);
+                    d2 = d2.AddSeconds(59);
+                    d2 = d2.AddMilliseconds(999);
+                    // .NET Port TODO: is this right?
+                }
+                part2 = DateTools.DateToString(d2, resolution);
+            }
+            catch (Exception e) { }
+
+            return NewRangeQuery(field, part1, part2, startInclusive, endInclusive);
+        }
+
+        protected BooleanQuery NewBooleanQuery(bool disableCoord)
+        {
+            return new BooleanQuery(disableCoord);
+        }
+
+        protected BooleanClause NewBooleanClause(Query q, Occur occur)
+        {
+            return new BooleanClause(q, occur);
+        }
+
+        protected Query NewTermQuery(Term term)
+        {
+            return new TermQuery(term);
+        }
+
+        protected PhraseQuery NewPhraseQuery()
+        {
+            return new PhraseQuery();
+        }
+
+        protected MultiPhraseQuery NewMultiPhraseQuery()
+        {
+            return new MultiPhraseQuery();
+        }
+
+        protected Query NewPrefixQuery(Term prefix)
+        {
+            PrefixQuery query = new PrefixQuery(prefix);
+            query.SetRewriteMethod(multiTermRewriteMethod);
+            return query;
+        }
+
+        protected Query NewRegexpQuery(Term regexp)
+        {
+            RegexpQuery query = new RegexpQuery(regexp);
+            query.SetRewriteMethod(multiTermRewriteMethod);
+            return query;
+        }
+
+        protected Query NewFuzzyQuery(Term term, float minimumSimilarity, int prefixLength)
+        {
+            // FuzzyQuery doesn't yet allow constant score rewrite
+            String text = term.Text;
+            int numEdits = FuzzyQuery.FloatToEdits(minimumSimilarity,
+                text.Length);
+            return new FuzzyQuery(term, numEdits, prefixLength);
+        }
+
+        // TODO: Should this be protected instead?
+        private BytesRef AnalyzeMultitermTerm(String field, String part)
+        {
+            return AnalyzeMultitermTerm(field, part, analyzer);
+        }
+
+        protected BytesRef AnalyzeMultitermTerm(String field, String part, Analyzer analyzerIn)
+        {
+            TokenStream source;
+
+            if (analyzerIn == null) analyzerIn = analyzer;
+
+            try
+            {
+                source = analyzerIn.TokenStream(field, new StringReader(part));
+                source.Reset();
+            }
+            catch (IOException e)
+            {
+                throw new SystemException("Unable to initialize TokenStream to analyze multiTerm term: " + part, e);
+            }
+
+            ITermToBytesRefAttribute termAtt = source.GetAttribute<ITermToBytesRefAttribute>();
+            BytesRef bytes = termAtt.BytesRef;
+
+            try
+            {
+                if (!source.IncrementToken())
+                    throw new ArgumentException("analyzer returned no terms for multiTerm term: " + part);
+                termAtt.FillBytesRef();
+                if (source.IncrementToken())
+                    throw new ArgumentException("analyzer returned too many terms for multiTerm term: " + part);
+            }
+            catch (IOException e)
+            {
+                throw new SystemException("error analyzing range part: " + part, e);
+            }
+
+            try
+            {
+                source.End();
+                source.Dispose();
+            }
+            catch (IOException e)
+            {
+                throw new SystemException("Unable to end & close TokenStream after analyzing multiTerm term: " + part, e);
+            }
+
+            return BytesRef.DeepCopyOf(bytes);
+        }
+
+        protected Query NewRangeQuery(String field, String part1, String part2, bool startInclusive, bool endInclusive)
+        {
+            BytesRef start;
+            BytesRef end;
+
+            if (part1 == null)
+            {
+                start = null;
+            }
+            else
+            {
+                start = analyzeRangeTerms ? AnalyzeMultitermTerm(field, part1) : new BytesRef(part1);
+            }
+
+            if (part2 == null)
+            {
+                end = null;
+            }
+            else
+            {
+                end = analyzeRangeTerms ? AnalyzeMultitermTerm(field, part2) : new BytesRef(part2);
+            }
+
+            TermRangeQuery query = new TermRangeQuery(field, start, end, startInclusive, endInclusive);
+
+            query.SetRewriteMethod(multiTermRewriteMethod);
+            return query;
+        }
+
+        protected Query NewMatchAllDocsQuery()
+        {
+            return new MatchAllDocsQuery();
+        }
+
+        protected Query NewWildcardQuery(Term t)
+        {
+            WildcardQuery query = new WildcardQuery(t);
+            query.SetRewriteMethod(multiTermRewriteMethod);
+            return query;
+        }
+
+        protected Query GetBooleanQuery(IList<BooleanClause> clauses)
+        {
+            return GetBooleanQuery(clauses, false);
+        }
+
+        protected Query GetBooleanQuery(IList<BooleanClause> clauses, bool disableCoord)
+        {
+            if (clauses.Count == 0)
+            {
+                return null; // all clause words were filtered away by the analyzer.
+            }
+            BooleanQuery query = NewBooleanQuery(disableCoord);
+            foreach (BooleanClause clause in clauses)
+            {
+                query.Add(clause);
+            }
+            return query;
+        }
+
+        protected Query GetWildcardQuery(String field, String termStr)
+        {
+            if ("*".Equals(field))
+            {
+                if ("*".Equals(termStr)) return NewMatchAllDocsQuery();
+            }
+            if (!allowLeadingWildcard && (termStr.StartsWith("*") || termStr.StartsWith("?")))
+                throw new ParseException("'*' or '?' not allowed as first character in WildcardQuery");
+            if (lowercaseExpandedTerms)
+            {
+                termStr = termStr.ToLower(locale);
+            }
+            Term t = new Term(field, termStr);
+            return NewWildcardQuery(t);
+        }
+
+        protected Query GetRegexpQuery(String field, String termStr)
+        {
+            if (lowercaseExpandedTerms)
+            {
+                termStr = termStr.ToLower(locale);
+            }
+            Term t = new Term(field, termStr);
+            return NewRegexpQuery(t);
+        }
+
+        protected Query GetPrefixQuery(String field, String termStr)
+        {
+            if (!allowLeadingWildcard && termStr.StartsWith("*"))
+                throw new ParseException("'*' not allowed as first character in PrefixQuery");
+            if (lowercaseExpandedTerms)
+            {
+                termStr = termStr.ToLower(locale);
+            }
+            Term t = new Term(field, termStr);
+            return NewPrefixQuery(t);
+        }
+
+        protected Query GetFuzzyQuery(String field, String termStr, float minSimilarity)
+        {
+            if (lowercaseExpandedTerms)
+            {
+                termStr = termStr.ToLower(locale);
+            }
+            Term t = new Term(field, termStr);
+            return NewFuzzyQuery(t, minSimilarity, fuzzyPrefixLength);
+        }
+
+        internal Query HandleBareTokenQuery(String qfield, Token term, Token fuzzySlop, bool prefix, bool wildcard, bool fuzzy, bool regexp)
+        {
+            Query q;
+
+            String termImage = DiscardEscapeChar(term.image);
+            if (wildcard)
+            {
+                q = GetWildcardQuery(qfield, term.image);
+            }
+            else if (prefix)
+            {
+                q = GetPrefixQuery(qfield,
+                    DiscardEscapeChar(term.image.Substring
+                        (0, term.image.Length - 1)));
+            }
+            else if (regexp)
+            {
+                q = GetRegexpQuery(qfield, term.image.Substring(1, term.image.Length - 1));
+            }
+            else if (fuzzy)
+            {
+                q = HandleBareFuzzy(qfield, fuzzySlop, termImage);
+            }
+            else
+            {
+                q = GetFieldQuery(qfield, termImage, false);
+            }
+            return q;
+        }
+
+        internal Query HandleBareFuzzy(String qfield, Token fuzzySlop, String termImage)
+        {
+            Query q;
+            float fms = fuzzyMinSim;
+            try
+            {
+                fms = float.Parse(fuzzySlop.image.Substring(1));
+            }
+            catch (Exception) { }
+            if (fms < 0.0f)
+            {
+                throw new ParseException("Minimum similarity for a FuzzyQuery has to be between 0.0f and 1.0f !");
+            }
+            else if (fms >= 1.0f && fms != (int)fms)
+            {
+                throw new ParseException("Fractional edit distances are not allowed!");
+            }
+            q = GetFuzzyQuery(qfield, termImage, fms);
+            return q;
+        }
+
+        internal Query HandleQuotedTerm(String qfield, Token term, Token fuzzySlop)
+        {
+            int s = phraseSlop;  // default
+            if (fuzzySlop != null)
+            {
+                try
+                {
+                    s = (int)float.Parse(fuzzySlop.image.Substring(1));
+                }
+                catch (Exception ignored) { }
+            }
+            return GetFieldQuery(qfield, DiscardEscapeChar(term.image.Substring(1, term.image.Length - 1)), s);
+        }
+
+        internal Query HandleBoost(Query q, Token boost)
+        {
+            if (boost != null)
+            {
+                float f = (float)1.0;
+                try
+                {
+                    f = float.Parse(boost.image);
+                }
+                catch (Exception)
+                {
+                    /* Should this be handled somehow? (defaults to "no boost", if
+                     * boost number is invalid)
+                     */
+                }
+
+                // avoid boosting null queries, such as those caused by stop words
+                if (q != null)
+                {
+                    q.Boost = f;
+                }
+            }
+            return q;
+        }
+
+        internal String DiscardEscapeChar(String input)
+        {
+            // Create char array to hold unescaped char sequence
+            char[] output = new char[input.Length];
+
+            // The length of the output can be less than the input
+            // due to discarded escape chars. This variable holds
+            // the actual length of the output
+            int length = 0;
+
+            // We remember whether the last processed character was
+            // an escape character
+            bool lastCharWasEscapeChar = false;
+
+            // The multiplier the current unicode digit must be multiplied with.
+            // E. g. the first digit must be multiplied with 16^3, the second with 16^2...
+            int codePointMultiplier = 0;
+
+            // Used to calculate the codepoint of the escaped unicode character
+            int codePoint = 0;
+
+            for (int i = 0; i < input.Length; i++)
+            {
+                char curChar = input[i];
+                if (codePointMultiplier > 0)
+                {
+                    codePoint += HexToInt(curChar) * codePointMultiplier;
+                    codePointMultiplier = Number.URShift(codePointMultiplier, 4);
+                    if (codePointMultiplier == 0)
+                    {
+                        output[length++] = (char)codePoint;
+                        codePoint = 0;
+                    }
+                }
+                else if (lastCharWasEscapeChar)
+                {
+                    if (curChar == 'u')
+                    {
+                        // found an escaped unicode character
+                        codePointMultiplier = 16 * 16 * 16;
+                    }
+                    else
+                    {
+                        // this character was escaped
+                        output[length] = curChar;
+                        length++;
+                    }
+                    lastCharWasEscapeChar = false;
+                }
+                else
+                {
+                    if (curChar == '\\')
+                    {
+                        lastCharWasEscapeChar = true;
+                    }
+                    else
+                    {
+                        output[length] = curChar;
+                        length++;
+                    }
+                }
+            }
+
+            if (codePointMultiplier > 0)
+            {
+                throw new ParseException("Truncated unicode escape sequence.");
+            }
+
+            if (lastCharWasEscapeChar)
+            {
+                throw new ParseException("Term can not end with escape character.");
+            }
+
+            return new String(output, 0, length);
+        }
+
+        internal static int HexToInt(char c)
+        {
+            if ('0' <= c && c <= '9')
+            {
+                return c - '0';
+            }
+            else if ('a' <= c && c <= 'f')
+            {
+                return c - 'a' + 10;
+            }
+            else if ('A' <= c && c <= 'F')
+            {
+                return c - 'A' + 10;
+            }
+            else
+            {
+                throw new ParseException("Non-hex character in Unicode escape sequence: " + c);
+            }
+        }
+
+        public static String Escape(String s)
+        {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < s.Length; i++)
+            {
+                char c = s[i];
+                // These characters are part of the query syntax and must be escaped
+                if (c == '\\' || c == '+' || c == '-' || c == '!' || c == '(' || c == ')' || c == ':'
+                  || c == '^' || c == '[' || c == ']' || c == '\"' || c == '{' || c == '}' || c == '~'
+                  || c == '*' || c == '?' || c == '|' || c == '&' || c == '/')
+                {
+                    sb.Append('\\');
+                }
+                sb.Append(c);
+            }
+            return sb.ToString();
+        }
+    }
+}