package org.apache.lucene.search; /** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.WeakHashMap; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.Term; import org.apache.lucene.index.TermDocs; import org.apache.lucene.index.TermEnum; import org.apache.lucene.util.StringHelper; import org.apache.lucene.util.FieldCacheSanityChecker; /** * Expert: The default cache implementation, storing all values in memory. * A WeakHashMap is used for storage. * *

Created: May 19, 2004 4:40:36 PM * * @since lucene 1.4 */ class FieldCacheImpl implements FieldCache { private Map,Cache> caches; FieldCacheImpl() { init(); } private synchronized void init() { caches = new HashMap,Cache>(7); caches.put(Byte.TYPE, new ByteCache(this)); caches.put(Short.TYPE, new ShortCache(this)); caches.put(Integer.TYPE, new IntCache(this)); caches.put(Float.TYPE, new FloatCache(this)); caches.put(Long.TYPE, new LongCache(this)); caches.put(Double.TYPE, new DoubleCache(this)); caches.put(String.class, new StringCache(this)); caches.put(StringIndex.class, new StringIndexCache(this)); } public void purgeAllCaches() { init(); } public CacheEntry[] getCacheEntries() { List result = new ArrayList(17); for(final Class cacheType: caches.keySet()) { Cache cache = caches.get(cacheType); for (final Object readerKey : cache.readerCache.keySet()) { // we've now materialized a hard ref // innerKeys was backed by WeakHashMap, sanity check // that it wasn't GCed before we made hard ref if (null != readerKey && cache.readerCache.containsKey(readerKey)) { Map innerCache = cache.readerCache.get(readerKey); for (final Map.Entry mapEntry : innerCache.entrySet()) { Entry entry = mapEntry.getKey(); result.add(new CacheEntryImpl(readerKey, entry.field, cacheType, entry.custom, mapEntry.getValue())); } } } } return result.toArray(new CacheEntry[result.size()]); } private static final class CacheEntryImpl extends CacheEntry { private final Object readerKey; private final String fieldName; private final Class cacheType; private final Object custom; private final Object value; CacheEntryImpl(Object readerKey, String fieldName, Class cacheType, Object custom, Object value) { this.readerKey = readerKey; this.fieldName = fieldName; this.cacheType = cacheType; this.custom = custom; this.value = value; // :HACK: for testing. // if (null != locale || SortField.CUSTOM != sortFieldType) { // throw new RuntimeException("Locale/sortFieldType: " + this); // } } @Override public Object getReaderKey() { return readerKey; } @Override public String getFieldName() { return fieldName; } @Override public Class getCacheType() { return cacheType; } @Override public Object getCustom() { return custom; } @Override public Object getValue() { return value; } } /** * Hack: When thrown from a Parser (NUMERIC_UTILS_* ones), this stops * processing terms and returns the current FieldCache * array. */ static final class StopFillCacheException extends RuntimeException { } /** Expert: Internal cache. */ abstract static class Cache { Cache() { this.wrapper = null; } Cache(FieldCache wrapper) { this.wrapper = wrapper; } final FieldCache wrapper; final Map> readerCache = new WeakHashMap>(); protected abstract Object createValue(IndexReader reader, Entry key) throws IOException; public Object get(IndexReader reader, Entry key) throws IOException { Map innerCache; Object value; final Object readerKey = reader.getFieldCacheKey(); synchronized (readerCache) { innerCache = readerCache.get(readerKey); if (innerCache == null) { innerCache = new HashMap(); readerCache.put(readerKey, innerCache); value = null; } else { value = innerCache.get(key); } if (value == null) { value = new CreationPlaceholder(); innerCache.put(key, value); } } if (value instanceof CreationPlaceholder) { synchronized (value) { CreationPlaceholder progress = (CreationPlaceholder) value; if (progress.value == null) { progress.value = createValue(reader, key); synchronized (readerCache) { innerCache.put(key, progress.value); } // Only check if key.custom (the parser) is // non-null; else, we check twice for a single // call to FieldCache.getXXX if (key.custom != null && wrapper != null) { final PrintStream infoStream = wrapper.getInfoStream(); if (infoStream != null) { printNewInsanity(infoStream, progress.value); } } } return progress.value; } } return value; } private void printNewInsanity(PrintStream infoStream, Object value) { final FieldCacheSanityChecker.Insanity[] insanities = FieldCacheSanityChecker.checkSanity(wrapper); for(int i=0;i= mterms.length) throw new RuntimeException ("there are more terms than " + "documents in field \"" + field + "\", but it's impossible to sort on " + "tokenized fields"); mterms[t] = term.text(); termDocs.seek (termEnum); while (termDocs.next()) { retArray[termDocs.doc()] = t; } t++; } while (termEnum.next()); } finally { termDocs.close(); termEnum.close(); } if (t == 0) { // if there are no terms, make the term array // have a single null entry mterms = new String[1]; } else if (t < mterms.length) { // if there are less terms than documents, // trim off the dead array space String[] terms = new String[t]; System.arraycopy (mterms, 0, terms, 0, t); mterms = terms; } StringIndex value = new StringIndex (retArray, mterms); return value; } }; private volatile PrintStream infoStream; public void setInfoStream(PrintStream stream) { infoStream = stream; } public PrintStream getInfoStream() { return infoStream; } }