/*
* 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 Directory = Lucene.Net.Store.Directory;
namespace Lucene.Net.Index
{
/// This stores a monotonically increasing set of pairs in a
/// Directory. Pairs are accessed either by Term or by ordinal position the
/// set.
///
public sealed class TermInfosReader
{
private Directory directory;
private System.String segment;
private FieldInfos fieldInfos;
private System.LocalDataStoreSlot enumerators = System.Threading.Thread.AllocateDataSlot();
private SegmentTermEnum origEnum;
private long size;
private Term[] indexTerms = null;
private TermInfo[] indexInfos;
private long[] indexPointers;
private SegmentTermEnum indexEnum;
public TermInfosReader(Directory dir, System.String seg, FieldInfos fis)
{
directory = dir;
segment = seg;
fieldInfos = fis;
origEnum = new SegmentTermEnum(directory.OpenInput(segment + ".tis"), fieldInfos, false);
size = origEnum.size;
indexEnum = new SegmentTermEnum(directory.OpenInput(segment + ".tii"), fieldInfos, true);
}
public int GetSkipInterval()
{
return origEnum.skipInterval;
}
public void Close()
{
if (origEnum != null)
origEnum.Close();
if (indexEnum != null)
indexEnum.Close();
}
/// Returns the number of term/value pairs in the set.
internal long Size()
{
return size;
}
private SegmentTermEnum GetEnum()
{
SegmentTermEnum termEnum = (SegmentTermEnum) System.Threading.Thread.GetData(enumerators);
if (termEnum == null)
{
termEnum = Terms();
System.Threading.Thread.SetData(enumerators, termEnum);
}
return termEnum;
}
private void EnsureIndexIsRead()
{
lock (this)
{
if (indexTerms != null)
// index already read
return ; // do nothing
try
{
int indexSize = (int) indexEnum.size; // otherwise read index
indexTerms = new Term[indexSize];
indexInfos = new TermInfo[indexSize];
indexPointers = new long[indexSize];
for (int i = 0; indexEnum.Next(); i++)
{
indexTerms[i] = indexEnum.Term();
indexInfos[i] = indexEnum.TermInfo();
indexPointers[i] = indexEnum.indexPointer;
}
}
finally
{
indexEnum.Close();
indexEnum = null;
}
}
}
/// Returns the offset of the greatest index entry which is less than or equal to term.
private int GetIndexOffset(Term term)
{
int lo = 0; // binary search indexTerms[]
int hi = indexTerms.Length - 1;
while (hi >= lo)
{
int mid = (lo + hi) >> 1;
int delta = term.CompareTo(indexTerms[mid]);
if (delta < 0)
hi = mid - 1;
else if (delta > 0)
lo = mid + 1;
else
return mid;
}
return hi;
}
private void SeekEnum(int indexOffset)
{
GetEnum().Seek(indexPointers[indexOffset], (indexOffset * GetEnum().indexInterval) - 1, indexTerms[indexOffset], indexInfos[indexOffset]);
}
/// Returns the TermInfo for a Term in the set, or null.
public TermInfo Get(Term term)
{
if (size == 0)
return null;
EnsureIndexIsRead();
// optimize sequential access: first try scanning cached enum w/o seeking
SegmentTermEnum enumerator = GetEnum();
if (enumerator.Term() != null && ((enumerator.Prev() != null && term.CompareTo(enumerator.Prev()) > 0) || term.CompareTo(enumerator.Term()) >= 0))
{
int enumOffset = (int) (enumerator.position / enumerator.indexInterval) + 1;
if (indexTerms.Length == enumOffset || term.CompareTo(indexTerms[enumOffset]) < 0)
return ScanEnum(term); // no need to seek
}
// random-access: must seek
SeekEnum(GetIndexOffset(term));
return ScanEnum(term);
}
/// Scans within block for matching term.
private TermInfo ScanEnum(Term term)
{
SegmentTermEnum enumerator = GetEnum();
enumerator.ScanTo(term);
if (enumerator.Term() != null && term.CompareTo(enumerator.Term()) == 0)
return enumerator.TermInfo();
else
return null;
}
/// Returns the nth term in the set.
internal Term Get(int position)
{
if (size == 0)
return null;
SegmentTermEnum enumerator = GetEnum();
if (enumerator != null && enumerator.Term() != null && position >= enumerator.position && position < (enumerator.position + enumerator.indexInterval))
return ScanEnum(position); // can avoid seek
SeekEnum(position / enumerator.indexInterval); // must seek
return ScanEnum(position);
}
private Term ScanEnum(int position)
{
SegmentTermEnum enumerator = GetEnum();
while (enumerator.position < position)
if (!enumerator.Next())
return null;
return enumerator.Term();
}
/// Returns the position of a Term in the set or -1.
internal long GetPosition(Term term)
{
if (size == 0)
return - 1;
EnsureIndexIsRead();
int indexOffset = GetIndexOffset(term);
SeekEnum(indexOffset);
SegmentTermEnum enumerator = GetEnum();
while (term.CompareTo(enumerator.Term()) > 0 && enumerator.Next())
{
}
if (term.CompareTo(enumerator.Term()) == 0)
return enumerator.position;
else
return - 1;
}
/// Returns an enumeration of all the Terms and TermInfos in the set.
public SegmentTermEnum Terms()
{
return (SegmentTermEnum) origEnum.Clone();
}
/// Returns an enumeration of terms starting at or after the named term.
public SegmentTermEnum Terms(Term term)
{
Get(term);
return (SegmentTermEnum) GetEnum().Clone();
}
}
}