blob: d0d30d93ff6cdbeb5bfa043054f91d5c45c8c6dd [file] [log] [blame]
using Lucene.Net.QueryParsers.Flexible.Core.Messages;
using Lucene.Net.QueryParsers.Flexible.Core.Parser;
using Lucene.Net.QueryParsers.Flexible.Core.Util;
using Lucene.Net.QueryParsers.Flexible.Messages;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Resources;
namespace Lucene.Net.QueryParsers.Flexible.Core.Nodes
{
/*
* 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>
/// A <see cref="QueryNode"/> is the default implementation of the interface
/// <see cref="IQueryNode"/>
/// </summary>
public abstract class QueryNode : IQueryNode
#if FEATURE_CLONEABLE
, System.ICloneable
#endif
{
/// <summary>
/// index default field
/// </summary>
// TODO remove PLAINTEXT_FIELD_NAME replacing it with configuration APIs
public static readonly string PLAINTEXT_FIELD_NAME = "_plain";
private bool isLeaf = true;
private Dictionary<string, object> tags = new Dictionary<string, object>();
private List<IQueryNode> clauses = null;
protected virtual void Allocate()
{
if (this.clauses == null)
{
this.clauses = new List<IQueryNode>();
}
else
{
this.clauses.Clear();
}
}
public void Add(IQueryNode child)
{
if (IsLeaf || this.clauses == null || child == null)
{
throw new ArgumentException(NLS
.GetLocalizedMessage(QueryParserMessages.NODE_ACTION_NOT_SUPPORTED));
}
this.clauses.Add(child);
((QueryNode)child).SetParent(this);
}
public void Add(IList<IQueryNode> children)
{
if (IsLeaf || this.clauses == null)
{
throw new ArgumentException(NLS
.GetLocalizedMessage(QueryParserMessages.NODE_ACTION_NOT_SUPPORTED));
}
foreach (IQueryNode child in children)
{
Add(child);
}
}
public virtual bool IsLeaf
{
get => this.isLeaf;
protected set => this.isLeaf = value;
}
public void Set(IList<IQueryNode> children)
{
if (IsLeaf || this.clauses == null)
{
var factory = NLS.GetResourceManagerFactory();
ResourceManager bundle = factory.Create(typeof(QueryParserMessages));
string message;
try
{
message = bundle.GetString(QueryParserMessages.NODE_ACTION_NOT_SUPPORTED);
}
finally
{
factory.Release(bundle);
}
throw new ArgumentException(message);
}
// reset parent value
foreach (IQueryNode child in children)
{
child.RemoveFromParent();
}
// LUCENENET specific: GetChildren already creates a new list, there is
// no need to do it again here and have another O(n) operation
IList<IQueryNode> existingChildren = GetChildren();
foreach (IQueryNode existingChild in existingChildren)
{
existingChild.RemoveFromParent();
}
// allocate new children list
Allocate();
// add new children and set parent
Add(children);
}
public virtual IQueryNode CloneTree()
{
QueryNode clone = (QueryNode)this.MemberwiseClone();
clone.isLeaf = this.isLeaf;
// Reset all tags
clone.tags = new Dictionary<string, object>();
// copy children
if (this.clauses != null)
{
List<IQueryNode> localClauses = new List<IQueryNode>();
foreach (IQueryNode clause in this.clauses)
{
localClauses.Add(clause.CloneTree());
}
clone.clauses = localClauses;
}
return clone;
}
public virtual object Clone()
{
return CloneTree();
}
/// <summary>
/// a List for QueryNode object. Returns null, for nodes that do not
/// contain children. All leaf Nodes return null.
/// </summary>
public IList<IQueryNode> GetChildren()
{
if (IsLeaf || this.clauses == null)
{
return null;
}
return new List<IQueryNode>(this.clauses);
}
public virtual void SetTag(string tagName, object value)
{
this.tags[CultureInfo.InvariantCulture.TextInfo.ToLower(tagName)] = value;
}
public virtual void UnsetTag(string tagName)
{
this.tags.Remove(CultureInfo.InvariantCulture.TextInfo.ToLower(tagName));
}
/// <summary>
/// verify if a node contains a tag
/// </summary>
public virtual bool ContainsTag(string tagName)
{
return this.tags.ContainsKey(CultureInfo.InvariantCulture.TextInfo.ToLower(tagName));
}
public virtual object GetTag(string tagName)
{
return this.tags[CultureInfo.InvariantCulture.TextInfo.ToLower(tagName)];
}
// LUCENENET TODO: API - Create TryGetTag method to combine the above 2 operations
private IQueryNode parent = null;
private void SetParent(IQueryNode parent)
{
if (this.parent != parent)
{
this.RemoveFromParent();
this.parent = parent;
}
}
public virtual IQueryNode Parent => this.parent;
protected virtual bool IsRoot => Parent == null;
/// <summary>
/// If set to true the the method toQueryString will not write field names
/// </summary>
protected internal bool m_toQueryStringIgnoreFields = false;
/// <summary>
/// This method is use toQueryString to detect if fld is the default field
/// </summary>
/// <param name="fld">field name</param>
/// <returns>true if fld is the default field</returns>
// TODO: remove this method, it's commonly used by
// <see cref="ToQueryString(IEscapeQuerySyntax)"/>
// to figure out what is the default field, however,
// <see cref="ToQueryString(IEscapeQuerySyntax)"/>
// should receive the default field value directly by parameter
protected virtual bool IsDefaultField(string fld)
{
if (this.m_toQueryStringIgnoreFields)
return true;
if (fld == null)
return true;
if (QueryNode.PLAINTEXT_FIELD_NAME.Equals(StringUtils.ToString(fld), StringComparison.Ordinal))
return true;
return false;
}
/// <summary>
/// Every implementation of this class should return pseudo xml like this:
///
/// For FieldQueryNode: &lt;field start='1' end='2' field='subject' text='foo'/&gt;
/// </summary>
/// <seealso cref="IQueryNode.ToString()"/>
public override string ToString()
{
return base.ToString();
}
/// <summary>
/// Gets a map containing all tags attached to this query node.
/// </summary>
public virtual IDictionary<string, object> TagMap => new Dictionary<string, object>(this.tags);
public virtual void RemoveFromParent()
{
if (this.parent != null)
{
IList<IQueryNode> parentChildren = this.parent.GetChildren();
// LUCENENET NOTE: Loop in reverse so we can remove items
// without screwing up our iterator.
for (int i = parentChildren.Count - 1; i >= 0; i--)
{
if (parentChildren[i] == this)
{
parentChildren.RemoveAt(i);
}
}
this.parent = null;
}
}
// LUCENENET specific - class must implement all members of IQueryNode
public abstract string ToQueryString(IEscapeQuerySyntax escapeSyntaxParser);
}
}