pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / java / org / apache / lucene / search / spans / SpanOrQuery.java
1 package org.apache.lucene.search.spans;
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 java.util.List;
23 import java.util.Collection;
24 import java.util.ArrayList;
25 import java.util.Iterator;
26 import java.util.Set;
27
28 import org.apache.lucene.index.IndexReader;
29 import org.apache.lucene.index.Term;
30 import org.apache.lucene.util.PriorityQueue;
31 import org.apache.lucene.util.ToStringUtils;
32 import org.apache.lucene.search.Query;
33
34 /** Matches the union of its clauses.*/
35 public class SpanOrQuery extends SpanQuery implements Cloneable {
36   private List<SpanQuery> clauses;
37   private String field;
38
39   /** Construct a SpanOrQuery merging the provided clauses. */
40   public SpanOrQuery(SpanQuery... clauses) {
41
42     // copy clauses array into an ArrayList
43     this.clauses = new ArrayList<SpanQuery>(clauses.length);
44     for (int i = 0; i < clauses.length; i++) {
45       addClause(clauses[i]);
46     }
47   }
48
49   /** Adds a clause to this query */
50   public final void addClause(SpanQuery clause) {
51     if (field == null) {
52       field = clause.getField();
53     } else if (!clause.getField().equals(field)) {
54       throw new IllegalArgumentException("Clauses must have same field.");
55     }
56     this.clauses.add(clause);
57   }
58   
59   /** Return the clauses whose spans are matched. */
60   public SpanQuery[] getClauses() {
61     return clauses.toArray(new SpanQuery[clauses.size()]);
62   }
63
64   @Override
65   public String getField() { return field; }
66
67   @Override
68   public void extractTerms(Set<Term> terms) {
69     for(final SpanQuery clause: clauses) {
70       clause.extractTerms(terms);
71     }
72   }
73   
74   @Override
75   public Object clone() {
76     int sz = clauses.size();
77     SpanQuery[] newClauses = new SpanQuery[sz];
78
79     for (int i = 0; i < sz; i++) {
80       newClauses[i] = (SpanQuery) clauses.get(i).clone();
81     }
82     SpanOrQuery soq = new SpanOrQuery(newClauses);
83     soq.setBoost(getBoost());
84     return soq;
85   }
86
87   @Override
88   public Query rewrite(IndexReader reader) throws IOException {
89     SpanOrQuery clone = null;
90     for (int i = 0 ; i < clauses.size(); i++) {
91       SpanQuery c = clauses.get(i);
92       SpanQuery query = (SpanQuery) c.rewrite(reader);
93       if (query != c) {                     // clause rewrote: must clone
94         if (clone == null)
95           clone = (SpanOrQuery) this.clone();
96         clone.clauses.set(i,query);
97       }
98     }
99     if (clone != null) {
100       return clone;                        // some clauses rewrote
101     } else {
102       return this;                         // no clauses rewrote
103     }
104   }
105
106   @Override
107   public String toString(String field) {
108     StringBuilder buffer = new StringBuilder();
109     buffer.append("spanOr([");
110     Iterator<SpanQuery> i = clauses.iterator();
111     while (i.hasNext()) {
112       SpanQuery clause = i.next();
113       buffer.append(clause.toString(field));
114       if (i.hasNext()) {
115         buffer.append(", ");
116       }
117     }
118     buffer.append("])");
119     buffer.append(ToStringUtils.boost(getBoost()));
120     return buffer.toString();
121   }
122
123   @Override
124   public boolean equals(Object o) {
125     if (this == o) return true;
126     if (o == null || getClass() != o.getClass()) return false;
127
128     final SpanOrQuery that = (SpanOrQuery) o;
129
130     if (!clauses.equals(that.clauses)) return false;
131     if (!clauses.isEmpty() && !field.equals(that.field)) return false;
132
133     return getBoost() == that.getBoost();
134   }
135
136   @Override
137   public int hashCode() {
138     int h = clauses.hashCode();
139     h ^= (h << 10) | (h >>> 23);
140     h ^= Float.floatToRawIntBits(getBoost());
141     return h;
142   }
143
144
145   private class SpanQueue extends PriorityQueue<Spans> {
146     public SpanQueue(int size) {
147       initialize(size);
148     }
149
150     @Override
151     protected final boolean lessThan(Spans spans1, Spans spans2) {
152       if (spans1.doc() == spans2.doc()) {
153         if (spans1.start() == spans2.start()) {
154           return spans1.end() < spans2.end();
155         } else {
156           return spans1.start() < spans2.start();
157         }
158       } else {
159         return spans1.doc() < spans2.doc();
160       }
161     }
162   }
163
164   @Override
165   public Spans getSpans(final IndexReader reader) throws IOException {
166     if (clauses.size() == 1)                      // optimize 1-clause case
167       return (clauses.get(0)).getSpans(reader);
168
169     return new Spans() {
170         private SpanQueue queue = null;
171
172         private boolean initSpanQueue(int target) throws IOException {
173           queue = new SpanQueue(clauses.size());
174           Iterator<SpanQuery> i = clauses.iterator();
175           while (i.hasNext()) {
176             Spans spans = i.next().getSpans(reader);
177             if (   ((target == -1) && spans.next())
178                 || ((target != -1) && spans.skipTo(target))) {
179               queue.add(spans);
180             }
181           }
182           return queue.size() != 0;
183         }
184
185         @Override
186         public boolean next() throws IOException {
187           if (queue == null) {
188             return initSpanQueue(-1);
189           }
190
191           if (queue.size() == 0) { // all done
192             return false;
193           }
194
195           if (top().next()) { // move to next
196             queue.updateTop();
197             return true;
198           }
199
200           queue.pop();  // exhausted a clause
201           return queue.size() != 0;
202         }
203
204         private Spans top() { return queue.top(); }
205
206         @Override
207         public boolean skipTo(int target) throws IOException {
208           if (queue == null) {
209             return initSpanQueue(target);
210           }
211   
212           boolean skipCalled = false;
213           while (queue.size() != 0 && top().doc() < target) {
214             if (top().skipTo(target)) {
215               queue.updateTop();
216             } else {
217               queue.pop();
218             }
219             skipCalled = true;
220           }
221   
222           if (skipCalled) {
223             return queue.size() != 0;
224           }
225           return next();
226         }
227
228         @Override
229         public int doc() { return top().doc(); }
230         @Override
231         public int start() { return top().start(); }
232         @Override
233         public int end() { return top().end(); }
234
235       @Override
236       public Collection<byte[]> getPayload() throws IOException {
237         ArrayList<byte[]> result = null;
238         Spans theTop = top();
239         if (theTop != null && theTop.isPayloadAvailable()) {
240           result = new ArrayList<byte[]>(theTop.getPayload());
241         }
242         return result;
243       }
244
245       @Override
246       public boolean isPayloadAvailable() {
247         Spans top = top();
248         return top != null && top.isPayloadAvailable();
249       }
250
251       @Override
252       public String toString() {
253           return "spans("+SpanOrQuery.this+")@"+
254             ((queue == null)?"START"
255              :(queue.size()>0?(doc()+":"+start()+"-"+end()):"END"));
256         }
257
258       };
259   }
260
261 }