/*
* 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.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lucene.Net.Util;
namespace Lucene.Net.Index
{
///
/// The type of parser for the value of the term.
///
public enum FieldParser
{
String,
Numeric
}
///
/// Base class for the typed enumerators.
///
///
/// There are five implementations of FieldEnumerator for
/// strings, integers, longs, floats, and doubles. The numeric enumerators support both
/// standard Field and NumericField implementations. The string and numeric enumerators
/// have slightly different options, but both should be used within a using statment
/// to close the underlying TermEnum/TermDocs. Refer to the unit tests for usage examples.
///
///
/// The type of data being enumerated.
public abstract class FieldEnumerator : IDisposable
{
///
/// Whether the enumerator will include TermDocs.
///
protected bool includeDocs;
///
/// The underlying TermEnum;
///
private TermEnum termEnum;
///
/// The optional TermDocs.
///
private TermDocs termDocs;
///
/// The specialized TermEnum enumerator.
///
protected TermEnumerator tEnum;
///
/// The specialized TermDoc enumerator.
///
private TermDocEnumerator.TermDocUsingTermsEnumerator tdEnum;
///
/// Whether or not the instance has been disposed.
///
private bool disposed;
///
/// Initialization method called by subclasses to simulate a shared
/// base constructor as generic classes cannot have a parameterized ctor.
///
/// The index reader to read from.
/// The field to enumerate.
protected void Init(IndexReader reader, string field)
{
this.Init(reader, field, true);
}
///
/// Initialization method called by subclasses to simulate a shared
/// base constructor as generic classes cannot have a parameterized ctor.
///
/// The index reader to read from.
/// The field to enumerate.
/// Whether this enumerator will support TermDocs.
protected void Init(IndexReader reader, string fieldName, bool includeDocs)
{
this.termEnum = reader.Terms(new Term(fieldName));
if (includeDocs)
{
this.termDocs = reader.TermDocs();
this.tdEnum = new TermDocEnumerator.TermDocUsingTermsEnumerator(this.termDocs, this.termEnum);
}
this.tEnum = new TermEnumerator(termEnum, termDocs, fieldName, this);
}
///
/// Method to attempt to parse out the value from the encoded string
/// and sets the value of Current.
///
/// The encoded string.
/// True if the value was successfully parsed, false if we reached the
/// end of encoded values in the fiele and only the tries remain.
protected abstract bool TryParse(string s);
///
/// Access the enumerator for the terms.
///
public TermEnumerator Terms
{
get { return this.tEnum; }
}
///
/// Access the enumerator for the TermDocs.
///
public TermDocEnumerator.TermDocUsingTermsEnumerator Docs
{
get
{
if (this.termDocs == null)
{
throw new NotSupportedException("This instance does not support enumeration over the document ids.");
}
else
{
return this.tdEnum;
}
}
}
#region IDisposable Members
///
/// Dispose of the instance.
///
public void Dispose()
{
if (!this.disposed)
{
this.disposed = true;
if (this.termEnum != null)
{
this.termEnum.Close();
}
if (this.termDocs != null)
{
this.termDocs.Close();
}
GC.SuppressFinalize(this);
}
}
#endregion
///
/// The enumerator over the terms in an index.
///
public class TermEnumerator : IEnumerator, IEnumerable
{
///
/// The underlying TermEnum;
///
private TermEnum termEnum;
///
/// The optional TermDocs.
///
private TermDocs termDocs;
///
/// The current term in the enum.
///
private Term currentTerm;
///
/// The field name, if any for the enum.
///
protected string fieldName;
///
/// Whether the enumerator has moved beyond the first position.
///
private bool isFirst = true;
///
/// THe enclosing instance, called back to in order to parse the field.
///
private FieldEnumerator enclosing;
///
/// Ctor.
///
/// The TermEnum to wrap.
/// The TermDocs to wrap.
///
///
public TermEnumerator(TermEnum termEnum, TermDocs termDocs, string field, FieldEnumerator enclosing)
{
this.termEnum = termEnum;
this.termDocs = termDocs;
this.fieldName = field;
this.enclosing = enclosing;
}
#region IEnumerator Members
///
/// The current item in the enumerator.
///
public T Current
{
get;
internal set;
}
#endregion
#region IEnumerator Members
///
/// Current item in the enumerator.
///
object IEnumerator.Current
{
get { return (object)this.Current; }
}
///
/// Advance to the next item.
///
///
public bool MoveNext()
{
if (this.isFirst)
{
this.isFirst = false;
}
else
{
if (!this.termEnum.Next())
{
return false;
}
}
this.currentTerm = termEnum.Term;
if (this.currentTerm == null || (!this.currentTerm.Field.Equals(this.fieldName)))
{
return false;
}
if (this.enclosing.TryParse(this.currentTerm.Text))
{
if (this.termDocs != null)
{
this.termDocs.Seek(this.termEnum);
}
return true;
}
return false;
}
///
/// Reset the enumerator to the beginngin.
///
public void Reset()
{
throw new NotSupportedException("The enumerator cannot be reset");
}
#endregion
#region IDisposable Members
public void Dispose()
{
// noop
}
#endregion
#region IEnumerable Members
///
/// Accessor to IEnumerator-T-."/>
///
///
public IEnumerator GetEnumerator()
{
return this;
}
#endregion
#region IEnumerable Members
///
/// Accessor to IEnumertor.
///
///
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
}
}
///
/// Class to handle creating a TermDocs and allowing for seeking and enumeration. Used
/// when you have a set of one or moreterms for which you want to enumerate over the
/// documents that contain those terms.
///
public class TermDocEnumerator : IEnumerable, IDisposable
{
///
/// The underlying TermDocs.
///
private TermDocs termDocs;
///
/// Ctor.
///
/// The TermDocs to wrap.
public TermDocEnumerator(TermDocs termDocs)
{
this.termDocs = termDocs;
}
///
/// Seek to a specific term.
///
///
public void Seek(Term t)
{
this.termDocs.Seek(t);
}
#region IEnumerable Members
public IEnumerator GetEnumerator()
{
return new TermDocUsingTermsEnumerator(this.termDocs);
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
#region IDisposable Members
///
/// Dispose of the instance, closing the termdocs.
///
public void Dispose()
{
if (this.termDocs != null)
{
termDocs.Close();
}
}
#endregion
///
/// Class to handle enumeration over the TermDocs that does NOT close them
/// on a call to Dispose!
///
public class TermDocUsingTermsEnumerator : IEnumerable, IEnumerator
{
///
/// A reference to an outside TermEnum that is used to position
/// the TermDocs. It can be null.
///
private TermEnum termEnum;
///
/// The underlying TermDocs.
///
private TermDocs termDocs;
///
/// Ctor.
///
/// TermDocs to wrap
internal TermDocUsingTermsEnumerator(TermDocs termDocs)
: this(termDocs, null)
{ }
///
/// Ctor.
///
/// Underlying TermDocs.
/// Enclosing field enum.
internal TermDocUsingTermsEnumerator(TermDocs td, TermEnum termEnum)
{
this.termDocs = td;
this.termEnum = termEnum;
}
///
/// Seel to a term in the underlying TermDocs.
///
/// The point to seek to.
internal void Seek(TermEnum te)
{
this.termDocs.Seek(te);
}
#region IEnumerable Members
///
/// Get the enumerator.
///
///
public IEnumerator GetEnumerator()
{
return this;
}
#endregion
#region IEnumerable Members
///
/// Get the enumerator.
///
///
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
#region IEnumerator Members
///
/// The current document id.
///
public int Current
{
get { return this.termDocs.Doc; }
}
#endregion
#region IDisposable Members
///
/// Dispose impl.
///
public void Dispose()
{
// noop as the closing of the underlying
// TermDocs is handled by the containing class
}
#endregion
#region IEnumerator Members
///
/// The current item.
///
object IEnumerator.Current
{
get { throw new NotImplementedException(); }
}
///
/// Move to the next item.
///
/// True if more, false if not.
public bool MoveNext()
{
return this.termDocs.Next();
}
///
/// Not implemented. Use Seek instead.
///
public void Reset()
{
throw new NotImplementedException();
}
#endregion
}
}
///
/// Implementation for enumerating over terms with a string value.
///
public class StringFieldEnumerator : FieldEnumerator
{
///
/// Construct an enumerator over one field.
///
/// Index reader.
/// The field to read.
public StringFieldEnumerator(IndexReader reader, string fieldName)
{
this.Init(reader, fieldName);
}
///
/// Construct an enumerator over one field.
///
/// Index reader.
/// The field to read.
/// Whether the TermDocs will also be enumerated.
public StringFieldEnumerator(IndexReader reader, string fieldName, bool includeDocs)
{
this.Init(reader, fieldName, includeDocs);
}
///
/// Sets the value of current.
///
/// The string.
/// Always true.
protected override bool TryParse(string s)
{
this.tEnum.Current = s;
return true;
}
}
///
/// Base for enumerating over numeric fields.
///
///
public abstract class NumericFieldEnum : FieldEnumerator
{
///
/// The parser type for the field being enumerated.
///
private FieldParser parser;
///
/// Initialize the instance.
///
/// The reader.
/// The field name.
/// Whether to include a TermDoc enum.
/// The parser to use on the field.
protected void Init(IndexReader reader, string field, bool includeDocs, FieldParser parser)
{
base.Init(reader, field, includeDocs);
this.parser = parser;
}
///
/// Overridden from base.
///
/// String to parse.
///
protected override bool TryParse(string s)
{
if (this.parser == FieldParser.Numeric)
{
return this.TryParseNumeric(s);
}
else
{
return this.TryParseString(s);
}
}
///
/// Parse out a standard string and set the value of current.
///
/// String to parse.
/// True on success.
protected abstract bool TryParseString(string s);
///
/// Parse out an encoded numeric string and set the value of current.
///
/// String to parse.
/// True on success.
protected abstract bool TryParseNumeric(string s);
}
///
/// Implementation for enumerating over all of the terms in an int numeric field.
///
public class IntFieldEnumerator : NumericFieldEnum
{
///
/// Construct an enumerator over one field.
///
/// Index reader.
/// The field to read.
public IntFieldEnumerator(IndexReader reader, string fieldName, FieldParser parser)
{
this.Init(reader, fieldName, true, parser);
}
///
/// Construct an enumerator over one field.
///
/// Index reader.
/// The field to read.
/// Whether the TermDocs will also be enumerated.
public IntFieldEnumerator(IndexReader reader, string fieldName, FieldParser parser, bool includeDocs)
{
this.Init(reader, fieldName, includeDocs, parser);
}
///
/// Parse the int from the string.
///
/// String to parse.
/// Always true.
protected override bool TryParseString(string s)
{
this.tEnum.Current = Int32.Parse(s);
return true;
}
///
/// Parse the int from an encoded string.
///
/// String to parse.
/// True if the parse was successful, false indicating failure
/// and the end of useful terms in the numeric field.
protected override bool TryParseNumeric(string s)
{
int shift = s[0] - NumericUtils.SHIFT_START_INT;
if (shift > 0 && shift <= 31)
{
return false;
}
else
{
this.tEnum.Current = NumericUtils.PrefixCodedToInt(s);
return true;
}
}
}
///
/// Implementation for enumerating over all of the terms in a float numeric field.
///
public class FloatFieldEnumerator : NumericFieldEnum
{
///
/// Construct an enumerator over one field.
///
/// Index reader.
/// The field to read.
public FloatFieldEnumerator(IndexReader reader, string fieldName, FieldParser parser)
{
this.Init(reader, fieldName, true, parser);
}
///
/// Construct an enumerator over one field.
///
/// Index reader.
/// The field to read.
/// Whether the TermDocs will also be enumerated.
public FloatFieldEnumerator(IndexReader reader, string fieldName, FieldParser parser, bool includeDocs)
{
this.Init(reader, fieldName, includeDocs, parser);
}
///
/// Parse the float from a string.
///
/// The string to parse.
/// Always true.
protected override bool TryParseString(string s)
{
this.tEnum.Current = float.Parse(s);
return true;
}
///
/// Parse the float from a numeric field.
///
/// The string to parse.
/// True if the string was parsed, false to signify the
/// end of usable terms in a numeric field.
protected override bool TryParseNumeric(string s)
{
int shift = s[0] - NumericUtils.SHIFT_START_INT;
if (shift > 0 && shift <= 31)
{
return false;
}
else
{
this.tEnum.Current = NumericUtils.SortableIntToFloat(NumericUtils.PrefixCodedToInt(s));
return true;
}
}
}
///
/// Implementation for enumerating over all of the terms in a double numeric field.
///
public class DoubleFieldEnumerator : NumericFieldEnum
{
///
/// Construct an enumerator over one field.
///
/// Index reader.
/// The field to read.
public DoubleFieldEnumerator(IndexReader reader, string fieldName, FieldParser parser)
{
this.Init(reader, fieldName, true, parser);
}
///
/// Construct an enumerator over one field.
///
/// Index reader.
/// The field to read.
/// Whether the TermDocs will also be enumerated.
public DoubleFieldEnumerator(IndexReader reader, string fieldName, FieldParser parser, bool includeDocs)
{
this.Init(reader, fieldName, includeDocs, parser);
}
///
/// Parse the double from a string.
///
/// The string to parse.
/// Always true.
protected override bool TryParseString(string s)
{
this.tEnum.Current = Double.Parse(s);
return true;
}
///
/// Parse the double from a numeric field.
///
/// The string to parse.
/// True if the string was parsed, false to indicate the end
/// of usable numeric terms.
protected override bool TryParseNumeric(string s)
{
int shift = s[0] - NumericUtils.SHIFT_START_LONG;
if (shift > 0 && shift <= 63)
{
return false;
}
else
{
this.tEnum.Current = NumericUtils.SortableLongToDouble(NumericUtils.PrefixCodedToLong(s));
return true;
}
}
}
///
/// Implementation for enumerating over all of the terms in a long numeric field.
///
public class LongFieldEnumerator : NumericFieldEnum
{
///
/// Construct an enumerator over one field.
///
/// Index reader.
/// The field to read.
public LongFieldEnumerator(IndexReader reader, string fieldName, FieldParser parser)
{
this.Init(reader, fieldName, true, parser);
}
///
/// Construct an enumerator over one field.
///
/// Index reader.
/// The field to read.
/// Whether the TermDocs will also be enumerated.
public LongFieldEnumerator(IndexReader reader, string fieldName, FieldParser parser, bool includeDocs)
{
this.Init(reader, fieldName, includeDocs, parser);
}
///
/// Parse the long from a string.
///
/// The string to parse.
/// Always true.
protected override bool TryParseString(string s)
{
this.tEnum.Current = long.Parse(s);
return true;
}
///
/// Parse the long from a numeric field.
///
/// The string to parse.
/// True if the string was parsed, false to inidicate
/// the end of usable terms in a numeric field.
protected override bool TryParseNumeric(string s)
{
int shift = s[0] - NumericUtils.SHIFT_START_LONG;
if (shift > 0 && shift <= 63)
{
return false;
}
else
{
this.tEnum.Current = NumericUtils.PrefixCodedToLong(s);
return true;
}
}
}
}