--- /dev/null
+package org.apache.lucene.search;
+/**
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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 org.apache.lucene.index.IndexReader;
+
+import java.io.IOException;
+
+/**
+ * Wraps another SpanFilter's result and caches it. The purpose is to allow
+ * filters to simply filter, and then wrap with this class to add caching.
+ */
+public class CachingSpanFilter extends SpanFilter {
+ private SpanFilter filter;
+
+ /**
+ * A transient Filter cache (package private because of test)
+ */
+ private final CachingWrapperFilter.FilterCache<SpanFilterResult> cache;
+
+ /**
+ * New deletions always result in a cache miss, by default
+ * ({@link CachingWrapperFilter.DeletesMode#RECACHE}.
+ * @param filter Filter to cache results of
+ */
+ public CachingSpanFilter(SpanFilter filter) {
+ this(filter, CachingWrapperFilter.DeletesMode.RECACHE);
+ }
+
+ /**
+ * @param filter Filter to cache results of
+ * @param deletesMode See {@link CachingWrapperFilter.DeletesMode}
+ */
+ public CachingSpanFilter(SpanFilter filter, CachingWrapperFilter.DeletesMode deletesMode) {
+ this.filter = filter;
+ if (deletesMode == CachingWrapperFilter.DeletesMode.DYNAMIC) {
+ throw new IllegalArgumentException("DeletesMode.DYNAMIC is not supported");
+ }
+ this.cache = new CachingWrapperFilter.FilterCache<SpanFilterResult>(deletesMode) {
+ @Override
+ protected SpanFilterResult mergeDeletes(final IndexReader reader, final SpanFilterResult value) {
+ throw new IllegalStateException("DeletesMode.DYNAMIC is not supported");
+ }
+ };
+ }
+
+ @Override
+ public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
+ SpanFilterResult result = getCachedResult(reader);
+ return result != null ? result.getDocIdSet() : null;
+ }
+
+ // for testing
+ int hitCount, missCount;
+
+ private SpanFilterResult getCachedResult(IndexReader reader) throws IOException {
+
+ final Object coreKey = reader.getCoreCacheKey();
+ final Object delCoreKey = reader.hasDeletions() ? reader.getDeletesCacheKey() : coreKey;
+
+ SpanFilterResult result = cache.get(reader, coreKey, delCoreKey);
+ if (result != null) {
+ hitCount++;
+ return result;
+ }
+
+ missCount++;
+ result = filter.bitSpans(reader);
+
+ cache.put(coreKey, delCoreKey, result);
+ return result;
+ }
+
+
+ @Override
+ public SpanFilterResult bitSpans(IndexReader reader) throws IOException {
+ return getCachedResult(reader);
+ }
+
+ @Override
+ public String toString() {
+ return "CachingSpanFilter("+filter+")";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof CachingSpanFilter)) return false;
+ return this.filter.equals(((CachingSpanFilter)o).filter);
+ }
+
+ @Override
+ public int hashCode() {
+ return filter.hashCode() ^ 0x1117BF25;
+ }
+}