001: package org.apache.lucene.index;
002:
003: /**
004: * Licensed to the Apache Software Foundation (ASF) under one or more
005: * contributor license agreements. See the NOTICE file distributed with
006: * this work for additional information regarding copyright ownership.
007: * The ASF licenses this file to You under the Apache License, Version 2.0
008: * (the "License"); you may not use this file except in compliance with
009: * the License. You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: */
019:
020: import org.apache.lucene.store.Directory;
021: import org.apache.lucene.store.IndexInput;
022: import org.apache.lucene.store.BufferedIndexInput;
023: import org.apache.lucene.store.IndexOutput;
024: import org.apache.lucene.store.Lock;
025:
026: import java.util.HashMap;
027: import java.io.IOException;
028:
029: /**
030: * Class for accessing a compound stream.
031: * This class implements a directory, but is limited to only read operations.
032: * Directory methods that would normally modify data throw an exception.
033: *
034: *
035: * @version $Id: CompoundFileReader.java 564236 2007-08-09 15:21:19Z gsingers $
036: */
037: class CompoundFileReader extends Directory {
038:
039: private int readBufferSize;
040:
041: private static final class FileEntry {
042: long offset;
043: long length;
044: }
045:
046: // Base info
047: private Directory directory;
048: private String fileName;
049:
050: private IndexInput stream;
051: private HashMap entries = new HashMap();
052:
053: public CompoundFileReader(Directory dir, String name)
054: throws IOException {
055: this (dir, name, BufferedIndexInput.BUFFER_SIZE);
056: }
057:
058: public CompoundFileReader(Directory dir, String name,
059: int readBufferSize) throws IOException {
060: directory = dir;
061: fileName = name;
062: this .readBufferSize = readBufferSize;
063:
064: boolean success = false;
065:
066: try {
067: stream = dir.openInput(name, readBufferSize);
068:
069: // read the directory and init files
070: int count = stream.readVInt();
071: FileEntry entry = null;
072: for (int i = 0; i < count; i++) {
073: long offset = stream.readLong();
074: String id = stream.readString();
075:
076: if (entry != null) {
077: // set length of the previous entry
078: entry.length = offset - entry.offset;
079: }
080:
081: entry = new FileEntry();
082: entry.offset = offset;
083: entries.put(id, entry);
084: }
085:
086: // set the length of the final entry
087: if (entry != null) {
088: entry.length = stream.length() - entry.offset;
089: }
090:
091: success = true;
092:
093: } finally {
094: if (!success && (stream != null)) {
095: try {
096: stream.close();
097: } catch (IOException e) {
098: }
099: }
100: }
101: }
102:
103: public Directory getDirectory() {
104: return directory;
105: }
106:
107: public String getName() {
108: return fileName;
109: }
110:
111: public synchronized void close() throws IOException {
112: if (stream == null)
113: throw new IOException("Already closed");
114:
115: entries.clear();
116: stream.close();
117: stream = null;
118: }
119:
120: public synchronized IndexInput openInput(String id)
121: throws IOException {
122: // Default to readBufferSize passed in when we were opened
123: return openInput(id, readBufferSize);
124: }
125:
126: public synchronized IndexInput openInput(String id,
127: int readBufferSize) throws IOException {
128: if (stream == null)
129: throw new IOException("Stream closed");
130:
131: FileEntry entry = (FileEntry) entries.get(id);
132: if (entry == null)
133: throw new IOException("No sub-file with id " + id
134: + " found");
135:
136: return new CSIndexInput(stream, entry.offset, entry.length,
137: readBufferSize);
138: }
139:
140: /** Returns an array of strings, one for each file in the directory. */
141: public String[] list() {
142: String res[] = new String[entries.size()];
143: return (String[]) entries.keySet().toArray(res);
144: }
145:
146: /** Returns true iff a file with the given name exists. */
147: public boolean fileExists(String name) {
148: return entries.containsKey(name);
149: }
150:
151: /** Returns the time the compound file was last modified. */
152: public long fileModified(String name) throws IOException {
153: return directory.fileModified(fileName);
154: }
155:
156: /** Set the modified time of the compound file to now. */
157: public void touchFile(String name) throws IOException {
158: directory.touchFile(fileName);
159: }
160:
161: /** Not implemented
162: * @throws UnsupportedOperationException */
163: public void deleteFile(String name) {
164: throw new UnsupportedOperationException();
165: }
166:
167: /** Not implemented
168: * @throws UnsupportedOperationException */
169: public void renameFile(String from, String to) {
170: throw new UnsupportedOperationException();
171: }
172:
173: /** Returns the length of a file in the directory.
174: * @throws IOException if the file does not exist */
175: public long fileLength(String name) throws IOException {
176: FileEntry e = (FileEntry) entries.get(name);
177: if (e == null)
178: throw new IOException("File " + name + " does not exist");
179: return e.length;
180: }
181:
182: /** Not implemented
183: * @throws UnsupportedOperationException */
184: public IndexOutput createOutput(String name) {
185: throw new UnsupportedOperationException();
186: }
187:
188: /** Not implemented
189: * @throws UnsupportedOperationException */
190: public Lock makeLock(String name) {
191: throw new UnsupportedOperationException();
192: }
193:
194: /** Implementation of an IndexInput that reads from a portion of the
195: * compound file. The visibility is left as "package" *only* because
196: * this helps with testing since JUnit test cases in a different class
197: * can then access package fields of this class.
198: */
199: static final class CSIndexInput extends BufferedIndexInput {
200:
201: IndexInput base;
202: long fileOffset;
203: long length;
204:
205: CSIndexInput(final IndexInput base, final long fileOffset,
206: final long length) {
207: this (base, fileOffset, length,
208: BufferedIndexInput.BUFFER_SIZE);
209: }
210:
211: CSIndexInput(final IndexInput base, final long fileOffset,
212: final long length, int readBufferSize) {
213: super (readBufferSize);
214: this .base = base;
215: this .fileOffset = fileOffset;
216: this .length = length;
217: }
218:
219: /** Expert: implements buffer refill. Reads bytes from the current
220: * position in the input.
221: * @param b the array to read bytes into
222: * @param offset the offset in the array to start storing bytes
223: * @param len the number of bytes to read
224: */
225: protected void readInternal(byte[] b, int offset, int len)
226: throws IOException {
227: synchronized (base) {
228: long start = getFilePointer();
229: if (start + len > length)
230: throw new IOException("read past EOF");
231: base.seek(fileOffset + start);
232: base.readBytes(b, offset, len, false);
233: }
234: }
235:
236: /** Expert: implements seek. Sets current position in this file, where
237: * the next {@link #readInternal(byte[],int,int)} will occur.
238: * @see #readInternal(byte[],int,int)
239: */
240: protected void seekInternal(long pos) {
241: }
242:
243: /** Closes the stream to further operations. */
244: public void close() {
245: }
246:
247: public long length() {
248: return length;
249: }
250:
251: }
252:
253: }
|