/*
* 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 Lucene.Net.Support;
namespace Lucene.Net.Store
{
/// A memory-resident implementation. Locking
/// implementation is by default the
/// but can be changed with .
///
[Serializable]
public class RAMDirectory:Directory
{
private const long serialVersionUID = 1L;
internal protected HashMap fileMap = new HashMap();
internal protected long internalSizeInBytes = 0;
// *****
// Lock acquisition sequence: RAMDirectory, then RAMFile
// *****
/// Constructs an empty .
public RAMDirectory()
{
SetLockFactory(new SingleInstanceLockFactory());
}
/// Creates a new RAMDirectory instance from a different
/// Directory implementation. This can be used to load
/// a disk-based index into memory.
///
/// This should be used only with indices that can fit into memory.
///
/// Note that the resulting RAMDirectory instance is fully
/// independent from the original Directory (it is a
/// complete copy). Any subsequent changes to the
/// original Directory will not be visible in the
/// RAMDirectory instance.
///
///
/// a Directory value
///
/// if an error occurs
///
public RAMDirectory(Directory dir):this(dir, false)
{
}
private RAMDirectory(Directory dir, bool closeDir):this()
{
Directory.Copy(dir, this, closeDir);
}
//https://issues.apache.org/jira/browse/LUCENENET-174
[System.Runtime.Serialization.OnDeserialized]
void OnDeserialized(System.Runtime.Serialization.StreamingContext context)
{
if (interalLockFactory == null)
{
SetLockFactory(new SingleInstanceLockFactory());
}
}
public override System.String[] ListAll()
{
lock (this)
{
EnsureOpen();
// TODO: may have better performance if our HashMap implmented KeySet() instead of generating one via HashSet
System.Collections.Generic.ISet fileNames = Support.Compatibility.SetFactory.CreateHashSet(fileMap.Keys);
System.String[] result = new System.String[fileNames.Count];
int i = 0;
foreach(string filename in fileNames)
{
result[i++] = filename;
}
return result;
}
}
/// Returns true iff the named file exists in this directory.
public override bool FileExists(System.String name)
{
EnsureOpen();
RAMFile file;
lock (this)
{
file = fileMap[name];
}
return file != null;
}
/// Returns the time the named file was last modified.
/// IOException if the file does not exist
public override long FileModified(System.String name)
{
EnsureOpen();
RAMFile file;
lock (this)
{
file = fileMap[name];
}
if (file == null)
throw new System.IO.FileNotFoundException(name);
// RAMOutputStream.Flush() was changed to use DateTime.UtcNow.
// Convert it back to local time before returning (previous behavior)
return new DateTime(file.LastModified*TimeSpan.TicksPerMillisecond, DateTimeKind.Utc).ToLocalTime().Ticks/
TimeSpan.TicksPerMillisecond;
}
/// Set the modified time of an existing file to now.
/// IOException if the file does not exist
public override void TouchFile(System.String name)
{
EnsureOpen();
RAMFile file;
lock (this)
{
file = fileMap[name];
}
if (file == null)
throw new System.IO.FileNotFoundException(name);
long ts2, ts1 = System.DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond;
do
{
try
{
System.Threading.Thread.Sleep(new System.TimeSpan((System.Int64) 10000 * 0 + 100 * 1));
}
catch (System.Threading.ThreadInterruptedException ie)
{
// In 3.0 we will change this to throw
// InterruptedException instead
ThreadClass.Current().Interrupt();
throw new System.SystemException(ie.Message, ie);
}
ts2 = System.DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond;
}
while (ts1 == ts2);
file.LastModified = ts2;
}
/// Returns the length in bytes of a file in the directory.
/// IOException if the file does not exist
public override long FileLength(System.String name)
{
EnsureOpen();
RAMFile file;
lock (this)
{
file = fileMap[name];
}
if (file == null)
throw new System.IO.FileNotFoundException(name);
return file.Length;
}
/// Return total size in bytes of all files in this
/// directory. This is currently quantized to
/// RAMOutputStream.BUFFER_SIZE.
///
public long SizeInBytes()
{
lock (this)
{
EnsureOpen();
return internalSizeInBytes;
}
}
/// Removes an existing file in the directory.
/// IOException if the file does not exist
public override void DeleteFile(System.String name)
{
lock (this)
{
EnsureOpen();
RAMFile file = fileMap[name];
if (file != null)
{
fileMap.Remove(name);
file.directory = null;
internalSizeInBytes -= file.sizeInBytes;
}
else
throw new System.IO.FileNotFoundException(name);
}
}
/// Creates a new, empty file in the directory with the given name. Returns a stream writing this file.
public override IndexOutput CreateOutput(System.String name)
{
EnsureOpen();
RAMFile file = new RAMFile(this);
lock (this)
{
RAMFile existing = fileMap[name];
if (existing != null)
{
internalSizeInBytes -= existing.sizeInBytes;
existing.directory = null;
}
fileMap[name] = file;
}
return new RAMOutputStream(file);
}
/// Returns a stream reading an existing file.
public override IndexInput OpenInput(System.String name)
{
EnsureOpen();
RAMFile file;
lock (this)
{
file = fileMap[name];
}
if (file == null)
throw new System.IO.FileNotFoundException(name);
return new RAMInputStream(file);
}
/// Closes the store to future operations, releasing associated memory.
protected override void Dispose(bool disposing)
{
isOpen = false;
fileMap = null;
}
//public HashMap fileMap_ForNUnit
//{
// get { return fileMap; }
//}
//public long sizeInBytes_ForNUnitTest
//{
// get { return sizeInBytes; }
// set { sizeInBytes = value; }
//}
}
}