001: package org.apache.lucene.search.spans;
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: import java.util.Collection;
023: import java.util.Set;
024:
025: import org.apache.lucene.index.IndexReader;
026: import org.apache.lucene.search.Query;
027: import org.apache.lucene.util.ToStringUtils;
028:
029: /** Removes matches which overlap with another SpanQuery. */
030: public class SpanNotQuery extends SpanQuery {
031: private SpanQuery include;
032: private SpanQuery exclude;
033:
034: /** Construct a SpanNotQuery matching spans from <code>include</code> which
035: * have no overlap with spans from <code>exclude</code>.*/
036: public SpanNotQuery(SpanQuery include, SpanQuery exclude) {
037: this .include = include;
038: this .exclude = exclude;
039:
040: if (!include.getField().equals(exclude.getField()))
041: throw new IllegalArgumentException(
042: "Clauses must have same field.");
043: }
044:
045: /** Return the SpanQuery whose matches are filtered. */
046: public SpanQuery getInclude() {
047: return include;
048: }
049:
050: /** Return the SpanQuery whose matches must not overlap those returned. */
051: public SpanQuery getExclude() {
052: return exclude;
053: }
054:
055: public String getField() {
056: return include.getField();
057: }
058:
059: /** Returns a collection of all terms matched by this query.
060: * @deprecated use extractTerms instead
061: * @see #extractTerms(Set)
062: */
063: public Collection getTerms() {
064: return include.getTerms();
065: }
066:
067: public void extractTerms(Set terms) {
068: include.extractTerms(terms);
069: }
070:
071: public String toString(String field) {
072: StringBuffer buffer = new StringBuffer();
073: buffer.append("spanNot(");
074: buffer.append(include.toString(field));
075: buffer.append(", ");
076: buffer.append(exclude.toString(field));
077: buffer.append(")");
078: buffer.append(ToStringUtils.boost(getBoost()));
079: return buffer.toString();
080: }
081:
082: public Spans getSpans(final IndexReader reader) throws IOException {
083: return new Spans() {
084: private Spans includeSpans = include.getSpans(reader);
085: private boolean moreInclude = true;
086:
087: private Spans excludeSpans = exclude.getSpans(reader);
088: private boolean moreExclude = excludeSpans.next();
089:
090: public boolean next() throws IOException {
091: if (moreInclude) // move to next include
092: moreInclude = includeSpans.next();
093:
094: while (moreInclude && moreExclude) {
095:
096: if (includeSpans.doc() > excludeSpans.doc()) // skip exclude
097: moreExclude = excludeSpans.skipTo(includeSpans
098: .doc());
099:
100: while (moreExclude // while exclude is before
101: && includeSpans.doc() == excludeSpans.doc()
102: && excludeSpans.end() <= includeSpans
103: .start()) {
104: moreExclude = excludeSpans.next(); // increment exclude
105: }
106:
107: if (!moreExclude // if no intersection
108: || includeSpans.doc() != excludeSpans.doc()
109: || includeSpans.end() <= excludeSpans
110: .start())
111: break; // we found a match
112:
113: moreInclude = includeSpans.next(); // intersected: keep scanning
114: }
115: return moreInclude;
116: }
117:
118: public boolean skipTo(int target) throws IOException {
119: if (moreInclude) // skip include
120: moreInclude = includeSpans.skipTo(target);
121:
122: if (!moreInclude)
123: return false;
124:
125: if (moreExclude // skip exclude
126: && includeSpans.doc() > excludeSpans.doc())
127: moreExclude = excludeSpans.skipTo(includeSpans
128: .doc());
129:
130: while (moreExclude // while exclude is before
131: && includeSpans.doc() == excludeSpans.doc()
132: && excludeSpans.end() <= includeSpans.start()) {
133: moreExclude = excludeSpans.next(); // increment exclude
134: }
135:
136: if (!moreExclude // if no intersection
137: || includeSpans.doc() != excludeSpans.doc()
138: || includeSpans.end() <= excludeSpans.start())
139: return true; // we found a match
140:
141: return next(); // scan to next match
142: }
143:
144: public int doc() {
145: return includeSpans.doc();
146: }
147:
148: public int start() {
149: return includeSpans.start();
150: }
151:
152: public int end() {
153: return includeSpans.end();
154: }
155:
156: public String toString() {
157: return "spans(" + SpanNotQuery.this .toString() + ")";
158: }
159:
160: };
161: }
162:
163: public Query rewrite(IndexReader reader) throws IOException {
164: SpanNotQuery clone = null;
165:
166: SpanQuery rewrittenInclude = (SpanQuery) include
167: .rewrite(reader);
168: if (rewrittenInclude != include) {
169: clone = (SpanNotQuery) this .clone();
170: clone.include = rewrittenInclude;
171: }
172: SpanQuery rewrittenExclude = (SpanQuery) exclude
173: .rewrite(reader);
174: if (rewrittenExclude != exclude) {
175: if (clone == null)
176: clone = (SpanNotQuery) this .clone();
177: clone.exclude = rewrittenExclude;
178: }
179:
180: if (clone != null) {
181: return clone; // some clauses rewrote
182: } else {
183: return this ; // no clauses rewrote
184: }
185: }
186:
187: /** Returns true iff <code>o</code> is equal to this. */
188: public boolean equals(Object o) {
189: if (this == o)
190: return true;
191: if (!(o instanceof SpanNotQuery))
192: return false;
193:
194: SpanNotQuery other = (SpanNotQuery) o;
195: return this .include.equals(other.include)
196: && this .exclude.equals(other.exclude)
197: && this .getBoost() == other.getBoost();
198: }
199:
200: public int hashCode() {
201: int h = include.hashCode();
202: h = (h << 1) | (h >>> 31); // rotate left
203: h ^= exclude.hashCode();
204: h = (h << 1) | (h >>> 31); // rotate left
205: h ^= Float.floatToRawIntBits(getBoost());
206: return h;
207: }
208:
209: }
|