001 package gate.creole.annic.apache.lucene.index;
002
003 /**
004 * Copyright 2004 The Apache Software Foundation
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 import gate.creole.annic.apache.lucene.store.Directory;
020 import gate.creole.annic.apache.lucene.store.InputStream;
021 import gate.creole.annic.apache.lucene.store.OutputStream;
022 import gate.creole.annic.apache.lucene.store.FSDirectory;
023 import gate.creole.annic.apache.lucene.store.Lock;
024 import java.util.HashMap;
025 import java.util.Iterator;
026 import java.io.IOException;
027
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 * @author Dmitry Serebrennikov
035 * @version $Id: CompoundFileReader.java 529 2004-10-05 11:55:26Z niraj $
036 */
037 class CompoundFileReader extends Directory {
038
039 private static final class FileEntry {
040 long offset;
041 long length;
042 }
043
044
045 // Base info
046 private Directory directory;
047 private String fileName;
048
049 // Reference count
050 private boolean open;
051
052 private InputStream stream;
053 private HashMap entries = new HashMap();
054
055
056 public CompoundFileReader(Directory dir, String name)
057 throws IOException
058 {
059 directory = dir;
060 fileName = name;
061
062 boolean success = false;
063
064 try {
065 stream = dir.openFile(name);
066
067 // read the directory and init files
068 int count = stream.readVInt();
069 FileEntry entry = null;
070 for (int i=0; i<count; i++) {
071 long offset = stream.readLong();
072 String id = stream.readString();
073
074 if (entry != null) {
075 // set length of the previous entry
076 entry.length = offset - entry.offset;
077 }
078
079 entry = new FileEntry();
080 entry.offset = offset;
081 entries.put(id, entry);
082 }
083
084 // set the length of the final entry
085 if (entry != null) {
086 entry.length = stream.length() - entry.offset;
087 }
088
089 success = true;
090
091 } finally {
092 if (! success) {
093 try {
094 stream.close();
095 } catch (IOException e) { }
096 }
097 }
098 }
099
100 public Directory getDirectory() {
101 return directory;
102 }
103
104 public String getName() {
105 return fileName;
106 }
107
108 public synchronized void close() throws IOException {
109 if (stream == null)
110 throw new IOException("Already closed");
111
112 entries.clear();
113 stream.close();
114 stream = null;
115 }
116
117 public synchronized InputStream openFile(String id)
118 throws IOException
119 {
120 if (stream == null)
121 throw new IOException("Stream closed");
122
123 FileEntry entry = (FileEntry) entries.get(id);
124 if (entry == null)
125 throw new IOException("No sub-file with id " + id + " found");
126
127 return new CSInputStream(stream, entry.offset, entry.length);
128 }
129
130 /** Returns an array of strings, one for each file in the directory. */
131 public String[] list() {
132 String res[] = new String[entries.size()];
133 return (String[]) entries.keySet().toArray(res);
134 }
135
136 /** Returns true iff a file with the given name exists. */
137 public boolean fileExists(String name) {
138 return entries.containsKey(name);
139 }
140
141 /** Returns the time the named file was last modified. */
142 public long fileModified(String name) throws IOException {
143 return directory.fileModified(fileName);
144 }
145
146 /** Set the modified time of an existing file to now. */
147 public void touchFile(String name) throws IOException {
148 directory.touchFile(fileName);
149 }
150
151 /** Removes an existing file in the directory. */
152 public void deleteFile(String name)
153 {
154 throw new UnsupportedOperationException();
155 }
156
157 /** Renames an existing file in the directory.
158 If a file already exists with the new name, then it is replaced.
159 This replacement should be atomic. */
160 public void renameFile(String from, String to)
161 {
162 throw new UnsupportedOperationException();
163 }
164
165 /** Returns the length of a file in the directory. */
166 public long fileLength(String name)
167 throws IOException
168 {
169 FileEntry e = (FileEntry) entries.get(name);
170 if (e == null)
171 throw new IOException("File " + name + " does not exist");
172 return e.length;
173 }
174
175 /** Creates a new, empty file in the directory with the given name.
176 Returns a stream writing this file. */
177 public OutputStream createFile(String name)
178 {
179 throw new UnsupportedOperationException();
180 }
181
182 /** Construct a {@link Lock}.
183 * @param name the name of the lock file
184 */
185 public Lock makeLock(String name)
186 {
187 throw new UnsupportedOperationException();
188 }
189
190 /** Implementation of an InputStream that reads from a portion of the
191 * compound file. The visibility is left as "package" *only* because
192 * this helps with testing since JUnit test cases in a different class
193 * can then access package fields of this class.
194 */
195 static final class CSInputStream extends InputStream {
196
197 InputStream base;
198 long fileOffset;
199
200 CSInputStream(final InputStream base, final long fileOffset, final long length)
201 throws IOException
202 {
203 this.base = base;
204 this.fileOffset = fileOffset;
205 this.length = length; // variable in the superclass
206 }
207
208 /** Expert: implements buffer refill. Reads bytes from the current
209 * position in the input.
210 * @param b the array to read bytes into
211 * @param offset the offset in the array to start storing bytes
212 * @param length the number of bytes to read
213 */
214 protected void readInternal(byte[] b, int offset, int len)
215 throws IOException
216 {
217 synchronized (base) {
218 long start = getFilePointer();
219 if(start + len > length) {
220 System.out.println("I m in compound");
221 throw new IOException("read past EOF");
222 }
223 base.seek(fileOffset + start);
224 base.readBytes(b, offset, len);
225 }
226 }
227
228 /** Expert: implements seek. Sets current position in this file, where
229 * the next {@link #readInternal(byte[],int,int)} will occur.
230 * @see #readInternal(byte[],int,int)
231 */
232 protected void seekInternal(long pos) throws IOException {}
233
234 /** Closes the stream to futher operations. */
235 public void close() throws IOException {}
236
237 }
238 }
|