package org.apache.lucene.index.codecs;
/**
* 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 java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
import java.util.IdentityHashMap;
import java.util.TreeMap;
import java.util.TreeSet;
import java.io.IOException;
import org.apache.lucene.index.FieldsEnum;
import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.FieldInfo;
import org.apache.lucene.index.FieldInfos;
import org.apache.lucene.index.SegmentInfo;
import org.apache.lucene.index.SegmentWriteState;
import org.apache.lucene.index.SegmentReadState;
import org.apache.lucene.store.Directory;
/** Simple Codec that dispatches field-specific codecs.
* You must ensure every field you index has a Codec, or
* the defaultCodec is non null. Also, the separate
* codecs cannot conflict on file names.
*
* @lucene.experimental */
public class PerFieldCodecWrapper extends Codec {
private final Map<String,Codec> fields = new IdentityHashMap<String,Codec>();
private final Codec defaultCodec;
public PerFieldCodecWrapper(Codec defaultCodec) {
name = "PerField";
this.defaultCodec = defaultCodec;
}
public void add(String field, Codec codec) {
fields.put(field, codec);
}
public Codec getCodec(String field) {
Codec codec = fields.get(field);
if (codec != null) {
return codec;
} else {
return defaultCodec;
}
}
@Override
public FieldsConsumer fieldsConsumer(SegmentWriteState state) throws IOException {
return new FieldsWriter(state);
}
private class FieldsWriter extends FieldsConsumer {
private final SegmentWriteState state;
private final Map<Codec,FieldsConsumer> codecs = new HashMap<Codec,FieldsConsumer>();
private final Set<String> fieldsSeen = new TreeSet<String>();
public FieldsWriter(SegmentWriteState state) {
this.state = state;
}
@Override
public TermsConsumer addField(FieldInfo field) throws IOException {
fieldsSeen.add(field.name);
Codec codec = getCodec(field.name);
FieldsConsumer fields = codecs.get(codec);
if (fields == null) {
fields = codec.fieldsConsumer(state);
codecs.put(codec, fields);
}
return fields.addField(field);
}
@Override
public void close() throws IOException {
Iterator<FieldsConsumer> it = codecs.values().iterator();
IOException err = null;
while(it.hasNext()) {
try {
it.next().close();
} catch (IOException ioe) {
// keep first IOException we hit but keep
// closing the rest
if (err == null) {
err = ioe;
}
}
}
if (err != null) {
throw err;
}
}
}
private class FieldsReader extends FieldsProducer {
private final Set<String> fields = new TreeSet<String>();
private final Map<Codec,FieldsProducer> codecs = new HashMap<Codec,FieldsProducer>();
public FieldsReader(Directory dir, FieldInfos fieldInfos,
SegmentInfo si, int readBufferSize,
int indexDivisor) throws IOException {
final int fieldCount = fieldInfos.size();
for(int i=0;i<fieldCount;i++) {
FieldInfo fi = fieldInfos.fieldInfo(i);
if (fi.isIndexed) {
fields.add(fi.name);
Codec codec = getCodec(fi.name);
if (!codecs.containsKey(codec)) {
codecs.put(codec, codec.fieldsProducer(new SegmentReadState(dir, si, fieldInfos, readBufferSize, indexDivisor)));
}
}
}
}
private final class FieldsIterator extends FieldsEnum {
private final Iterator<String> it;
private String current;
public FieldsIterator() {
it = fields.iterator();
}
@Override
public String next() {
if (it.hasNext()) {
current = it.next();
} else {
current = null;
}
return current;
}
@Override
public TermsEnum terms() throws IOException {
Terms terms = codecs.get(getCodec(current)).terms(current);
if (terms != null) {
return terms.iterator();
} else {
return null;
}
}
}
@Override
public FieldsEnum iterator() throws IOException {
return new FieldsIterator();
}
@Override
public Terms terms(String field) throws IOException {
Codec codec = getCodec(field);
FieldsProducer fields = codecs.get(codec);
assert fields != null;
return fields.terms(field);
}
@Override
public void close() throws IOException {
Iterator<FieldsProducer> it = codecs.values().iterator();
IOException err = null;
while(it.hasNext()) {
try {
it.next().close();
} catch (IOException ioe) {
// keep first IOException we hit but keep
// closing the rest
if (err == null) {
err = ioe;
}
}
}
if (err != null) {
throw err;
}
}
@Override
public void loadTermsIndex(int indexDivisor) throws IOException {
Iterator<FieldsProducer> it = codecs.values().iterator();
while(it.hasNext()) {
it.next().loadTermsIndex(indexDivisor);
}
}
}
public FieldsProducer fieldsProducer(SegmentReadState state)
throws IOException {
return new FieldsReader(state.dir, state.fieldInfos, state.segmentInfo, state.readBufferSize, state.termsIndexDivisor);
}
@Override
public void files(Directory dir, SegmentInfo info, Set<String> files) throws IOException {
Iterator<Codec> it = fields.values().iterator();
Set<Codec> seen = new HashSet<Codec>();
while(it.hasNext()) {
final Codec codec = it.next();
if (!seen.contains(codec)) {
seen.add(codec);
codec.files(dir, info, files);
}
}
}
@Override
public void getExtensions(Set<String> extensions) {
Iterator<Codec> it = fields.values().iterator();
while(it.hasNext()) {
final Codec codec = it.next();
codec.getExtensions(extensions);
}
}
}