package com.manning.hsia.dvdstore.action.filter;

import java.io.IOException;
import java.util.BitSet;
import java.util.Map;

import org.apache.commons.collections.map.ReferenceMap;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermDocs;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.Filter;
import org.apache.lucene.util.DocIdBitSet;
import org.hibernate.search.engine.DocumentBuilder;

import com.manning.hsia.dvdstore.action.StockAction;
import com.manning.hsia.dvdstore.action.StockActionImpl;
import com.manning.hsia.dvdstore.model.Item;

/**
 * Example 8.12
 */
public class StockFilter extends Filter {
	private volatile long lastUpdateTime;  //update timestamp

	@SuppressWarnings("unchecked") 
	private final Map<IndexReader, DocIdSet> cache = 
		new ReferenceMap(ReferenceMap.SOFT, ReferenceMap.HARD);  //keep cache in a SoftHashMap

	@Override
	public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
		StockAction action = getStockAction();    //retrieve the service
		long lastUpdateTime = action.geLastUpdateTime();
		if ( lastUpdateTime != this.lastUpdateTime ) {
			synchronized (cache) {  //clear outdated cache  
				cache.clear();
			}
		}
		synchronized (cache) {
			DocIdSet cached = cache.get(reader);  //check if in cache already
			if (cached != null) return cached;
		}
		//not in cache, build info
		final BitSet bitSet = getAllPositiveBitSet( reader.maxDoc() ); //by default, all documents pass
		
		Term clazzTerm = new Term(DocumentBuilder.CLASS_FIELDNAME, Item.class.getName() );
		if ( reader.docFreq( clazzTerm ) == 0) {  //no need to filter
			//index does not contain Item objects
			//no-op
		}
		else {
			//for each item out of stock, find the corresponding document id by item id
			//and switch off the corresponding bit
			for ( String ean : action.getEanOfItemsOutOfStock() ) {  //invoke external service
				Term term = new Term( "ean", ean );
				TermDocs termDocs = reader.termDocs(term);  //find document by ean
				while ( termDocs.next() ) {
					bitSet.clear(termDocs.doc());
				}
			}
		}
		DocIdSet docIdSet = new DocIdBitSet(bitSet); //build DocIdSet from BitSet
		
		synchronized (cache) {
			cache.put(reader, docIdSet);  //put results in the cache
		}
		this.lastUpdateTime = lastUpdateTime;  //update timestamp
		return docIdSet;
	}

	private BitSet getAllPositiveBitSet(int maxDoc) {
		final BitSet bitSet = new BitSet(maxDoc);
		bitSet.set(0, maxDoc-1);        //new BitSet with all bits on
		return bitSet;
	}

	private StockAction getStockAction() {
		return new StockActionImpl();
	}

}

@@ -92,24 +92,54 @@
 til.BitSet;%0A
+import java.util.Collections;%0A
 import java.
@@ -861,16 +861,21 @@
 ocIdSet%3E
+%0A%09   
  cache =
@@ -875,19 +875,50 @@
 cache = 
-%0A%09%09
+Collections.synchronizedMap(%0A%09%09   
 new Refe
@@ -963,16 +963,18 @@
 ap.HARD)
+ )
 ;  //kee
@@ -1254,31 +1254,22 @@
 %0A%09%09%09
-synchronized (cache) %7B 
+cache.clear();
  //c
@@ -1296,60 +1296,10 @@
  %0A%09%09
-%09%09cache.clear();%0A%09%09%09%7D%0A%09%09%7D%0A%09%09synchronized (cache) %7B%0A%09
+%7D%0A
 %09%09Do
@@ -1324,22 +1324,24 @@
 che.get(
+ 
 reader
+ 
 );  //ch
@@ -1366,21 +1366,21 @@
 ready%0A%09%09
-%09
 if (
+ 
 cached !
@@ -1385,16 +1385,17 @@
  != null
+ 
 ) return
@@ -1403,20 +1403,16 @@
 cached;%0A
-%09%09%7D%0A
 %09%09//not 
@@ -1561,16 +1561,17 @@
 ew Term(
+ 
 Document
@@ -2035,20 +2035,22 @@
 ermDocs(
+ 
 term
+ 
 );  //fi
@@ -2118,16 +2118,17 @@
 t.clear(
+ 
 termDocs
@@ -2133,16 +2133,17 @@
 cs.doc()
+ 
 );%0A%09%09%09%09%7D
@@ -2190,22 +2190,24 @@
 dBitSet(
+ 
 bitSet
+ 
 ); //bui
@@ -2236,37 +2236,8 @@
 t%0A%09%09
-%0A%09%09synchronized (cache) %7B%0A%09%09%09
 cach
@@ -2242,16 +2242,17 @@
 che.put(
+ 
 reader, 
@@ -2259,16 +2259,17 @@
 docIdSet
+ 
 );  //pu
@@ -2291,20 +2291,16 @@
 e cache%0A
-%09%09%7D%0A
 %09%09this.l
@@ -2460,22 +2460,24 @@
  BitSet(
+ 
 maxDoc
+ 
 );%0A%09%09bit
@@ -2484,16 +2484,17 @@
 Set.set(
+ 
 0, maxDo
@@ -2496,16 +2496,17 @@
 maxDoc-1
+ 
 );      

use Collections.synchronizedMap instead of synchronization