pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / java / org / apache / lucene / index / SegmentInfo.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.store.Directory;
21 import org.apache.lucene.store.IndexOutput;
22 import org.apache.lucene.store.IndexInput;
23 import org.apache.lucene.util.BitVector;
24 import org.apache.lucene.util.Constants;
25
26 import java.io.IOException;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.HashMap;
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.Set;
34
35 /**
36  * Information about a segment such as it's name, directory, and files related
37  * to the segment.
38  * 
39  * @lucene.experimental
40  */
41 public final class SegmentInfo implements Cloneable {
42
43   static final int NO = -1;          // e.g. no norms; no deletes;
44   static final int YES = 1;          // e.g. have norms; have deletes;
45   static final int CHECK_DIR = 0;    // e.g. must check dir to see if there are norms/deletions
46   static final int WITHOUT_GEN = 0;  // a file name that has no GEN in it. 
47
48   public String name;                             // unique name in dir
49   public int docCount;                            // number of docs in seg
50   public Directory dir;                           // where segment resides
51
52   private boolean preLockless;                    // true if this is a segments file written before
53                                                   // lock-less commits (2.1)
54
55   private long delGen;                            // current generation of del file; NO if there
56                                                   // are no deletes; CHECK_DIR if it's a pre-2.1 segment
57                                                   // (and we must check filesystem); YES or higher if
58                                                   // there are deletes at generation N
59    
60   private long[] normGen;                         // current generation of each field's norm file.
61                                                   // If this array is null, for lockLess this means no 
62                                                   // separate norms.  For preLockLess this means we must 
63                                                   // check filesystem. If this array is not null, its 
64                                                   // values mean: NO says this field has no separate  
65                                                   // norms; CHECK_DIR says it is a preLockLess segment and    
66                                                   // filesystem must be checked; >= YES says this field  
67                                                   // has separate norms with the specified generation
68
69   private byte isCompoundFile;                    // NO if it is not; YES if it is; CHECK_DIR if it's
70                                                   // pre-2.1 (ie, must check file system to see
71                                                   // if <name>.cfs and <name>.nrm exist)         
72
73   private boolean hasSingleNormFile;              // true if this segment maintains norms in a single file; 
74                                                   // false otherwise
75                                                   // this is currently false for segments populated by DocumentWriter
76                                                   // and true for newly created merged segments (both
77                                                   // compound and non compound).
78   
79   private volatile List<String> files;            // cached list of files that this segment uses
80                                                   // in the Directory
81
82   private volatile long sizeInBytesNoStore = -1;           // total byte size of all but the store files (computed on demand)
83   private volatile long sizeInBytesWithStore = -1;         // total byte size of all of our files (computed on demand)
84
85   private int docStoreOffset;                     // if this segment shares stored fields & vectors, this
86                                                   // offset is where in that file this segment's docs begin
87   private String docStoreSegment;                 // name used to derive fields/vectors file we share with
88                                                   // other segments
89   private boolean docStoreIsCompoundFile;         // whether doc store files are stored in compound file (*.cfx)
90
91   private int delCount;                           // How many deleted docs in this segment, or -1 if not yet known
92                                                   // (if it's an older index)
93
94   private boolean hasProx;                        // True if this segment has any fields with omitTermFreqAndPositions==false
95
96   private boolean hasVectors;                     // True if this segment wrote term vectors
97
98   private Map<String,String> diagnostics;
99
100   // Tracks the Lucene version this segment was created with, since 3.1. The
101   // format expected is "x.y" - "2.x" for pre-3.0 indexes, and specific versions
102   // afterwards ("3.0", "3.1" etc.).
103   // see Constants.LUCENE_MAIN_VERSION.
104   private String version;
105
106   // NOTE: only used in-RAM by IW to track buffered deletes;
107   // this is never written to/read from the Directory
108   private long bufferedDeletesGen;
109   
110   public SegmentInfo(String name, int docCount, Directory dir, boolean isCompoundFile, boolean hasSingleNormFile,
111                      boolean hasProx, boolean hasVectors) { 
112     this.name = name;
113     this.docCount = docCount;
114     this.dir = dir;
115     delGen = NO;
116     this.isCompoundFile = (byte) (isCompoundFile ? YES : NO);
117     preLockless = false;
118     this.hasSingleNormFile = hasSingleNormFile;
119     this.docStoreOffset = -1;
120     delCount = 0;
121     this.hasProx = hasProx;
122     this.hasVectors = hasVectors;
123     this.version = Constants.LUCENE_MAIN_VERSION;
124   }
125
126   /**
127    * Copy everything from src SegmentInfo into our instance.
128    */
129   void reset(SegmentInfo src) {
130     clearFiles();
131     version = src.version;
132     name = src.name;
133     docCount = src.docCount;
134     dir = src.dir;
135     preLockless = src.preLockless;
136     delGen = src.delGen;
137     docStoreOffset = src.docStoreOffset;
138     docStoreIsCompoundFile = src.docStoreIsCompoundFile;
139     hasVectors = src.hasVectors;
140     hasProx = src.hasProx;
141     if (src.normGen == null) {
142       normGen = null;
143     } else {
144       normGen = new long[src.normGen.length];
145       System.arraycopy(src.normGen, 0, normGen, 0, src.normGen.length);
146     }
147     isCompoundFile = src.isCompoundFile;
148     hasSingleNormFile = src.hasSingleNormFile;
149     delCount = src.delCount;
150   }
151
152   void setDiagnostics(Map<String, String> diagnostics) {
153     this.diagnostics = diagnostics;
154   }
155
156   public Map<String, String> getDiagnostics() {
157     return diagnostics;
158   }
159
160   /**
161    * Construct a new SegmentInfo instance by reading a
162    * previously saved SegmentInfo from input.
163    *
164    * @param dir directory to load from
165    * @param format format of the segments info file
166    * @param input input handle to read segment info from
167    */
168   SegmentInfo(Directory dir, int format, IndexInput input) throws IOException {
169     this.dir = dir;
170     if (format <= SegmentInfos.FORMAT_3_1) {
171       version = input.readString();
172     }
173     name = input.readString();
174     docCount = input.readInt();
175     if (format <= SegmentInfos.FORMAT_LOCKLESS) {
176       delGen = input.readLong();
177       if (format <= SegmentInfos.FORMAT_SHARED_DOC_STORE) {
178         docStoreOffset = input.readInt();
179         if (docStoreOffset != -1) {
180           docStoreSegment = input.readString();
181           docStoreIsCompoundFile = (1 == input.readByte());
182         } else {
183           docStoreSegment = name;
184           docStoreIsCompoundFile = false;
185         }
186       } else {
187         docStoreOffset = -1;
188         docStoreSegment = name;
189         docStoreIsCompoundFile = false;
190       }
191       if (format <= SegmentInfos.FORMAT_SINGLE_NORM_FILE) {
192         hasSingleNormFile = (1 == input.readByte());
193       } else {
194         hasSingleNormFile = false;
195       }
196       int numNormGen = input.readInt();
197       if (numNormGen == NO) {
198         normGen = null;
199       } else {
200         normGen = new long[numNormGen];
201         for(int j=0;j<numNormGen;j++) {
202           normGen[j] = input.readLong();
203         }
204       }
205       isCompoundFile = input.readByte();
206       preLockless = (isCompoundFile == CHECK_DIR);
207       if (format <= SegmentInfos.FORMAT_DEL_COUNT) {
208         delCount = input.readInt();
209         assert delCount <= docCount;
210       } else
211         delCount = -1;
212       if (format <= SegmentInfos.FORMAT_HAS_PROX)
213         hasProx = input.readByte() == 1;
214       else
215         hasProx = true;
216
217       if (format <= SegmentInfos.FORMAT_DIAGNOSTICS) {
218         diagnostics = input.readStringStringMap();
219       } else {
220         diagnostics = Collections.<String,String>emptyMap();
221       }
222
223       if (format <= SegmentInfos.FORMAT_HAS_VECTORS) {
224         hasVectors = input.readByte() == 1;
225       } else {
226         final String storesSegment;
227         final String ext;
228         final boolean isCompoundFile;
229         if (docStoreOffset != -1) {
230           storesSegment = docStoreSegment;
231           isCompoundFile = docStoreIsCompoundFile;
232           ext = IndexFileNames.COMPOUND_FILE_STORE_EXTENSION;
233         } else {
234           storesSegment = name;
235           isCompoundFile = getUseCompoundFile();
236           ext = IndexFileNames.COMPOUND_FILE_EXTENSION;
237         }
238         final Directory dirToTest;
239         if (isCompoundFile) {
240           dirToTest = new CompoundFileReader(dir, IndexFileNames.segmentFileName(storesSegment, ext));
241         } else {
242           dirToTest = dir;
243         }
244         try {
245           hasVectors = dirToTest.fileExists(IndexFileNames.segmentFileName(storesSegment, IndexFileNames.VECTORS_INDEX_EXTENSION));
246         } finally {
247           if (isCompoundFile) {
248             dirToTest.close();
249           }
250         }
251       }
252     } else {
253       delGen = CHECK_DIR;
254       normGen = null;
255       isCompoundFile = CHECK_DIR;
256       preLockless = true;
257       hasSingleNormFile = false;
258       docStoreOffset = -1;
259       docStoreIsCompoundFile = false;
260       docStoreSegment = null;
261       delCount = -1;
262       hasProx = true;
263       diagnostics = Collections.<String,String>emptyMap();
264     }
265   }
266   
267   void setNumFields(int numFields) {
268     if (normGen == null) {
269       // normGen is null if we loaded a pre-2.1 segment
270       // file, or, if this segments file hasn't had any
271       // norms set against it yet:
272       normGen = new long[numFields];
273
274       if (preLockless) {
275         // Do nothing: thus leaving normGen[k]==CHECK_DIR (==0), so that later we know  
276         // we have to check filesystem for norm files, because this is prelockless.
277         
278       } else {
279         // This is a FORMAT_LOCKLESS segment, which means
280         // there are no separate norms:
281         for(int i=0;i<numFields;i++) {
282           normGen[i] = NO;
283         }
284       }
285     }
286   }
287
288   /**
289    * Returns total size in bytes of all of files used by this segment (if
290    * {@code includeDocStores} is true), or the size of all files except the store
291    * files otherwise.
292    */
293   public long sizeInBytes(boolean includeDocStores) throws IOException {
294     if (includeDocStores) {
295       if (sizeInBytesWithStore != -1) {
296         return sizeInBytesWithStore;
297       }
298       long sum = 0;
299       for (final String fileName : files()) {
300         // We don't count bytes used by a shared doc store
301         // against this segment
302         if (docStoreOffset == -1 || !IndexFileNames.isDocStoreFile(fileName)) {
303           sum += dir.fileLength(fileName);
304         }
305       }
306       sizeInBytesWithStore = sum;
307       return sizeInBytesWithStore;
308     } else {
309       if (sizeInBytesNoStore != -1) {
310         return sizeInBytesNoStore;
311       }
312       long sum = 0;
313       for (final String fileName : files()) {
314         if (IndexFileNames.isDocStoreFile(fileName)) {
315           continue;
316         }
317         sum += dir.fileLength(fileName);
318       }
319       sizeInBytesNoStore = sum;
320       return sizeInBytesNoStore;
321     }
322   }
323
324   public boolean getHasVectors() throws IOException {
325     return hasVectors;
326   }
327
328   public void setHasVectors(boolean v) {
329     hasVectors = v;
330     clearFiles();
331   }
332
333   public boolean hasDeletions()
334     throws IOException {
335     // Cases:
336     //
337     //   delGen == NO: this means this segment was written
338     //     by the LOCKLESS code and for certain does not have
339     //     deletions yet
340     //
341     //   delGen == CHECK_DIR: this means this segment was written by
342     //     pre-LOCKLESS code which means we must check
343     //     directory to see if .del file exists
344     //
345     //   delGen >= YES: this means this segment was written by
346     //     the LOCKLESS code and for certain has
347     //     deletions
348     //
349     if (delGen == NO) {
350       return false;
351     } else if (delGen >= YES) {
352       return true;
353     } else {
354       return dir.fileExists(getDelFileName());
355     }
356   }
357
358   void advanceDelGen() {
359     // delGen 0 is reserved for pre-LOCKLESS format
360     if (delGen == NO) {
361       delGen = YES;
362     } else {
363       delGen++;
364     }
365     clearFiles();
366   }
367
368   void clearDelGen() {
369     delGen = NO;
370     clearFiles();
371   }
372
373   @Override
374   public Object clone() {
375     SegmentInfo si = new SegmentInfo(name, docCount, dir, false, hasSingleNormFile,
376                                      hasProx, hasVectors);
377     si.docStoreOffset = docStoreOffset;
378     si.docStoreSegment = docStoreSegment;
379     si.docStoreIsCompoundFile = docStoreIsCompoundFile;
380     si.delGen = delGen;
381     si.delCount = delCount;
382     si.preLockless = preLockless;
383     si.isCompoundFile = isCompoundFile;
384     si.diagnostics = new HashMap<String, String>(diagnostics);
385     if (normGen != null) {
386       si.normGen = normGen.clone();
387     }
388     si.version = version;
389     return si;
390   }
391
392   public String getDelFileName() {
393     if (delGen == NO) {
394       // In this case we know there is no deletion filename
395       // against this segment
396       return null;
397     } else {
398       // If delGen is CHECK_DIR, it's the pre-lockless-commit file format
399       return IndexFileNames.fileNameFromGeneration(name, IndexFileNames.DELETES_EXTENSION, delGen); 
400     }
401   }
402
403   /**
404    * Returns true if this field for this segment has saved a separate norms file (_<segment>_N.sX).
405    *
406    * @param fieldNumber the field index to check
407    */
408   public boolean hasSeparateNorms(int fieldNumber)
409     throws IOException {
410     if ((normGen == null && preLockless) || (normGen != null && normGen[fieldNumber] == CHECK_DIR)) {
411       // Must fallback to directory file exists check:
412       String fileName = name + ".s" + fieldNumber;
413       return dir.fileExists(fileName);
414     } else if (normGen == null || normGen[fieldNumber] == NO) {
415       return false;
416     } else {
417       return true;
418     }
419   }
420
421   /**
422    * Returns true if any fields in this segment have separate norms.
423    */
424   public boolean hasSeparateNorms()
425     throws IOException {
426     if (normGen == null) {
427       if (!preLockless) {
428         // This means we were created w/ LOCKLESS code and no
429         // norms are written yet:
430         return false;
431       } else {
432         // This means this segment was saved with pre-LOCKLESS
433         // code.  So we must fallback to the original
434         // directory list check:
435         String[] result = dir.listAll();
436         if (result == null)
437           throw new IOException("cannot read directory " + dir + ": listAll() returned null");
438
439         final IndexFileNameFilter filter = IndexFileNameFilter.getFilter();
440         String pattern;
441         pattern = name + ".s";
442         int patternLength = pattern.length();
443         for(int i = 0; i < result.length; i++){
444           String fileName = result[i];
445           if (filter.accept(null, fileName) && fileName.startsWith(pattern) && Character.isDigit(fileName.charAt(patternLength)))
446               return true;
447         }
448         return false;
449       }
450     } else {
451       // This means this segment was saved with LOCKLESS
452       // code so we first check whether any normGen's are >= 1
453       // (meaning they definitely have separate norms):
454       for(int i=0;i<normGen.length;i++) {
455         if (normGen[i] >= YES) {
456           return true;
457         }
458       }
459       // Next we look for any == 0.  These cases were
460       // pre-LOCKLESS and must be checked in directory:
461       for(int i=0;i<normGen.length;i++) {
462         if (normGen[i] == CHECK_DIR) {
463           if (hasSeparateNorms(i)) {
464             return true;
465           }
466         }
467       }
468     }
469
470     return false;
471   }
472
473   /**
474    * Increment the generation count for the norms file for
475    * this field.
476    *
477    * @param fieldIndex field whose norm file will be rewritten
478    */
479   void advanceNormGen(int fieldIndex) {
480     if (normGen[fieldIndex] == NO) {
481       normGen[fieldIndex] = YES;
482     } else {
483       normGen[fieldIndex]++;
484     }
485     clearFiles();
486   }
487
488   /**
489    * Get the file name for the norms file for this field.
490    *
491    * @param number field index
492    */
493   public String getNormFileName(int number) throws IOException {
494     long gen;
495     if (normGen == null) {
496       gen = CHECK_DIR;
497     } else {
498       gen = normGen[number];
499     }
500     
501     if (hasSeparateNorms(number)) {
502       // case 1: separate norm
503       return IndexFileNames.fileNameFromGeneration(name, "s" + number, gen);
504     }
505
506     if (hasSingleNormFile) {
507       // case 2: lockless (or nrm file exists) - single file for all norms 
508       return IndexFileNames.fileNameFromGeneration(name, IndexFileNames.NORMS_EXTENSION, WITHOUT_GEN);
509     }
510       
511     // case 3: norm file for each field
512     return IndexFileNames.fileNameFromGeneration(name, "f" + number, WITHOUT_GEN);
513   }
514
515   /**
516    * Mark whether this segment is stored as a compound file.
517    *
518    * @param isCompoundFile true if this is a compound file;
519    * else, false
520    */
521   void setUseCompoundFile(boolean isCompoundFile) {
522     if (isCompoundFile) {
523       this.isCompoundFile = YES;
524     } else {
525       this.isCompoundFile = NO;
526     }
527     clearFiles();
528   }
529
530   /**
531    * Returns true if this segment is stored as a compound
532    * file; else, false.
533    */
534   public boolean getUseCompoundFile() throws IOException {
535     if (isCompoundFile == NO) {
536       return false;
537     } else if (isCompoundFile == YES) {
538       return true;
539     } else {
540       return dir.fileExists(IndexFileNames.segmentFileName(name, IndexFileNames.COMPOUND_FILE_EXTENSION));
541     }
542   }
543
544   public int getDelCount() throws IOException {
545     if (delCount == -1) {
546       if (hasDeletions()) {
547         final String delFileName = getDelFileName();
548         delCount = new BitVector(dir, delFileName).count();
549       } else
550         delCount = 0;
551     }
552     assert delCount <= docCount;
553     return delCount;
554   }
555
556   void setDelCount(int delCount) {
557     this.delCount = delCount;
558     assert delCount <= docCount;
559   }
560
561   public int getDocStoreOffset() {
562     return docStoreOffset;
563   }
564   
565   public boolean getDocStoreIsCompoundFile() {
566     return docStoreIsCompoundFile;
567   }
568   
569   void setDocStoreIsCompoundFile(boolean v) {
570     docStoreIsCompoundFile = v;
571     clearFiles();
572   }
573   
574   public String getDocStoreSegment() {
575     return docStoreSegment;
576   }
577   
578   public void setDocStoreSegment(String segment) {
579     docStoreSegment = segment;
580   }
581   
582   void setDocStoreOffset(int offset) {
583     docStoreOffset = offset;
584     clearFiles();
585   }
586
587   void setDocStore(int offset, String segment, boolean isCompoundFile) {        
588     docStoreOffset = offset;
589     docStoreSegment = segment;
590     docStoreIsCompoundFile = isCompoundFile;
591     clearFiles();
592   }
593   
594   /**
595    * Save this segment's info.
596    */
597   void write(IndexOutput output)
598     throws IOException {
599     assert delCount <= docCount: "delCount=" + delCount + " docCount=" + docCount + " segment=" + name;
600     // Write the Lucene version that created this segment, since 3.1
601     output.writeString(version); 
602     output.writeString(name);
603     output.writeInt(docCount);
604     output.writeLong(delGen);
605     output.writeInt(docStoreOffset);
606     if (docStoreOffset != -1) {
607       output.writeString(docStoreSegment);
608       output.writeByte((byte) (docStoreIsCompoundFile ? 1:0));
609     }
610
611     output.writeByte((byte) (hasSingleNormFile ? 1:0));
612     if (normGen == null) {
613       output.writeInt(NO);
614     } else {
615       output.writeInt(normGen.length);
616       for(int j = 0; j < normGen.length; j++) {
617         output.writeLong(normGen[j]);
618       }
619     }
620     output.writeByte(isCompoundFile);
621     output.writeInt(delCount);
622     output.writeByte((byte) (hasProx ? 1:0));
623     output.writeStringStringMap(diagnostics);
624     output.writeByte((byte) (hasVectors ? 1 : 0));
625   }
626
627   void setHasProx(boolean hasProx) {
628     this.hasProx = hasProx;
629     clearFiles();
630   }
631
632   public boolean getHasProx() {
633     return hasProx;
634   }
635
636   private void addIfExists(Set<String> files, String fileName) throws IOException {
637     if (dir.fileExists(fileName))
638       files.add(fileName);
639   }
640
641   /*
642    * Return all files referenced by this SegmentInfo.  The
643    * returns List is a locally cached List so you should not
644    * modify it.
645    */
646
647   public List<String> files() throws IOException {
648
649     if (files != null) {
650       // Already cached:
651       return files;
652     }
653     
654     HashSet<String> filesSet = new HashSet<String>();
655     
656     boolean useCompoundFile = getUseCompoundFile();
657
658     if (useCompoundFile) {
659       filesSet.add(IndexFileNames.segmentFileName(name, IndexFileNames.COMPOUND_FILE_EXTENSION));
660     } else {
661       for (String ext : IndexFileNames.NON_STORE_INDEX_EXTENSIONS)
662         addIfExists(filesSet, IndexFileNames.segmentFileName(name, ext));
663     }
664
665     if (docStoreOffset != -1) {
666       // We are sharing doc stores (stored fields, term
667       // vectors) with other segments
668       assert docStoreSegment != null;
669       if (docStoreIsCompoundFile) {
670         filesSet.add(IndexFileNames.segmentFileName(docStoreSegment, IndexFileNames.COMPOUND_FILE_STORE_EXTENSION));
671       } else {
672         filesSet.add(IndexFileNames.segmentFileName(docStoreSegment, IndexFileNames.FIELDS_INDEX_EXTENSION));
673         filesSet.add(IndexFileNames.segmentFileName(docStoreSegment, IndexFileNames.FIELDS_EXTENSION));
674         if (hasVectors) {
675           filesSet.add(IndexFileNames.segmentFileName(docStoreSegment, IndexFileNames.VECTORS_INDEX_EXTENSION));
676           filesSet.add(IndexFileNames.segmentFileName(docStoreSegment, IndexFileNames.VECTORS_DOCUMENTS_EXTENSION));
677           filesSet.add(IndexFileNames.segmentFileName(docStoreSegment, IndexFileNames.VECTORS_FIELDS_EXTENSION));
678         }
679       }
680     } else if (!useCompoundFile) {
681       filesSet.add(IndexFileNames.segmentFileName(name, IndexFileNames.FIELDS_INDEX_EXTENSION));
682       filesSet.add(IndexFileNames.segmentFileName(name, IndexFileNames.FIELDS_EXTENSION));
683       if (hasVectors) {
684         filesSet.add(IndexFileNames.segmentFileName(name, IndexFileNames.VECTORS_INDEX_EXTENSION));
685         filesSet.add(IndexFileNames.segmentFileName(name, IndexFileNames.VECTORS_DOCUMENTS_EXTENSION));
686         filesSet.add(IndexFileNames.segmentFileName(name, IndexFileNames.VECTORS_FIELDS_EXTENSION));
687       }      
688     }
689
690     String delFileName = IndexFileNames.fileNameFromGeneration(name, IndexFileNames.DELETES_EXTENSION, delGen);
691     if (delFileName != null && (delGen >= YES || dir.fileExists(delFileName))) {
692       filesSet.add(delFileName);
693     }
694
695     // Careful logic for norms files    
696     if (normGen != null) {
697       for(int i=0;i<normGen.length;i++) {
698         long gen = normGen[i];
699         if (gen >= YES) {
700           // Definitely a separate norm file, with generation:
701           filesSet.add(IndexFileNames.fileNameFromGeneration(name, IndexFileNames.SEPARATE_NORMS_EXTENSION + i, gen));
702         } else if (NO == gen) {
703           // No separate norms but maybe plain norms
704           // in the non compound file case:
705           if (!hasSingleNormFile && !useCompoundFile) {
706             String fileName = IndexFileNames.segmentFileName(name, IndexFileNames.PLAIN_NORMS_EXTENSION + i);
707             if (dir.fileExists(fileName)) {
708               filesSet.add(fileName);
709             }
710           }
711         } else if (CHECK_DIR == gen) {
712           // Pre-2.1: we have to check file existence
713           String fileName = null;
714           if (useCompoundFile) {
715             fileName = IndexFileNames.segmentFileName(name, IndexFileNames.SEPARATE_NORMS_EXTENSION + i);
716           } else if (!hasSingleNormFile) {
717             fileName = IndexFileNames.segmentFileName(name, IndexFileNames.PLAIN_NORMS_EXTENSION + i);
718           }
719           if (fileName != null && dir.fileExists(fileName)) {
720             filesSet.add(fileName);
721           }
722         }
723       }
724     } else if (preLockless || (!hasSingleNormFile && !useCompoundFile)) {
725       // Pre-2.1: we have to scan the dir to find all
726       // matching _X.sN/_X.fN files for our segment:
727       String prefix;
728       if (useCompoundFile)
729         prefix = IndexFileNames.segmentFileName(name, IndexFileNames.SEPARATE_NORMS_EXTENSION);
730       else
731         prefix = IndexFileNames.segmentFileName(name, IndexFileNames.PLAIN_NORMS_EXTENSION);
732       int prefixLength = prefix.length();
733       String[] allFiles = dir.listAll();
734       final IndexFileNameFilter filter = IndexFileNameFilter.getFilter();
735       for(int i=0;i<allFiles.length;i++) {
736         String fileName = allFiles[i];
737         if (filter.accept(null, fileName) && fileName.length() > prefixLength && Character.isDigit(fileName.charAt(prefixLength)) && fileName.startsWith(prefix)) {
738           filesSet.add(fileName);
739         }
740       }
741     }
742     return files = new ArrayList<String>(filesSet);
743   }
744
745   /* Called whenever any change is made that affects which
746    * files this segment has. */
747   private void clearFiles() {
748     files = null;
749     sizeInBytesNoStore = -1;
750     sizeInBytesWithStore = -1;
751   }
752
753   /** {@inheritDoc} */
754   @Override
755   public String toString() {
756     return toString(dir, 0);
757   }
758
759   /** Used for debugging.  Format may suddenly change.
760    * 
761    *  <p>Current format looks like
762    *  <code>_a(3.1):c45/4->_1</code>, which means the segment's
763    *  name is <code>_a</code>; it was created with Lucene 3.1 (or
764    *  '?' if it's unkown); it's using compound file
765    *  format (would be <code>C</code> if not compound); it
766    *  has 45 documents; it has 4 deletions (this part is
767    *  left off when there are no deletions); it's using the
768    *  shared doc stores named <code>_1</code> (this part is
769    *  left off if doc stores are private).</p>
770    */
771   public String toString(Directory dir, int pendingDelCount) {
772
773     StringBuilder s = new StringBuilder();
774     s.append(name).append('(').append(version == null ? "?" : version).append(')').append(':');
775
776     char cfs;
777     try {
778       if (getUseCompoundFile()) {
779         cfs = 'c';
780       } else {
781         cfs = 'C';
782       }
783     } catch (IOException ioe) {
784       cfs = '?';
785     }
786     s.append(cfs);
787
788     if (this.dir != dir) {
789       s.append('x');
790     }
791     if (hasVectors) {
792       s.append('v');
793     }
794     s.append(docCount);
795
796     int delCount;
797     try {
798       delCount = getDelCount();
799     } catch (IOException ioe) {
800       delCount = -1;
801     }
802     if (delCount != -1) {
803       delCount += pendingDelCount;
804     }
805     if (delCount != 0) {
806       s.append('/');
807       if (delCount == -1) {
808         s.append('?');
809       } else {
810         s.append(delCount);
811       }
812     }
813     
814     if (docStoreOffset != -1) {
815       s.append("->").append(docStoreSegment);
816       if (docStoreIsCompoundFile) {
817         s.append('c');
818       } else {
819         s.append('C');
820       }
821       s.append('+').append(docStoreOffset);
822     }
823
824     return s.toString();
825   }
826
827   /** We consider another SegmentInfo instance equal if it
828    *  has the same dir and same name. */
829   @Override
830   public boolean equals(Object obj) {
831     if (this == obj) return true;
832     if (obj instanceof SegmentInfo) {
833       final SegmentInfo other = (SegmentInfo) obj;
834       return other.dir == dir && other.name.equals(name);
835     } else {
836       return false;
837     }
838   }
839
840   @Override
841   public int hashCode() {
842     return dir.hashCode() + name.hashCode();
843   }
844
845   /**
846    * Used by SegmentInfos to upgrade segments that do not record their code
847    * version (either "2.x" or "3.0").
848    * <p>
849    * <b>NOTE:</b> this method is used for internal purposes only - you should
850    * not modify the version of a SegmentInfo, or it may result in unexpected
851    * exceptions thrown when you attempt to open the index.
852    * 
853    * @lucene.internal
854    */
855   void setVersion(String version) {
856     this.version = version;
857   }
858   
859   /** Returns the version of the code which wrote the segment. */
860   public String getVersion() {
861     return version;
862   }
863
864   long getBufferedDeletesGen() {
865     return bufferedDeletesGen;
866   }
867
868   void setBufferedDeletesGen(long v) {
869     bufferedDeletesGen = v;
870   }
871 }