-
Notifications
You must be signed in to change notification settings - Fork 624
/
Directory.cs
407 lines (367 loc) · 16.7 KB
/
Directory.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
using Lucene.Net.Support;
using System;
using System.Collections.Generic;
using System.IO;
namespace Lucene.Net.Store
{
/*
* 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 IOUtils = Lucene.Net.Util.IOUtils;
/// <summary>
/// A <see cref="Directory"/> is a flat list of files. Files may be written once, when they
/// are created. Once a file is created it may only be opened for read, or
/// deleted. Random access is permitted both when reading and writing.
/// <para/>
/// .NET's i/o APIs not used directly, but rather all i/o is
/// through this API. This permits things such as:
/// <list type="bullet">
/// <item><description> implementation of RAM-based indices;</description></item>
/// <item><description> implementation indices stored in a database;</description></item>
/// <item><description> implementation of an index as a single file;</description></item>
/// </list>
/// <para/>
/// Directory locking is implemented by an instance of
/// <see cref="Store.LockFactory"/>, and can be changed for each <see cref="Directory"/>
/// instance using <see cref="SetLockFactory"/>.
/// </summary>
public abstract class Directory : IDisposable // LUCENENET TODO: Subclass System.IO.FileSystemInfo ?
{
/// <summary>
/// Returns an array of strings, one for each file in the directory.
/// </summary>
/// <exception cref="DirectoryNotFoundException"> if the directory is not prepared for any
/// write operations (such as <see cref="CreateOutput(string, IOContext)"/>). </exception>
/// <exception cref="IOException"> in case of other IO errors </exception>
public abstract string[] ListAll();
/// <summary>
/// Returns <c>true</c> iff a file with the given name exists.
/// </summary>
[Obsolete("this method will be removed in 5.0")]
public abstract bool FileExists(string name);
/// <summary>
/// Removes an existing file in the directory. </summary>
public abstract void DeleteFile(string name);
/// <summary>
/// Returns the length of a file in the directory. this method follows the
/// following contract:
/// <list>
/// <item><description>Throws <see cref="FileNotFoundException"/>
/// if the file does not exist.</description></item>
/// <item><description>Returns a value >=0 if the file exists, which specifies its length.</description></item>
/// </list>
/// </summary>
/// <param name="name"> the name of the file for which to return the length. </param>
/// <exception cref="IOException"> if there was an IO error while retrieving the file's
/// length. </exception>
public abstract long FileLength(string name);
/// <summary>
/// Creates a new, empty file in the directory with the given name.
/// Returns a stream writing this file.
/// </summary>
public abstract IndexOutput CreateOutput(string name, IOContext context);
/// <summary>
/// Ensure that any writes to these files are moved to
/// stable storage. Lucene uses this to properly commit
/// changes to the index, to prevent a machine/OS crash
/// from corrupting the index.<br/>
/// <br/>
/// NOTE: Clients may call this method for same files over
/// and over again, so some impls might optimize for that.
/// For other impls the operation can be a noop, for various
/// reasons.
/// </summary>
public abstract void Sync(ICollection<string> names);
/// <summary>
/// Returns a stream reading an existing file, with the
/// specified read buffer size. The particular <see cref="Directory"/>
/// implementation may ignore the buffer size. Currently
/// the only <see cref="Directory"/> implementations that respect this
/// parameter are <see cref="FSDirectory"/> and
/// <see cref="CompoundFileDirectory"/>.
/// <para/>Throws <see cref="FileNotFoundException"/>
/// if the file does not exist.
/// </summary>
public abstract IndexInput OpenInput(string name, IOContext context);
/// <summary>
/// Returns a stream reading an existing file, computing checksum as it reads </summary>
public virtual ChecksumIndexInput OpenChecksumInput(string name, IOContext context)
{
return new BufferedChecksumIndexInput(OpenInput(name, context));
}
/// <summary>
/// Construct a <see cref="Lock"/>. </summary>
/// <param name="name"> the name of the lock file </param>
public abstract Lock MakeLock(string name);
/// <summary>
/// Attempt to clear (forcefully unlock and remove) the
/// specified lock. Only call this at a time when you are
/// certain this lock is no longer in use. </summary>
/// <param name="name"> name of the lock to be cleared. </param>
public abstract void ClearLock(string name);
/// <summary>
/// Disposes the store. </summary>
// LUCENENET specific - implementing proper dispose pattern
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Disposes the store. </summary>
protected abstract void Dispose(bool disposing);
/// <summary>
/// Set the <see cref="Store.LockFactory"/> that this <see cref="Directory"/> instance should
/// use for its locking implementation. Each * instance of
/// <see cref="Store.LockFactory"/> should only be used for one directory (ie,
/// do not share a single instance across multiple
/// Directories).
/// </summary>
/// <param name="lockFactory"> instance of <see cref="Store.LockFactory"/>. </param>
public abstract void SetLockFactory(LockFactory lockFactory);
/// <summary>
/// Get the <see cref="Store.LockFactory"/> that this <see cref="Directory"/> instance is
/// using for its locking implementation. Note that this
/// may be null for <see cref="Directory"/> implementations that provide
/// their own locking implementation.
/// </summary>
public abstract LockFactory LockFactory { get; }
/// <summary>
/// Return a string identifier that uniquely differentiates
/// this <see cref="Directory"/> instance from other <see cref="Directory"/> instances.
/// This ID should be the same if two <see cref="Directory"/> instances
/// (even in different AppDomains and/or on different machines)
/// are considered "the same index". This is how locking
/// "scopes" to the right index.
/// </summary>
public virtual string GetLockID()
{
return this.ToString();
}
public override string ToString()
{
return this.GetType().Name + '@' + GetHashCode().ToString("x") + " lockFactory=" + LockFactory;
}
/// <summary>
/// Copies the file <paramref name="src"/> to <seealso cref="Directory"/> <paramref name="to"/> under the new
/// file name <paramref name="dest"/>.
/// <para/>
/// If you want to copy the entire source directory to the destination one, you
/// can do so like this:
///
/// <code>
/// Directory to; // the directory to copy to
/// foreach (string file in dir.ListAll()) {
/// dir.Copy(to, file, newFile, IOContext.DEFAULT); // newFile can be either file, or a new name
/// }
/// </code>
/// <para/>
/// <b>NOTE:</b> this method does not check whether <paramref name="dest"/> exist and will
/// overwrite it if it does.
/// </summary>
public virtual void Copy(Directory to, string src, string dest, IOContext context)
{
IndexOutput os = null;
IndexInput @is = null;
IOException priorException = null;
try
{
os = to.CreateOutput(dest, context);
@is = OpenInput(src, context);
os.CopyBytes(@is, @is.Length);
}
catch (IOException ioe)
{
priorException = ioe;
}
finally
{
bool success = false;
try
{
IOUtils.DisposeWhileHandlingException(priorException, os, @is);
success = true;
}
finally
{
if (!success)
{
try
{
to.DeleteFile(dest);
}
catch (Exception)
{
}
}
}
}
}
/// <summary>
/// Creates an <see cref="IndexInputSlicer"/> for the given file name.
/// <see cref="IndexInputSlicer"/> allows other <see cref="Directory"/> implementations to
/// efficiently open one or more sliced <see cref="IndexInput"/> instances from a
/// single file handle. The underlying file handle is kept open until the
/// <see cref="IndexInputSlicer"/> is closed.
/// <para/>Throws <see cref="FileNotFoundException"/>
/// if the file does not exist.
/// <para/>
/// @lucene.internal
/// @lucene.experimental
/// </summary>
/// <exception cref="IOException">
/// if an <seealso cref="IOException"/> occurs</exception>
public virtual IndexInputSlicer CreateSlicer(string name, IOContext context)
{
EnsureOpen();
return new IndexInputSlicerAnonymousClass(OpenInput(name, context));
}
private class IndexInputSlicerAnonymousClass : IndexInputSlicer
{
private readonly IndexInput @base;
public IndexInputSlicerAnonymousClass(IndexInput @base)
{
this.@base = @base;
}
public override IndexInput OpenSlice(string sliceDescription, long offset, long length)
{
return new SlicedIndexInput("SlicedIndexInput(" + sliceDescription + " in " + @base + ")", @base, offset, length);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
@base.Dispose();
}
}
[Obsolete("Only for reading CFS files from 3.x indexes.")]
public override IndexInput OpenFullSlice()
{
return (IndexInput)@base.Clone();
}
}
/// <exception cref="ObjectDisposedException"> if this Directory is closed </exception>
protected internal virtual void EnsureOpen()
{
}
/// <summary>
/// Allows to create one or more sliced <see cref="IndexInput"/> instances from a single
/// file handle. Some <see cref="Directory"/> implementations may be able to efficiently map slices of a file
/// into memory when only certain parts of a file are required.
/// <para/>
/// @lucene.internal
/// @lucene.experimental
/// </summary>
public abstract class IndexInputSlicer : IDisposable
{
/// <summary>
/// Returns an <see cref="IndexInput"/> slice starting at the given offset with the given length.
/// </summary>
public abstract IndexInput OpenSlice(string sliceDescription, long offset, long length);
/// <summary>
/// Returns an <see cref="IndexInput"/> slice starting at offset <c>0</c> with a
/// length equal to the length of the underlying file </summary>
[Obsolete("Only for reading CFS files from 3.x indexes.")]
public abstract IndexInput OpenFullSlice(); // can we remove this somehow?
protected abstract void Dispose(bool disposing);
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
/// <summary>
/// Implementation of an <see cref="IndexInput"/> that reads from a portion of
/// a file.
/// </summary>
private sealed class SlicedIndexInput : BufferedIndexInput
{
private IndexInput @base;
private long fileOffset;
private long length;
internal SlicedIndexInput(string sliceDescription, IndexInput @base, long fileOffset, long length)
: this(sliceDescription, @base, fileOffset, length, BufferedIndexInput.BUFFER_SIZE)
{
}
internal SlicedIndexInput(string sliceDescription, IndexInput @base, long fileOffset, long length, int readBufferSize)
: base("SlicedIndexInput(" + sliceDescription + " in " + @base + " slice=" + fileOffset + ":" + (fileOffset + length) + ")", readBufferSize)
{
this.@base = (IndexInput)@base.Clone();
this.fileOffset = fileOffset;
this.length = length;
}
public override object Clone()
{
SlicedIndexInput clone = (SlicedIndexInput)base.Clone();
clone.@base = (IndexInput)@base.Clone();
clone.fileOffset = fileOffset;
clone.length = length;
return clone;
}
/// <summary>
/// Expert: implements buffer refill. Reads bytes from the current
/// position in the input. </summary>
/// <param name="b"> the array to read bytes into </param>
/// <param name="offset"> the offset in the array to start storing bytes </param>
/// <param name="len"> the number of bytes to read </param>
protected override void ReadInternal(byte[] b, int offset, int len)
{
long start = GetFilePointer();
if (start + len > length)
{
throw new Exception("read past EOF: " + this);
}
@base.Seek(fileOffset + start);
@base.ReadBytes(b, offset, len, false);
}
/// <summary>
/// Expert: implements seek. Sets current position in this file, where
/// the next <see cref="ReadInternal(byte[], int, int)"/> will occur.
/// </summary>
/// <seealso cref="ReadInternal(byte[], int, int)"/>
protected override void SeekInternal(long pos)
{
}
/// <summary>
/// Closes the stream to further operations.
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
@base.Dispose();
}
}
public override long Length => length;
}
// LUCENENET specific - formatter to defer building the string of directory contents in string.Format().
// This struct is meant to wrap a directory parameter when passed as a string.Format() argument.
internal struct ListAllFormatter // For assert/test/debug
{
#pragma warning disable IDE0044 // Add readonly modifier
private Directory directory;
#pragma warning restore IDE0044 // Add readonly modifier
public ListAllFormatter(Directory directory)
{
this.directory = directory ?? throw new ArgumentNullException(nameof(directory));
}
public override string ToString()
{
return Arrays.ToString(directory.ListAll());
}
}
}
}