| /* |
| * 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. |
| */ |
| package org.apache.lucene.index; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Random; |
| |
| /** |
| * Shuffles field numbers around to try to trip bugs where field numbers |
| * are assumed to always be consistent across segments. |
| */ |
| public class MismatchedLeafReader extends FilterLeafReader { |
| final FieldInfos shuffled; |
| |
| /** Creates a new reader which will renumber fields in {@code in} */ |
| public MismatchedLeafReader(LeafReader in, Random random) { |
| super(in); |
| shuffled = shuffleInfos(in.getFieldInfos(), random); |
| } |
| |
| @Override |
| public FieldInfos getFieldInfos() { |
| return shuffled; |
| } |
| |
| @Override |
| public void document(int docID, StoredFieldVisitor visitor) throws IOException { |
| in.document(docID, new MismatchedVisitor(visitor)); |
| } |
| |
| @Override |
| public CacheHelper getCoreCacheHelper() { |
| return in.getCoreCacheHelper(); |
| } |
| |
| @Override |
| public CacheHelper getReaderCacheHelper() { |
| return in.getReaderCacheHelper(); |
| } |
| |
| static FieldInfos shuffleInfos(FieldInfos infos, Random random) { |
| // first, shuffle the order |
| List<FieldInfo> shuffled = new ArrayList<>(); |
| for (FieldInfo info : infos) { |
| shuffled.add(info); |
| } |
| Collections.shuffle(shuffled, random); |
| |
| // now renumber: |
| for (int i = 0; i < shuffled.size(); i++) { |
| FieldInfo oldInfo = shuffled.get(i); |
| // TODO: should we introduce "gaps" too? |
| FieldInfo newInfo = new FieldInfo(oldInfo.name, // name |
| i, // number |
| oldInfo.hasVectors(), // storeTermVector |
| oldInfo.omitsNorms(), // omitNorms |
| oldInfo.hasPayloads(), // storePayloads |
| oldInfo.getIndexOptions(), // indexOptions |
| oldInfo.getDocValuesType(), // docValuesType |
| oldInfo.getDocValuesGen(), // dvGen |
| oldInfo.attributes(), // attributes |
| oldInfo.getPointDimensionCount(), // data dimension count |
| oldInfo.getPointIndexDimensionCount(), // index dimension count |
| oldInfo.getPointNumBytes(), // dimension numBytes |
| oldInfo.isSoftDeletesField()); // used as soft-deletes field |
| shuffled.set(i, newInfo); |
| } |
| |
| return new FieldInfos(shuffled.toArray(new FieldInfo[shuffled.size()])); |
| } |
| |
| /** |
| * StoredFieldsVisitor that remaps actual field numbers |
| * to our new shuffled ones. |
| */ |
| // TODO: its strange this part of our IR api exposes FieldInfo, |
| // no other "user-accessible" codec apis do this? |
| class MismatchedVisitor extends StoredFieldVisitor { |
| final StoredFieldVisitor in; |
| |
| MismatchedVisitor(StoredFieldVisitor in) { |
| this.in = in; |
| } |
| |
| @Override |
| public void binaryField(FieldInfo fieldInfo, byte[] value) throws IOException { |
| in.binaryField(renumber(fieldInfo), value); |
| } |
| |
| @Override |
| public void stringField(FieldInfo fieldInfo, byte[] value) throws IOException { |
| in.stringField(renumber(fieldInfo), value); |
| } |
| |
| @Override |
| public void intField(FieldInfo fieldInfo, int value) throws IOException { |
| in.intField(renumber(fieldInfo), value); |
| } |
| |
| @Override |
| public void longField(FieldInfo fieldInfo, long value) throws IOException { |
| in.longField(renumber(fieldInfo), value); |
| } |
| |
| @Override |
| public void floatField(FieldInfo fieldInfo, float value) throws IOException { |
| in.floatField(renumber(fieldInfo), value); |
| } |
| |
| @Override |
| public void doubleField(FieldInfo fieldInfo, double value) throws IOException { |
| in.doubleField(renumber(fieldInfo), value); |
| } |
| |
| @Override |
| public Status needsField(FieldInfo fieldInfo) throws IOException { |
| return in.needsField(renumber(fieldInfo)); |
| } |
| |
| FieldInfo renumber(FieldInfo original) { |
| FieldInfo renumbered = shuffled.fieldInfo(original.name); |
| if (renumbered == null) { |
| throw new AssertionError("stored fields sending bogus infos!"); |
| } |
| return renumbered; |
| } |
| } |
| } |