001: package org.apache.lucene.search;
002:
003: /**
004: * Licensed to the Apache Software Foundation (ASF) under one or more
005: * contributor license agreements. See the NOTICE file distributed with
006: * this work for additional information regarding copyright ownership.
007: * The ASF licenses this file to You under the Apache License, Version 2.0
008: * (the "License"); you may not use this file except in compliance with
009: * the License. You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: */
019:
020: import java.io.IOException;
021:
022: /** A Scorer for queries with a required subscorer and an excluding (prohibited) subscorer.
023: * <br>
024: * This <code>Scorer</code> implements {@link Scorer#skipTo(int)},
025: * and it uses the skipTo() on the given scorers.
026: */
027: public class ReqExclScorer extends Scorer {
028: private Scorer reqScorer, exclScorer;
029:
030: /** Construct a <code>ReqExclScorer</code>.
031: * @param reqScorer The scorer that must match, except where
032: * @param exclScorer indicates exclusion.
033: */
034: public ReqExclScorer(Scorer reqScorer, Scorer exclScorer) {
035: super (null); // No similarity used.
036: this .reqScorer = reqScorer;
037: this .exclScorer = exclScorer;
038: }
039:
040: private boolean firstTime = true;
041:
042: public boolean next() throws IOException {
043: if (firstTime) {
044: if (!exclScorer.next()) {
045: exclScorer = null; // exhausted at start
046: }
047: firstTime = false;
048: }
049: if (reqScorer == null) {
050: return false;
051: }
052: if (!reqScorer.next()) {
053: reqScorer = null; // exhausted, nothing left
054: return false;
055: }
056: if (exclScorer == null) {
057: return true; // reqScorer.next() already returned true
058: }
059: return toNonExcluded();
060: }
061:
062: /** Advance to non excluded doc.
063: * <br>On entry:
064: * <ul>
065: * <li>reqScorer != null,
066: * <li>exclScorer != null,
067: * <li>reqScorer was advanced once via next() or skipTo()
068: * and reqScorer.doc() may still be excluded.
069: * </ul>
070: * Advances reqScorer a non excluded required doc, if any.
071: * @return true iff there is a non excluded required doc.
072: */
073: private boolean toNonExcluded() throws IOException {
074: int exclDoc = exclScorer.doc();
075: do {
076: int reqDoc = reqScorer.doc(); // may be excluded
077: if (reqDoc < exclDoc) {
078: return true; // reqScorer advanced to before exclScorer, ie. not excluded
079: } else if (reqDoc > exclDoc) {
080: if (!exclScorer.skipTo(reqDoc)) {
081: exclScorer = null; // exhausted, no more exclusions
082: return true;
083: }
084: exclDoc = exclScorer.doc();
085: if (exclDoc > reqDoc) {
086: return true; // not excluded
087: }
088: }
089: } while (reqScorer.next());
090: reqScorer = null; // exhausted, nothing left
091: return false;
092: }
093:
094: public int doc() {
095: return reqScorer.doc(); // reqScorer may be null when next() or skipTo() already return false
096: }
097:
098: /** Returns the score of the current document matching the query.
099: * Initially invalid, until {@link #next()} is called the first time.
100: * @return The score of the required scorer.
101: */
102: public float score() throws IOException {
103: return reqScorer.score(); // reqScorer may be null when next() or skipTo() already return false
104: }
105:
106: /** Skips to the first match beyond the current whose document number is
107: * greater than or equal to a given target.
108: * <br>When this method is used the {@link #explain(int)} method should not be used.
109: * @param target The target document number.
110: * @return true iff there is such a match.
111: */
112: public boolean skipTo(int target) throws IOException {
113: if (firstTime) {
114: firstTime = false;
115: if (!exclScorer.skipTo(target)) {
116: exclScorer = null; // exhausted
117: }
118: }
119: if (reqScorer == null) {
120: return false;
121: }
122: if (exclScorer == null) {
123: return reqScorer.skipTo(target);
124: }
125: if (!reqScorer.skipTo(target)) {
126: reqScorer = null;
127: return false;
128: }
129: return toNonExcluded();
130: }
131:
132: public Explanation explain(int doc) throws IOException {
133: Explanation res = new Explanation();
134: if (exclScorer.skipTo(doc) && (exclScorer.doc() == doc)) {
135: res.setDescription("excluded");
136: } else {
137: res.setDescription("not excluded");
138: res.addDetail(reqScorer.explain(doc));
139: }
140: return res;
141: }
142: }
|