/* 
 * 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.Generic;
using Lucene.Net.Documents;
using Document = Lucene.Net.Documents.Document;
using FieldSelector = Lucene.Net.Documents.FieldSelector;
using Lucene.Net.Store;
using Similarity = Lucene.Net.Search.Similarity;

namespace Lucene.Net.Index
{
	
	/// <summary>IndexReader is an abstract class, providing an interface for accessing an
	/// index.  Search of an index is done entirely through this abstract interface,
	/// so that any subclass which implements it is searchable.
	/// <p/> Concrete subclasses of IndexReader are usually constructed with a call to
	/// one of the static <c>open()</c> methods, e.g. <see cref="Open(Lucene.Net.Store.Directory, bool)" />
	///.
	/// <p/> For efficiency, in this API documents are often referred to via
	/// <i>document numbers</i>, non-negative integers which each name a unique
	/// document in the index.  These document numbers are ephemeral--they may change
	/// as documents are added to and deleted from an index.  Clients should thus not
	/// rely on a given document having the same number between sessions.
	/// <p/> An IndexReader can be opened on a directory for which an IndexWriter is
	/// opened already, but it cannot be used to delete documents from the index then.
	/// <p/>
	/// <b>NOTE</b>: for backwards API compatibility, several methods are not listed 
	/// as abstract, but have no useful implementations in this base class and 
	/// instead always throw UnsupportedOperationException.  Subclasses are 
	/// strongly encouraged to override these methods, but in many cases may not 
	/// need to.
	/// <p/>
	/// <p/>
	/// <b>NOTE</b>: as of 2.4, it's possible to open a read-only
	/// IndexReader using the static open methods that accepts the
	/// boolean readOnly parameter.  Such a reader has better
	/// better concurrency as it's not necessary to synchronize on the
	/// isDeleted method.  You must explicitly specify false
	/// if you want to make changes with the resulting IndexReader.
	/// <p/>
	/// <a name="thread-safety"></a><p/><b>NOTE</b>: <see cref="IndexReader" />
	/// instances are completely thread
	/// safe, meaning multiple threads can call any of its methods,
	/// concurrently.  If your application requires external
	/// synchronization, you should <b>not</b> synchronize on the
	/// <c>IndexReader</c> instance; use your own
	/// (non-Lucene) objects instead.
	/// </summary>
	public abstract class IndexReader : System.ICloneable, System.IDisposable
	{
		private class AnonymousClassFindSegmentsFile : SegmentInfos.FindSegmentsFile
		{
			private void  InitBlock(Lucene.Net.Store.Directory directory2)
			{
				this.directory2 = directory2;
			}
			private Lucene.Net.Store.Directory directory2;
			internal AnonymousClassFindSegmentsFile(Lucene.Net.Store.Directory directory2, Lucene.Net.Store.Directory Param1):base(Param1)
			{
				InitBlock(directory2);
			}
			public override System.Object DoBody(System.String segmentFileName)
			{
				return (long) directory2.FileModified(segmentFileName);
			}
		}
		
		/// <summary> Constants describing field properties, for example used for
		/// <see cref="IndexReader.GetFieldNames(FieldOption)" />.
		/// </summary>
		public sealed class FieldOption
		{
			private readonly System.String option;
			internal FieldOption()
			{
			}
			internal FieldOption(System.String option)
			{
				this.option = option;
			}
			public override System.String ToString()
			{
				return this.option;
			}
			/// <summary>All fields </summary>
			public static readonly FieldOption ALL = new FieldOption("ALL");
			/// <summary>All indexed fields </summary>
			public static readonly FieldOption INDEXED = new FieldOption("INDEXED");
			/// <summary>All fields that store payloads </summary>
			public static readonly FieldOption STORES_PAYLOADS = new FieldOption("STORES_PAYLOADS");
			/// <summary>All fields that omit tf </summary>
			public static readonly FieldOption OMIT_TERM_FREQ_AND_POSITIONS = new FieldOption("OMIT_TERM_FREQ_AND_POSITIONS");
			/// <summary>All fields which are not indexed </summary>
			public static readonly FieldOption UNINDEXED = new FieldOption("UNINDEXED");
			/// <summary>All fields which are indexed with termvectors enabled </summary>
			public static readonly FieldOption INDEXED_WITH_TERMVECTOR = new FieldOption("INDEXED_WITH_TERMVECTOR");
			/// <summary>All fields which are indexed but don't have termvectors enabled </summary>
			public static readonly FieldOption INDEXED_NO_TERMVECTOR = new FieldOption("INDEXED_NO_TERMVECTOR");
			/// <summary>All fields with termvectors enabled. Please note that only standard termvector fields are returned </summary>
			public static readonly FieldOption TERMVECTOR = new FieldOption("TERMVECTOR");
			/// <summary>All fields with termvectors with position values enabled </summary>
			public static readonly FieldOption TERMVECTOR_WITH_POSITION = new FieldOption("TERMVECTOR_WITH_POSITION");
			/// <summary>All fields with termvectors with offset values enabled </summary>
			public static readonly FieldOption TERMVECTOR_WITH_OFFSET = new FieldOption("TERMVECTOR_WITH_OFFSET");
			/// <summary>All fields with termvectors with offset values and position values enabled </summary>
			public static readonly FieldOption TERMVECTOR_WITH_POSITION_OFFSET = new FieldOption("TERMVECTOR_WITH_POSITION_OFFSET");
		}
		
		private bool closed;
		protected internal bool hasChanges;
		
		private int refCount;
		
		protected internal static int DEFAULT_TERMS_INDEX_DIVISOR = 1;

	    /// <summary>Expert: returns the current refCount for this reader </summary>
	    public virtual int RefCount
	    {
	        get
	        {
	            lock (this)
	            {
	                return refCount;
	            }
	        }
	    }

	    /// <summary> Expert: increments the refCount of this IndexReader
		/// instance.  RefCounts are used to determine when a
		/// reader can be closed safely, i.e. as soon as there are
		/// no more references.  Be sure to always call a
		/// corresponding <see cref="DecRef" />, in a finally clause;
		/// otherwise the reader may never be closed.  Note that
		/// <see cref="Close" /> simply calls decRef(), which means that
		/// the IndexReader will not really be closed until <see cref="DecRef" />
		/// has been called for all outstanding
		/// references.
		/// 
		/// </summary>
		/// <seealso cref="DecRef">
		/// </seealso>
		public virtual void  IncRef()
		{
			lock (this)
			{
				System.Diagnostics.Debug.Assert(refCount > 0);
				EnsureOpen();
				refCount++;
			}
		}
		
		/// <summary> Expert: decreases the refCount of this IndexReader
		/// instance.  If the refCount drops to 0, then pending
		/// changes (if any) are committed to the index and this
		/// reader is closed.
		/// 
		/// </summary>
		/// <throws>  IOException in case an IOException occurs in commit() or doClose() </throws>
		/// <summary> 
		/// </summary>
		/// <seealso cref="IncRef">
		/// </seealso>
		public virtual void  DecRef()
		{
			lock (this)
			{
				System.Diagnostics.Debug.Assert(refCount > 0);
				EnsureOpen();
				if (refCount == 1)
				{
					Commit();
					DoClose();
				}
				refCount--;
			}
		}
		
		protected internal IndexReader()
		{
			refCount = 1;
		}
		
		/// <throws>  AlreadyClosedException if this IndexReader is closed </throws>
        protected internal void EnsureOpen()
		{
		    if (refCount <= 0)
		    {
		        throw new AlreadyClosedException("this IndexReader is closed");
		    }
		}
		
		/// <summary>Returns an IndexReader reading the index in the given
		/// Directory.  You should pass readOnly=true, since it
		/// gives much better concurrent performance, unless you
		/// intend to do write operations (delete documents or
		/// change norms) with the reader.
		/// </summary>
		/// <param name="directory">the index directory</param>
        /// <param name="readOnly">true if no changes (deletions, norms) will be made with this IndexReader</param>
        /// <exception cref="CorruptIndexException">CorruptIndexException if the index is corrupt</exception>
        /// <exception cref="System.IO.IOException">IOException if there is a low-level IO error</exception>
		public static IndexReader Open(Directory directory, bool readOnly)
		{
			return Open(directory, null, null, readOnly, DEFAULT_TERMS_INDEX_DIVISOR);
		}
		
		/// <summary>Expert: returns an IndexReader reading the index in the given
		/// <see cref="IndexCommit" />.  You should pass readOnly=true, since it
		/// gives much better concurrent performance, unless you
		/// intend to do write operations (delete documents or
		/// change norms) with the reader.
		/// </summary>
		/// <param name="commit">the commit point to open
		/// </param>
		/// <param name="readOnly">true if no changes (deletions, norms) will be made with this IndexReader
		/// </param>
		/// <throws>  CorruptIndexException if the index is corrupt </throws>
		/// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public static IndexReader Open(IndexCommit commit, bool readOnly)
		{
			return Open(commit.Directory, null, commit, readOnly, DEFAULT_TERMS_INDEX_DIVISOR);
		}
		
		/// <summary>Expert: returns an IndexReader reading the index in
		/// the given Directory, with a custom <see cref="IndexDeletionPolicy" />
		///.  You should pass readOnly=true,
		/// since it gives much better concurrent performance,
		/// unless you intend to do write operations (delete
		/// documents or change norms) with the reader.
		/// </summary>
		/// <param name="directory">the index directory
		/// </param>
		/// <param name="deletionPolicy">a custom deletion policy (only used
		/// if you use this reader to perform deletes or to set
		/// norms); see <see cref="IndexWriter" /> for details.
		/// </param>
		/// <param name="readOnly">true if no changes (deletions, norms) will be made with this IndexReader
		/// </param>
		/// <throws>  CorruptIndexException if the index is corrupt </throws>
		/// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public static IndexReader Open(Directory directory, IndexDeletionPolicy deletionPolicy, bool readOnly)
		{
			return Open(directory, deletionPolicy, null, readOnly, DEFAULT_TERMS_INDEX_DIVISOR);
		}

		/// <summary>Expert: returns an IndexReader reading the index in
		/// the given Directory, with a custom <see cref="IndexDeletionPolicy" />
		///.  You should pass readOnly=true,
		/// since it gives much better concurrent performance,
		/// unless you intend to do write operations (delete
		/// documents or change norms) with the reader.
		/// </summary>
		/// <param name="directory">the index directory
		/// </param>
		/// <param name="deletionPolicy">a custom deletion policy (only used
		/// if you use this reader to perform deletes or to set
		/// norms); see <see cref="IndexWriter" /> for details.
		/// </param>
		/// <param name="readOnly">true if no changes (deletions, norms) will be made with this IndexReader
		/// </param>
		/// <param name="termInfosIndexDivisor">Subsamples which indexed
		/// terms are loaded into RAM. This has the same effect as <see>
		///                                                          <cref>IndexWriter.SetTermIndexInterval</cref>
		///                                                        </see> except that setting
		/// must be done at indexing time while this setting can be
		/// set per reader.  When set to N, then one in every
		/// N*termIndexInterval terms in the index is loaded into
		/// memory.  By setting this to a value > 1 you can reduce
		/// memory usage, at the expense of higher latency when
		/// loading a TermInfo.  The default value is 1.  Set this
		/// to -1 to skip loading the terms index entirely.
		/// </param>
		/// <throws>  CorruptIndexException if the index is corrupt </throws>
		/// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public static IndexReader Open(Directory directory, IndexDeletionPolicy deletionPolicy, bool readOnly, int termInfosIndexDivisor)
		{
			return Open(directory, deletionPolicy, null, readOnly, termInfosIndexDivisor);
		}
		
		/// <summary>Expert: returns an IndexReader reading the index in
		/// the given Directory, using a specific commit and with
		/// a custom <see cref="IndexDeletionPolicy" />.  You should pass
		/// readOnly=true, since it gives much better concurrent
		/// performance, unless you intend to do write operations
		/// (delete documents or change norms) with the reader.
		/// </summary>
		/// <param name="commit">the specific <see cref="IndexCommit" /> to open;
		/// see <see cref="IndexReader.ListCommits" /> to list all commits
		/// in a directory
		/// </param>
		/// <param name="deletionPolicy">a custom deletion policy (only used
		/// if you use this reader to perform deletes or to set
		/// norms); see <see cref="IndexWriter" /> for details.
		/// </param>
		/// <param name="readOnly">true if no changes (deletions, norms) will be made with this IndexReader
		/// </param>
		/// <throws>  CorruptIndexException if the index is corrupt </throws>
		/// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public static IndexReader Open(IndexCommit commit, IndexDeletionPolicy deletionPolicy, bool readOnly)
		{
			return Open(commit.Directory, deletionPolicy, commit, readOnly, DEFAULT_TERMS_INDEX_DIVISOR);
		}

		/// <summary>Expert: returns an IndexReader reading the index in
		/// the given Directory, using a specific commit and with
		/// a custom <see cref="IndexDeletionPolicy" />.  You should pass
		/// readOnly=true, since it gives much better concurrent
		/// performance, unless you intend to do write operations
		/// (delete documents or change norms) with the reader.
		/// </summary>
		/// <param name="commit">the specific <see cref="IndexCommit" /> to open;
		/// see <see cref="IndexReader.ListCommits" /> to list all commits
		/// in a directory
		/// </param>
		/// <param name="deletionPolicy">a custom deletion policy (only used
		/// if you use this reader to perform deletes or to set
		/// norms); see <see cref="IndexWriter" /> for details.
		/// </param>
		/// <param name="readOnly">true if no changes (deletions, norms) will be made with this IndexReader
		/// </param>
		/// <param name="termInfosIndexDivisor">Subsambles which indexed
		/// terms are loaded into RAM. This has the same effect as <see>
		///                                                          <cref>IndexWriter.SetTermIndexInterval</cref>
		///                                                        </see> except that setting
		/// must be done at indexing time while this setting can be
		/// set per reader.  When set to N, then one in every
		/// N*termIndexInterval terms in the index is loaded into
		/// memory.  By setting this to a value > 1 you can reduce
		/// memory usage, at the expense of higher latency when
		/// loading a TermInfo.  The default value is 1.  Set this
		/// to -1 to skip loading the terms index entirely.
		/// </param>
		/// <throws>  CorruptIndexException if the index is corrupt </throws>
		/// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public static IndexReader Open(IndexCommit commit, IndexDeletionPolicy deletionPolicy, bool readOnly, int termInfosIndexDivisor)
		{
			return Open(commit.Directory, deletionPolicy, commit, readOnly, termInfosIndexDivisor);
		}
		
		private static IndexReader Open(Directory directory, IndexDeletionPolicy deletionPolicy, IndexCommit commit, bool readOnly, int termInfosIndexDivisor)
		{
			return DirectoryReader.Open(directory, deletionPolicy, commit, readOnly, termInfosIndexDivisor);
		}
		
		/// <summary> Refreshes an IndexReader if the index has changed since this instance 
		/// was (re)opened. 
		/// <p/>
		/// Opening an IndexReader is an expensive operation. This method can be used
		/// to refresh an existing IndexReader to reduce these costs. This method 
		/// tries to only load segments that have changed or were created after the 
		/// IndexReader was (re)opened.
		/// <p/>
		/// If the index has not changed since this instance was (re)opened, then this
		/// call is a NOOP and returns this instance. Otherwise, a new instance is 
		/// returned. The old instance is <b>not</b> closed and remains usable.<br/>
		/// <p/>   
		/// If the reader is reopened, even though they share
		/// resources internally, it's safe to make changes
		/// (deletions, norms) with the new reader.  All shared
		/// mutable state obeys "copy on write" semantics to ensure
		/// the changes are not seen by other readers.
		/// <p/>
		/// You can determine whether a reader was actually reopened by comparing the
		/// old instance with the instance returned by this method: 
        /// <code>
		/// IndexReader reader = ... 
		/// ...
		/// IndexReader newReader = r.reopen();
		/// if (newReader != reader) {
		/// ...     // reader was reopened
		/// reader.close(); 
		/// }
		/// reader = newReader;
		/// ...
        /// </code>
		/// 
		/// Be sure to synchronize that code so that other threads,
		/// if present, can never use reader after it has been
		/// closed and before it's switched to newReader.
		/// 
		/// <p/><b>NOTE</b>: If this reader is a near real-time
		/// reader (obtained from <see cref="IndexWriter.GetReader()" />,
		/// reopen() will simply call writer.getReader() again for
		/// you, though this may change in the future.
		/// 
		/// </summary>
		/// <throws>  CorruptIndexException if the index is corrupt </throws>
		/// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public virtual IndexReader Reopen()
		{
			lock (this)
			{
				throw new NotSupportedException("This reader does not support reopen().");
			}
		}
		
		
		/// <summary>Just like <see cref="Reopen()" />, except you can change the
		/// readOnly of the original reader.  If the index is
		/// unchanged but readOnly is different then a new reader
		/// will be returned. 
		/// </summary>
		public virtual IndexReader Reopen(bool openReadOnly)
		{
			lock (this)
			{
				throw new NotSupportedException("This reader does not support reopen().");
			}
		}
		
		/// <summary>Expert: reopen this reader on a specific commit point.
		/// This always returns a readOnly reader.  If the
		/// specified commit point matches what this reader is
		/// already on, and this reader is already readOnly, then
		/// this same instance is returned; if it is not already
		/// readOnly, a readOnly clone is returned. 
		/// </summary>
		public virtual IndexReader Reopen(IndexCommit commit)
		{
			lock (this)
			{
				throw new NotSupportedException("This reader does not support reopen(IndexCommit).");
			}
		}
		
		/// <summary> Efficiently clones the IndexReader (sharing most
		/// internal state).
		/// <p/>
		/// On cloning a reader with pending changes (deletions,
		/// norms), the original reader transfers its write lock to
		/// the cloned reader.  This means only the cloned reader
		/// may make further changes to the index, and commit the
		/// changes to the index on close, but the old reader still
		/// reflects all changes made up until it was cloned.
		/// <p/>
		/// Like <see cref="Reopen()" />, it's safe to make changes to
		/// either the original or the cloned reader: all shared
		/// mutable state obeys "copy on write" semantics to ensure
		/// the changes are not seen by other readers.
		/// <p/>
		/// </summary>
		/// <throws>  CorruptIndexException if the index is corrupt </throws>
		/// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public virtual System.Object Clone()
		{
			throw new System.NotSupportedException("This reader does not implement clone()");
		}
		
		/// <summary> Clones the IndexReader and optionally changes readOnly.  A readOnly 
		/// reader cannot open a writeable reader.  
		/// </summary>
		/// <throws>  CorruptIndexException if the index is corrupt </throws>
		/// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public virtual IndexReader Clone(bool openReadOnly)
		{
			lock (this)
			{
				throw new System.NotSupportedException("This reader does not implement clone()");
			}
		}
		
		/// <summary> Returns the directory associated with this index.  The Default 
		/// implementation returns the directory specified by subclasses when 
		/// delegating to the IndexReader(Directory) constructor, or throws an 
		/// UnsupportedOperationException if one was not specified.
		/// </summary>
		/// <throws>  UnsupportedOperationException if no directory </throws>
		public virtual Directory Directory()
		{
			EnsureOpen();
            throw new NotSupportedException("This reader does not support this method.");
		}
		
		/// <summary> Returns the time the index in the named directory was last modified. 
		/// Do not use this to check whether the reader is still up-to-date, use
		/// <see cref="IsCurrent()" /> instead. 
		/// </summary>
		/// <throws>  CorruptIndexException if the index is corrupt </throws>
		/// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public static long LastModified(Directory directory2)
		{
			return (long) ((System.Int64) new AnonymousClassFindSegmentsFile(directory2, directory2).Run());
		}
		
		/// <summary> Reads version number from segments files. The version number is
		/// initialized with a timestamp and then increased by one for each change of
		/// the index.
		/// 
		/// </summary>
		/// <param name="directory">where the index resides.
		/// </param>
		/// <returns> version number.
		/// </returns>
		/// <throws>  CorruptIndexException if the index is corrupt </throws>
		/// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public static long GetCurrentVersion(Directory directory)
		{
			return SegmentInfos.ReadCurrentVersion(directory);
		}

        /// <summary> Reads commitUserData, previously passed to 
        /// <see cref="IndexWriter.Commit(System.Collections.Generic.IDictionary{string, string})" />,
		/// from current index segments file.  This will return null if 
        /// <see cref="IndexWriter.Commit(System.Collections.Generic.IDictionary{string, string})" />
		/// has never been called for this index.
		/// </summary>
		/// <param name="directory">where the index resides.
		/// </param>
		/// <returns> commit userData.
		/// </returns>
		/// <throws>  CorruptIndexException if the index is corrupt </throws>
		/// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		/// <summary> 
		/// </summary>
		/// <seealso cref="GetCommitUserData(Store.Directory)">
		/// </seealso>
        public static System.Collections.Generic.IDictionary<string, string> GetCommitUserData(Directory directory)
		{
			return SegmentInfos.ReadCurrentUserData(directory);
		}

	    /// <summary> Version number when this IndexReader was opened. Not implemented in the
	    /// IndexReader base class.
	    /// 
	    /// <p/>
	    /// If this reader is based on a Directory (ie, was created by calling
	    /// <see cref="Open(Lucene.Net.Store.Directory, bool)" />, or <see cref="Reopen()" /> 
	    /// on a reader based on a Directory), then
	    /// this method returns the version recorded in the commit that the reader
	    /// opened. This version is advanced every time <see cref="IndexWriter.Commit()" /> is
	    /// called.
	    /// <p/>
	    /// 
	    /// <p/>
	    /// If instead this reader is a near real-time reader (ie, obtained by a call
	    /// to <see cref="IndexWriter.GetReader()" />, or by calling <see cref="Reopen()" /> on a near
	    /// real-time reader), then this method returns the version of the last
	    /// commit done by the writer. Note that even as further changes are made
	    /// with the writer, the version will not changed until a commit is
	    /// completed. Thus, you should not rely on this method to determine when a
	    /// near real-time reader should be opened. Use <see cref="IsCurrent" /> instead.
	    /// <p/>
	    /// 
	    /// </summary>
	    /// <throws>  UnsupportedOperationException </throws>
	    /// <summary>             unless overridden in subclass
	    /// </summary>
	    public virtual long Version
	    {
	        get { throw new System.NotSupportedException("This reader does not support this method."); }
	    }

	    /// <summary> Retrieve the String userData optionally passed to
	    /// <see cref="IndexWriter.Commit(System.Collections.Generic.IDictionary{string, string})" />.  
	    /// This will return null if 
	    /// <see cref="IndexWriter.Commit(System.Collections.Generic.IDictionary{string, string})" />
	    /// has never been called for this index.
	    /// </summary>
	    /// <seealso cref="GetCommitUserData(Store.Directory)">
	    /// </seealso>
	    public virtual IDictionary<string, string> CommitUserData
	    {
	        get { throw new System.NotSupportedException("This reader does not support this method."); }
	    }

		/// <summary> Check whether any new changes have occurred to the index since this
		/// reader was opened.
		/// 
		/// <p/>
		/// If this reader is based on a Directory (ie, was created by calling
		/// <see>
		///   <cref>Open(Store.Directory)</cref>
		/// </see> , or <see cref="Reopen()" /> on a reader based on a Directory), then
		/// this method checks if any further commits (see <see cref="IndexWriter.Commit()" />
		/// have occurred in that directory).
		/// <p/>
		/// 
		/// <p/>
		/// If instead this reader is a near real-time reader (ie, obtained by a call
		/// to <see cref="IndexWriter.GetReader()" />, or by calling <see cref="Reopen()" /> on a near
		/// real-time reader), then this method checks if either a new commmit has
		/// occurred, or any new uncommitted changes have taken place via the writer.
		/// Note that even if the writer has only performed merging, this method will
		/// still return false.
		/// <p/>
		/// 
		/// <p/>
		/// In any event, if this returns false, you should call <see cref="Reopen()" /> to
		/// get a new reader that sees the changes.
		/// <p/>
		/// 
		/// </summary>
		/// <throws>  CorruptIndexException if the index is corrupt </throws>
		/// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		/// <throws>  UnsupportedOperationException unless overridden in subclass </throws>
		public virtual bool IsCurrent()
	    {
	        throw new NotSupportedException("This reader does not support this method.");
	    }

	    /// <summary> Checks is the index is optimized (if it has a single segment and 
	    /// no deletions).  Not implemented in the IndexReader base class.
	    /// </summary>
	    /// <returns> &amp;lt;c&amp;gt;true&amp;lt;/c&amp;gt; if the index is optimized; &amp;lt;c&amp;gt;false&amp;lt;/c&amp;gt; otherwise </returns>
	    /// <throws>  UnsupportedOperationException unless overridden in subclass </throws>
	    public virtual bool IsOptimized()
	    {
	        throw new NotSupportedException("This reader does not support this method.");
	    }

	    /// <summary> Return an array of term frequency vectors for the specified document.
		/// The array contains a vector for each vectorized field in the document.
		/// Each vector contains terms and frequencies for all terms in a given vectorized field.
		/// If no such fields existed, the method returns null. The term vectors that are
		/// returned may either be of type <see cref="ITermFreqVector" />
		/// or of type <see cref="TermPositionVector" /> if
		/// positions or offsets have been stored.
		/// 
		/// </summary>
		/// <param name="docNumber">document for which term frequency vectors are returned
		/// </param>
		/// <returns> array of term frequency vectors. May be null if no term vectors have been
		/// stored for the specified document.
		/// </returns>
		/// <throws>  IOException if index cannot be accessed </throws>
		/// <seealso cref="Lucene.Net.Documents.Field.TermVector">
		/// </seealso>
		abstract public ITermFreqVector[] GetTermFreqVectors(int docNumber);
		
		
		/// <summary> Return a term frequency vector for the specified document and field. The
		/// returned vector contains terms and frequencies for the terms in
		/// the specified field of this document, if the field had the storeTermVector
		/// flag set. If termvectors had been stored with positions or offsets, a 
		/// <see cref="TermPositionVector" /> is returned.
		/// 
		/// </summary>
		/// <param name="docNumber">document for which the term frequency vector is returned
		/// </param>
		/// <param name="field">field for which the term frequency vector is returned.
		/// </param>
		/// <returns> term frequency vector May be null if field does not exist in the specified
		/// document or term vector was not stored.
		/// </returns>
		/// <throws>  IOException if index cannot be accessed </throws>
		/// <seealso cref="Lucene.Net.Documents.Field.TermVector">
		/// </seealso>
		abstract public ITermFreqVector GetTermFreqVector(int docNumber, String field);
		
		/// <summary> Load the Term Vector into a user-defined data structure instead of relying on the parallel arrays of
		/// the <see cref="ITermFreqVector" />.
		/// </summary>
		/// <param name="docNumber">The number of the document to load the vector for
		/// </param>
		/// <param name="field">The name of the field to load
		/// </param>
		/// <param name="mapper">The <see cref="TermVectorMapper" /> to process the vector.  Must not be null
		/// </param>
		/// <throws>  IOException if term vectors cannot be accessed or if they do not exist on the field and doc. specified. </throws>
		/// <summary> 
		/// </summary>
		abstract public void  GetTermFreqVector(int docNumber, String field, TermVectorMapper mapper);
		
		/// <summary> Map all the term vectors for all fields in a Document</summary>
		/// <param name="docNumber">The number of the document to load the vector for
		/// </param>
		/// <param name="mapper">The <see cref="TermVectorMapper" /> to process the vector.  Must not be null
		/// </param>
		/// <throws>  IOException if term vectors cannot be accessed or if they do not exist on the field and doc. specified. </throws>
		abstract public void  GetTermFreqVector(int docNumber, TermVectorMapper mapper);
		
		/// <summary> Returns <c>true</c> if an index exists at the specified directory.
		/// If the directory does not exist or if there is no index in it.
		/// </summary>
		/// <param name="directory">the directory to check for an index
		/// </param>
		/// <returns> <c>true</c> if an index exists; <c>false</c> otherwise
		/// </returns>
		/// <throws>  IOException if there is a problem with accessing the index </throws>
		public static bool IndexExists(Directory directory)
		{
			return SegmentInfos.GetCurrentSegmentGeneration(directory) != - 1;
		}

	    /// <summary>Returns the number of documents in this index. </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        public abstract int NumDocs();

	    /// <summary>Returns one greater than the largest possible document number.
	    /// This may be used to, e.g., determine how big to allocate an array which
	    /// will have an element for every document number in an index.
	    /// </summary>
	    public abstract int MaxDoc { get; }

	    /// <summary>Returns the number of deleted documents. </summary>
	    public virtual int NumDeletedDocs
	    {
	        get { return MaxDoc - NumDocs(); }
	    }

	    /// <summary> Returns the stored fields of the <c>n</c><sup>th</sup>
		/// <c>Document</c> in this index.
		/// <p/>
		/// <b>NOTE:</b> for performance reasons, this method does not check if the
		/// requested document is deleted, and therefore asking for a deleted document
		/// may yield unspecified results. Usually this is not required, however you
		/// can call <see cref="IsDeleted(int)" /> with the requested document ID to verify
		/// the document is not deleted.
		/// 
		/// </summary>
		/// <throws>  CorruptIndexException if the index is corrupt </throws>
		/// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public virtual Document Document(int n)
		{
			EnsureOpen();
			return Document(n, null);
		}

        /// <summary> Returns the stored fields of the <c>n</c><sup>th</sup>
        /// <c>Document</c> in this index.
        /// <p/>
        /// <b>NOTE:</b> for performance reasons, this method does not check if the
        /// requested document is deleted, and therefore asking for a deleted document
        /// may yield unspecified results. Usually this is not required, however you
        /// can call <see cref="IsDeleted(int)" /> with the requested document ID to verify
        /// the document is not deleted.
        /// 
        /// </summary>
        /// <throws>  CorruptIndexException if the index is corrupt </throws>
        /// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
	    public Document this[int doc]
	    {
	        get { return Document(doc); }
	    }
		
		/// <summary> Get the <see cref="Lucene.Net.Documents.Document" /> at the <c>n</c>
		/// <sup>th</sup> position. The <see cref="FieldSelector" /> may be used to determine
		/// what <see cref="Lucene.Net.Documents.Field" />s to load and how they should
		/// be loaded. <b>NOTE:</b> If this Reader (more specifically, the underlying
		/// <c>FieldsReader</c>) is closed before the lazy
		/// <see cref="Lucene.Net.Documents.Field" /> is loaded an exception may be
		/// thrown. If you want the value of a lazy
		/// <see cref="Lucene.Net.Documents.Field" /> to be available after closing you
		/// must explicitly load it or fetch the Document again with a new loader.
		/// <p/>
		/// <b>NOTE:</b> for performance reasons, this method does not check if the
		/// requested document is deleted, and therefore asking for a deleted document
		/// may yield unspecified results. Usually this is not required, however you
		/// can call <see cref="IsDeleted(int)" /> with the requested document ID to verify
		/// the document is not deleted.
		/// 
		/// </summary>
		/// <param name="n">Get the document at the <c>n</c><sup>th</sup> position
		/// </param>
		/// <param name="fieldSelector">The <see cref="FieldSelector" /> to use to determine what
		/// Fields should be loaded on the Document. May be null, in which case
		/// all Fields will be loaded.
		/// </param>
		/// <returns> The stored fields of the
		/// <see cref="Lucene.Net.Documents.Document" /> at the nth position
		/// </returns>
		/// <throws>  CorruptIndexException if the index is corrupt </throws>
		/// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		/// <seealso cref="IFieldable">
		/// </seealso>
		/// <seealso cref="Lucene.Net.Documents.FieldSelector">
		/// </seealso>
		/// <seealso cref="Lucene.Net.Documents.SetBasedFieldSelector">
		/// </seealso>
		/// <seealso cref="Lucene.Net.Documents.LoadFirstFieldSelector">
		/// </seealso>
		// TODO (1.5): When we convert to JDK 1.5 make this Set<String>
		public abstract Document Document(int n, FieldSelector fieldSelector);
		
		/// <summary>Returns true if document <i>n</i> has been deleted </summary>
		public abstract bool IsDeleted(int n);

	    /// <summary>Returns true if any documents have been deleted </summary>
	    public abstract bool HasDeletions { get; }

	    /// <summary>Returns true if there are norms stored for this field. </summary>
		public virtual bool HasNorms(System.String field)
		{
			// backward compatible implementation.
			// SegmentReader has an efficient implementation.
			EnsureOpen();
			return Norms(field) != null;
		}

		/// <summary>
		/// Returns the byte-encoded normalization factor for the named field of
		/// every document.  This is used by the search code to score documents.
		/// </summary>
        /// <seealso cref="Lucene.Net.Documents.AbstractField.Boost" />
		public abstract byte[] Norms(System.String field);
		
		/// <summary>
		/// Reads the byte-encoded normalization factor for the named field of every
		/// document.  This is used by the search code to score documents.
		/// </summary>
		/// <seealso cref="Lucene.Net.Documents.AbstractField.Boost" />
		public abstract void  Norms(System.String field, byte[] bytes, int offset);
		
		/// <summary>Expert: Resets the normalization factor for the named field of the named
		/// document.  The norm represents the product of the field's <see cref="IFieldable.Boost">boost</see>
        /// and its <see cref="Similarity.LengthNorm(String,int)">length normalization</see>.  Thus, to preserve the length normalization
		/// values when resetting this, one should base the new value upon the old.
		/// 
		/// <b>NOTE:</b> If this field does not store norms, then
		/// this method call will silently do nothing.
		/// </summary>
		/// <seealso cref="Norms(String)" />
		/// <seealso cref="Similarity.DecodeNorm(byte)" />
		/// <exception cref="StaleReaderException">
        /// If the index has changed since this reader was opened
		/// </exception>
        /// <exception cref="CorruptIndexException">
        /// If the index is corrupt
		/// </exception>
		/// <exception cref="LockObtainFailedException">
        /// If another writer has this index open (<c>write.lock</c> could not be obtained)
		/// </exception>
		/// <exception cref="System.IO.IOException">
        /// If there is a low-level IO error
		/// </exception>
		public virtual void  SetNorm(int doc, String field, byte value)
		{
			lock (this)
			{
				EnsureOpen();
				AcquireWriteLock();
				hasChanges = true;
				DoSetNorm(doc, field, value);
			}
		}
		
		/// <summary>Implements setNorm in subclass.</summary>
		protected internal abstract void  DoSetNorm(int doc, System.String field, byte value_Renamed);
		
		/// <summary>
		/// Expert: Resets the normalization factor for the named field of the named document.
		/// </summary>
		/// <seealso cref="Norms(String)" />
        /// <seealso cref="Similarity.DecodeNorm(byte)" />
        /// <exception cref="StaleReaderException">
        /// If the index has changed since this reader was opened
        /// </exception>
        /// <exception cref="CorruptIndexException">
        /// If the index is corrupt
        /// </exception>
        /// <exception cref="LockObtainFailedException">
        /// If another writer has this index open (<c>write.lock</c> could not be obtained)
        /// </exception>
        /// <exception cref="System.IO.IOException">
        /// If there is a low-level IO error
        /// </exception>
		public virtual void  SetNorm(int doc, System.String field, float value)
		{
			EnsureOpen();
			SetNorm(doc, field, Similarity.EncodeNorm(value));
		}
		
		/// <summary>Returns an enumeration of all the terms in the index. The
		/// enumeration is ordered by Term.compareTo(). Each term is greater
		/// than all that precede it in the enumeration. Note that after
		/// calling terms(), <see cref="TermEnum.Next()" /> must be called
		/// on the resulting enumeration before calling other methods such as
		/// <see cref="TermEnum.Term" />.
		/// </summary>
		/// <exception cref="System.IO.IOException">
        /// If there is a low-level IO error 
		/// </exception>
		public abstract TermEnum Terms();
		
		/// <summary>Returns an enumeration of all terms starting at a given term. If
		/// the given term does not exist, the enumeration is positioned at the
		/// first term greater than the supplied term. The enumeration is
		/// ordered by Term.compareTo(). Each term is greater than all that
		/// precede it in the enumeration.
        /// </summary>
        /// <exception cref="System.IO.IOException">
        /// If there is a low-level IO error
        /// </exception>
		public abstract TermEnum Terms(Term t);

        /// <summary>Returns the number of documents containing the term <c>t</c>.</summary>
        /// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public abstract int DocFreq(Term t);
		
		/// <summary>Returns an enumeration of all the documents which contain
		/// <c>term</c>. For each document, the document number, the frequency of
		/// the term in that document is also provided, for use in
		/// search scoring.  If term is null, then all non-deleted
		/// docs are returned with freq=1.
		/// Thus, this method implements the mapping:
		/// <p/><list>
		/// Term &#160;&#160; =&gt; &#160;&#160; &lt;docNum, freq&gt;<sup>*</sup>
		/// </list>
		/// <p/>The enumeration is ordered by document number.  Each document number
		/// is greater than all that precede it in the enumeration.
        /// </summary>
        /// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public virtual TermDocs TermDocs(Term term)
		{
			EnsureOpen();
			TermDocs termDocs = TermDocs();
			termDocs.Seek(term);
			return termDocs;
		}

        /// <summary>Returns an unpositioned <see cref="Lucene.Net.Index.TermDocs" /> enumerator.</summary>
        /// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public abstract TermDocs TermDocs();
		
		/// <summary>Returns an enumeration of all the documents which contain
		/// <c>term</c>.  For each document, in addition to the document number
		/// and frequency of the term in that document, a list of all of the ordinal
		/// positions of the term in the document is available.  Thus, this method
		/// implements the mapping:
		/// 
		/// <p/><list>
		/// Term &#160;&#160; =&gt; &#160;&#160; &lt;docNum, freq,
		/// &lt;pos<sub>1</sub>, pos<sub>2</sub>, ...
		/// pos<sub>freq-1</sub>&gt;
		/// &gt;<sup>*</sup>
		/// </list>
		/// <p/> This positional information facilitates phrase and proximity searching.
		/// <p/>The enumeration is ordered by document number.  Each document number is
		/// greater than all that precede it in the enumeration.
        /// </summary>
        /// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public virtual TermPositions TermPositions(Term term)
		{
			EnsureOpen();
			TermPositions termPositions = TermPositions();
			termPositions.Seek(term);
			return termPositions;
		}

        /// <summary>Returns an unpositioned <see cref="Lucene.Net.Index.TermPositions" /> enumerator.</summary>
        /// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public abstract TermPositions TermPositions();
		
		
		
		/// <summary>
		/// Deletes the document numbered <c>docNum</c>.  Once a document is
		/// deleted it will not appear in TermDocs or TermPostitions enumerations.
		/// Attempts to read its field with the <see cref="Document(int)" />
		/// method will result in an error.  The presence of this document may still be
		/// reflected in the <see cref="DocFreq" /> statistic, though
		/// this will be corrected eventually as the index is further modified.
		/// </summary>
		/// <exception cref="StaleReaderException">
        /// If the index has changed since this reader was opened
		/// </exception>
		/// <exception cref="CorruptIndexException">If the index is corrupt</exception>
		/// <exception cref="LockObtainFailedException">
        /// If another writer has this index open (<c>write.lock</c> could not be obtained)
        /// </exception>
        /// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public virtual void  DeleteDocument(int docNum)
		{
			lock (this)
			{
				EnsureOpen();
				AcquireWriteLock();
				hasChanges = true;
				DoDelete(docNum);
			}
		}
		
		
		/// <summary>Implements deletion of the document numbered <c>docNum</c>.
		/// Applications should call <see cref="DeleteDocument(int)" /> or <see cref="DeleteDocuments(Term)" />.
		/// </summary>
		protected internal abstract void  DoDelete(int docNum);
		
		
		/// <summary>
		/// Deletes all documents that have a given <c>term</c> indexed.
		/// This is useful if one uses a document field to hold a unique ID string for
		/// the document.  Then to delete such a document, one merely constructs a
		/// term with the appropriate field and the unique ID string as its text and
		/// passes it to this method.
		/// See <see cref="DeleteDocument(int)" /> for information about when this deletion will 
		/// become effective.
		/// </summary>
		/// <returns>The number of documents deleted</returns>
        /// <exception cref="StaleReaderException">
        /// If the index has changed since this reader was opened
        /// </exception>
        /// <exception cref="CorruptIndexException">If the index is corrupt</exception>
        /// <exception cref="LockObtainFailedException">
        /// If another writer has this index open (<c>write.lock</c> could not be obtained)
        /// </exception>
        /// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public virtual int DeleteDocuments(Term term)
		{
			EnsureOpen();
			TermDocs docs = TermDocs(term);
			if (docs == null)
				return 0;
			int n = 0;
			try
			{
				while (docs.Next())
				{
					DeleteDocument(docs.Doc);
					n++;
				}
			}
			finally
			{
				docs.Close();
			}
			return n;
		}
		
		/// <summary>Undeletes all documents currently marked as deleted in this index.
		/// 
        /// </summary>
        /// <exception cref="StaleReaderException">
        /// If the index has changed since this reader was opened
        /// </exception>
        /// <exception cref="CorruptIndexException">If the index is corrupt</exception>
        /// <exception cref="LockObtainFailedException">
        /// If another writer has this index open (<c>write.lock</c> could not be obtained)
        /// </exception>
        /// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public virtual void  UndeleteAll()
		{
			lock (this)
			{
				EnsureOpen();
				AcquireWriteLock();
				hasChanges = true;
				DoUndeleteAll();
			}
		}
		
		/// <summary>Implements actual undeleteAll() in subclass. </summary>
		protected internal abstract void  DoUndeleteAll();
		
		/// <summary>
		/// Does nothing by default. Subclasses that require a write lock for
		/// index modifications must implement this method. 
		/// </summary>
		protected internal virtual void  AcquireWriteLock()
		{
			lock (this)
			{
				/* NOOP */
			}
		}
		
		/// <summary> </summary>
		/// <exception cref="System.IO.IOException" />
		public void  Flush()
		{
			lock (this)
			{
				EnsureOpen();
				Commit();
			}
		}
		
		/// <param name="commitUserData">Opaque Map (String -> String)
		/// that's recorded into the segments file in the index,
		/// and retrievable by <see cref="IndexReader.GetCommitUserData" />
        /// </param>
        /// <exception cref="System.IO.IOException" />
        public void Flush(IDictionary<string, string> commitUserData)
		{
			lock (this)
			{
				EnsureOpen();
				Commit(commitUserData);
			}
		}
		
		/// <summary> Commit changes resulting from delete, undeleteAll, or
		/// setNorm operations
		/// 
		/// If an exception is hit, then either no changes or all
		/// changes will have been committed to the index
		/// (transactional semantics).
		/// </summary>
        /// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
		public /*protected internal*/ void  Commit()
		{
			lock (this)
			{
				Commit(null);
			}
		}
		
		/// <summary> Commit changes resulting from delete, undeleteAll, or
		/// setNorm operations
		/// 
		/// If an exception is hit, then either no changes or all
		/// changes will have been committed to the index
		/// (transactional semantics).
		/// </summary>
		/// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
        public void Commit(IDictionary<string, string> commitUserData)
		{
			lock (this)
			{
				if (hasChanges)
				{
					DoCommit(commitUserData);
				}
				hasChanges = false;
			}
		}
		
		/// <summary>Implements commit.</summary>
	    protected internal abstract void DoCommit(IDictionary<string, string> commitUserData);

        [Obsolete("Use Dispose() instead")]
		public void Close()
		{
		    Dispose();
		}

        /// <summary> Closes files associated with this index.
        /// Also saves any new deletions to disk.
        /// No other methods should be called after this has been called.
        /// </summary>
        /// <exception cref="System.IO.IOException">If there is a low-level IO error</exception>
        public void Dispose()
        {
            Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                lock (this)
                {
                    if (!closed)
                    {
                        DecRef();
                        closed = true;
                    }
                }
            }
        }
		
		/// <summary>Implements close. </summary>
		protected internal abstract void  DoClose();
		
		
		/// <summary> Get a list of unique field names that exist in this index and have the specified
		/// field option information.
		/// </summary>
		/// <param name="fldOption">specifies which field option should be available for the returned fields
		/// </param>
		/// <returns> Collection of Strings indicating the names of the fields.
		/// </returns>
		/// <seealso cref="IndexReader.FieldOption">
		/// </seealso>
		public abstract ICollection<string> GetFieldNames(FieldOption fldOption);

	    /// <summary> Expert: return the IndexCommit that this reader has
	    /// opened.  This method is only implemented by those
	    /// readers that correspond to a Directory with its own
	    /// segments_N file.
	    /// 
	    /// <p/><b>WARNING</b>: this API is new and experimental and
	    /// may suddenly change.<p/>
	    /// </summary>
	    public virtual IndexCommit IndexCommit
	    {
	        get { throw new NotSupportedException("This reader does not support this method."); }
	    }

	    /// <summary> Prints the filename and size of each file within a given compound file.
		/// Add the -extract flag to extract files to the current working directory.
		/// In order to make the extracted version of the index work, you have to copy
		/// the segments file from the compound index into the directory where the extracted files are stored.
		/// </summary>
		/// <param name="args">Usage: Lucene.Net.Index.IndexReader [-extract] &lt;cfsfile&gt;
		/// </param>
		[STAThread]
		public static void  Main(String[] args)
		{
			System.String filename = null;
			bool extract = false;
			
			foreach (string t in args)
			{
				if (t.Equals("-extract"))
				{
					extract = true;
				}
				else if (filename == null)
				{
					filename = t;
				}
			}

	    	if (filename == null)
			{
				System.Console.Out.WriteLine("Usage: Lucene.Net.Index.IndexReader [-extract] <cfsfile>");
				return ;
			}
			
			Directory dir = null;
			CompoundFileReader cfr = null;
			
			try
			{
				var file = new System.IO.FileInfo(filename);
				System.String dirname = new System.IO.FileInfo(file.FullName).DirectoryName;
				filename = file.Name;
				dir = FSDirectory.Open(new System.IO.DirectoryInfo(dirname));
				cfr = new CompoundFileReader(dir, filename);
				
				System.String[] files = cfr.ListAll();
				System.Array.Sort(files); // sort the array of filename so that the output is more readable
				
				foreach (string t in files)
				{
					long len = cfr.FileLength(t);
					
					if (extract)
					{
						System.Console.Out.WriteLine("extract " + t + " with " + len + " bytes to local directory...");
						IndexInput ii = cfr.OpenInput(t);
						
						var f = new System.IO.FileStream(t, System.IO.FileMode.Create);
						
						// read and write with a small buffer, which is more effectiv than reading byte by byte
						var buffer = new byte[1024];
						int chunk = buffer.Length;
						while (len > 0)
						{
							var bufLen = (int) System.Math.Min(chunk, len);
							ii.ReadBytes(buffer, 0, bufLen);
							f.Write(buffer, 0, bufLen);
							len -= bufLen;
						}
						
						f.Close();
						ii.Close();
					}
					else
						System.Console.Out.WriteLine(t + ": " + len + " bytes");
				}
			}
			catch (System.IO.IOException ioe)
			{
				System.Console.Error.WriteLine(ioe.StackTrace);
			}
			finally
			{
				try
				{
					if (dir != null)
						dir.Close();
					if (cfr != null)
						cfr.Close();
				}
				catch (System.IO.IOException ioe)
				{
					System.Console.Error.WriteLine(ioe.StackTrace);
				}
			}
		}
		
		/// <summary>Returns all commit points that exist in the Directory.
		/// Normally, because the default is <see cref="KeepOnlyLastCommitDeletionPolicy" />
		///, there would be only
		/// one commit point.  But if you're using a custom <see cref="IndexDeletionPolicy" />
		/// then there could be many commits.
		/// Once you have a given commit, you can open a reader on
		/// it by calling <see cref="IndexReader.Open(IndexCommit,bool)" />
		/// There must be at least one commit in
		/// the Directory, else this method throws <see cref="System.IO.IOException" />.  
		/// Note that if a commit is in
		/// progress while this method is running, that commit
		/// may or may not be returned array.  
		/// </summary>
		public static System.Collections.Generic.ICollection<IndexCommit> ListCommits(Directory dir)
		{
			return DirectoryReader.ListCommits(dir);
		}

	    /// <summary>Expert: returns the sequential sub readers that this
	    /// reader is logically composed of.  For example,
	    /// IndexSearcher uses this API to drive searching by one
	    /// sub reader at a time.  If this reader is not composed
	    /// of sequential child readers, it should return null.
	    /// If this method returns an empty array, that means this
	    /// reader is a null reader (for example a MultiReader
	    /// that has no sub readers).
	    /// <p/>
	    /// NOTE: You should not try using sub-readers returned by
	    /// this method to make any changes (setNorm, deleteDocument,
	    /// etc.). While this might succeed for one composite reader
	    /// (like MultiReader), it will most likely lead to index
	    /// corruption for other readers (like DirectoryReader obtained
	    /// through <see cref="IndexReader.Open(Lucene.Net.Store.Directory,bool)" />. Use the parent reader directly. 
	    /// </summary>
	    public virtual IndexReader[] GetSequentialSubReaders()
	    {
	        return null;
	    }

	    /// <summary>Expert</summary>
	    public virtual object FieldCacheKey
	    {
	        get { return this; }
	    }

	    /* Expert.  Warning: this returns null if the reader has
          *  no deletions 
          */

	    public virtual object DeletesCacheKey
	    {
	        get { return this; }
	    }

	    /// <summary>Returns the number of unique terms (across all fields)
	    /// in this reader.
	    /// 
	    /// This method returns long, even though internally
	    /// Lucene cannot handle more than 2^31 unique terms, for
	    /// a possible future when this limitation is removed.
	    /// 
	    /// </summary>
	    /// <throws>  UnsupportedOperationException if this count </throws>
	    /// <summary>  cannot be easily determined (eg Multi*Readers).
	    /// Instead, you should call <see cref="GetSequentialSubReaders" />
	    /// and ask each sub reader for
	    /// its unique term count. 
	    /// </summary>
	    public virtual long UniqueTermCount
	    {
	        get { throw new System.NotSupportedException("this reader does not implement getUniqueTermCount()"); }
	    }

	    /// <summary>
	    /// For IndexReader implementations that use
	    /// TermInfosReader to read terms, this returns the
	    /// current indexDivisor as specified when the reader was
	    /// opened.
	    /// </summary>
	    public virtual int TermInfosIndexDivisor
	    {
	        get { throw new NotSupportedException("This reader does not support this method."); }
	    }
	}
}