/*
* 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.search.uhighlight;
import java.io.Closeable;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import org.apache.lucene.index.PostingsEnum;
import org.apache.lucene.util.BytesRef;
/**
* Holds the term ({@link BytesRef}), {@link PostingsEnum}, offset iteration tracking.
* It is advanced with the underlying postings and is placed in a priority queue by
* {@link FieldHighlighter#highlightOffsetsEnums(List)} based on the start offset.
*
* @lucene.internal
*/
public class OffsetsEnum implements Comparable<OffsetsEnum>, Closeable {
private final BytesRef term;
private final PostingsEnum postingsEnum; // with offsets
private float weight; // set once in highlightOffsetsEnums
private int posCounter = 0; // the occurrence counter of this term within the text being highlighted.
public OffsetsEnum(BytesRef term, PostingsEnum postingsEnum) throws IOException {
this.term = term; // can be null
this.postingsEnum = Objects.requireNonNull(postingsEnum);
}
// note: the ordering clearly changes as the postings enum advances
@Override
public int compareTo(OffsetsEnum other) {
try {
int cmp = Integer.compare(startOffset(), other.startOffset());
if (cmp != 0) {
return cmp; // vast majority of the time we return here.
}
if (this.term == null || other.term == null) {
if (this.term == null && other.term == null) {
return 0;
} else if (this.term == null) {
return 1; // put "this" (wildcard mtq enum) last
} else {
return -1;
}
}
return term.compareTo(other.term);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/** The term at this position; usually always the same. This term is a reference that is safe to continue to refer to,
* even after we move to next position. */
public BytesRef getTerm() throws IOException {
// TODO TokenStreamOffsetStrategy could override OffsetsEnum; then remove this hack here
return term != null ? term : postingsEnum.getPayload(); // abusing payload like this is a total hack!
}
public PostingsEnum getPostingsEnum() {
return postingsEnum;
}
public int freq() throws IOException {
return postingsEnum.freq();
}
public boolean hasMorePositions() throws IOException {
return posCounter < postingsEnum.freq();
}
public void nextPosition() throws IOException {
assert hasMorePositions();
posCounter++;
postingsEnum.nextPosition();
}
public int startOffset() throws IOException {
return postingsEnum.startOffset();
}
public int endOffset() throws IOException {
return postingsEnum.endOffset();
}
public float getWeight() {
return weight;
}
public void setWeight(float weight) {
this.weight = weight;
}
@Override
public void close() throws IOException {
// TODO TokenStreamOffsetStrategy could override OffsetsEnum; then this base impl would be no-op.
if (postingsEnum instanceof Closeable) {
((Closeable) postingsEnum).close();
}
}
}