/*
* 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.Linq;
using Lucene.Net.Support;
using BufferedIndexInput = Lucene.Net.Store.BufferedIndexInput;
using Directory = Lucene.Net.Store.Directory;
using IndexInput = Lucene.Net.Store.IndexInput;
using IndexOutput = Lucene.Net.Store.IndexOutput;
using Lock = Lucene.Net.Store.Lock;
namespace Lucene.Net.Index
{
/// Class for accessing a compound stream.
/// This class implements a directory, but is limited to only read operations.
/// Directory methods that would normally modify data throw an exception.
///
public class CompoundFileReader : Directory
{
private readonly int readBufferSize;
private sealed class FileEntry
{
internal long offset;
internal long length;
}
private bool isDisposed;
// Base info
private readonly Directory directory;
private readonly System.String fileName;
private IndexInput stream;
private HashMap entries = new HashMap();
public CompoundFileReader(Directory dir, System.String name):this(dir, name, BufferedIndexInput.BUFFER_SIZE)
{
}
public CompoundFileReader(Directory dir, System.String name, int readBufferSize)
{
directory = dir;
fileName = name;
this.readBufferSize = readBufferSize;
bool success = false;
try
{
stream = dir.OpenInput(name, readBufferSize);
// read the directory and init files
int count = stream.ReadVInt();
FileEntry entry = null;
for (int i = 0; i < count; i++)
{
long offset = stream.ReadLong();
System.String id = stream.ReadString();
if (entry != null)
{
// set length of the previous entry
entry.length = offset - entry.offset;
}
entry = new FileEntry {offset = offset};
entries[id] = entry;
}
// set the length of the final entry
if (entry != null)
{
entry.length = stream.Length() - entry.offset;
}
success = true;
}
finally
{
if (!success && (stream != null))
{
try
{
stream.Close();
}
catch (System.IO.IOException)
{
}
}
}
}
public virtual Directory Directory
{
get { return directory; }
}
public virtual string Name
{
get { return fileName; }
}
protected override void Dispose(bool disposing)
{
lock (this)
{
if (isDisposed) return;
if (disposing)
{
if (entries != null)
{
entries.Clear();
}
if (stream != null)
{
stream.Close();
}
}
entries = null;
stream = null;
isDisposed = true;
}
}
public override IndexInput OpenInput(System.String id)
{
lock (this)
{
// Default to readBufferSize passed in when we were opened
return OpenInput(id, readBufferSize);
}
}
public override IndexInput OpenInput(System.String id, int readBufferSize)
{
lock (this)
{
if (stream == null)
throw new System.IO.IOException("Stream closed");
FileEntry entry = entries[id];
if (entry == null)
throw new System.IO.IOException("No sub-file with id " + id + " found");
return new CSIndexInput(stream, entry.offset, entry.length, readBufferSize);
}
}
/// Returns an array of strings, one for each file in the directory.
public override System.String[] ListAll()
{
return entries.Keys.ToArray();
}
/// Returns true iff a file with the given name exists.
public override bool FileExists(System.String name)
{
return entries.ContainsKey(name);
}
/// Returns the time the compound file was last modified.
public override long FileModified(System.String name)
{
return directory.FileModified(fileName);
}
/// Set the modified time of the compound file to now.
public override void TouchFile(System.String name)
{
directory.TouchFile(fileName);
}
/// Not implemented
/// UnsupportedOperationException
public override void DeleteFile(System.String name)
{
throw new System.NotSupportedException();
}
/// Not implemented
/// UnsupportedOperationException
public void RenameFile(System.String from, System.String to)
{
throw new System.NotSupportedException();
}
/// Returns the length of a file in the directory.
/// IOException if the file does not exist
public override long FileLength(System.String name)
{
FileEntry e = entries[name];
if (e == null)
throw new System.IO.IOException("File " + name + " does not exist");
return e.length;
}
/// Not implemented
/// UnsupportedOperationException
public override IndexOutput CreateOutput(System.String name)
{
throw new System.NotSupportedException();
}
/// Not implemented
/// UnsupportedOperationException
public override Lock MakeLock(System.String name)
{
throw new System.NotSupportedException();
}
/// Implementation of an IndexInput that reads from a portion of the
/// compound file. The visibility is left as "package" *only* because
/// this helps with testing since JUnit test cases in a different class
/// can then access package fields of this class.
///
public /*internal*/ sealed class CSIndexInput : BufferedIndexInput
{
internal IndexInput base_Renamed;
internal long fileOffset;
internal long length;
private bool isDisposed;
internal CSIndexInput(IndexInput @base, long fileOffset, long length):this(@base, fileOffset, length, BufferedIndexInput.BUFFER_SIZE)
{
}
internal CSIndexInput(IndexInput @base, long fileOffset, long length, int readBufferSize):base(readBufferSize)
{
this.base_Renamed = (IndexInput) @base.Clone();
this.fileOffset = fileOffset;
this.length = length;
}
public override System.Object Clone()
{
var clone = (CSIndexInput) base.Clone();
clone.base_Renamed = (IndexInput) base_Renamed.Clone();
clone.fileOffset = fileOffset;
clone.length = length;
return clone;
}
/// Expert: implements buffer refill. Reads bytes from the current
/// position in the input.
///
/// the array to read bytes into
///
/// the offset in the array to start storing bytes
///
/// the number of bytes to read
///
public override void ReadInternal(byte[] b, int offset, int len)
{
long start = FilePointer;
if (start + len > length)
throw new System.IO.IOException("read past EOF");
base_Renamed.Seek(fileOffset + start);
base_Renamed.ReadBytes(b, offset, len, false);
}
/// Expert: implements seek. Sets current position in this file, where
/// the next will occur.
///
///
///
public override void SeekInternal(long pos)
{
}
protected override void Dispose(bool disposing)
{
if (isDisposed) return;
if (disposing)
{
if (base_Renamed != null)
{
base_Renamed.Close();
}
}
isDisposed = true;
}
public override long Length()
{
return length;
}
public IndexInput base_Renamed_ForNUnit
{
get { return base_Renamed; }
}
}
}
}