lucenenet-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From synhers...@apache.org
Subject [13/50] [abbrv] lucenenet git commit: Ported QueryParser.Ext namespace + tests.
Date Sun, 11 Sep 2016 21:30:44 GMT
Ported QueryParser.Ext namespace + tests.


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

Branch: refs/heads/master
Commit: e45f3289d5710bbcf3f9a4cbe995f31d05af9530
Parents: 071b60c
Author: Shad Storhaug <shad@shadstorhaug.com>
Authored: Mon Aug 1 05:54:43 2016 +0700
Committer: Shad Storhaug <shad@shadstorhaug.com>
Committed: Fri Sep 2 22:30:29 2016 +0700

----------------------------------------------------------------------
 .../Ext/ExtendableQueryParser.cs                | 131 +++++++++++++++
 Lucene.Net.QueryParser/Ext/ExtensionQuery.cs    |  54 ++++++
 Lucene.Net.QueryParser/Ext/Extensions.cs        | 167 +++++++++++++++++++
 Lucene.Net.QueryParser/Ext/ParserExtension.cs   |  50 ++++++
 .../Lucene.Net.QueryParser.csproj               |   4 +
 .../Ext/ExtensionStub.cs                        |  30 ++++
 .../Ext/TestExtendableQueryParser.cs            | 145 ++++++++++++++++
 .../Ext/TestExtensions.cs                       |  97 +++++++++++
 .../Lucene.Net.Tests.QueryParser.csproj         |   3 +
 .../Util/QueryParserTestBase.cs                 |   2 +-
 10 files changed, 682 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/Lucene.Net.QueryParser/Ext/ExtendableQueryParser.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.QueryParser/Ext/ExtendableQueryParser.cs b/Lucene.Net.QueryParser/Ext/ExtendableQueryParser.cs
new file mode 100644
index 0000000..6418f87
--- /dev/null
+++ b/Lucene.Net.QueryParser/Ext/ExtendableQueryParser.cs
@@ -0,0 +1,131 @@
+using Lucene.Net.Analysis;
+using Lucene.Net.Search;
+using Lucene.Net.Util;
+using System;
+
+namespace Lucene.Net.QueryParser.Ext
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// The <see cref="ExtendableQueryParser"/> enables arbitrary query parser extension
+    /// based on a customizable field naming scheme. The lucene query syntax allows
+    /// implicit and explicit field definitions as query prefix followed by a colon
+    /// (':') character. The <see cref="ExtendableQueryParser"/> allows to encode extension
+    /// keys into the field symbol associated with a registered instance of
+    /// <see cref="ParserExtension"/>. A customizable separation character separates
the
+    /// extension key from the actual field symbol. The <see cref="ExtendableQueryParser"/>
+    /// splits (<see cref="Extensions.SplitExtensionField(String, String)"/>) the
+    /// extension key from the field symbol and tries to resolve the associated
+    /// <see cref="ParserExtension"/>. If the parser can't resolve the key or the field
+    /// token does not contain a separation character, <see cref="ExtendableQueryParser"/>
+    /// yields the same behavior as its super class <see cref="QueryParser"/>. Otherwise,
+    /// if the key is associated with a <see cref="ParserExtension"/> instance, the
parser
+    /// builds an instance of <see cref="ExtensionQuery"/> to be processed by
+    /// <see cref="ParserExtension.Parse(ExtensionQuery)"/>.If a extension field does
not
+    /// contain a field part the default field for the query will be used.
+    /// <p>
+    /// To guarantee that an extension field is processed with its associated
+    /// extension, the extension query part must escape any special characters like
+    /// '*' or '['. If the extension query contains any whitespace characters, the
+    /// extension query part must be enclosed in quotes.
+    /// Example ('_' used as separation character):
+    /// <pre>
+    ///   title_customExt:"Apache Lucene\?" OR content_customExt:prefix\*
+    /// </pre>
+    /// 
+    /// Search on the default field:
+    /// <pre>
+    ///   _customExt:"Apache Lucene\?" OR _customExt:prefix\*
+    /// </pre>
+    /// </p>
+    /// <p>
+    /// The <see cref="ExtendableQueryParser"/> itself does not implement the logic
how
+    /// field and extension key are separated or ordered. All logic regarding the
+    /// extension key and field symbol parsing is located in <see cref="Extensions"/>.
+    /// Customized extension schemes should be implemented by sub-classing
+    /// <see cref="Extensions"/>.
+    /// </p>
+    /// <p>
+    /// For details about the default encoding scheme see <see cref="Extensions"/>.
+    /// </p>
+    /// 
+    /// <see cref="Extensions"/>
+    /// <see cref="ParserExtension"/>
+    /// <see cref="ExtensionQuery"/>
+    /// </summary>
+    public class ExtendableQueryParser : Classic.QueryParser
+    {
+        private readonly string defaultField;
+        private readonly Extensions extensions;
+
+  
+        /// <summary>
+        ///  Default empty extensions instance
+        /// </summary>
+        private static readonly Extensions DEFAULT_EXTENSION = new Extensions();
+
+        /// <summary>
+        /// Creates a new <see cref="ExtendableQueryParser"/> instance
+        /// </summary>
+        /// <param name="matchVersion">the lucene version to use.</param>
+        /// <param name="f">the default query field</param>
+        /// <param name="a">the analyzer used to find terms in a query string</param>
+        public ExtendableQueryParser(LuceneVersion matchVersion, string f, Analyzer a)
+            : base(matchVersion, f, a)
+        {
+        }
+
+        /// <summary>
+        /// Creates a new <see cref="ExtendableQueryParser"/> instance
+        /// </summary>
+        /// <param name="matchVersion">the lucene version to use.</param>
+        /// <param name="f">the default query field</param>
+        /// <param name="a">the analyzer used to find terms in a query string</param>
+        /// <param name="ext">the query parser extensions</param>
+        public ExtendableQueryParser(LuceneVersion matchVersion, string f, Analyzer a, Extensions
ext)
+            : base(matchVersion, f, a)
+        {
+            this.defaultField = f;
+            this.extensions = ext;
+        }
+
+        /// <summary>
+        /// Returns the extension field delimiter character.
+        /// </summary>
+        /// <returns>the extension field delimiter character.</returns>
+        public char ExtensionFieldDelimiter
+        {
+            get { return extensions.ExtensionFieldDelimiter; }
+        }
+
+        protected internal override Query GetFieldQuery(string field, string queryText, bool
quoted)
+        {
+            Tuple<string, string> splitExtensionField = this.extensions
+                .SplitExtensionField(defaultField, field);
+            ParserExtension extension = this.extensions
+                .GetExtension(splitExtensionField.Item2);
+            if (extension != null)
+            {
+                return extension.Parse(new ExtensionQuery(this, splitExtensionField.Item1,
+                    queryText));
+            }
+            return base.GetFieldQuery(field, queryText, quoted);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/Lucene.Net.QueryParser/Ext/ExtensionQuery.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.QueryParser/Ext/ExtensionQuery.cs b/Lucene.Net.QueryParser/Ext/ExtensionQuery.cs
new file mode 100644
index 0000000..610e4ad
--- /dev/null
+++ b/Lucene.Net.QueryParser/Ext/ExtensionQuery.cs
@@ -0,0 +1,54 @@
+namespace Lucene.Net.QueryParser.Ext
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// <see cref="ExtensionQuery"/> holds all query components extracted from the
original
+    /// query string like the query field and the extension query string.
+    /// </summary>
+    public class ExtensionQuery
+    {
+        /// <summary>
+        /// Creates a new <see cref="ExtensionQuery"/>
+        /// </summary>
+        /// <param name="topLevelParser"></param>
+        /// <param name="field">the query field</param>
+        /// <param name="rawQueryString">the raw extension query string</param>
+        public ExtensionQuery(Classic.QueryParser topLevelParser, string field, string rawQueryString)
+        {
+            this.Field = field;
+            this.RawQueryString = rawQueryString;
+            this.TopLevelParser = topLevelParser;
+        }
+
+        /// <summary>
+        /// Returns the query field
+        /// </summary>
+        public string Field { get; protected set; }
+
+        /// <summary>
+        /// Returns the raw extension query string
+        /// </summary>
+        public string RawQueryString { get; protected set; }
+
+        /// <summary>
+        /// Returns the top level parser which created this <see cref="ExtensionQuery"/>
+        /// </summary>
+        public Classic.QueryParser TopLevelParser { get; protected set; }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/Lucene.Net.QueryParser/Ext/Extensions.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.QueryParser/Ext/Extensions.cs b/Lucene.Net.QueryParser/Ext/Extensions.cs
new file mode 100644
index 0000000..6895268
--- /dev/null
+++ b/Lucene.Net.QueryParser/Ext/Extensions.cs
@@ -0,0 +1,167 @@
+using Lucene.Net.QueryParser.Classic;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Lucene.Net.QueryParser.Ext
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    public class Extensions
+    {
+        private readonly IDictionary<string, ParserExtension> extensions = new Dictionary<string,
ParserExtension>();
+        private readonly char extensionFieldDelimiter;
+
+        /// <summary>
+        /// The default extension field delimiter character. This constant is set to ':'
+        /// </summary>
+        public static readonly char DEFAULT_EXTENSION_FIELD_DELIMITER = ':';
+
+        /// <summary>
+        /// Creates a new <see cref="Extensions"/> instance with the
+        /// <see cref="#DEFAULT_EXTENSION_FIELD_DELIMITER"/> as a delimiter character.
+        /// </summary>
+        public Extensions()
+            : this(DEFAULT_EXTENSION_FIELD_DELIMITER)
+        {
+        }
+
+        /// <summary>
+        /// Creates a new <see cref="Extensions"/> instance
+        /// </summary>
+        /// <param name="extensionFieldDelimiter">the extensions field delimiter character</param>
+        public Extensions(char extensionFieldDelimiter)
+        {
+            this.extensionFieldDelimiter = extensionFieldDelimiter;
+        }
+
+        /// <summary>
+        /// Adds a new <see cref="ParserExtension"/> instance associated with the given
key.
+        /// </summary>
+        /// <param name="key">the parser extension key</param>
+        /// <param name="extension">the parser extension</param>
+        public virtual void Add(string key, ParserExtension extension)
+        {
+            this.extensions[key] = extension;
+        }
+
+        /// <summary>
+        /// Returns the <see cref="ParserExtension"/> instance for the given key or
+        /// <code>null</code> if no extension can be found for the key.
+        /// </summary>
+        /// <param name="key">the extension key</param>
+        /// <returns>the <see cref="ParserExtension"/> instance for the given
key or
+        /// <code>null</code> if no extension can be found for the key.</returns>
+        public ParserExtension GetExtension(string key)
+        {
+            if (key == null || !this.extensions.ContainsKey(key)) return null;
+            return this.extensions[key];
+        }
+
+        /// <summary>
+        /// Returns the extension field delimiter
+        /// </summary>
+        public virtual char ExtensionFieldDelimiter
+        {
+            get { return extensionFieldDelimiter; }
+        }
+
+        /// <summary>
+        /// Splits a extension field and returns the field / extension part as a
+        /// <see cref="Tuple{String,String}"/>. This method tries to split on the first
occurrence of the
+        /// extension field delimiter, if the delimiter is not present in the string
+        /// the result will contain a <code>null</code> value for the extension
key and
+        /// the given field string as the field value. If the given extension field
+        /// string contains no field identifier the result pair will carry the given
+        /// default field as the field value.
+        /// </summary>
+        /// <param name="defaultField">the default query field</param>
+        /// <param name="field">the extension field string</param>
+        /// <returns>a {<see cref="Tuple{String,String}"/> with the field name
as the <see cref="Tuple{String,String}.Item1"/> and the
+        /// extension key as the <see cref="Tuple{String,String}.Item2"/></returns>
+        public Tuple<string, string> SplitExtensionField(string defaultField, string
field)
+        {
+            int indexOf = field.IndexOf(this.extensionFieldDelimiter);
+            if (indexOf < 0)
+                return new Tuple<string, string>(field, null);
+            string indexField = indexOf == 0 ? defaultField : field.Substring(0, indexOf);
+            string extensionKey = field.Substring(indexOf + 1);
+            return new Tuple<string, string>(indexField, extensionKey);
+        }
+
+        /// <summary>
+        /// Escapes an extension field. The default implementation is equivalent to
+        /// <see cref="QueryParser.Escape(String)"/>.
+        /// </summary>
+        /// <param name="extfield">the extension field identifier</param>
+        /// <returns>the extension field identifier with all special chars escaped
with
+        /// a backslash character.</returns>
+        public string EscapeExtensionField(string extfield)
+        {
+            return QueryParserBase.Escape(extfield);
+        }
+
+        /// <summary>
+        /// Builds an extension field string from a given extension key and the default
+        /// query field. The default field and the key are delimited with the extension
+        /// field delimiter character. This method makes no assumption about the order
+        /// of the extension key and the field. By default the extension key is
+        /// appended to the end of the returned string while the field is added to the
+        /// beginning. Special Query characters are escaped in the result.
+        /// <p>
+        /// Note: <see cref="Extensions"/> subclasses must maintain the contract between
+        /// <see cref="M:BuildExtensionField(String)"/> and
+        /// <see cref="M:BuildExtensionField(String, String)"/> where the latter inverts
the
+        /// former.
+        /// </p>
+        /// </summary>
+        /// <param name="extensionKey">the extension key</param>
+        /// <returns>escaped extension field identifier</returns>
+        public string BuildExtensionField(string extensionKey)
+        {
+            return BuildExtensionField(extensionKey, "");
+        }
+
+        /// <summary>
+        /// Builds an extension field string from a given extension key and the default
+        /// query field. The default field and the key are delimited with the extension
+        /// field delimiter character. This method makes no assumption about the order
+        /// of the extension key and the field. By default the extension key is
+        /// appended to the end of the returned string while the field is added to the
+        /// beginning. Special Query characters are escaped in the result.
+        /// <p>
+        /// Note: <see cref="Extensions"/> subclasses must maintain the contract between
+        /// <see cref="M:BuildExtensionField(String)"/> and
+        /// <see cref="M:BuildExtensionField(String, String)"/> where the latter inverts
the
+        /// former.
+        /// </summary>
+        /// <param name="extensionKey">the extension key</param>
+        /// <param name="field">the field to apply the extension on.</param>
+        /// <returns>escaped extension field identifier</returns>
+        /// <remarks>See <see cref="M:BuildExtensionField(String)"/> to use the
default query field</remarks>
+        public string BuildExtensionField(string extensionKey, string field)
+        {
+            StringBuilder builder = new StringBuilder(field);
+            builder.Append(this.extensionFieldDelimiter);
+            builder.Append(extensionKey);
+            return EscapeExtensionField(builder.ToString());
+        }
+
+        // NOTE: Pair<T, T> was eliminated in favor of the built in Tuple<T, T>
type.
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/Lucene.Net.QueryParser/Ext/ParserExtension.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.QueryParser/Ext/ParserExtension.cs b/Lucene.Net.QueryParser/Ext/ParserExtension.cs
new file mode 100644
index 0000000..27b9212
--- /dev/null
+++ b/Lucene.Net.QueryParser/Ext/ParserExtension.cs
@@ -0,0 +1,50 @@
+using Lucene.Net.QueryParser.Classic;
+using Lucene.Net.Search;
+
+namespace Lucene.Net.QueryParser.Ext
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// This class represents an extension base class to the Lucene standard
+    /// <see cref="Classic.QueryParser"/>. The
+    /// <see cref="Classic.QueryParser"/> is generated by the JavaCC
+    /// parser generator. Changing or adding functionality or syntax in the standard
+    /// query parser requires changes to the JavaCC source file. To enable extending
+    /// the standard query parser without changing the JavaCC sources and re-generate
+    /// the parser the <see cref="ParserExtension"/> can be customized and plugged
into an
+    /// instance of <see cref="ExtendableQueryParser"/>, a direct subclass of
+    /// <see cref="Classic.QueryParser"/>.
+    ///  
+    /// <see cref="Extensions"/>
+    /// <see cref="ExtendableQueryParser"/>
+    /// </summary>
+    public abstract class ParserExtension
+    {
+        /// <summary>
+        /// Processes the given <see cref="ExtensionQuery"/> and returns a corresponding
+        /// <see cref="Query"/> instance. Subclasses must either return a <see cref="Query"/>
+        /// instance or raise a <see cref="ParseException"/>. This method must not
return
+        /// <code>null</code>.
+        /// </summary>
+        /// <param name="query">the extension query</param>
+        /// <returns>a new query instance</returns>
+        /// <exception cref="ParseException">if the query can not be parsed.</exception>
+        public abstract Query Parse(ExtensionQuery query);
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/Lucene.Net.QueryParser/Lucene.Net.QueryParser.csproj
----------------------------------------------------------------------
diff --git a/Lucene.Net.QueryParser/Lucene.Net.QueryParser.csproj b/Lucene.Net.QueryParser/Lucene.Net.QueryParser.csproj
index 0b18336..b42ed22 100644
--- a/Lucene.Net.QueryParser/Lucene.Net.QueryParser.csproj
+++ b/Lucene.Net.QueryParser/Lucene.Net.QueryParser.csproj
@@ -51,6 +51,10 @@
     <Compile Include="Classic\Token.cs" />
     <Compile Include="Classic\TokenMgrError.cs" />
     <Compile Include="ComplexPhrase\ComplexPhraseQueryParser.cs" />
+    <Compile Include="Ext\ExtendableQueryParser.cs" />
+    <Compile Include="Ext\ExtensionQuery.cs" />
+    <Compile Include="Ext\Extensions.cs" />
+    <Compile Include="Ext\ParserExtension.cs" />
     <Compile Include="Flexible\Standard\CommonQueryParserConfiguration.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/Lucene.Net.Tests.QueryParser/Ext/ExtensionStub.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Tests.QueryParser/Ext/ExtensionStub.cs b/Lucene.Net.Tests.QueryParser/Ext/ExtensionStub.cs
new file mode 100644
index 0000000..cbef5d8
--- /dev/null
+++ b/Lucene.Net.Tests.QueryParser/Ext/ExtensionStub.cs
@@ -0,0 +1,30 @@
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+
+namespace Lucene.Net.QueryParser.Ext
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    internal class ExtensionStub : ParserExtension
+    {
+        public override Query Parse(ExtensionQuery components)
+        {
+            return new TermQuery(new Term(components.Field, components.RawQueryString));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/Lucene.Net.Tests.QueryParser/Ext/TestExtendableQueryParser.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Tests.QueryParser/Ext/TestExtendableQueryParser.cs b/Lucene.Net.Tests.QueryParser/Ext/TestExtendableQueryParser.cs
new file mode 100644
index 0000000..7e2e99e
--- /dev/null
+++ b/Lucene.Net.Tests.QueryParser/Ext/TestExtendableQueryParser.cs
@@ -0,0 +1,145 @@
+using Lucene.Net.Analysis;
+using Lucene.Net.QueryParser.Classic;
+using Lucene.Net.Search;
+using NUnit.Framework;
+using System.Globalization;
+
+namespace Lucene.Net.QueryParser.Ext
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// Testcase for the class <see cref="ExtendableQueryParser"/>
+    /// </summary>
+    [TestFixture]
+    public class TestExtendableQueryParser : TestQueryParser
+    {
+        private static char[] DELIMITERS = new char[] {
+            Extensions.DEFAULT_EXTENSION_FIELD_DELIMITER, '-', '|' };
+
+        public override Classic.QueryParser GetParser(Analyzer a)
+        {
+            return GetParser(a, null);
+        }
+
+        public Classic.QueryParser GetParser(Analyzer a, Extensions extensions)
+        {
+            if (a == null)
+                a = new MockAnalyzer(Random(), MockTokenizer.SIMPLE, true);
+            Classic.QueryParser qp = extensions == null ? new ExtendableQueryParser(
+                TEST_VERSION_CURRENT, DefaultField, a) : new ExtendableQueryParser(
+                TEST_VERSION_CURRENT, DefaultField, a, extensions);
+            qp.DefaultOperator = QueryParserBase.OR_OPERATOR;
+            return qp;
+        }
+
+        [Test]
+        public void TestUnescapedExtDelimiter()
+        {
+            Extensions ext = NewExtensions(':');
+            ext.Add("testExt", new ExtensionStub());
+            ExtendableQueryParser parser = (ExtendableQueryParser)GetParser(null, ext);
+            try
+            {
+                parser.Parse("aField:testExt:\"foo \\& bar\"");
+                fail("extension field delimiter is not escaped");
+            }
+            catch (ParseException e)
+            {
+            }
+        }
+
+        [Test]
+        public void TestExtFieldUnqoted()
+        {
+            for (int i = 0; i < DELIMITERS.Length; i++)
+            {
+                Extensions ext = NewExtensions(DELIMITERS[i]);
+                ext.Add("testExt", new ExtensionStub());
+                ExtendableQueryParser parser = (ExtendableQueryParser)GetParser(null,
+                    ext);
+                string field = ext.BuildExtensionField("testExt", "aField");
+                Query query = parser.Parse(string.Format(CultureInfo.InvariantCulture, "{0}:foo
bar", field));
+                assertTrue("expected instance of BooleanQuery but was "
+                    + query.GetType(), query is BooleanQuery);
+                BooleanQuery bquery = (BooleanQuery)query;
+                BooleanClause[] clauses = bquery.Clauses;
+                assertEquals(2, clauses.Length);
+                BooleanClause booleanClause = clauses[0];
+                query = booleanClause.Query;
+                assertTrue("expected instance of TermQuery but was " + query.GetType(),
+                    query is TermQuery);
+                TermQuery tquery = (TermQuery)query;
+                assertEquals("aField", tquery.Term
+                    .Field);
+                assertEquals("foo", tquery.Term.Text());
+
+                booleanClause = clauses[1];
+                query = booleanClause.Query;
+                assertTrue("expected instance of TermQuery but was " + query.GetType(),
+                    query is TermQuery);
+                tquery = (TermQuery)query;
+                assertEquals(DefaultField, tquery.Term.Field);
+                assertEquals("bar", tquery.Term.Text());
+            }
+        }
+
+        [Test]
+        public void TestExtDefaultField()
+        {
+            for (int i = 0; i < DELIMITERS.Length; i++)
+            {
+                Extensions ext = NewExtensions(DELIMITERS[i]);
+                ext.Add("testExt", new ExtensionStub());
+                ExtendableQueryParser parser = (ExtendableQueryParser)GetParser(null,
+                    ext);
+                string field = ext.BuildExtensionField("testExt");
+                Query parse = parser.Parse(string.Format(CultureInfo.InvariantCulture, "{0}:\"foo
\\& bar\"", field));
+                assertTrue("expected instance of TermQuery but was " + parse.GetType(),
+                    parse is TermQuery);
+                TermQuery tquery = (TermQuery)parse;
+                assertEquals(DefaultField, tquery.Term.Field);
+                assertEquals("foo & bar", tquery.Term.Text());
+            }
+        }
+
+        public Extensions NewExtensions(char delimiter)
+        {
+            return new Extensions(delimiter);
+        }
+
+        [Test]
+        public void TestExtField()
+        {
+            for (int i = 0; i < DELIMITERS.Length; i++)
+            {
+                Extensions ext = NewExtensions(DELIMITERS[i]);
+                ext.Add("testExt", new ExtensionStub());
+                ExtendableQueryParser parser = (ExtendableQueryParser)GetParser(null,
+                    ext);
+                string field = ext.BuildExtensionField("testExt", "afield");
+                Query parse = parser.Parse(string.Format(CultureInfo.InvariantCulture, "{0}:\"foo
\\& bar\"", field));
+                assertTrue("expected instance of TermQuery but was " + parse.GetType(),
+                    parse is TermQuery);
+                TermQuery tquery = (TermQuery)parse;
+                assertEquals("afield", tquery.Term.Field);
+                assertEquals("foo & bar", tquery.Term.Text());
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/Lucene.Net.Tests.QueryParser/Ext/TestExtensions.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Tests.QueryParser/Ext/TestExtensions.cs b/Lucene.Net.Tests.QueryParser/Ext/TestExtensions.cs
new file mode 100644
index 0000000..4850987
--- /dev/null
+++ b/Lucene.Net.Tests.QueryParser/Ext/TestExtensions.cs
@@ -0,0 +1,97 @@
+using Lucene.Net.Util;
+using NUnit.Framework;
+using System;
+
+namespace Lucene.Net.QueryParser.Ext
+{
+    /*
+     * Licensed to the Apache Software Foundation (ASF) under one or more
+     * contributor license agreements.  See the NOTICE file distributed with
+     * this work for additional information regarding copyright ownership.
+     * The ASF licenses this file to You under the Apache License, Version 2.0
+     * (the "License"); you may not use this file except in compliance with
+     * the License.  You may obtain a copy of the License at
+     *
+     *     http://www.apache.org/licenses/LICENSE-2.0
+     *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     * See the License for the specific language governing permissions and
+     * limitations under the License.
+     */
+
+    /// <summary>
+    /// Testcase for the <see cref="Extensions"/> class
+    /// </summary>
+    [TestFixture]
+    public class TestExtensions : LuceneTestCase
+    {
+        private Extensions ext;
+
+        public override void SetUp()
+        {
+            base.SetUp();
+            this.ext = new Extensions();
+        }
+
+        [Test]
+        public void TestBuildExtensionField()
+        {
+            assertEquals("field\\:key", ext.BuildExtensionField("key", "field"));
+            assertEquals("\\:key", ext.BuildExtensionField("key"));
+
+            ext = new Extensions('.');
+            assertEquals("field.key", ext.BuildExtensionField("key", "field"));
+            assertEquals(".key", ext.BuildExtensionField("key"));
+        }
+
+        [Test]
+        public void TestSplitExtensionField()
+        {
+            assertEquals("field\\:key", ext.BuildExtensionField("key", "field"));
+            assertEquals("\\:key", ext.BuildExtensionField("key"));
+            
+            ext = new Extensions('.');
+            assertEquals("field.key", ext.BuildExtensionField("key", "field"));
+            assertEquals(".key", ext.BuildExtensionField("key"));
+        }
+
+        [Test]
+        public void TestAddGetExtension()
+        {
+            ParserExtension extension = new ExtensionStub();
+            assertNull(ext.GetExtension("foo"));
+            ext.Add("foo", extension);
+            Assert.AreSame(extension, ext.GetExtension("foo"));
+            ext.Add("foo", null);
+            assertNull(ext.GetExtension("foo"));
+        }
+
+        [Test]
+        public void TestGetExtDelimiter()
+        {
+            assertEquals(Extensions.DEFAULT_EXTENSION_FIELD_DELIMITER, this.ext
+                .ExtensionFieldDelimiter);
+            ext = new Extensions('?');
+            assertEquals('?', this.ext.ExtensionFieldDelimiter);
+        }
+
+        [Test]
+        public void TestEscapeExtension()
+        {
+            assertEquals("abc\\:\\?\\{\\}\\[\\]\\\\\\(\\)\\+\\-\\!\\~", ext
+                .EscapeExtensionField("abc:?{}[]\\()+-!~"));
+            try
+            {
+                ext.EscapeExtensionField(null);
+                fail("should throw NPE - escape string is null");
+            }
+            //catch (NullPointerException e)
+            catch (Exception e)
+            {
+                // 
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/Lucene.Net.Tests.QueryParser/Lucene.Net.Tests.QueryParser.csproj
----------------------------------------------------------------------
diff --git a/Lucene.Net.Tests.QueryParser/Lucene.Net.Tests.QueryParser.csproj b/Lucene.Net.Tests.QueryParser/Lucene.Net.Tests.QueryParser.csproj
index b263dc8..adab182 100644
--- a/Lucene.Net.Tests.QueryParser/Lucene.Net.Tests.QueryParser.csproj
+++ b/Lucene.Net.Tests.QueryParser/Lucene.Net.Tests.QueryParser.csproj
@@ -48,6 +48,9 @@
     <Compile Include="Classic\TestMultiPhraseQueryParsing.cs" />
     <Compile Include="Classic\TestQueryParser.cs" />
     <Compile Include="ComplexPhrase\TestComplexPhraseQuery.cs" />
+    <Compile Include="Ext\ExtensionStub.cs" />
+    <Compile Include="Ext\TestExtendableQueryParser.cs" />
+    <Compile Include="Ext\TestExtensions.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Classic\TestMultiAnalyzer.cs" />
     <Compile Include="Util\QueryParserTestBase.cs" />

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/e45f3289/Lucene.Net.Tests.QueryParser/Util/QueryParserTestBase.cs
----------------------------------------------------------------------
diff --git a/Lucene.Net.Tests.QueryParser/Util/QueryParserTestBase.cs b/Lucene.Net.Tests.QueryParser/Util/QueryParserTestBase.cs
index bcf8792..b879008 100644
--- a/Lucene.Net.Tests.QueryParser/Util/QueryParserTestBase.cs
+++ b/Lucene.Net.Tests.QueryParser/Util/QueryParserTestBase.cs
@@ -114,7 +114,7 @@ namespace Lucene.Net.QueryParser.Util
         }
 
         // Moved from TestQueryParser
-        public Classic.QueryParser GetParser(Analyzer a)
+        public virtual Classic.QueryParser GetParser(Analyzer a)
         {
             if (a == null) a = new MockAnalyzer(Random(), MockTokenizer.SIMPLE, true);
             Classic.QueryParser qp = new Classic.QueryParser(TEST_VERSION_CURRENT, DefaultField,
a);


Mime
View raw message