--- /dev/null
+package org.apache.lucene.index;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.IndexOutput;
+import org.apache.lucene.store.IndexInput;
+import org.apache.lucene.util.BitVector;
+import org.apache.lucene.util.Constants;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * Information about a segment such as it's name, directory, and files related
+ * to the segment.
+ *
+ * @lucene.experimental
+ */
+public final class SegmentInfo implements Cloneable {
+
+ static final int NO = -1; // e.g. no norms; no deletes;
+ static final int YES = 1; // e.g. have norms; have deletes;
+ static final int CHECK_DIR = 0; // e.g. must check dir to see if there are norms/deletions
+ static final int WITHOUT_GEN = 0; // a file name that has no GEN in it.
+
+ public String name; // unique name in dir
+ public int docCount; // number of docs in seg
+ public Directory dir; // where segment resides
+
+ private boolean preLockless; // true if this is a segments file written before
+ // lock-less commits (2.1)
+
+ private long delGen; // current generation of del file; NO if there
+ // are no deletes; CHECK_DIR if it's a pre-2.1 segment
+ // (and we must check filesystem); YES or higher if
+ // there are deletes at generation N
+
+ private long[] normGen; // current generation of each field's norm file.
+ // If this array is null, for lockLess this means no
+ // separate norms. For preLockLess this means we must
+ // check filesystem. If this array is not null, its
+ // values mean: NO says this field has no separate
+ // norms; CHECK_DIR says it is a preLockLess segment and
+ // filesystem must be checked; >= YES says this field
+ // has separate norms with the specified generation
+
+ private byte isCompoundFile; // NO if it is not; YES if it is; CHECK_DIR if it's
+ // pre-2.1 (ie, must check file system to see
+ // if <name>.cfs and <name>.nrm exist)
+
+ private boolean hasSingleNormFile; // true if this segment maintains norms in a single file;
+ // false otherwise
+ // this is currently false for segments populated by DocumentWriter
+ // and true for newly created merged segments (both
+ // compound and non compound).
+
+ private volatile List<String> files; // cached list of files that this segment uses
+ // in the Directory
+
+ private volatile long sizeInBytesNoStore = -1; // total byte size of all but the store files (computed on demand)
+ private volatile long sizeInBytesWithStore = -1; // total byte size of all of our files (computed on demand)
+
+ private int docStoreOffset; // if this segment shares stored fields & vectors, this
+ // offset is where in that file this segment's docs begin
+ private String docStoreSegment; // name used to derive fields/vectors file we share with
+ // other segments
+ private boolean docStoreIsCompoundFile; // whether doc store files are stored in compound file (*.cfx)
+
+ private int delCount; // How many deleted docs in this segment, or -1 if not yet known
+ // (if it's an older index)
+
+ private boolean hasProx; // True if this segment has any fields with omitTermFreqAndPositions==false
+
+ private boolean hasVectors; // True if this segment wrote term vectors
+
+ private Map<String,String> diagnostics;
+
+ // Tracks the Lucene version this segment was created with, since 3.1. The
+ // format expected is "x.y" - "2.x" for pre-3.0 indexes, and specific versions
+ // afterwards ("3.0", "3.1" etc.).
+ // see Constants.LUCENE_MAIN_VERSION.
+ private String version;
+
+ // NOTE: only used in-RAM by IW to track buffered deletes;
+ // this is never written to/read from the Directory
+ private long bufferedDeletesGen;
+
+ public SegmentInfo(String name, int docCount, Directory dir, boolean isCompoundFile, boolean hasSingleNormFile,
+ boolean hasProx, boolean hasVectors) {
+ this.name = name;
+ this.docCount = docCount;
+ this.dir = dir;
+ delGen = NO;
+ this.isCompoundFile = (byte) (isCompoundFile ? YES : NO);
+ preLockless = false;
+ this.hasSingleNormFile = hasSingleNormFile;
+ this.docStoreOffset = -1;
+ delCount = 0;
+ this.hasProx = hasProx;
+ this.hasVectors = hasVectors;
+ this.version = Constants.LUCENE_MAIN_VERSION;
+ }
+
+ /**
+ * Copy everything from src SegmentInfo into our instance.
+ */
+ void reset(SegmentInfo src) {
+ clearFiles();
+ version = src.version;
+ name = src.name;
+ docCount = src.docCount;
+ dir = src.dir;
+ preLockless = src.preLockless;
+ delGen = src.delGen;
+ docStoreOffset = src.docStoreOffset;
+ docStoreIsCompoundFile = src.docStoreIsCompoundFile;
+ hasVectors = src.hasVectors;
+ hasProx = src.hasProx;
+ if (src.normGen == null) {
+ normGen = null;
+ } else {
+ normGen = new long[src.normGen.length];
+ System.arraycopy(src.normGen, 0, normGen, 0, src.normGen.length);
+ }
+ isCompoundFile = src.isCompoundFile;
+ hasSingleNormFile = src.hasSingleNormFile;
+ delCount = src.delCount;
+ }
+
+ void setDiagnostics(Map<String, String> diagnostics) {
+ this.diagnostics = diagnostics;
+ }
+
+ public Map<String, String> getDiagnostics() {
+ return diagnostics;
+ }
+
+ /**
+ * Construct a new SegmentInfo instance by reading a
+ * previously saved SegmentInfo from input.
+ *
+ * @param dir directory to load from
+ * @param format format of the segments info file
+ * @param input input handle to read segment info from
+ */
+ SegmentInfo(Directory dir, int format, IndexInput input) throws IOException {
+ this.dir = dir;
+ if (format <= SegmentInfos.FORMAT_3_1) {
+ version = input.readString();
+ }
+ name = input.readString();
+ docCount = input.readInt();
+ if (format <= SegmentInfos.FORMAT_LOCKLESS) {
+ delGen = input.readLong();
+ if (format <= SegmentInfos.FORMAT_SHARED_DOC_STORE) {
+ docStoreOffset = input.readInt();
+ if (docStoreOffset != -1) {
+ docStoreSegment = input.readString();
+ docStoreIsCompoundFile = (1 == input.readByte());
+ } else {
+ docStoreSegment = name;
+ docStoreIsCompoundFile = false;
+ }
+ } else {
+ docStoreOffset = -1;
+ docStoreSegment = name;
+ docStoreIsCompoundFile = false;
+ }
+ if (format <= SegmentInfos.FORMAT_SINGLE_NORM_FILE) {
+ hasSingleNormFile = (1 == input.readByte());
+ } else {
+ hasSingleNormFile = false;
+ }
+ int numNormGen = input.readInt();
+ if (numNormGen == NO) {
+ normGen = null;
+ } else {
+ normGen = new long[numNormGen];
+ for(int j=0;j<numNormGen;j++) {
+ normGen[j] = input.readLong();
+ }
+ }
+ isCompoundFile = input.readByte();
+ preLockless = (isCompoundFile == CHECK_DIR);
+ if (format <= SegmentInfos.FORMAT_DEL_COUNT) {
+ delCount = input.readInt();
+ assert delCount <= docCount;
+ } else
+ delCount = -1;
+ if (format <= SegmentInfos.FORMAT_HAS_PROX)
+ hasProx = input.readByte() == 1;
+ else
+ hasProx = true;
+
+ if (format <= SegmentInfos.FORMAT_DIAGNOSTICS) {
+ diagnostics = input.readStringStringMap();
+ } else {
+ diagnostics = Collections.<String,String>emptyMap();
+ }
+
+ if (format <= SegmentInfos.FORMAT_HAS_VECTORS) {
+ hasVectors = input.readByte() == 1;
+ } else {
+ final String storesSegment;
+ final String ext;
+ final boolean isCompoundFile;
+ if (docStoreOffset != -1) {
+ storesSegment = docStoreSegment;
+ isCompoundFile = docStoreIsCompoundFile;
+ ext = IndexFileNames.COMPOUND_FILE_STORE_EXTENSION;
+ } else {
+ storesSegment = name;
+ isCompoundFile = getUseCompoundFile();
+ ext = IndexFileNames.COMPOUND_FILE_EXTENSION;
+ }
+ final Directory dirToTest;
+ if (isCompoundFile) {
+ dirToTest = new CompoundFileReader(dir, IndexFileNames.segmentFileName(storesSegment, ext));
+ } else {
+ dirToTest = dir;
+ }
+ try {
+ hasVectors = dirToTest.fileExists(IndexFileNames.segmentFileName(storesSegment, IndexFileNames.VECTORS_INDEX_EXTENSION));
+ } finally {
+ if (isCompoundFile) {
+ dirToTest.close();
+ }
+ }
+ }
+ } else {
+ delGen = CHECK_DIR;
+ normGen = null;
+ isCompoundFile = CHECK_DIR;
+ preLockless = true;
+ hasSingleNormFile = false;
+ docStoreOffset = -1;
+ docStoreIsCompoundFile = false;
+ docStoreSegment = null;
+ delCount = -1;
+ hasProx = true;
+ diagnostics = Collections.<String,String>emptyMap();
+ }
+ }
+
+ void setNumFields(int numFields) {
+ if (normGen == null) {
+ // normGen is null if we loaded a pre-2.1 segment
+ // file, or, if this segments file hasn't had any
+ // norms set against it yet:
+ normGen = new long[numFields];
+
+ if (preLockless) {
+ // Do nothing: thus leaving normGen[k]==CHECK_DIR (==0), so that later we know
+ // we have to check filesystem for norm files, because this is prelockless.
+
+ } else {
+ // This is a FORMAT_LOCKLESS segment, which means
+ // there are no separate norms:
+ for(int i=0;i<numFields;i++) {
+ normGen[i] = NO;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns total size in bytes of all of files used by this segment (if
+ * {@code includeDocStores} is true), or the size of all files except the store
+ * files otherwise.
+ */
+ public long sizeInBytes(boolean includeDocStores) throws IOException {
+ if (includeDocStores) {
+ if (sizeInBytesWithStore != -1) {
+ return sizeInBytesWithStore;
+ }
+ long sum = 0;
+ for (final String fileName : files()) {
+ // We don't count bytes used by a shared doc store
+ // against this segment
+ if (docStoreOffset == -1 || !IndexFileNames.isDocStoreFile(fileName)) {
+ sum += dir.fileLength(fileName);
+ }
+ }
+ sizeInBytesWithStore = sum;
+ return sizeInBytesWithStore;
+ } else {
+ if (sizeInBytesNoStore != -1) {
+ return sizeInBytesNoStore;
+ }
+ long sum = 0;
+ for (final String fileName : files()) {
+ if (IndexFileNames.isDocStoreFile(fileName)) {
+ continue;
+ }
+ sum += dir.fileLength(fileName);
+ }
+ sizeInBytesNoStore = sum;
+ return sizeInBytesNoStore;
+ }
+ }
+
+ public boolean getHasVectors() throws IOException {
+ return hasVectors;
+ }
+
+ public void setHasVectors(boolean v) {
+ hasVectors = v;
+ clearFiles();
+ }
+
+ public boolean hasDeletions()
+ throws IOException {
+ // Cases:
+ //
+ // delGen == NO: this means this segment was written
+ // by the LOCKLESS code and for certain does not have
+ // deletions yet
+ //
+ // delGen == CHECK_DIR: this means this segment was written by
+ // pre-LOCKLESS code which means we must check
+ // directory to see if .del file exists
+ //
+ // delGen >= YES: this means this segment was written by
+ // the LOCKLESS code and for certain has
+ // deletions
+ //
+ if (delGen == NO) {
+ return false;
+ } else if (delGen >= YES) {
+ return true;
+ } else {
+ return dir.fileExists(getDelFileName());
+ }
+ }
+
+ void advanceDelGen() {
+ // delGen 0 is reserved for pre-LOCKLESS format
+ if (delGen == NO) {
+ delGen = YES;
+ } else {
+ delGen++;
+ }
+ clearFiles();
+ }
+
+ void clearDelGen() {
+ delGen = NO;
+ clearFiles();
+ }
+
+ @Override
+ public Object clone() {
+ SegmentInfo si = new SegmentInfo(name, docCount, dir, false, hasSingleNormFile,
+ hasProx, hasVectors);
+ si.docStoreOffset = docStoreOffset;
+ si.docStoreSegment = docStoreSegment;
+ si.docStoreIsCompoundFile = docStoreIsCompoundFile;
+ si.delGen = delGen;
+ si.delCount = delCount;
+ si.preLockless = preLockless;
+ si.isCompoundFile = isCompoundFile;
+ si.diagnostics = new HashMap<String, String>(diagnostics);
+ if (normGen != null) {
+ si.normGen = normGen.clone();
+ }
+ si.version = version;
+ return si;
+ }
+
+ public String getDelFileName() {
+ if (delGen == NO) {
+ // In this case we know there is no deletion filename
+ // against this segment
+ return null;
+ } else {
+ // If delGen is CHECK_DIR, it's the pre-lockless-commit file format
+ return IndexFileNames.fileNameFromGeneration(name, IndexFileNames.DELETES_EXTENSION, delGen);
+ }
+ }
+
+ /**
+ * Returns true if this field for this segment has saved a separate norms file (_<segment>_N.sX).
+ *
+ * @param fieldNumber the field index to check
+ */
+ public boolean hasSeparateNorms(int fieldNumber)
+ throws IOException {
+ if ((normGen == null && preLockless) || (normGen != null && normGen[fieldNumber] == CHECK_DIR)) {
+ // Must fallback to directory file exists check:
+ String fileName = name + ".s" + fieldNumber;
+ return dir.fileExists(fileName);
+ } else if (normGen == null || normGen[fieldNumber] == NO) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Returns true if any fields in this segment have separate norms.
+ */
+ public boolean hasSeparateNorms()
+ throws IOException {
+ if (normGen == null) {
+ if (!preLockless) {
+ // This means we were created w/ LOCKLESS code and no
+ // norms are written yet:
+ return false;
+ } else {
+ // This means this segment was saved with pre-LOCKLESS
+ // code. So we must fallback to the original
+ // directory list check:
+ String[] result = dir.listAll();
+ if (result == null)
+ throw new IOException("cannot read directory " + dir + ": listAll() returned null");
+
+ final IndexFileNameFilter filter = IndexFileNameFilter.getFilter();
+ String pattern;
+ pattern = name + ".s";
+ int patternLength = pattern.length();
+ for(int i = 0; i < result.length; i++){
+ String fileName = result[i];
+ if (filter.accept(null, fileName) && fileName.startsWith(pattern) && Character.isDigit(fileName.charAt(patternLength)))
+ return true;
+ }
+ return false;
+ }
+ } else {
+ // This means this segment was saved with LOCKLESS
+ // code so we first check whether any normGen's are >= 1
+ // (meaning they definitely have separate norms):
+ for(int i=0;i<normGen.length;i++) {
+ if (normGen[i] >= YES) {
+ return true;
+ }
+ }
+ // Next we look for any == 0. These cases were
+ // pre-LOCKLESS and must be checked in directory:
+ for(int i=0;i<normGen.length;i++) {
+ if (normGen[i] == CHECK_DIR) {
+ if (hasSeparateNorms(i)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Increment the generation count for the norms file for
+ * this field.
+ *
+ * @param fieldIndex field whose norm file will be rewritten
+ */
+ void advanceNormGen(int fieldIndex) {
+ if (normGen[fieldIndex] == NO) {
+ normGen[fieldIndex] = YES;
+ } else {
+ normGen[fieldIndex]++;
+ }
+ clearFiles();
+ }
+
+ /**
+ * Get the file name for the norms file for this field.
+ *
+ * @param number field index
+ */
+ public String getNormFileName(int number) throws IOException {
+ long gen;
+ if (normGen == null) {
+ gen = CHECK_DIR;
+ } else {
+ gen = normGen[number];
+ }
+
+ if (hasSeparateNorms(number)) {
+ // case 1: separate norm
+ return IndexFileNames.fileNameFromGeneration(name, "s" + number, gen);
+ }
+
+ if (hasSingleNormFile) {
+ // case 2: lockless (or nrm file exists) - single file for all norms
+ return IndexFileNames.fileNameFromGeneration(name, IndexFileNames.NORMS_EXTENSION, WITHOUT_GEN);
+ }
+
+ // case 3: norm file for each field
+ return IndexFileNames.fileNameFromGeneration(name, "f" + number, WITHOUT_GEN);
+ }
+
+ /**
+ * Mark whether this segment is stored as a compound file.
+ *
+ * @param isCompoundFile true if this is a compound file;
+ * else, false
+ */
+ void setUseCompoundFile(boolean isCompoundFile) {
+ if (isCompoundFile) {
+ this.isCompoundFile = YES;
+ } else {
+ this.isCompoundFile = NO;
+ }
+ clearFiles();
+ }
+
+ /**
+ * Returns true if this segment is stored as a compound
+ * file; else, false.
+ */
+ public boolean getUseCompoundFile() throws IOException {
+ if (isCompoundFile == NO) {
+ return false;
+ } else if (isCompoundFile == YES) {
+ return true;
+ } else {
+ return dir.fileExists(IndexFileNames.segmentFileName(name, IndexFileNames.COMPOUND_FILE_EXTENSION));
+ }
+ }
+
+ public int getDelCount() throws IOException {
+ if (delCount == -1) {
+ if (hasDeletions()) {
+ final String delFileName = getDelFileName();
+ delCount = new BitVector(dir, delFileName).count();
+ } else
+ delCount = 0;
+ }
+ assert delCount <= docCount;
+ return delCount;
+ }
+
+ void setDelCount(int delCount) {
+ this.delCount = delCount;
+ assert delCount <= docCount;
+ }
+
+ public int getDocStoreOffset() {
+ return docStoreOffset;
+ }
+
+ public boolean getDocStoreIsCompoundFile() {
+ return docStoreIsCompoundFile;
+ }
+
+ void setDocStoreIsCompoundFile(boolean v) {
+ docStoreIsCompoundFile = v;
+ clearFiles();
+ }
+
+ public String getDocStoreSegment() {
+ return docStoreSegment;
+ }
+
+ public void setDocStoreSegment(String segment) {
+ docStoreSegment = segment;
+ }
+
+ void setDocStoreOffset(int offset) {
+ docStoreOffset = offset;
+ clearFiles();
+ }
+
+ void setDocStore(int offset, String segment, boolean isCompoundFile) {
+ docStoreOffset = offset;
+ docStoreSegment = segment;
+ docStoreIsCompoundFile = isCompoundFile;
+ clearFiles();
+ }
+
+ /**
+ * Save this segment's info.
+ */
+ void write(IndexOutput output)
+ throws IOException {
+ assert delCount <= docCount: "delCount=" + delCount + " docCount=" + docCount + " segment=" + name;
+ // Write the Lucene version that created this segment, since 3.1
+ output.writeString(version);
+ output.writeString(name);
+ output.writeInt(docCount);
+ output.writeLong(delGen);
+ output.writeInt(docStoreOffset);
+ if (docStoreOffset != -1) {
+ output.writeString(docStoreSegment);
+ output.writeByte((byte) (docStoreIsCompoundFile ? 1:0));
+ }
+
+ output.writeByte((byte) (hasSingleNormFile ? 1:0));
+ if (normGen == null) {
+ output.writeInt(NO);
+ } else {
+ output.writeInt(normGen.length);
+ for(int j = 0; j < normGen.length; j++) {
+ output.writeLong(normGen[j]);
+ }
+ }
+ output.writeByte(isCompoundFile);
+ output.writeInt(delCount);
+ output.writeByte((byte) (hasProx ? 1:0));
+ output.writeStringStringMap(diagnostics);
+ output.writeByte((byte) (hasVectors ? 1 : 0));
+ }
+
+ void setHasProx(boolean hasProx) {
+ this.hasProx = hasProx;
+ clearFiles();
+ }
+
+ public boolean getHasProx() {
+ return hasProx;
+ }
+
+ private void addIfExists(Set<String> files, String fileName) throws IOException {
+ if (dir.fileExists(fileName))
+ files.add(fileName);
+ }
+
+ /*
+ * Return all files referenced by this SegmentInfo. The
+ * returns List is a locally cached List so you should not
+ * modify it.
+ */
+
+ public List<String> files() throws IOException {
+
+ if (files != null) {
+ // Already cached:
+ return files;
+ }
+
+ HashSet<String> filesSet = new HashSet<String>();
+
+ boolean useCompoundFile = getUseCompoundFile();
+
+ if (useCompoundFile) {
+ filesSet.add(IndexFileNames.segmentFileName(name, IndexFileNames.COMPOUND_FILE_EXTENSION));
+ } else {
+ for (String ext : IndexFileNames.NON_STORE_INDEX_EXTENSIONS)
+ addIfExists(filesSet, IndexFileNames.segmentFileName(name, ext));
+ }
+
+ if (docStoreOffset != -1) {
+ // We are sharing doc stores (stored fields, term
+ // vectors) with other segments
+ assert docStoreSegment != null;
+ if (docStoreIsCompoundFile) {
+ filesSet.add(IndexFileNames.segmentFileName(docStoreSegment, IndexFileNames.COMPOUND_FILE_STORE_EXTENSION));
+ } else {
+ filesSet.add(IndexFileNames.segmentFileName(docStoreSegment, IndexFileNames.FIELDS_INDEX_EXTENSION));
+ filesSet.add(IndexFileNames.segmentFileName(docStoreSegment, IndexFileNames.FIELDS_EXTENSION));
+ if (hasVectors) {
+ filesSet.add(IndexFileNames.segmentFileName(docStoreSegment, IndexFileNames.VECTORS_INDEX_EXTENSION));
+ filesSet.add(IndexFileNames.segmentFileName(docStoreSegment, IndexFileNames.VECTORS_DOCUMENTS_EXTENSION));
+ filesSet.add(IndexFileNames.segmentFileName(docStoreSegment, IndexFileNames.VECTORS_FIELDS_EXTENSION));
+ }
+ }
+ } else if (!useCompoundFile) {
+ filesSet.add(IndexFileNames.segmentFileName(name, IndexFileNames.FIELDS_INDEX_EXTENSION));
+ filesSet.add(IndexFileNames.segmentFileName(name, IndexFileNames.FIELDS_EXTENSION));
+ if (hasVectors) {
+ filesSet.add(IndexFileNames.segmentFileName(name, IndexFileNames.VECTORS_INDEX_EXTENSION));
+ filesSet.add(IndexFileNames.segmentFileName(name, IndexFileNames.VECTORS_DOCUMENTS_EXTENSION));
+ filesSet.add(IndexFileNames.segmentFileName(name, IndexFileNames.VECTORS_FIELDS_EXTENSION));
+ }
+ }
+
+ String delFileName = IndexFileNames.fileNameFromGeneration(name, IndexFileNames.DELETES_EXTENSION, delGen);
+ if (delFileName != null && (delGen >= YES || dir.fileExists(delFileName))) {
+ filesSet.add(delFileName);
+ }
+
+ // Careful logic for norms files
+ if (normGen != null) {
+ for(int i=0;i<normGen.length;i++) {
+ long gen = normGen[i];
+ if (gen >= YES) {
+ // Definitely a separate norm file, with generation:
+ filesSet.add(IndexFileNames.fileNameFromGeneration(name, IndexFileNames.SEPARATE_NORMS_EXTENSION + i, gen));
+ } else if (NO == gen) {
+ // No separate norms but maybe plain norms
+ // in the non compound file case:
+ if (!hasSingleNormFile && !useCompoundFile) {
+ String fileName = IndexFileNames.segmentFileName(name, IndexFileNames.PLAIN_NORMS_EXTENSION + i);
+ if (dir.fileExists(fileName)) {
+ filesSet.add(fileName);
+ }
+ }
+ } else if (CHECK_DIR == gen) {
+ // Pre-2.1: we have to check file existence
+ String fileName = null;
+ if (useCompoundFile) {
+ fileName = IndexFileNames.segmentFileName(name, IndexFileNames.SEPARATE_NORMS_EXTENSION + i);
+ } else if (!hasSingleNormFile) {
+ fileName = IndexFileNames.segmentFileName(name, IndexFileNames.PLAIN_NORMS_EXTENSION + i);
+ }
+ if (fileName != null && dir.fileExists(fileName)) {
+ filesSet.add(fileName);
+ }
+ }
+ }
+ } else if (preLockless || (!hasSingleNormFile && !useCompoundFile)) {
+ // Pre-2.1: we have to scan the dir to find all
+ // matching _X.sN/_X.fN files for our segment:
+ String prefix;
+ if (useCompoundFile)
+ prefix = IndexFileNames.segmentFileName(name, IndexFileNames.SEPARATE_NORMS_EXTENSION);
+ else
+ prefix = IndexFileNames.segmentFileName(name, IndexFileNames.PLAIN_NORMS_EXTENSION);
+ int prefixLength = prefix.length();
+ String[] allFiles = dir.listAll();
+ final IndexFileNameFilter filter = IndexFileNameFilter.getFilter();
+ for(int i=0;i<allFiles.length;i++) {
+ String fileName = allFiles[i];
+ if (filter.accept(null, fileName) && fileName.length() > prefixLength && Character.isDigit(fileName.charAt(prefixLength)) && fileName.startsWith(prefix)) {
+ filesSet.add(fileName);
+ }
+ }
+ }
+ return files = new ArrayList<String>(filesSet);
+ }
+
+ /* Called whenever any change is made that affects which
+ * files this segment has. */
+ private void clearFiles() {
+ files = null;
+ sizeInBytesNoStore = -1;
+ sizeInBytesWithStore = -1;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return toString(dir, 0);
+ }
+
+ /** Used for debugging. Format may suddenly change.
+ *
+ * <p>Current format looks like
+ * <code>_a(3.1):c45/4->_1</code>, which means the segment's
+ * name is <code>_a</code>; it was created with Lucene 3.1 (or
+ * '?' if it's unkown); it's using compound file
+ * format (would be <code>C</code> if not compound); it
+ * has 45 documents; it has 4 deletions (this part is
+ * left off when there are no deletions); it's using the
+ * shared doc stores named <code>_1</code> (this part is
+ * left off if doc stores are private).</p>
+ */
+ public String toString(Directory dir, int pendingDelCount) {
+
+ StringBuilder s = new StringBuilder();
+ s.append(name).append('(').append(version == null ? "?" : version).append(')').append(':');
+
+ char cfs;
+ try {
+ if (getUseCompoundFile()) {
+ cfs = 'c';
+ } else {
+ cfs = 'C';
+ }
+ } catch (IOException ioe) {
+ cfs = '?';
+ }
+ s.append(cfs);
+
+ if (this.dir != dir) {
+ s.append('x');
+ }
+ if (hasVectors) {
+ s.append('v');
+ }
+ s.append(docCount);
+
+ int delCount;
+ try {
+ delCount = getDelCount();
+ } catch (IOException ioe) {
+ delCount = -1;
+ }
+ if (delCount != -1) {
+ delCount += pendingDelCount;
+ }
+ if (delCount != 0) {
+ s.append('/');
+ if (delCount == -1) {
+ s.append('?');
+ } else {
+ s.append(delCount);
+ }
+ }
+
+ if (docStoreOffset != -1) {
+ s.append("->").append(docStoreSegment);
+ if (docStoreIsCompoundFile) {
+ s.append('c');
+ } else {
+ s.append('C');
+ }
+ s.append('+').append(docStoreOffset);
+ }
+
+ return s.toString();
+ }
+
+ /** We consider another SegmentInfo instance equal if it
+ * has the same dir and same name. */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj instanceof SegmentInfo) {
+ final SegmentInfo other = (SegmentInfo) obj;
+ return other.dir == dir && other.name.equals(name);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return dir.hashCode() + name.hashCode();
+ }
+
+ /**
+ * Used by SegmentInfos to upgrade segments that do not record their code
+ * version (either "2.x" or "3.0").
+ * <p>
+ * <b>NOTE:</b> this method is used for internal purposes only - you should
+ * not modify the version of a SegmentInfo, or it may result in unexpected
+ * exceptions thrown when you attempt to open the index.
+ *
+ * @lucene.internal
+ */
+ void setVersion(String version) {
+ this.version = version;
+ }
+
+ /** Returns the version of the code which wrote the segment. */
+ public String getVersion() {
+ return version;
+ }
+
+ long getBufferedDeletesGen() {
+ return bufferedDeletesGen;
+ }
+
+ void setBufferedDeletesGen(long v) {
+ bufferedDeletesGen = v;
+ }
+}