FieldDocSortedHitQueue.java
001 package gate.creole.annic.apache.lucene.search;
002 
003 /**
004  * Copyright 2004 The Apache Software Foundation
005  *
006  * Licensed under the Apache License, Version 2.0 (the "License");
007  * you may not use this file except in compliance with the License.
008  * You may obtain a copy of the License at
009  *
010  *     http://www.apache.org/licenses/LICENSE-2.0
011  *
012  * Unless required by applicable law or agreed to in writing, software
013  * distributed under the License is distributed on an "AS IS" BASIS,
014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015  * See the License for the specific language governing permissions and
016  * limitations under the License.
017  */
018 
019 import gate.creole.annic.apache.lucene.util.PriorityQueue;
020 
021 import java.io.IOException;
022 import java.text.Collator;
023 import java.util.Locale;
024 
025 /**
026  * Expert: Collects sorted results from Searchable's and collates them.
027  * The elements put into this queue must be of type FieldDoc.
028  *
029  <p>Created: Feb 11, 2004 2:04:21 PM
030  *
031  @author  Tim Jones (Nacimiento Software)
032  @since   lucene 1.4
033  @version $Id: FieldDocSortedHitQueue.java 529 2004-10-05 11:55:26Z niraj $
034  */
035 class FieldDocSortedHitQueue
036 extends PriorityQueue {
037 
038   // this cannot contain AUTO fields - any AUTO fields should
039   // have been resolved by the time this class is used.
040   volatile SortField[] fields;
041 
042   // used in the case where the fields are sorted by locale
043   // based strings
044   volatile Collator[] collators;
045 
046 
047   /**
048    * Creates a hit queue sorted by the given list of fields.
049    @param fields Field names, in priority order (highest priority first).
050    @param size  The number of hits to retain.  Must be greater than zero.
051    @throws IOException
052    */
053   FieldDocSortedHitQueue (SortField[] fields, int size)
054   throws IOException {
055     this.fields = fields;
056     this.collators = hasCollators (fields);
057     initialize (size);
058   }
059 
060 
061   /**
062    * Allows redefinition of sort fields if they are <code>null</code>.
063    * This is to handle the case using ParallelMultiSearcher where the
064    * original list contains AUTO and we don't know the actual sort
065    * type until the values come back.  The fields can only be set once.
066    * This method is thread safe.
067    @param fields
068    */
069   synchronized void setFields (SortField[] fields) {
070     if (this.fields == null) {
071       this.fields = fields;
072       this.collators = hasCollators (fields);
073     }
074   }
075 
076 
077   /** Returns the fields being used to sort. */
078   SortField[] getFields() {
079     return fields;
080   }
081 
082 
083   /** Returns an array of collators, possibly <code>null</code>.  The collators
084    * correspond to any SortFields which were given a specific locale.
085    @param fields Array of sort fields.
086    @return Array, possibly <code>null</code>.
087    */
088   private Collator[] hasCollators (final SortField[] fields) {
089     if (fields == nullreturn null;
090     Collator[] ret = new Collator[fields.length];
091     for (int i=0; i<fields.length; ++i) {
092       Locale locale = fields[i].getLocale();
093       if (locale != null)
094         ret[i= Collator.getInstance (locale);
095     }
096     return ret;
097   }
098 
099 
100   /**
101    * Returns whether <code>a</code> is less relevant than <code>b</code>.
102    @param a ScoreDoc
103    @param b ScoreDoc
104    @return <code>true</code> if document <code>a</code> should be sorted after document <code>b</code>.
105    */
106   protected final boolean lessThan (final Object a, final Object b) {
107     final FieldDoc docA = (FieldDoca;
108     final FieldDoc docB = (FieldDocb;
109     final int n = fields.length;
110     int c = 0;
111     for (int i=0; i<n && c==0; ++i) {
112       final int type = fields[i].getType();
113       if (fields[i].getReverse()) {
114         switch (type) {
115           case SortField.SCORE:
116             float r1 = ((Float)docA.fields[i]).floatValue();
117             float r2 = ((Float)docB.fields[i]).floatValue();
118             if (r1 < r2c = -1;
119             if (r1 > r2c = 1;
120             break;
121           case SortField.DOC:
122           case SortField.INT:
123             int i1 = ((Integer)docA.fields[i]).intValue();
124             int i2 = ((Integer)docB.fields[i]).intValue();
125             if (i1 > i2c = -1;
126             if (i1 < i2c = 1;
127             break;
128           case SortField.STRING:
129             String s1 = (StringdocA.fields[i];
130             String s2 = (StringdocB.fields[i];
131             if (s2 == nullc = -1;      // could be null if there are
132             else if (s1 == nullc = 1;  // no terms in the given field
133             else if (fields[i].getLocale() == null) {
134               c = s2.compareTo(s1);
135             else {
136               c = collators[i].compare (s2, s1);
137             }
138             break;
139           case SortField.FLOAT:
140             float f1 = ((Float)docA.fields[i]).floatValue();
141             float f2 = ((Float)docB.fields[i]).floatValue();
142             if (f1 > f2c = -1;
143             if (f1 < f2c = 1;
144             break;
145           case SortField.CUSTOM:
146             c = docB.fields[i].compareTo (docA.fields[i]);
147             break;
148           case SortField.AUTO:
149             // we cannot handle this - even if we determine the type of object (Float or
150             // Integer), we don't necessarily know how to compare them (both SCORE and
151             // FLOAT both contain floats, but are sorted opposite of each other). Before
152             // we get here, each AUTO should have been replaced with its actual value.
153             throw new RuntimeException ("FieldDocSortedHitQueue cannot use an AUTO SortField");
154           default:
155             throw new RuntimeException ("invalid SortField type: "+type);
156         }
157       else {
158         switch (type) {
159           case SortField.SCORE:
160             float r1 = ((Float)docA.fields[i]).floatValue();
161             float r2 = ((Float)docB.fields[i]).floatValue();
162             if (r1 > r2c = -1;
163             if (r1 < r2c = 1;
164             break;
165           case SortField.DOC:
166           case SortField.INT:
167             int i1 = ((Integer)docA.fields[i]).intValue();
168             int i2 = ((Integer)docB.fields[i]).intValue();
169             if (i1 < i2c = -1;
170             if (i1 > i2c = 1;
171             break;
172           case SortField.STRING:
173             String s1 = (StringdocA.fields[i];
174             String s2 = (StringdocB.fields[i];
175             // null values need to be sorted first, because of how FieldCache.getStringIndex()
176             // works - in that routine, any documents without a value in the given field are
177             // put first.
178             if (s1 == nullc = -1;      // could be null if there are
179             else if (s2 == nullc = 1;  // no terms in the given field
180             else if (fields[i].getLocale() == null) {
181               c = s1.compareTo(s2);
182             else {
183               c = collators[i].compare (s1, s2);
184             }
185             break;
186           case SortField.FLOAT:
187             float f1 = ((Float)docA.fields[i]).floatValue();
188             float f2 = ((Float)docB.fields[i]).floatValue();
189             if (f1 < f2c = -1;
190             if (f1 > f2c = 1;
191             break;
192           case SortField.CUSTOM:
193             c = docA.fields[i].compareTo (docB.fields[i]);
194             break;
195           case SortField.AUTO:
196             // we cannot handle this - even if we determine the type of object (Float or
197             // Integer), we don't necessarily know how to compare them (both SCORE and
198             // FLOAT both contain floats, but are sorted opposite of each other). Before
199             // we get here, each AUTO should have been replaced with its actual value.
200             throw new RuntimeException ("FieldDocSortedHitQueue cannot use an AUTO SortField");
201           default:
202             throw new RuntimeException ("invalid SortField type: "+type);
203         }
204       }
205     }
206     return c > 0;
207   }
208 }