001: package org.apache.lucene.search;
002:
003: /**
004: * Licensed to the Apache Software Foundation (ASF) under one or more
005: * contributor license agreements. See the NOTICE file distributed with
006: * this work for additional information regarding copyright ownership.
007: * The ASF licenses this file to You under the Apache License, Version 2.0
008: * (the "License"); you may not use this file except in compliance with
009: * the License. You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: */
019:
020: import org.apache.lucene.index.IndexReader;
021: import org.apache.lucene.index.Term;
022: import org.apache.lucene.index.TermDocs;
023: import org.apache.lucene.index.TermEnum;
024:
025: import java.io.IOException;
026: import java.util.HashMap;
027: import java.util.Locale;
028: import java.util.Map;
029: import java.util.WeakHashMap;
030:
031: /**
032: * Expert: The default cache implementation, storing all values in memory.
033: * A WeakHashMap is used for storage.
034: *
035: * <p>Created: May 19, 2004 4:40:36 PM
036: *
037: * @author Tim Jones (Nacimiento Software)
038: * @since lucene 1.4
039: * @version $Id: FieldCacheImpl.java 605225 2007-12-18 15:13:05Z gsingers $
040: */
041: class FieldCacheImpl implements FieldCache {
042:
043: /** Expert: Internal cache. */
044: abstract static class Cache {
045: private final Map readerCache = new WeakHashMap();
046:
047: protected abstract Object createValue(IndexReader reader,
048: Object key) throws IOException;
049:
050: public Object get(IndexReader reader, Object key)
051: throws IOException {
052: Map innerCache;
053: Object value;
054: synchronized (readerCache) {
055: innerCache = (Map) readerCache.get(reader);
056: if (innerCache == null) {
057: innerCache = new HashMap();
058: readerCache.put(reader, innerCache);
059: value = null;
060: } else {
061: value = innerCache.get(key);
062: }
063: if (value == null) {
064: value = new CreationPlaceholder();
065: innerCache.put(key, value);
066: }
067: }
068: if (value instanceof CreationPlaceholder) {
069: synchronized (value) {
070: CreationPlaceholder progress = (CreationPlaceholder) value;
071: if (progress.value == null) {
072: progress.value = createValue(reader, key);
073: synchronized (readerCache) {
074: innerCache.put(key, progress.value);
075: }
076: }
077: return progress.value;
078: }
079: }
080: return value;
081: }
082: }
083:
084: static final class CreationPlaceholder {
085: Object value;
086: }
087:
088: /** Expert: Every composite-key in the internal cache is of this type. */
089: static class Entry {
090: final String field; // which Fieldable
091: final int type; // which SortField type
092: final Object custom; // which custom comparator
093: final Locale locale; // the locale we're sorting (if string)
094:
095: /** Creates one of these objects. */
096: Entry(String field, int type, Locale locale) {
097: this .field = field.intern();
098: this .type = type;
099: this .custom = null;
100: this .locale = locale;
101: }
102:
103: /** Creates one of these objects for a custom comparator. */
104: Entry(String field, Object custom) {
105: this .field = field.intern();
106: this .type = SortField.CUSTOM;
107: this .custom = custom;
108: this .locale = null;
109: }
110:
111: /** Two of these are equal iff they reference the same field and type. */
112: public boolean equals(Object o) {
113: if (o instanceof Entry) {
114: Entry other = (Entry) o;
115: if (other.field == field && other.type == type) {
116: if (other.locale == null ? locale == null
117: : other.locale.equals(locale)) {
118: if (other.custom == null) {
119: if (custom == null)
120: return true;
121: } else if (other.custom.equals(custom)) {
122: return true;
123: }
124: }
125: }
126: }
127: return false;
128: }
129:
130: /** Composes a hashcode based on the field and type. */
131: public int hashCode() {
132: return field.hashCode() ^ type
133: ^ (custom == null ? 0 : custom.hashCode())
134: ^ (locale == null ? 0 : locale.hashCode());
135: }
136: }
137:
138: private static final ByteParser BYTE_PARSER = new ByteParser() {
139: public byte parseByte(String value) {
140: return Byte.parseByte(value);
141: }
142: };
143:
144: private static final ShortParser SHORT_PARSER = new ShortParser() {
145: public short parseShort(String value) {
146: return Short.parseShort(value);
147: }
148: };
149:
150: private static final IntParser INT_PARSER = new IntParser() {
151: public int parseInt(String value) {
152: return Integer.parseInt(value);
153: }
154: };
155:
156: private static final FloatParser FLOAT_PARSER = new FloatParser() {
157: public float parseFloat(String value) {
158: return Float.parseFloat(value);
159: }
160: };
161:
162: // inherit javadocs
163: public byte[] getBytes(IndexReader reader, String field)
164: throws IOException {
165: return getBytes(reader, field, BYTE_PARSER);
166: }
167:
168: // inherit javadocs
169: public byte[] getBytes(IndexReader reader, String field,
170: ByteParser parser) throws IOException {
171: return (byte[]) bytesCache
172: .get(reader, new Entry(field, parser));
173: }
174:
175: Cache bytesCache = new Cache() {
176:
177: protected Object createValue(IndexReader reader, Object entryKey)
178: throws IOException {
179: Entry entry = (Entry) entryKey;
180: String field = entry.field;
181: ByteParser parser = (ByteParser) entry.custom;
182: final byte[] retArray = new byte[reader.maxDoc()];
183: TermDocs termDocs = reader.termDocs();
184: TermEnum termEnum = reader.terms(new Term(field, ""));
185: try {
186: do {
187: Term term = termEnum.term();
188: if (term == null || term.field() != field)
189: break;
190: byte termval = parser.parseByte(term.text());
191: termDocs.seek(termEnum);
192: while (termDocs.next()) {
193: retArray[termDocs.doc()] = termval;
194: }
195: } while (termEnum.next());
196: } finally {
197: termDocs.close();
198: termEnum.close();
199: }
200: return retArray;
201: }
202: };
203:
204: // inherit javadocs
205: public short[] getShorts(IndexReader reader, String field)
206: throws IOException {
207: return getShorts(reader, field, SHORT_PARSER);
208: }
209:
210: // inherit javadocs
211: public short[] getShorts(IndexReader reader, String field,
212: ShortParser parser) throws IOException {
213: return (short[]) shortsCache.get(reader, new Entry(field,
214: parser));
215: }
216:
217: Cache shortsCache = new Cache() {
218:
219: protected Object createValue(IndexReader reader, Object entryKey)
220: throws IOException {
221: Entry entry = (Entry) entryKey;
222: String field = entry.field;
223: ShortParser parser = (ShortParser) entry.custom;
224: final short[] retArray = new short[reader.maxDoc()];
225: TermDocs termDocs = reader.termDocs();
226: TermEnum termEnum = reader.terms(new Term(field, ""));
227: try {
228: do {
229: Term term = termEnum.term();
230: if (term == null || term.field() != field)
231: break;
232: short termval = parser.parseShort(term.text());
233: termDocs.seek(termEnum);
234: while (termDocs.next()) {
235: retArray[termDocs.doc()] = termval;
236: }
237: } while (termEnum.next());
238: } finally {
239: termDocs.close();
240: termEnum.close();
241: }
242: return retArray;
243: }
244: };
245:
246: // inherit javadocs
247: public int[] getInts(IndexReader reader, String field)
248: throws IOException {
249: return getInts(reader, field, INT_PARSER);
250: }
251:
252: // inherit javadocs
253: public int[] getInts(IndexReader reader, String field,
254: IntParser parser) throws IOException {
255: return (int[]) intsCache.get(reader, new Entry(field, parser));
256: }
257:
258: Cache intsCache = new Cache() {
259:
260: protected Object createValue(IndexReader reader, Object entryKey)
261: throws IOException {
262: Entry entry = (Entry) entryKey;
263: String field = entry.field;
264: IntParser parser = (IntParser) entry.custom;
265: final int[] retArray = new int[reader.maxDoc()];
266: TermDocs termDocs = reader.termDocs();
267: TermEnum termEnum = reader.terms(new Term(field, ""));
268: try {
269: do {
270: Term term = termEnum.term();
271: if (term == null || term.field() != field)
272: break;
273: int termval = parser.parseInt(term.text());
274: termDocs.seek(termEnum);
275: while (termDocs.next()) {
276: retArray[termDocs.doc()] = termval;
277: }
278: } while (termEnum.next());
279: } finally {
280: termDocs.close();
281: termEnum.close();
282: }
283: return retArray;
284: }
285: };
286:
287: // inherit javadocs
288: public float[] getFloats(IndexReader reader, String field)
289: throws IOException {
290: return getFloats(reader, field, FLOAT_PARSER);
291: }
292:
293: // inherit javadocs
294: public float[] getFloats(IndexReader reader, String field,
295: FloatParser parser) throws IOException {
296: return (float[]) floatsCache.get(reader, new Entry(field,
297: parser));
298: }
299:
300: Cache floatsCache = new Cache() {
301:
302: protected Object createValue(IndexReader reader, Object entryKey)
303: throws IOException {
304: Entry entry = (Entry) entryKey;
305: String field = entry.field;
306: FloatParser parser = (FloatParser) entry.custom;
307: final float[] retArray = new float[reader.maxDoc()];
308: TermDocs termDocs = reader.termDocs();
309: TermEnum termEnum = reader.terms(new Term(field, ""));
310: try {
311: do {
312: Term term = termEnum.term();
313: if (term == null || term.field() != field)
314: break;
315: float termval = parser.parseFloat(term.text());
316: termDocs.seek(termEnum);
317: while (termDocs.next()) {
318: retArray[termDocs.doc()] = termval;
319: }
320: } while (termEnum.next());
321: } finally {
322: termDocs.close();
323: termEnum.close();
324: }
325: return retArray;
326: }
327: };
328:
329: // inherit javadocs
330: public String[] getStrings(IndexReader reader, String field)
331: throws IOException {
332: return (String[]) stringsCache.get(reader, field);
333: }
334:
335: Cache stringsCache = new Cache() {
336:
337: protected Object createValue(IndexReader reader, Object fieldKey)
338: throws IOException {
339: String field = ((String) fieldKey).intern();
340: final String[] retArray = new String[reader.maxDoc()];
341: TermDocs termDocs = reader.termDocs();
342: TermEnum termEnum = reader.terms(new Term(field, ""));
343: try {
344: do {
345: Term term = termEnum.term();
346: if (term == null || term.field() != field)
347: break;
348: String termval = term.text();
349: termDocs.seek(termEnum);
350: while (termDocs.next()) {
351: retArray[termDocs.doc()] = termval;
352: }
353: } while (termEnum.next());
354: } finally {
355: termDocs.close();
356: termEnum.close();
357: }
358: return retArray;
359: }
360: };
361:
362: // inherit javadocs
363: public StringIndex getStringIndex(IndexReader reader, String field)
364: throws IOException {
365: return (StringIndex) stringsIndexCache.get(reader, field);
366: }
367:
368: Cache stringsIndexCache = new Cache() {
369:
370: protected Object createValue(IndexReader reader, Object fieldKey)
371: throws IOException {
372: String field = ((String) fieldKey).intern();
373: final int[] retArray = new int[reader.maxDoc()];
374: String[] mterms = new String[reader.maxDoc() + 1];
375: TermDocs termDocs = reader.termDocs();
376: TermEnum termEnum = reader.terms(new Term(field, ""));
377: int t = 0; // current term number
378:
379: // an entry for documents that have no terms in this field
380: // should a document with no terms be at top or bottom?
381: // this puts them at the top - if it is changed, FieldDocSortedHitQueue
382: // needs to change as well.
383: mterms[t++] = null;
384:
385: try {
386: do {
387: Term term = termEnum.term();
388: if (term == null || term.field() != field)
389: break;
390:
391: // store term text
392: // we expect that there is at most one term per document
393: if (t >= mterms.length)
394: throw new RuntimeException(
395: "there are more terms than "
396: + "documents in field \""
397: + field
398: + "\", but it's impossible to sort on "
399: + "tokenized fields");
400: mterms[t] = term.text();
401:
402: termDocs.seek(termEnum);
403: while (termDocs.next()) {
404: retArray[termDocs.doc()] = t;
405: }
406:
407: t++;
408: } while (termEnum.next());
409: } finally {
410: termDocs.close();
411: termEnum.close();
412: }
413:
414: if (t == 0) {
415: // if there are no terms, make the term array
416: // have a single null entry
417: mterms = new String[1];
418: } else if (t < mterms.length) {
419: // if there are less terms than documents,
420: // trim off the dead array space
421: String[] terms = new String[t];
422: System.arraycopy(mterms, 0, terms, 0, t);
423: mterms = terms;
424: }
425:
426: StringIndex value = new StringIndex(retArray, mterms);
427: return value;
428: }
429: };
430:
431: /** The pattern used to detect integer values in a field */
432: /** removed for java 1.3 compatibility
433: protected static final Pattern pIntegers = Pattern.compile ("[0-9\\-]+");
434: **/
435:
436: /** The pattern used to detect float values in a field */
437: /**
438: * removed for java 1.3 compatibility
439: * protected static final Object pFloats = Pattern.compile ("[0-9+\\-\\.eEfFdD]+");
440: */
441:
442: // inherit javadocs
443: public Object getAuto(IndexReader reader, String field)
444: throws IOException {
445: return autoCache.get(reader, field);
446: }
447:
448: Cache autoCache = new Cache() {
449:
450: protected Object createValue(IndexReader reader, Object fieldKey)
451: throws IOException {
452: String field = ((String) fieldKey).intern();
453: TermEnum enumerator = reader.terms(new Term(field, ""));
454: try {
455: Term term = enumerator.term();
456: if (term == null) {
457: throw new RuntimeException("no terms in field "
458: + field + " - cannot determine sort type");
459: }
460: Object ret = null;
461: if (term.field() == field) {
462: String termtext = term.text().trim();
463:
464: /**
465: * Java 1.4 level code:
466:
467: if (pIntegers.matcher(termtext).matches())
468: return IntegerSortedHitQueue.comparator (reader, enumerator, field);
469:
470: else if (pFloats.matcher(termtext).matches())
471: return FloatSortedHitQueue.comparator (reader, enumerator, field);
472: */
473:
474: // Java 1.3 level code:
475: try {
476: Integer.parseInt(termtext);
477: ret = getInts(reader, field);
478: } catch (NumberFormatException nfe1) {
479: try {
480: Float.parseFloat(termtext);
481: ret = getFloats(reader, field);
482: } catch (NumberFormatException nfe3) {
483: ret = getStringIndex(reader, field);
484: }
485: }
486: } else {
487: throw new RuntimeException("field \"" + field
488: + "\" does not appear to be indexed");
489: }
490: return ret;
491: } finally {
492: enumerator.close();
493: }
494: }
495: };
496:
497: // inherit javadocs
498: public Comparable[] getCustom(IndexReader reader, String field,
499: SortComparator comparator) throws IOException {
500: return (Comparable[]) customCache.get(reader, new Entry(field,
501: comparator));
502: }
503:
504: Cache customCache = new Cache() {
505:
506: protected Object createValue(IndexReader reader, Object entryKey)
507: throws IOException {
508: Entry entry = (Entry) entryKey;
509: String field = entry.field;
510: SortComparator comparator = (SortComparator) entry.custom;
511: final Comparable[] retArray = new Comparable[reader
512: .maxDoc()];
513: TermDocs termDocs = reader.termDocs();
514: TermEnum termEnum = reader.terms(new Term(field, ""));
515: try {
516: do {
517: Term term = termEnum.term();
518: if (term == null || term.field() != field)
519: break;
520: Comparable termval = comparator.getComparable(term
521: .text());
522: termDocs.seek(termEnum);
523: while (termDocs.next()) {
524: retArray[termDocs.doc()] = termval;
525: }
526: } while (termEnum.next());
527: } finally {
528: termDocs.close();
529: termEnum.close();
530: }
531: return retArray;
532: }
533: };
534:
535: }
|