pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / java / org / apache / lucene / index / CompoundFileReader.java
1 package org.apache.lucene.index;
2
3 /**
4  * Licensed to the Apache Software Foundation (ASF) under one or more
5  * contributor license agreements.  See the NOTICE file distributed with
6  * this work for additional information regarding copyright ownership.
7  * The ASF licenses this file to You under the Apache License, Version 2.0
8  * (the "License"); you may not use this file except in compliance with
9  * the License.  You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 import java.io.EOFException;
21 import java.io.FileNotFoundException;
22 import java.io.IOException;
23 import java.util.HashMap;
24
25 import org.apache.lucene.store.BufferedIndexInput;
26 import org.apache.lucene.store.Directory;
27 import org.apache.lucene.store.IndexInput;
28 import org.apache.lucene.store.IndexOutput;
29 import org.apache.lucene.store.Lock;
30
31 /**
32  * Class for accessing a compound stream.
33  * This class implements a directory, but is limited to only read operations.
34  * Directory methods that would normally modify data throw an exception.
35  */
36 class CompoundFileReader extends Directory {
37   
38   private int readBufferSize;
39   
40   private static final class FileEntry {
41     long offset;
42     long length;
43   }
44     
45   // Base info
46   private Directory directory;
47   private String fileName;
48   
49   private IndexInput stream;
50   private HashMap<String,FileEntry> entries = new HashMap<String,FileEntry>();
51   
52   public CompoundFileReader(Directory dir, String name) throws IOException {
53     this(dir, name, BufferedIndexInput.BUFFER_SIZE);
54   }
55   
56   public CompoundFileReader(Directory dir, String name, int readBufferSize) throws IOException {
57     assert !(dir instanceof CompoundFileReader) : "compound file inside of compound file: " + name;
58     directory = dir;
59     fileName = name;
60     this.readBufferSize = readBufferSize;
61     
62     boolean success = false;
63     
64     try {
65       stream = dir.openInput(name, readBufferSize);
66       
67       // read the first VInt. If it is negative, it's the version number
68       // otherwise it's the count (pre-3.1 indexes)
69       int firstInt = stream.readVInt();
70       
71       final int count;
72       final boolean stripSegmentName;
73       if (firstInt < CompoundFileWriter.FORMAT_PRE_VERSION) {
74         if (firstInt < CompoundFileWriter.FORMAT_CURRENT) {
75           throw new CorruptIndexException("Incompatible format version: "
76               + firstInt + " expected " + CompoundFileWriter.FORMAT_CURRENT + " (resource: " + stream + ")");
77         }
78         // It's a post-3.1 index, read the count.
79         count = stream.readVInt();
80         stripSegmentName = false;
81       } else {
82         count = firstInt;
83         stripSegmentName = true;
84       }
85       
86       // read the directory and init files
87       FileEntry entry = null;
88       for (int i=0; i<count; i++) {
89         long offset = stream.readLong();
90         String id = stream.readString();
91         
92         if (stripSegmentName) {
93           // Fix the id to not include the segment names. This is relevant for
94           // pre-3.1 indexes.
95           id = IndexFileNames.stripSegmentName(id);
96         }
97         
98         if (entry != null) {
99           // set length of the previous entry
100           entry.length = offset - entry.offset;
101         }
102         
103         entry = new FileEntry();
104         entry.offset = offset;
105         entries.put(id, entry);
106       }
107       
108       // set the length of the final entry
109       if (entry != null) {
110         entry.length = stream.length() - entry.offset;
111       }
112       
113       success = true;
114       
115     } finally {
116       if (!success && (stream != null)) {
117         try {
118           stream.close();
119         } catch (IOException e) { }
120       }
121     }
122   }
123   
124   public Directory getDirectory() {
125     return directory;
126   }
127   
128   public String getName() {
129     return fileName;
130   }
131   
132   @Override
133   public synchronized void close() throws IOException {
134     if (stream == null)
135       throw new IOException("Already closed");
136     
137     entries.clear();
138     stream.close();
139     stream = null;
140   }
141   
142   @Override
143   public synchronized IndexInput openInput(String id) throws IOException {
144     // Default to readBufferSize passed in when we were opened
145     return openInput(id, readBufferSize);
146   }
147   
148   @Override
149   public synchronized IndexInput openInput(String id, int readBufferSize) throws IOException {
150     if (stream == null)
151       throw new IOException("Stream closed");
152     
153     id = IndexFileNames.stripSegmentName(id);
154     FileEntry entry = entries.get(id);
155     if (entry == null) {
156       throw new IOException("No sub-file with id " + id + " found (fileName=" + fileName + " files: " + entries.keySet() + ")");
157     }
158     
159     return new CSIndexInput(stream, entry.offset, entry.length, readBufferSize);
160   }
161   
162   /** Returns an array of strings, one for each file in the directory. */
163   @Override
164   public String[] listAll() {
165     String[] res = entries.keySet().toArray(new String[entries.size()]);
166     // Add the segment name
167     String seg = fileName.substring(0, fileName.indexOf('.'));
168     for (int i = 0; i < res.length; i++) {
169       res[i] = seg + res[i];
170     }
171     return res;
172   }
173   
174   /** Returns true iff a file with the given name exists. */
175   @Override
176   public boolean fileExists(String name) {
177     return entries.containsKey(IndexFileNames.stripSegmentName(name));
178   }
179   
180   /** Returns the time the compound file was last modified. */
181   @Override
182   public long fileModified(String name) throws IOException {
183     return directory.fileModified(fileName);
184   }
185   
186   /** Set the modified time of the compound file to now.
187    *  @deprecated Lucene never uses this API; it will be
188    *  removed in 4.0. */
189   @Override
190   @Deprecated
191   public void touchFile(String name) throws IOException {
192     directory.touchFile(fileName);
193   }
194   
195   /** Not implemented
196    * @throws UnsupportedOperationException */
197   @Override
198   public void deleteFile(String name) {
199     throw new UnsupportedOperationException();
200   }
201   
202   /** Not implemented
203    * @throws UnsupportedOperationException */
204   public void renameFile(String from, String to) {
205     throw new UnsupportedOperationException();
206   }
207   
208   /** Returns the length of a file in the directory.
209    * @throws IOException if the file does not exist */
210   @Override
211   public long fileLength(String name) throws IOException {
212     FileEntry e = entries.get(IndexFileNames.stripSegmentName(name));
213     if (e == null)
214       throw new FileNotFoundException(name);
215     return e.length;
216   }
217   
218   /** Not implemented
219    * @throws UnsupportedOperationException */
220   @Override
221   public IndexOutput createOutput(String name) {
222     throw new UnsupportedOperationException();
223   }
224   
225   /** Not implemented
226    * @throws UnsupportedOperationException */
227   @Override
228   public Lock makeLock(String name) {
229     throw new UnsupportedOperationException();
230   }
231   
232   /** Implementation of an IndexInput that reads from a portion of the
233    *  compound file. The visibility is left as "package" *only* because
234    *  this helps with testing since JUnit test cases in a different class
235    *  can then access package fields of this class.
236    */
237   static final class CSIndexInput extends BufferedIndexInput {
238     IndexInput base;
239     long fileOffset;
240     long length;
241     
242     CSIndexInput(final IndexInput base, final long fileOffset, final long length) {
243       this(base, fileOffset, length, BufferedIndexInput.BUFFER_SIZE);
244     }
245     
246     CSIndexInput(final IndexInput base, final long fileOffset, final long length, int readBufferSize) {
247       super(readBufferSize);
248       this.base = (IndexInput)base.clone();
249       this.fileOffset = fileOffset;
250       this.length = length;
251     }
252     
253     @Override
254     public Object clone() {
255       CSIndexInput clone = (CSIndexInput)super.clone();
256       clone.base = (IndexInput)base.clone();
257       clone.fileOffset = fileOffset;
258       clone.length = length;
259       return clone;
260     }
261     
262     /** Expert: implements buffer refill.  Reads bytes from the current
263      *  position in the input.
264      * @param b the array to read bytes into
265      * @param offset the offset in the array to start storing bytes
266      * @param len the number of bytes to read
267      */
268     @Override
269     protected void readInternal(byte[] b, int offset, int len) throws IOException {
270       long start = getFilePointer();
271       if(start + len > length)
272         throw new EOFException("read past EOF (resource: " + base + ")");
273       base.seek(fileOffset + start);
274       base.readBytes(b, offset, len, false);
275     }
276     
277     /** Expert: implements seek.  Sets current position in this file, where
278      *  the next {@link #readInternal(byte[],int,int)} will occur.
279      * @see #readInternal(byte[],int,int)
280      */
281     @Override
282     protected void seekInternal(long pos) {}
283     
284     /** Closes the stream to further operations. */
285     @Override
286     public void close() throws IOException {
287       base.close();
288     }
289     
290     @Override
291     public long length() {
292       return length;
293     }
294     
295     @Override
296     public void copyBytes(IndexOutput out, long numBytes) throws IOException {
297       // Copy first whatever is in the buffer
298       numBytes -= flushBuffer(out, numBytes);
299       
300       // If there are more bytes left to copy, delegate the copy task to the
301       // base IndexInput, in case it can do an optimized copy.
302       if (numBytes > 0) {
303         long start = getFilePointer();
304         if (start + numBytes > length) {
305           throw new EOFException("read past EOF (resource: " + base + ")");
306         }
307         base.seek(fileOffset + start);
308         base.copyBytes(out, numBytes);
309       }
310     }
311   }
312 }