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: import java.util.ArrayList;
022: import java.util.BitSet;
023:
024: import org.apache.lucene.index.IndexReader;
025: import org.apache.lucene.search.BooleanClause.Occur;
026:
027: /**
028: * A container Filter that allows Boolean composition of Filters.
029: * Filters are allocated into one of three logical constructs;
030: * SHOULD, MUST NOT, MUST
031: * The results Filter BitSet is constructed as follows:
032: * SHOULD Filters are OR'd together
033: * The resulting Filter is NOT'd with the NOT Filters
034: * The resulting Filter is AND'd with the MUST Filters
035: * @author BPDThebault
036: */
037:
038: public class BooleanFilter extends Filter {
039: //ArrayList of SHOULD filters
040: ArrayList shouldFilters = null;
041: //ArrayList of NOT filters
042: ArrayList notFilters = null;
043: //ArrayList of MUST filters
044: ArrayList mustFilters = null;
045:
046: /**
047: * Returns the a BitSet representing the Boolean composition
048: * of the filters that have been added.
049: */
050:
051: public BitSet bits(IndexReader reader) throws IOException {
052: //create a new bitSet
053: BitSet returnBits = null;
054:
055: //SHOULD filters
056: if (shouldFilters != null) {
057: returnBits = ((Filter) shouldFilters.get(0)).bits(reader);
058: // avoid changing the original bitset - it may be cached
059: returnBits = (BitSet) returnBits.clone();
060: if (shouldFilters.size() > 1) {
061: for (int i = 1; i < shouldFilters.size(); i++) {
062: returnBits.or(((Filter) shouldFilters.get(i))
063: .bits(reader));
064: }
065: }
066: }
067:
068: //NOT filters
069: if (notFilters != null) {
070: for (int i = 0; i < notFilters.size(); i++) {
071: BitSet notBits = ((Filter) notFilters.get(i))
072: .bits(reader);
073: if (returnBits == null) {
074: returnBits = (BitSet) notBits.clone();
075: returnBits.flip(0, reader.maxDoc());
076: } else {
077: returnBits.andNot(notBits);
078: }
079: }
080: }
081:
082: //MUST filters
083: if (mustFilters != null) {
084: for (int i = 0; i < mustFilters.size(); i++) {
085: BitSet mustBits = ((Filter) mustFilters.get(i))
086: .bits(reader);
087: if (returnBits == null) {
088: if (mustFilters.size() == 1) {
089: returnBits = mustBits;
090:
091: } else {
092: //don't mangle the bitset
093: returnBits = (BitSet) mustBits.clone();
094: }
095: } else {
096: returnBits.and(mustBits);
097: }
098: }
099: }
100: if (returnBits == null) {
101: returnBits = new BitSet(reader.maxDoc());
102: }
103: return returnBits;
104: }
105:
106: /**
107: * Adds a new FilterClause to the Boolean Filter container
108: * @param filterClause A FilterClause object containing a Filter and an Occur parameter
109: */
110:
111: public void add(FilterClause filterClause) {
112: if (filterClause.getOccur().equals(Occur.MUST)) {
113: if (mustFilters == null) {
114: mustFilters = new ArrayList();
115: }
116: mustFilters.add(filterClause.getFilter());
117: }
118: if (filterClause.getOccur().equals(Occur.SHOULD)) {
119: if (shouldFilters == null) {
120: shouldFilters = new ArrayList();
121: }
122: shouldFilters.add(filterClause.getFilter());
123: }
124: if (filterClause.getOccur().equals(Occur.MUST_NOT)) {
125: if (notFilters == null) {
126: notFilters = new ArrayList();
127: }
128: notFilters.add(filterClause.getFilter());
129: }
130: }
131:
132: public boolean equals(Object obj) {
133: if (this == obj)
134: return true;
135: if ((obj == null) || (obj.getClass() != this .getClass()))
136: return false;
137: BooleanFilter test = (BooleanFilter) obj;
138: return (notFilters == test.notFilters || (notFilters != null && notFilters
139: .equals(test.notFilters)))
140: && (mustFilters == test.mustFilters || (mustFilters != null && mustFilters
141: .equals(test.mustFilters)))
142: && (shouldFilters == test.shouldFilters || (shouldFilters != null && shouldFilters
143: .equals(test.shouldFilters)));
144: }
145:
146: public int hashCode() {
147: int hash = 7;
148: hash = 31 * hash
149: + (null == mustFilters ? 0 : mustFilters.hashCode());
150: hash = 31 * hash
151: + (null == notFilters ? 0 : notFilters.hashCode());
152: hash = 31
153: * hash
154: + (null == shouldFilters ? 0 : shouldFilters.hashCode());
155: return hash;
156: }
157:
158: /** Prints a user-readable version of this query. */
159: public String toString() {
160: StringBuffer buffer = new StringBuffer();
161:
162: buffer.append("BooleanFilter(");
163:
164: appendFilters(shouldFilters, null, buffer);
165: appendFilters(mustFilters, "+", buffer);
166: appendFilters(notFilters, "-", buffer);
167:
168: buffer.append(")");
169:
170: return buffer.toString();
171: }
172:
173: private void appendFilters(ArrayList filters, String occurString,
174: StringBuffer buffer) {
175: if (filters == null)
176: return;
177:
178: for (int i = 0; i < filters.size(); i++) {
179: Filter filter = (Filter) filters.get(i);
180: if (occurString != null) {
181: buffer.append(occurString);
182: }
183:
184: buffer.append(filter);
185:
186: if (i < filters.size() - 1) {
187: buffer.append(' ');
188: }
189: }
190: }
191: }
|