/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2005, Institut de Recherche pour le Développement * (C) 2007-2009, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotoolkit.lucene.filter; import org.apache.lucene.index.CorruptIndexException; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.search.DocIdSet; import org.apache.lucene.search.Filter; import org.apache.lucene.util.BitDocIdSet; import org.apache.lucene.util.BitSet; import org.apache.lucene.util.Bits; import org.apache.lucene.util.FixedBitSet; import org.geotoolkit.index.LogicalFilterType; import org.geotoolkit.index.tree.Tree; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Objects; import static org.geotoolkit.index.LogicalFilterType.*; /** * * Provide a serial chain filter, passing the bitset in with the * index reader to each of the filters in an ordered fashion. * * Based off chain filter, but will some improvements to allow a narrowed down * filtering. Traditional filter required iteration through an IndexReader. * * By implementing the ISerialChainFilter class, you can create a bits(IndexReader reader, BitSet bits) * * * @author Patrick O'Leary * @author Guilhem Legal * @module */ public class SerialChainFilter extends Filter implements org.geotoolkit.lucene.filter.Filter { /** * For cross-version compatibility. */ private static final long serialVersionUID = -8132561537335553911L; private final List<Filter> chain; private LogicalFilterType[] actionType; public SerialChainFilter(final List<Filter> chain) { this.chain = chain; this.actionType = new LogicalFilterType[]{DEFAULT}; } public SerialChainFilter(final List<Filter> chain, final LogicalFilterType[] actionType) { this.chain = chain; this.actionType = actionType.clone(); } /* (non-Javadoc) * @see org.apache.lucene.search.Filter#bits(org.apache.lucene.index.IndexReader) */ @Override public DocIdSet getDocIdSet(final LeafReaderContext ctx, final Bits b) throws CorruptIndexException, IOException { final int chainSize = chain.size(); final int actionSize = actionType.length; final FixedBitSet bits = (FixedBitSet) ((BitDocIdSet)chain.get(0).getDocIdSet(ctx, b)).bits(); //if there is only an operand not we don't enter the loop int j = 0; if (actionType[j] == NOT) { bits.flip(0, ctx.reader().maxDoc()); j++; } for (int i = 1; i < chainSize; i++) { LogicalFilterType action; if (j < actionSize) { action = actionType[j]; j++; } else { action = DEFAULT; } final FixedBitSet nextFilterResponse = (FixedBitSet) ((BitDocIdSet)chain.get(i).getDocIdSet(ctx, b)).bits(); //if the next operator is NOT we have to process the action before the current operand if (j < actionSize && actionType[j] == NOT) { nextFilterResponse.flip(0, ctx.reader().maxDoc()); j++; } switch (action) { case AND: bits.and(nextFilterResponse); break; case OR: bits.or(nextFilterResponse); break; case XOR: bits.xor(nextFilterResponse); break; default: bits.or(nextFilterResponse); break; } } // invalidate deleted document return invalidateDeletedDocument(bits, b); } private BitDocIdSet invalidateDeletedDocument(final BitSet results, final Bits initial) { if (initial != null) { for (int i = 0; i < initial.length(); i++) { if (!initial.get(i)) { results.clear(i); } } } return new BitDocIdSet(results); } /** * @return the chain */ public List<Filter> getChain() { return chain; } /** * @return the actionType */ public LogicalFilterType[] getActionType() { return actionType.clone(); } /** * Return the flag correspounding to the specified filterName. * * @param filterName A filter name : And, Or, Xor or Not. * * @return an int flag. */ public static LogicalFilterType valueOf(final String filterName) { if (filterName.equals("And")) { return AND; } else if (filterName.equals("Or")) { return OR; } else if (filterName.equals("Xor")) { return XOR; } else if (filterName.equals("Not")) { return NOT; } else { return DEFAULT; } } /** * Return the filterName correspounding to the specified flag. * * @param flag an int flag. * * @return A filter name : And, Or, Xor or Not. */ public static String valueOf(final LogicalFilterType flag) { switch (flag) { case AND: return "AND"; case OR: return "OR"; case NOT: return "NOT"; case XOR: return "XOR"; default: return "unknow"; } } @Override public void applyRtreeOnFilter(final Tree rTree, final boolean envelopeOnly) { for (Filter f : chain) { if (f instanceof org.geotoolkit.lucene.filter.Filter) { ((org.geotoolkit.lucene.filter.Filter)f).applyRtreeOnFilter(rTree, envelopeOnly); } } } /** * Returns true if <code>o</code> is equal to this. * * @see org.apache.lucene.search.RangeFilter#equals */ @Override public boolean equals(final Object o) { if (this == o) { return true; } if (o instanceof SerialChainFilter) { final SerialChainFilter other = (SerialChainFilter) o; if (this.chain.size() != other.getChain().size() || this.actionType.length != other.getActionType().length) { return false; } for (int i = 0; i < this.chain.size(); i++) { if (!this.chain.get(i).equals(other.getChain().get(i))) { return false; } } for (int i = 0; i < this.actionType.length; i++) { if (this.actionType[i] != other.getActionType()[i]) { return false; } } return true; } return false; } @Override public int hashCode() { int hash = 7; hash = 37 * hash + Objects.hashCode(this.chain); hash = 37 * hash + Arrays.hashCode(this.actionType); return hash; } @Override public String toString(String s) { final StringBuffer buf = new StringBuffer(); buf.append("[SerialChainFilter]").append('\n'); if (chain != null && chain.size() == 1) { buf.append("NOT ").append('\n'); buf.append('\t').append(chain.get(0)); } else if (chain != null && chain.size() > 0) { buf.append('\t').append(chain.get(0)).append('\n'); for (int i = 0; i < actionType.length; i++) { switch(actionType[i]) { case AND: buf.append("AND"); break; case OR: buf.append("OR"); break; case NOT: buf.append("NOT"); break; case XOR: buf.append("XOR"); break; default: buf.append(actionType[i]); } buf.append('\n'); if (chain.size() > i + 1) { buf.append('\t').append(" ").append(chain.get(i + 1)).append('\n'); } } } buf.append('\n'); return buf.toString().trim(); } }