package org.apache.lucene.search;
/**
* 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.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.util.FixedBitSet;
/**
* A container Filter that allows Boolean composition of Filters.
* Filters are allocated into one of three logical constructs;
* SHOULD, MUST NOT, MUST
* The results Filter BitSet is constructed as follows:
* SHOULD Filters are OR'd together
* The resulting Filter is NOT'd with the NOT Filters
* The resulting Filter is AND'd with the MUST Filters
*/
public class BooleanFilter extends Filter implements Iterable<FilterClause> {
private final List<FilterClause> clauses = new ArrayList<FilterClause>();
/**
* Returns the a DocIdSetIterator representing the Boolean composition
* of the filters that have been added.
*/
@Override
public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
FixedBitSet res = null;
for (final FilterClause fc : clauses) {
if (fc.getOccur() == Occur.SHOULD) {
final DocIdSetIterator disi = getDISI(fc.getFilter(), reader);
if (disi == null) continue;
if (res == null) {
res = new FixedBitSet(reader.maxDoc());
}
res.or(disi);
}
}
for (final FilterClause fc : clauses) {
if (fc.getOccur() == Occur.MUST_NOT) {
if (res == null) {
res = new FixedBitSet(reader.maxDoc());
res.set(0, reader.maxDoc()); // NOTE: may set bits on deleted docs
}
final DocIdSetIterator disi = getDISI(fc.getFilter(), reader);
if (disi != null) {
res.andNot(disi);
}
}
}
for (final FilterClause fc : clauses) {
if (fc.getOccur() == Occur.MUST) {
final DocIdSetIterator disi = getDISI(fc.getFilter(), reader);
if (disi == null) {
return DocIdSet.EMPTY_DOCIDSET; // no documents can match
}
if (res == null) {
res = new FixedBitSet(reader.maxDoc());
res.or(disi);
} else {
res.and(disi);
}
}
}
return res != null ? res : DocIdSet.EMPTY_DOCIDSET;
}
private static DocIdSetIterator getDISI(Filter filter, IndexReader reader)
throws IOException {
final DocIdSet set = filter.getDocIdSet(reader);
return (set == null || set == DocIdSet.EMPTY_DOCIDSET) ? null : set.iterator();
}
/**
* Adds a new FilterClause to the Boolean Filter container
* @param filterClause A FilterClause object containing a Filter and an Occur parameter
*/
public void add(FilterClause filterClause) {
clauses.add(filterClause);
}
public final void add(Filter filter, Occur occur) {
add(new FilterClause(filter, occur));
}
/**
* Returns the list of clauses
*/
public List<FilterClause> clauses() {
return clauses;
}
/** Returns an iterator on the clauses in this query. It implements the {@link Iterable} interface to
* make it possible to do:
* <pre>for (FilterClause clause : booleanFilter) {}</pre>
*/
public final Iterator<FilterClause> iterator() {
return clauses().iterator();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if ((obj == null) || (obj.getClass() != this.getClass())) {
return false;
}
final BooleanFilter other = (BooleanFilter)obj;
return clauses.equals(other.clauses);
}
@Override
public int hashCode() {
return 657153718 ^ clauses.hashCode();
}
/** Prints a user-readable version of this Filter. */
@Override
public String toString() {
final StringBuilder buffer = new StringBuilder("BooleanFilter(");
final int minLen = buffer.length();
for (final FilterClause c : clauses) {
if (buffer.length() > minLen) {
buffer.append(' ');
}
buffer.append(c);
}
return buffer.append(')').toString();
}
}