/**
* Bobo Browse Engine - High performance faceted/parametric search implementation
* that handles various types of semi-structured data. Written in Java.
*
* Copyright (C) 2005-2006 John Wang
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* To contact the project administrators for the bobo-browse project,
* please go to https://sourceforge.net/projects/bobo-browse/, or
* send mail to owner@browseengine.com.
*/
package com.browseengine.bobo.api;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.DocumentStoredFieldVisitor;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.index.AtomicReader;
import org.apache.lucene.index.FilterAtomicReader;
import org.apache.lucene.index.StoredFieldVisitor;
import com.browseengine.bobo.facets.FacetHandler;
import com.browseengine.bobo.facets.RuntimeFacetHandler;
import com.browseengine.bobo.facets.RuntimeFacetHandlerFactory;
public class BoboSegmentReader extends FilterAtomicReader {
private static Logger logger = Logger.getLogger(BoboSegmentReader.class);
protected Map<String, FacetHandler<?>> _facetHandlerMap;
protected Collection<FacetHandler<?>> _facetHandlers;
protected Collection<RuntimeFacetHandlerFactory<?, ?>> _runtimeFacetHandlerFactories;
protected Map<String, RuntimeFacetHandlerFactory<?, ?>> _runtimeFacetHandlerFactoryMap;
protected WorkArea _workArea;
private final Map<String, Object> _facetDataMap = new HashMap<String, Object>();
private final ThreadLocal<Map<String, Object>> _runtimeFacetDataMap = new ThreadLocal<Map<String, Object>>() {
@Override
protected Map<String, Object> initialValue() {
return new HashMap<String, Object>();
}
};
private final ThreadLocal<Map<String, RuntimeFacetHandler<?>>> _runtimeFacetHandlerMap = new ThreadLocal<Map<String, RuntimeFacetHandler<?>>>() {
@Override
protected Map<String, RuntimeFacetHandler<?>> initialValue() {
return new HashMap<String, RuntimeFacetHandler<?>>();
}
};
public static BoboSegmentReader getInstance(AtomicReader reader,
Collection<FacetHandler<?>> facetHandlers,
Collection<RuntimeFacetHandlerFactory<?, ?>> facetHandlerFactories) throws IOException {
return getInstance(reader, facetHandlers, facetHandlerFactories, new WorkArea());
}
private static BoboSegmentReader getInstance(AtomicReader reader,
Collection<FacetHandler<?>> facetHandlers,
Collection<RuntimeFacetHandlerFactory<?, ?>> facetHandlerFactories, WorkArea workArea)
throws IOException {
BoboSegmentReader boboReader = new BoboSegmentReader(reader, facetHandlers,
facetHandlerFactories, workArea);
boboReader.facetInit();
return boboReader;
}
public Object getFacetData(String name) {
return _facetDataMap.get(name);
}
public Object putFacetData(String name, Object data) {
return _facetDataMap.put(name, data);
}
public Object getRuntimeFacetData(String name) {
Map<String, Object> map = _runtimeFacetDataMap.get();
if (map == null) return null;
return map.get(name);
}
public Object putRuntimeFacetData(String name, Object data) {
Map<String, Object> map = _runtimeFacetDataMap.get();
if (map == null) {
map = new HashMap<String, Object>();
_runtimeFacetDataMap.set(map);
}
return map.put(name, data);
}
public void clearRuntimeFacetData() {
_runtimeFacetDataMap.set(null);
}
public RuntimeFacetHandler<?> getRuntimeFacetHandler(String name) {
Map<String, RuntimeFacetHandler<?>> map = _runtimeFacetHandlerMap.get();
if (map == null) return null;
return map.get(name);
}
public void putRuntimeFacetHandler(String name, RuntimeFacetHandler<?> data) {
Map<String, RuntimeFacetHandler<?>> map = _runtimeFacetHandlerMap.get();
if (map == null) {
map = new HashMap<String, RuntimeFacetHandler<?>>();
_runtimeFacetHandlerMap.set(map);
}
map.put(name, data);
}
public void clearRuntimeFacetHandler() {
_runtimeFacetHandlerMap.set(null);
}
@Override
protected void doClose() throws IOException {
// do nothing
}
private void loadFacetHandler(String name, Set<String> loaded, Set<String> visited,
WorkArea workArea) throws IOException {
FacetHandler<?> facetHandler = _facetHandlerMap.get(name);
if (facetHandler != null && !loaded.contains(name)) {
visited.add(name);
Set<String> dependsOn = facetHandler.getDependsOn();
if (dependsOn.size() > 0) {
Iterator<String> iter = dependsOn.iterator();
while (iter.hasNext()) {
String f = iter.next();
if (name.equals(f)) continue;
if (!loaded.contains(f)) {
if (visited.contains(f)) {
throw new IOException("Facet handler dependency cycle detected, facet handler: "
+ name + " not loaded");
}
loadFacetHandler(f, loaded, visited, workArea);
}
if (!loaded.contains(f)) {
throw new IOException("unable to load facet handler: " + f);
}
facetHandler.putDependedFacetHandler(_facetHandlerMap.get(f));
}
}
long start = System.currentTimeMillis();
facetHandler.loadFacetData(this, workArea);
long end = System.currentTimeMillis();
if (logger.isDebugEnabled()) {
StringBuffer buf = new StringBuffer();
buf.append("facetHandler loaded: ").append(name).append(", took: ").append(end - start)
.append(" ms");
logger.debug(buf.toString());
}
loaded.add(name);
}
}
private void loadFacetHandlers(WorkArea workArea) throws IOException {
Set<String> loaded = new HashSet<String>();
Set<String> visited = new HashSet<String>();
for (String name : _facetHandlerMap.keySet()) {
loadFacetHandler(name, loaded, visited, workArea);
}
}
protected void initialize(Collection<FacetHandler<?>> facetHandlers) throws IOException {
_facetHandlers = facetHandlers;
_facetHandlerMap = new HashMap<String, FacetHandler<?>>();
for (FacetHandler<?> facetHandler : facetHandlers) {
_facetHandlerMap.put(facetHandler.getName(), facetHandler);
}
}
/**
* @param reader
* @param facetHandlers
* @param facetHandlerFactories
* @param workArea
* the inner reader. false => we use the given reader as the inner reader.
* @throws IOException
*/
protected BoboSegmentReader(AtomicReader reader, Collection<FacetHandler<?>> facetHandlers,
Collection<RuntimeFacetHandlerFactory<?, ?>> facetHandlerFactories, WorkArea workArea)
throws IOException {
super(reader);
_runtimeFacetHandlerFactories = facetHandlerFactories;
_runtimeFacetHandlerFactoryMap = new HashMap<String, RuntimeFacetHandlerFactory<?, ?>>();
if (_runtimeFacetHandlerFactories != null) {
for (RuntimeFacetHandlerFactory<?, ?> factory : _runtimeFacetHandlerFactories) {
_runtimeFacetHandlerFactoryMap.put(factory.getName(), factory);
}
}
_facetHandlers = facetHandlers;
_workArea = workArea;
}
protected void facetInit() throws IOException {
initialize(_facetHandlers);
loadFacetHandlers(_workArea);
}
/**
* Gets all the facet field names
*
* @return Set of facet field names
*/
public Set<String> getFacetNames() {
return _facetHandlerMap.keySet();
}
/**
* Gets a facet handler
*
* @param fieldname
* name
* @return facet handler
*/
public FacetHandler<?> getFacetHandler(String fieldname) {
FacetHandler<?> f = _facetHandlerMap.get(fieldname);
if (f == null) f = getRuntimeFacetHandler(fieldname);
return f;
}
/**
* Gets the facet handler map
*
* @return facet handler map
*/
public Map<String, FacetHandler<?>> getFacetHandlerMap() {
return _facetHandlerMap;
}
/**
* @return the map of RuntimeFacetHandlerFactories
*/
public Map<String, RuntimeFacetHandlerFactory<?, ?>> getRuntimeFacetHandlerFactoryMap() {
return _runtimeFacetHandlerFactoryMap;
}
/**
* @return the map of RuntimeFacetHandlers
*/
public Map<String, RuntimeFacetHandler<?>> getRuntimeFacetHandlerMap() {
return _runtimeFacetHandlerMap.get();
}
/**
* @return the map of RuntimeFacetData
*/
public Map<String, Object> getRuntimeFacetDataMap() {
return _runtimeFacetDataMap.get();
}
public void setRuntimeFacetHandlerMap(Map<String, RuntimeFacetHandler<?>> map) {
_runtimeFacetHandlerMap.set(map);
}
public void setRuntimeFacetDataMap(Map<String, Object> map) {
_runtimeFacetDataMap.set(map);
}
@Override
public void document(int docID, StoredFieldVisitor visitor) throws IOException {
super.document(docID, visitor);
if (!(visitor instanceof DocumentStoredFieldVisitor)) {
return;
}
Document doc = ((DocumentStoredFieldVisitor) visitor).getDocument();
Collection<FacetHandler<?>> facetHandlers = _facetHandlerMap.values();
for (FacetHandler<?> facetHandler : facetHandlers) {
String[] vals = facetHandler.getFieldValues(this, docID);
if (vals != null) {
String[] values = doc.getValues(facetHandler.getName());
Set<String> storedVals = new HashSet<String>(Arrays.asList(values));
for (String val : vals) {
storedVals.add(val);
}
doc.removeField(facetHandler.getName());
for (String val : storedVals) {
doc.add(new StringField(facetHandler.getName(), val, Field.Store.NO));
}
}
}
}
public String[] getStoredFieldValue(int docid, final String fieldname) throws IOException {
DocumentStoredFieldVisitor visitor = new DocumentStoredFieldVisitor(fieldname);
super.document(docid, visitor);
Document doc = visitor.getDocument();
return doc.getValues(fieldname);
}
/**
* Work area for loading
*/
public static class WorkArea {
HashMap<Class<?>, Object> map = new HashMap<Class<?>, Object>();
@SuppressWarnings("unchecked")
public <T> T get(Class<T> cls) {
T obj = (T) map.get(cls);
return obj;
}
public void put(Object obj) {
map.put(obj.getClass(), obj);
}
public void clear() {
map.clear();
}
@Override
public String toString() {
return map.toString();
}
}
private BoboSegmentReader(AtomicReader in) {
super(in);
}
public BoboSegmentReader copy(AtomicReader in) {
BoboSegmentReader copy = new BoboSegmentReader(in);
copy._facetHandlerMap = this._facetHandlerMap;
copy._facetHandlers = this._facetHandlers;
copy._runtimeFacetHandlerFactories = this._runtimeFacetHandlerFactories;
copy._runtimeFacetHandlerFactoryMap = this._runtimeFacetHandlerFactoryMap;
copy._workArea = this._workArea;
copy._facetDataMap.putAll(this._facetDataMap);
return copy;
}
public AtomicReader getInnerReader() {
return in;
}
}