add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / src / java / org / apache / lucene / index / FieldInfos.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 org.apache.lucene.document.Document;
21 import org.apache.lucene.document.Fieldable;
22 import org.apache.lucene.index.FieldInfo.IndexOptions;
23 import org.apache.lucene.store.Directory;
24 import org.apache.lucene.store.IndexInput;
25 import org.apache.lucene.store.IndexOutput;
26 import org.apache.lucene.util.StringHelper;
27
28 import java.io.IOException;
29 import java.util.*;
30
31 /** Access to the Fieldable Info file that describes document fields and whether or
32  *  not they are indexed. Each segment has a separate Fieldable Info file. Objects
33  *  of this class are thread-safe for multiple readers, but only one thread can
34  *  be adding documents at a time, with no other reader or writer threads
35  *  accessing this object.
36  */
37 final class FieldInfos {
38
39   // Used internally (ie not written to *.fnm files) for pre-2.9 files
40   public static final int FORMAT_PRE = -1;
41
42   // First used in 2.9; prior to 2.9 there was no format header
43   public static final int FORMAT_START = -2;
44
45   // First used in 3.4: omit only positional information
46   public static final int FORMAT_OMIT_POSITIONS = -3;
47
48   // whenever you add a new format, make it 1 smaller (negative version logic)!
49   static final int CURRENT_FORMAT = FORMAT_OMIT_POSITIONS;
50   
51   static final byte IS_INDEXED = 0x1;
52   static final byte STORE_TERMVECTOR = 0x2;
53   static final byte STORE_POSITIONS_WITH_TERMVECTOR = 0x4;
54   static final byte STORE_OFFSET_WITH_TERMVECTOR = 0x8;
55   static final byte OMIT_NORMS = 0x10;
56   static final byte STORE_PAYLOADS = 0x20;
57   static final byte OMIT_TERM_FREQ_AND_POSITIONS = 0x40;
58   static final byte OMIT_POSITIONS = -128;
59
60   private final ArrayList<FieldInfo> byNumber = new ArrayList<FieldInfo>();
61   private final HashMap<String,FieldInfo> byName = new HashMap<String,FieldInfo>();
62   private int format;
63
64   FieldInfos() { }
65
66   /**
67    * Construct a FieldInfos object using the directory and the name of the file
68    * IndexInput
69    * @param d The directory to open the IndexInput from
70    * @param name The name of the file to open the IndexInput from in the Directory
71    * @throws IOException
72    */
73   FieldInfos(Directory d, String name) throws IOException {
74     IndexInput input = d.openInput(name);
75     try {
76       try {
77         read(input, name);
78       } catch (IOException ioe) {
79         if (format == FORMAT_PRE) {
80           // LUCENE-1623: FORMAT_PRE (before there was a
81           // format) may be 2.3.2 (pre-utf8) or 2.4.x (utf8)
82           // encoding; retry with input set to pre-utf8
83           input.seek(0);
84           input.setModifiedUTF8StringsMode();
85           byNumber.clear();
86           byName.clear();
87           try {
88             read(input, name);
89           } catch (Throwable t) {
90             // Ignore any new exception & throw original IOE
91             throw ioe;
92           }
93         } else {
94           // The IOException cannot be caused by
95           // LUCENE-1623, so re-throw it
96           throw ioe;
97         }
98       }
99     } finally {
100       input.close();
101     }
102   }
103
104   /**
105    * Returns a deep clone of this FieldInfos instance.
106    */
107   @Override
108   synchronized public Object clone() {
109     FieldInfos fis = new FieldInfos();
110     final int numField = byNumber.size();
111     for(int i=0;i<numField;i++) {
112       FieldInfo fi = (FieldInfo) ( byNumber.get(i)).clone();
113       fis.byNumber.add(fi);
114       fis.byName.put(fi.name, fi);
115     }
116     return fis;
117   }
118
119   /** Adds field info for a Document. */
120   synchronized public void add(Document doc) {
121     List<Fieldable> fields = doc.getFields();
122     for (Fieldable field : fields) {
123       add(field.name(), field.isIndexed(), field.isTermVectorStored(), field.isStorePositionWithTermVector(),
124               field.isStoreOffsetWithTermVector(), field.getOmitNorms(), false, field.getIndexOptions());
125     }
126   }
127
128   /** Returns true if any fields do not omitTermFreqAndPositions */
129   boolean hasProx() {
130     final int numFields = byNumber.size();
131     for(int i=0;i<numFields;i++) {
132       final FieldInfo fi = fieldInfo(i);
133       if (fi.isIndexed && fi.indexOptions == IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) {
134         return true;
135       }
136     }
137     return false;
138   }
139   
140   /**
141    * Add fields that are indexed. Whether they have termvectors has to be specified.
142    * 
143    * @param names The names of the fields
144    * @param storeTermVectors Whether the fields store term vectors or not
145    * @param storePositionWithTermVector true if positions should be stored.
146    * @param storeOffsetWithTermVector true if offsets should be stored
147    */
148   synchronized public void addIndexed(Collection<String> names, boolean storeTermVectors, boolean storePositionWithTermVector, 
149                          boolean storeOffsetWithTermVector) {
150     for (String name : names) {
151       add(name, true, storeTermVectors, storePositionWithTermVector, storeOffsetWithTermVector);
152     }
153   }
154
155   /**
156    * Assumes the fields are not storing term vectors.
157    * 
158    * @param names The names of the fields
159    * @param isIndexed Whether the fields are indexed or not
160    * 
161    * @see #add(String, boolean)
162    */
163   synchronized public void add(Collection<String> names, boolean isIndexed) {
164     for (String name : names) {
165       add(name, isIndexed);
166     }
167   }
168
169   /**
170    * Calls 5 parameter add with false for all TermVector parameters.
171    * 
172    * @param name The name of the Fieldable
173    * @param isIndexed true if the field is indexed
174    * @see #add(String, boolean, boolean, boolean, boolean)
175    */
176   synchronized public void add(String name, boolean isIndexed) {
177     add(name, isIndexed, false, false, false, false);
178   }
179
180   /**
181    * Calls 5 parameter add with false for term vector positions and offsets.
182    * 
183    * @param name The name of the field
184    * @param isIndexed  true if the field is indexed
185    * @param storeTermVector true if the term vector should be stored
186    */
187   synchronized public void add(String name, boolean isIndexed, boolean storeTermVector){
188     add(name, isIndexed, storeTermVector, false, false, false);
189   }
190   
191   /** If the field is not yet known, adds it. If it is known, checks to make
192    *  sure that the isIndexed flag is the same as was given previously for this
193    *  field. If not - marks it as being indexed.  Same goes for the TermVector
194    * parameters.
195    * 
196    * @param name The name of the field
197    * @param isIndexed true if the field is indexed
198    * @param storeTermVector true if the term vector should be stored
199    * @param storePositionWithTermVector true if the term vector with positions should be stored
200    * @param storeOffsetWithTermVector true if the term vector with offsets should be stored
201    */
202   synchronized public void add(String name, boolean isIndexed, boolean storeTermVector,
203                   boolean storePositionWithTermVector, boolean storeOffsetWithTermVector) {
204
205     add(name, isIndexed, storeTermVector, storePositionWithTermVector, storeOffsetWithTermVector, false);
206   }
207
208     /** If the field is not yet known, adds it. If it is known, checks to make
209    *  sure that the isIndexed flag is the same as was given previously for this
210    *  field. If not - marks it as being indexed.  Same goes for the TermVector
211    * parameters.
212    *
213    * @param name The name of the field
214    * @param isIndexed true if the field is indexed
215    * @param storeTermVector true if the term vector should be stored
216    * @param storePositionWithTermVector true if the term vector with positions should be stored
217    * @param storeOffsetWithTermVector true if the term vector with offsets should be stored
218    * @param omitNorms true if the norms for the indexed field should be omitted
219    */
220   synchronized public void add(String name, boolean isIndexed, boolean storeTermVector,
221                   boolean storePositionWithTermVector, boolean storeOffsetWithTermVector, boolean omitNorms) {
222     add(name, isIndexed, storeTermVector, storePositionWithTermVector,
223         storeOffsetWithTermVector, omitNorms, false, IndexOptions.DOCS_AND_FREQS_AND_POSITIONS);
224   }
225   
226   /** If the field is not yet known, adds it. If it is known, checks to make
227    *  sure that the isIndexed flag is the same as was given previously for this
228    *  field. If not - marks it as being indexed.  Same goes for the TermVector
229    * parameters.
230    *
231    * @param name The name of the field
232    * @param isIndexed true if the field is indexed
233    * @param storeTermVector true if the term vector should be stored
234    * @param storePositionWithTermVector true if the term vector with positions should be stored
235    * @param storeOffsetWithTermVector true if the term vector with offsets should be stored
236    * @param omitNorms true if the norms for the indexed field should be omitted
237    * @param storePayloads true if payloads should be stored for this field
238    * @param indexOptions if term freqs should be omitted for this field
239    */
240   synchronized public FieldInfo add(String name, boolean isIndexed, boolean storeTermVector,
241                        boolean storePositionWithTermVector, boolean storeOffsetWithTermVector,
242                        boolean omitNorms, boolean storePayloads, IndexOptions indexOptions) {
243     FieldInfo fi = fieldInfo(name);
244     if (fi == null) {
245       return addInternal(name, isIndexed, storeTermVector, storePositionWithTermVector, storeOffsetWithTermVector, omitNorms, storePayloads, indexOptions);
246     } else {
247       fi.update(isIndexed, storeTermVector, storePositionWithTermVector, storeOffsetWithTermVector, omitNorms, storePayloads, indexOptions);
248     }
249     assert fi.indexOptions == IndexOptions.DOCS_AND_FREQS_AND_POSITIONS || !fi.storePayloads;
250     return fi;
251   }
252
253   synchronized public FieldInfo add(FieldInfo fi) {
254     return add(fi.name, fi.isIndexed, fi.storeTermVector,
255                fi.storePositionWithTermVector, fi.storeOffsetWithTermVector,
256                fi.omitNorms, fi.storePayloads,
257                fi.indexOptions);
258   }
259
260   private FieldInfo addInternal(String name, boolean isIndexed,
261                                 boolean storeTermVector, boolean storePositionWithTermVector, 
262                                 boolean storeOffsetWithTermVector, boolean omitNorms, boolean storePayloads, IndexOptions indexOptions) {
263     name = StringHelper.intern(name);
264     FieldInfo fi = new FieldInfo(name, isIndexed, byNumber.size(), storeTermVector, storePositionWithTermVector,
265                                  storeOffsetWithTermVector, omitNorms, storePayloads, indexOptions);
266     byNumber.add(fi);
267     byName.put(name, fi);
268     return fi;
269   }
270
271   public int fieldNumber(String fieldName) {
272     FieldInfo fi = fieldInfo(fieldName);
273     return (fi != null) ? fi.number : -1;
274   }
275
276   public FieldInfo fieldInfo(String fieldName) {
277     return  byName.get(fieldName);
278   }
279
280   /**
281    * Return the fieldName identified by its number.
282    * 
283    * @param fieldNumber
284    * @return the fieldName or an empty string when the field
285    * with the given number doesn't exist.
286    */  
287   public String fieldName(int fieldNumber) {
288         FieldInfo fi = fieldInfo(fieldNumber);
289         return (fi != null) ? fi.name : "";
290   }
291
292   /**
293    * Return the fieldinfo object referenced by the fieldNumber.
294    * @param fieldNumber
295    * @return the FieldInfo object or null when the given fieldNumber
296    * doesn't exist.
297    */  
298   public FieldInfo fieldInfo(int fieldNumber) {
299         return (fieldNumber >= 0) ? byNumber.get(fieldNumber) : null;
300   }
301
302   public int size() {
303     return byNumber.size();
304   }
305
306   public boolean hasVectors() {
307     boolean hasVectors = false;
308     for (int i = 0; i < size(); i++) {
309       if (fieldInfo(i).storeTermVector) {
310         hasVectors = true;
311         break;
312       }
313     }
314     return hasVectors;
315   }
316
317   public void write(Directory d, String name) throws IOException {
318     IndexOutput output = d.createOutput(name);
319     try {
320       write(output);
321     } finally {
322       output.close();
323     }
324   }
325
326   public void write(IndexOutput output) throws IOException {
327     output.writeVInt(CURRENT_FORMAT);
328     output.writeVInt(size());
329     for (int i = 0; i < size(); i++) {
330       FieldInfo fi = fieldInfo(i);
331       assert fi.indexOptions == IndexOptions.DOCS_AND_FREQS_AND_POSITIONS || !fi.storePayloads;
332       byte bits = 0x0;
333       if (fi.isIndexed) bits |= IS_INDEXED;
334       if (fi.storeTermVector) bits |= STORE_TERMVECTOR;
335       if (fi.storePositionWithTermVector) bits |= STORE_POSITIONS_WITH_TERMVECTOR;
336       if (fi.storeOffsetWithTermVector) bits |= STORE_OFFSET_WITH_TERMVECTOR;
337       if (fi.omitNorms) bits |= OMIT_NORMS;
338       if (fi.storePayloads) bits |= STORE_PAYLOADS;
339       if (fi.indexOptions == IndexOptions.DOCS_ONLY)
340         bits |= OMIT_TERM_FREQ_AND_POSITIONS;
341       else if (fi.indexOptions == IndexOptions.DOCS_AND_FREQS)
342         bits |= OMIT_POSITIONS;
343       
344       output.writeString(fi.name);
345       output.writeByte(bits);
346     }
347   }
348
349   private void read(IndexInput input, String fileName) throws IOException {
350     int firstInt = input.readVInt();
351
352     if (firstInt < 0) {
353       // This is a real format
354       format = firstInt;
355     } else {
356       format = FORMAT_PRE;
357     }
358
359     if (format != FORMAT_PRE && format != FORMAT_START && format != FORMAT_OMIT_POSITIONS) {
360       throw new CorruptIndexException("unrecognized format " + format + " in file \"" + fileName + "\"");
361     }
362
363     int size;
364     if (format == FORMAT_PRE) {
365       size = firstInt;
366     } else {
367       size = input.readVInt(); //read in the size
368     }
369
370     for (int i = 0; i < size; i++) {
371       String name = StringHelper.intern(input.readString());
372       byte bits = input.readByte();
373       boolean isIndexed = (bits & IS_INDEXED) != 0;
374       boolean storeTermVector = (bits & STORE_TERMVECTOR) != 0;
375       boolean storePositionsWithTermVector = (bits & STORE_POSITIONS_WITH_TERMVECTOR) != 0;
376       boolean storeOffsetWithTermVector = (bits & STORE_OFFSET_WITH_TERMVECTOR) != 0;
377       boolean omitNorms = (bits & OMIT_NORMS) != 0;
378       boolean storePayloads = (bits & STORE_PAYLOADS) != 0;
379       final IndexOptions indexOptions;
380       if ((bits & OMIT_TERM_FREQ_AND_POSITIONS) != 0) {
381         indexOptions = IndexOptions.DOCS_ONLY;
382       } else if ((bits & OMIT_POSITIONS) != 0) {
383         if (format <= FORMAT_OMIT_POSITIONS) {
384           indexOptions = IndexOptions.DOCS_AND_FREQS;
385         } else {
386           throw new CorruptIndexException("Corrupt fieldinfos, OMIT_POSITIONS set but format=" + format);
387         }
388       } else {
389         indexOptions = IndexOptions.DOCS_AND_FREQS_AND_POSITIONS;
390       }
391       
392       // LUCENE-3027: past indices were able to write
393       // storePayloads=true when omitTFAP is also true,
394       // which is invalid.  We correct that, here:
395       if (indexOptions != IndexOptions.DOCS_AND_FREQS_AND_POSITIONS) {
396         storePayloads = false;
397       }
398
399       addInternal(name, isIndexed, storeTermVector, storePositionsWithTermVector, storeOffsetWithTermVector, omitNorms, storePayloads, indexOptions);
400     }
401
402     if (input.getFilePointer() != input.length()) {
403       throw new CorruptIndexException("did not read all bytes from file \"" + fileName + "\": read " + input.getFilePointer() + " vs size " + input.length());
404     }    
405   }
406
407 }