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 org.apache.lucene.index.IndexReader;
21 import org.apache.lucene.index.Term;
22 import org.apache.lucene.index.TermDocs;
23 import org.apache.lucene.index.TermEnum;
24
25 import java.io.IOException;
26 import java.util.HashMap;
27 import java.util.Locale;
28 import java.util.Map;
29 import java.util.WeakHashMap;
30
31 /**
32 * Expert: The default cache implementation, storing all values in memory.
33 * A WeakHashMap is used for storage.
34 *
35 * <p>Created: May 19, 2004 4:40:36 PM
36 *
37 * @since lucene 1.4
38 * @version $Id: FieldCacheImpl.java 695514 2008-09-15 15:42:11Z otis $
39 */
40 class FieldCacheImpl
41 implements FieldCache {
42
43 /** Expert: Internal cache. */
44 abstract static class Cache {
45 private final Map readerCache = new WeakHashMap();
46
47 protected abstract Object createValue(IndexReader reader, Object key)
48 throws IOException;
49
50 public Object get(IndexReader reader, Object key) throws IOException {
51 Map innerCache;
52 Object value;
53 synchronized (readerCache) {
54 innerCache = (Map) readerCache.get(reader);
55 if (innerCache == null) {
56 innerCache = new HashMap();
57 readerCache.put(reader, innerCache);
58 value = null;
59 } else {
60 value = innerCache.get(key);
61 }
62 if (value == null) {
63 value = new CreationPlaceholder();
64 innerCache.put(key, value);
65 }
66 }
67 if (value instanceof CreationPlaceholder) {
68 synchronized (value) {
69 CreationPlaceholder progress = (CreationPlaceholder) value;
70 if (progress.value == null) {
71 progress.value = createValue(reader, key);
72 synchronized (readerCache) {
73 innerCache.put(key, progress.value);
74 }
75 }
76 return progress.value;
77 }
78 }
79 return value;
80 }
81 }
82
83 static final class CreationPlaceholder {
84 Object value;
85 }
86
87 /** Expert: Every composite-key in the internal cache is of this type. */
88 static class Entry {
89 final String field; // which Fieldable
90 final int type; // which SortField type
91 final Object custom; // which custom comparator
92 final Locale locale; // the locale we're sorting (if string)
93
94 /** Creates one of these objects. */
95 Entry (String field, int type, Locale locale) {
96 this.field = field.intern();
97 this.type = type;
98 this.custom = null;
99 this.locale = locale;
100 }
101
102 /** Creates one of these objects for a custom comparator. */
103 Entry (String field, Object custom) {
104 this.field = field.intern();
105 this.type = SortField.CUSTOM;
106 this.custom = custom;
107 this.locale = null;
108 }
109
110 /** Two of these are equal iff they reference the same field and type. */
111 public boolean equals (Object o) {
112 if (o instanceof Entry) {
113 Entry other = (Entry) o;
114 if (other.field == field && other.type == type) {
115 if (other.locale == null ? locale == null : other.locale.equals(locale)) {
116 if (other.custom == null) {
117 if (custom == null) return true;
118 } else if (other.custom.equals (custom)) {
119 return true;
120 }
121 }
122 }
123 }
124 return false;
125 }
126
127 /** Composes a hashcode based on the field and type. */
128 public int hashCode() {
129 return field.hashCode() ^ type ^ (custom==null ? 0 : custom.hashCode()) ^ (locale==null ? 0 : locale.hashCode());
130 }
131 }
132
133 private static final ByteParser BYTE_PARSER = new ByteParser() {
134 public byte parseByte(String value) {
135 return Byte.parseByte(value);
136 }
137 };
138
139 private static final ShortParser SHORT_PARSER = new ShortParser() {
140 public short parseShort(String value) {
141 return Short.parseShort(value);
142 }
143 };
144
145 private static final IntParser INT_PARSER = new IntParser() {
146 public int parseInt(String value) {
147 return Integer.parseInt(value);
148 }
149 };
150
151
152 private static final FloatParser FLOAT_PARSER = new FloatParser() {
153 public float parseFloat(String value) {
154 return Float.parseFloat(value);
155 }
156 };
157
158 // inherit javadocs
159 public byte[] getBytes (IndexReader reader, String field) throws IOException {
160 return getBytes(reader, field, BYTE_PARSER);
161 }
162
163 // inherit javadocs
164 public byte[] getBytes(IndexReader reader, String field, ByteParser parser)
165 throws IOException {
166 return (byte[]) bytesCache.get(reader, new Entry(field, parser));
167 }
168
169 Cache bytesCache = new Cache() {
170
171 protected Object createValue(IndexReader reader, Object entryKey)
172 throws IOException {
173 Entry entry = (Entry) entryKey;
174 String field = entry.field;
175 ByteParser parser = (ByteParser) entry.custom;
176 final byte[] retArray = new byte[reader.maxDoc()];
177 TermDocs termDocs = reader.termDocs();
178 TermEnum termEnum = reader.terms (new Term (field));
179 try {
180 do {
181 Term term = termEnum.term();
182 if (term==null || term.field() != field) break;
183 byte termval = parser.parseByte(term.text());
184 termDocs.seek (termEnum);
185 while (termDocs.next()) {
186 retArray[termDocs.doc()] = termval;
187 }
188 } while (termEnum.next());
189 } finally {
190 termDocs.close();
191 termEnum.close();
192 }
193 return retArray;
194 }
195 };
196
197 // inherit javadocs
198 public short[] getShorts (IndexReader reader, String field) throws IOException {
199 return getShorts(reader, field, SHORT_PARSER);
200 }
201
202 // inherit javadocs
203 public short[] getShorts(IndexReader reader, String field, ShortParser parser)
204 throws IOException {
205 return (short[]) shortsCache.get(reader, new Entry(field, parser));
206 }
207
208 Cache shortsCache = new Cache() {
209
210 protected Object createValue(IndexReader reader, Object entryKey)
211 throws IOException {
212 Entry entry = (Entry) entryKey;
213 String field = entry.field;
214 ShortParser parser = (ShortParser) entry.custom;
215 final short[] retArray = new short[reader.maxDoc()];
216 TermDocs termDocs = reader.termDocs();
217 TermEnum termEnum = reader.terms (new Term (field));
218 try {
219 do {
220 Term term = termEnum.term();
221 if (term==null || term.field() != field) break;
222 short termval = parser.parseShort(term.text());
223 termDocs.seek (termEnum);
224 while (termDocs.next()) {
225 retArray[termDocs.doc()] = termval;
226 }
227 } while (termEnum.next());
228 } finally {
229 termDocs.close();
230 termEnum.close();
231 }
232 return retArray;
233 }
234 };
235
236 // inherit javadocs
237 public int[] getInts (IndexReader reader, String field) throws IOException {
238 return getInts(reader, field, INT_PARSER);
239 }
240
241 // inherit javadocs
242 public int[] getInts(IndexReader reader, String field, IntParser parser)
243 throws IOException {
244 return (int[]) intsCache.get(reader, new Entry(field, parser));
245 }
246
247 Cache intsCache = new Cache() {
248
249 protected Object createValue(IndexReader reader, Object entryKey)
250 throws IOException {
251 Entry entry = (Entry) entryKey;
252 String field = entry.field;
253 IntParser parser = (IntParser) entry.custom;
254 final int[] retArray = new int[reader.maxDoc()];
255 TermDocs termDocs = reader.termDocs();
256 TermEnum termEnum = reader.terms (new Term (field));
257 try {
258 do {
259 Term term = termEnum.term();
260 if (term==null || term.field() != field) break;
261 int termval = parser.parseInt(term.text());
262 termDocs.seek (termEnum);
263 while (termDocs.next()) {
264 retArray[termDocs.doc()] = termval;
265 }
266 } while (termEnum.next());
267 } finally {
268 termDocs.close();
269 termEnum.close();
270 }
271 return retArray;
272 }
273 };
274
275
276 // inherit javadocs
277 public float[] getFloats (IndexReader reader, String field)
278 throws IOException {
279 return getFloats(reader, field, FLOAT_PARSER);
280 }
281
282 // inherit javadocs
283 public float[] getFloats(IndexReader reader, String field, FloatParser parser)
284 throws IOException {
285 return (float[]) floatsCache.get(reader, new Entry(field, parser));
286 }
287
288 Cache floatsCache = new Cache() {
289
290 protected Object createValue(IndexReader reader, Object entryKey)
291 throws IOException {
292 Entry entry = (Entry) entryKey;
293 String field = entry.field;
294 FloatParser parser = (FloatParser) entry.custom;
295 final float[] retArray = new float[reader.maxDoc()];
296 TermDocs termDocs = reader.termDocs();
297 TermEnum termEnum = reader.terms (new Term (field));
298 try {
299 do {
300 Term term = termEnum.term();
301 if (term==null || term.field() != field) break;
302 float termval = parser.parseFloat(term.text());
303 termDocs.seek (termEnum);
304 while (termDocs.next()) {
305 retArray[termDocs.doc()] = termval;
306 }
307 } while (termEnum.next());
308 } finally {
309 termDocs.close();
310 termEnum.close();
311 }
312 return retArray;
313 }
314 };
315
316 // inherit javadocs
317 public String[] getStrings(IndexReader reader, String field)
318 throws IOException {
319 return (String[]) stringsCache.get(reader, field);
320 }
321
322 Cache stringsCache = new Cache() {
323
324 protected Object createValue(IndexReader reader, Object fieldKey)
325 throws IOException {
326 String field = ((String) fieldKey).intern();
327 final String[] retArray = new String[reader.maxDoc()];
328 TermDocs termDocs = reader.termDocs();
329 TermEnum termEnum = reader.terms (new Term (field));
330 try {
331 do {
332 Term term = termEnum.term();
333 if (term==null || term.field() != field) break;
334 String termval = term.text();
335 termDocs.seek (termEnum);
336 while (termDocs.next()) {
337 retArray[termDocs.doc()] = termval;
338 }
339 } while (termEnum.next());
340 } finally {
341 termDocs.close();
342 termEnum.close();
343 }
344 return retArray;
345 }
346 };
347
348 // inherit javadocs
349 public StringIndex getStringIndex(IndexReader reader, String field)
350 throws IOException {
351 return (StringIndex) stringsIndexCache.get(reader, field);
352 }
353
354 Cache stringsIndexCache = new Cache() {
355
356 protected Object createValue(IndexReader reader, Object fieldKey)
357 throws IOException {
358 String field = ((String) fieldKey).intern();
359 final int[] retArray = new int[reader.maxDoc()];
360 String[] mterms = new String[reader.maxDoc()+1];
361 TermDocs termDocs = reader.termDocs();
362 TermEnum termEnum = reader.terms (new Term (field));
363 int t = 0; // current term number
364
365 // an entry for documents that have no terms in this field
366 // should a document with no terms be at top or bottom?
367 // this puts them at the top - if it is changed, FieldDocSortedHitQueue
368 // needs to change as well.
369 mterms[t++] = null;
370
371 try {
372 do {
373 Term term = termEnum.term();
374 if (term==null || term.field() != field) break;
375
376 // store term text
377 // we expect that there is at most one term per document
378 if (t >= mterms.length) throw new RuntimeException ("there are more terms than " +
379 "documents in field \"" + field + "\", but it's impossible to sort on " +
380 "tokenized fields");
381 mterms[t] = term.text();
382
383 termDocs.seek (termEnum);
384 while (termDocs.next()) {
385 retArray[termDocs.doc()] = t;
386 }
387
388 t++;
389 } while (termEnum.next());
390 } finally {
391 termDocs.close();
392 termEnum.close();
393 }
394
395 if (t == 0) {
396 // if there are no terms, make the term array
397 // have a single null entry
398 mterms = new String[1];
399 } else if (t < mterms.length) {
400 // if there are less terms than documents,
401 // trim off the dead array space
402 String[] terms = new String[t];
403 System.arraycopy (mterms, 0, terms, 0, t);
404 mterms = terms;
405 }
406
407 StringIndex value = new StringIndex (retArray, mterms);
408 return value;
409 }
410 };
411
412 /** The pattern used to detect integer values in a field */
413 /** removed for java 1.3 compatibility
414 protected static final Pattern pIntegers = Pattern.compile ("[0-9\\-]+");
415 **/
416
417 /** The pattern used to detect float values in a field */
418 /**
419 * removed for java 1.3 compatibility
420 * protected static final Object pFloats = Pattern.compile ("[0-9+\\-\\.eEfFdD]+");
421 */
422
423 // inherit javadocs
424 public Object getAuto(IndexReader reader, String field) throws IOException {
425 return autoCache.get(reader, field);
426 }
427
428 Cache autoCache = new Cache() {
429
430 protected Object createValue(IndexReader reader, Object fieldKey)
431 throws IOException {
432 String field = ((String)fieldKey).intern();
433 TermEnum enumerator = reader.terms (new Term (field));
434 try {
435 Term term = enumerator.term();
436 if (term == null) {
437 throw new RuntimeException ("no terms in field " + field + " - cannot determine sort type");
438 }
439 Object ret = null;
440 if (term.field() == field) {
441 String termtext = term.text().trim();
442
443 /**
444 * Java 1.4 level code:
445
446 if (pIntegers.matcher(termtext).matches())
447 return IntegerSortedHitQueue.comparator (reader, enumerator, field);
448
449 else if (pFloats.matcher(termtext).matches())
450 return FloatSortedHitQueue.comparator (reader, enumerator, field);
451 */
452
453 // Java 1.3 level code:
454 try {
455 Integer.parseInt (termtext);
456 ret = getInts (reader, field);
457 } catch (NumberFormatException nfe1) {
458 try {
459 Float.parseFloat (termtext);
460 ret = getFloats (reader, field);
461 } catch (NumberFormatException nfe3) {
462 ret = getStringIndex (reader, field);
463 }
464 }
465 } else {
466 throw new RuntimeException ("field \"" + field + "\" does not appear to be indexed");
467 }
468 return ret;
469 } finally {
470 enumerator.close();
471 }
472 }
473 };
474
475 // inherit javadocs
476 public Comparable[] getCustom(IndexReader reader, String field,
477 SortComparator comparator) throws IOException {
478 return (Comparable[]) customCache.get(reader, new Entry(field, comparator));
479 }
480
481 Cache customCache = new Cache() {
482
483 protected Object createValue(IndexReader reader, Object entryKey)
484 throws IOException {
485 Entry entry = (Entry) entryKey;
486 String field = entry.field;
487 SortComparator comparator = (SortComparator) entry.custom;
488 final Comparable[] retArray = new Comparable[reader.maxDoc()];
489 TermDocs termDocs = reader.termDocs();
490 TermEnum termEnum = reader.terms (new Term (field));
491 try {
492 do {
493 Term term = termEnum.term();
494 if (term==null || term.field() != field) break;
495 Comparable termval = comparator.getComparable (term.text());
496 termDocs.seek (termEnum);
497 while (termDocs.next()) {
498 retArray[termDocs.doc()] = termval;
499 }
500 } while (termEnum.next());
501 } finally {
502 termDocs.close();
503 termEnum.close();
504 }
505 return retArray;
506 }
507 };
508
509 }
510