add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / contrib / queries / src / java / org / apache / lucene / search / ChainedFilter.java
1 package org.apache.lucene.search;
2
3 /**
4  * Licensed to the Apache Software Foundation (ASF) under one or more
5  * contributor license agreements.  See the NOTICE file distributed with
6  * this work for additional information regarding copyright ownership.
7  * The ASF licenses this file to You under the Apache License, Version 2.0
8  * (the "License"); you may not use this file except in compliance with
9  * the License.  You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 import java.io.IOException;
21
22 import org.apache.lucene.index.IndexReader;
23 import org.apache.lucene.search.DocIdSet;
24 import org.apache.lucene.search.DocIdSetIterator;
25 import org.apache.lucene.search.Filter;
26 import org.apache.lucene.util.OpenBitSet;
27 import org.apache.lucene.util.OpenBitSetDISI;
28
29 /**
30  * <p>
31  * Allows multiple {@link Filter}s to be chained.
32  * Logical operations such as <b>NOT</b> and <b>XOR</b>
33  * are applied between filters. One operation can be used
34  * for all filters, or a specific operation can be declared
35  * for each filter.
36  * </p>
37  * <p>
38  * Order in which filters are called depends on
39  * the position of the filter in the chain. It's probably
40  * more efficient to place the most restrictive filters
41  * /least computationally-intensive filters first.
42  * </p>
43  *
44  */
45 public class ChainedFilter extends Filter
46 {
47     public static final int OR = 0;
48     public static final int AND = 1;
49     public static final int ANDNOT = 2;
50     public static final int XOR = 3;
51     /**
52      * Logical operation when none is declared. Defaults to
53      * OR.
54      */
55     public static int DEFAULT = OR;
56
57     /** The filter chain */
58     private Filter[] chain = null;
59
60     private int[] logicArray;
61
62     private int logic = -1;
63
64     /**
65      * Ctor.
66      * @param chain The chain of filters
67      */
68     public ChainedFilter(Filter[] chain)
69     {
70         this.chain = chain;
71     }
72
73     /**
74      * Ctor.
75      * @param chain The chain of filters
76      * @param logicArray Logical operations to apply between filters
77      */
78     public ChainedFilter(Filter[] chain, int[] logicArray)
79     {
80         this.chain = chain;
81         this.logicArray = logicArray;
82     }
83
84     /**
85      * Ctor.
86      * @param chain The chain of filters
87      * @param logic Logical operation to apply to ALL filters
88      */
89     public ChainedFilter(Filter[] chain, int logic)
90     {
91         this.chain = chain;
92         this.logic = logic;
93     }
94
95     /**
96      * {@link Filter#getDocIdSet}.
97      */
98     @Override
99     public DocIdSet getDocIdSet(IndexReader reader) throws IOException
100     {
101         int[] index = new int[1]; // use array as reference to modifiable int; 
102         index[0] = 0;             // an object attribute would not be thread safe.
103         if (logic != -1)
104             return getDocIdSet(reader, logic, index);
105         else if (logicArray != null)
106             return getDocIdSet(reader, logicArray, index);
107         else
108             return getDocIdSet(reader, DEFAULT, index);
109     }
110
111     private DocIdSetIterator getDISI(Filter filter, IndexReader reader)
112     throws IOException {
113         DocIdSet docIdSet = filter.getDocIdSet(reader);
114         if (docIdSet == null) {
115           return DocIdSet.EMPTY_DOCIDSET.iterator();
116         } else {
117           DocIdSetIterator iter = docIdSet.iterator();
118           if (iter == null) {
119             return DocIdSet.EMPTY_DOCIDSET.iterator();
120           } else {
121             return iter;
122           }
123         }
124     }
125
126     private OpenBitSetDISI initialResult(IndexReader reader, int logic, int[] index)
127     throws IOException
128     {
129         OpenBitSetDISI result;
130         /**
131          * First AND operation takes place against a completely false
132          * bitset and will always return zero results.
133          */
134         if (logic == AND)
135         {
136             result = new OpenBitSetDISI(getDISI(chain[index[0]], reader), reader.maxDoc());
137             ++index[0];
138         }
139         else if (logic == ANDNOT)
140         {
141             result = new OpenBitSetDISI(getDISI(chain[index[0]], reader), reader.maxDoc());
142             result.flip(0,reader.maxDoc()); // NOTE: may set bits for deleted docs.
143             ++index[0];
144         }
145         else
146         {
147             result = new OpenBitSetDISI(reader.maxDoc());
148         }
149         return result;
150     }
151
152     /** Provide a SortedVIntList when it is definitely
153      *  smaller than an OpenBitSet
154      *  @deprecated Either use CachingWrapperFilter, or
155      *  switch to a different DocIdSet implementation yourself.
156      *  This method will be removed in Lucene 4.0 
157      **/
158     @Deprecated
159     protected final DocIdSet finalResult(OpenBitSetDISI result, int maxDocs) {
160         return result;
161     }
162         
163
164     /**
165      * Delegates to each filter in the chain.
166      * @param reader IndexReader
167      * @param logic Logical operation
168      * @return DocIdSet
169      */
170     private DocIdSet getDocIdSet(IndexReader reader, int logic, int[] index)
171     throws IOException
172     {
173         OpenBitSetDISI result = initialResult(reader, logic, index);
174         for (; index[0] < chain.length; index[0]++)
175         {
176             doChain(result, logic, chain[index[0]].getDocIdSet(reader));
177         }
178         return finalResult(result, reader.maxDoc());
179     }
180
181     /**
182      * Delegates to each filter in the chain.
183      * @param reader IndexReader
184      * @param logic Logical operation
185      * @return DocIdSet
186      */
187     private DocIdSet getDocIdSet(IndexReader reader, int[] logic, int[] index)
188     throws IOException
189     {
190         if (logic.length != chain.length)
191             throw new IllegalArgumentException("Invalid number of elements in logic array");
192
193         OpenBitSetDISI result = initialResult(reader, logic[0], index);
194         for (; index[0] < chain.length; index[0]++)
195         {
196             doChain(result, logic[index[0]], chain[index[0]].getDocIdSet(reader));
197         }
198         return finalResult(result, reader.maxDoc());
199     }
200
201     @Override
202     public String toString()
203     {
204         StringBuilder sb = new StringBuilder();
205         sb.append("ChainedFilter: [");
206         for (int i = 0; i < chain.length; i++)
207         {
208             sb.append(chain[i]);
209             sb.append(' ');
210         }
211         sb.append(']');
212         return sb.toString();
213     }
214
215     private void doChain(OpenBitSetDISI result, int logic, DocIdSet dis)
216     throws IOException {
217       
218       if (dis instanceof OpenBitSet) {
219         // optimized case for OpenBitSets
220         switch (logic) {
221             case OR:
222                 result.or((OpenBitSet) dis);
223                 break;
224             case AND:
225                 result.and((OpenBitSet) dis);
226                 break;
227             case ANDNOT:
228                 result.andNot((OpenBitSet) dis);
229                 break;
230             case XOR:
231                 result.xor((OpenBitSet) dis);
232                 break;
233             default:
234                 doChain(result, DEFAULT, dis);
235                 break;
236         }
237       } else {
238         DocIdSetIterator disi;
239         if (dis == null) {
240           disi = DocIdSet.EMPTY_DOCIDSET.iterator();
241         } else {
242           disi = dis.iterator();
243           if (disi == null) {
244             disi = DocIdSet.EMPTY_DOCIDSET.iterator();            
245           }
246         }
247
248         switch (logic) {
249             case OR:
250                 result.inPlaceOr(disi);
251                 break;
252             case AND:
253                 result.inPlaceAnd(disi);
254                 break;
255             case ANDNOT:
256                 result.inPlaceNot(disi);
257                 break;
258             case XOR:
259                 result.inPlaceXor(disi);
260                 break;
261             default:
262                 doChain(result, DEFAULT, dis);
263                 break;
264         }
265       }
266     }
267
268 }