/* * Created on Oct 12, 2006 Copyright (C) 2001-6, Anthony Harrison anh23@pitt.edu * (jactr.org) 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., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ package org.jactr.core.module.declarative.search.local; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Map; import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jactr.core.buffer.IActivationBuffer; import org.jactr.core.chunk.IChunk; import org.jactr.core.chunktype.IChunkType; import org.jactr.core.module.declarative.IDeclarativeModule; import org.jactr.core.module.declarative.search.ISearchSystem; import org.jactr.core.module.declarative.search.map.BooleanTypeValueMap; import org.jactr.core.module.declarative.search.map.ITypeValueMap; import org.jactr.core.module.declarative.search.map.NullTypeValueMap; import org.jactr.core.module.declarative.search.map.NumericTypeValueMap; import org.jactr.core.module.declarative.search.map.StringTypeValueMap; import org.jactr.core.production.IProduction; import org.jactr.core.production.request.ChunkTypeRequest; import org.jactr.core.slot.IConditionalSlot; import org.jactr.core.slot.ISlot; public class DefaultSearchSystem implements ISearchSystem { /** * logger definition */ static public final Log LOGGER = LogFactory .getLog(DefaultSearchSystem.class); private ReentrantReadWriteLock _lock = new ReentrantReadWriteLock(); // private ACTREventDispatcher<IDeclarativeModule,ISearchListener> // _eventDispatcher; private Map<String, Collection<ITypeValueMap<?, IChunk>>> _slotMap; private IDeclarativeModule _module; public DefaultSearchSystem(IDeclarativeModule module) { _slotMap = new TreeMap<String, Collection<ITypeValueMap<?, IChunk>>>(); // _eventDispatcher = new ACTREventDispatcher<IDeclarativeModule, // ISearchListener>(); _module = module; } public void clear() { try { _lock.writeLock().lock(); for (Collection<ITypeValueMap<?, IChunk>> collection : _slotMap.values()) { if (collection != null) { for (ITypeValueMap<?, IChunk> tvm : collection) tvm.clear(); collection.clear(); } } _slotMap.clear(); } finally { _lock.writeLock().unlock(); } } protected Collection<ITypeValueMap<?, IChunk>> instantiateTypeValueMapCollection() { return new ArrayList<ITypeValueMap<?, IChunk>>(); } protected ITypeValueMap<?, IChunk> instantiateTypeValueMap(Object value) { if (value == null) return new NullTypeValueMap<IChunk>(); if (value instanceof String) return new StringTypeValueMap<IChunk>(); if (value instanceof Number) return new NumericTypeValueMap<IChunk>(); if (value instanceof Boolean) return new BooleanTypeValueMap<IChunk>(); if (value instanceof IChunk) return new ChunkTypeValueMap<IChunk>(); if (value instanceof IChunkType) return new ChunkTypeTypeValueMap<IChunk>(); if (value instanceof IProduction) return new ProductionTypeValueMap<IChunk>(); if (value instanceof IActivationBuffer) return new ActivationBufferTypeValueMap<IChunk>(); if (LOGGER.isWarnEnabled()) LOGGER .warn("Could not determine what type of value map to provide given " + value + " of " + value.getClass()); return null; } protected ReentrantReadWriteLock getLock() { return _lock; } /** * this implementation fails fast * * @see org.jactr.core.module.declarative.search.ISearchSystem#findExact(ChunkTypeRequest, * java.util.Comparator) */ public Collection<IChunk> findExact(ChunkTypeRequest pattern, Comparator<IChunk> sortRule) { /* * second pass, ditch all those that don't match our chunktype */ HashSet<IChunk> candidates = new HashSet<IChunk>(); IChunkType chunkType = pattern.getChunkType(); if (chunkType != null) candidates.addAll(chunkType.getSymbolicChunkType().getChunks()); /* * first things first, find all the candidates based on the content of the * pattern */ boolean first = chunkType == null; for (IConditionalSlot slot : pattern.getConditionalSlots()) { if (first) { candidates.addAll(find(slot)); first = false; } else candidates.retainAll(find(slot)); if (candidates.size() == 0) break; } if (LOGGER.isDebugEnabled()) LOGGER.debug("First pass candidates for " + pattern + " chunks: " + candidates); if (sortRule != null) { /* * finally, we sort them */ TreeSet<IChunk> sortedResults = new TreeSet<IChunk>(sortRule); sortedResults.addAll(candidates); return sortedResults; } return candidates; } public Collection<IChunk> findFuzzy(ChunkTypeRequest pattern, Comparator<IChunk> sortRule) { /* * second pass, ditch all those that don't match our chunktype */ HashSet<IChunk> candidates = new HashSet<IChunk>(); IChunkType chunkType = pattern.getChunkType(); /* * first things first, find all the candidates based on the content of the * pattern. this is the same as findExact, but without the retainAll */ for (IConditionalSlot slot : pattern.getConditionalSlots()) { Collection<IChunk> containers = find(slot); if (chunkType == null) candidates.addAll(containers); else for (IChunk candidate : containers) if (candidate.isA(chunkType)) candidates.add(candidate); } if (LOGGER.isDebugEnabled()) LOGGER.debug("First pass candidates for " + pattern + " chunks: " + candidates); if (sortRule != null) { /* * finally, we sort them */ TreeSet<IChunk> sortedResults = new TreeSet<IChunk>(sortRule); sortedResults.addAll(candidates); return sortedResults; } return candidates; } protected Collection<IChunk> find(IConditionalSlot conditionalSlot) { HashSet<IChunk> rtn = new HashSet<IChunk>(); switch (conditionalSlot.getCondition()) { case IConditionalSlot.EQUALS: rtn.addAll(equals(conditionalSlot)); break; case IConditionalSlot.GREATER_THAN: rtn.addAll(greaterThan(conditionalSlot)); break; case IConditionalSlot.GREATER_THAN_EQUALS: rtn.addAll(greaterThan(conditionalSlot)); rtn.addAll(equals(conditionalSlot)); break; case IConditionalSlot.LESS_THAN: rtn.addAll(lessThan(conditionalSlot)); break; case IConditionalSlot.LESS_THAN_EQUALS: rtn.addAll(lessThan(conditionalSlot)); rtn.addAll(equals(conditionalSlot)); break; case IConditionalSlot.NOT_EQUALS: rtn.addAll(not(conditionalSlot)); break; case IConditionalSlot.WITHIN: default: if (LOGGER.isWarnEnabled()) LOGGER.warn("No clue what to do with this search condition " + conditionalSlot); } if (LOGGER.isDebugEnabled()) LOGGER.debug("Search for " + conditionalSlot + " yielded " + rtn.size() + " results"); return rtn; } protected Collection<IChunk> equals(ISlot slot) { ITypeValueMap<?, IChunk> typeValueMap = getSlotNameTypeValueMap(slot .getName(), slot.getValue(), false); if (typeValueMap != null) return typeValueMap.get(slot.getValue()); return Collections.EMPTY_LIST; } protected Collection<IChunk> lessThan(ISlot slot) { ITypeValueMap<?, IChunk> typeValueMap = getSlotNameTypeValueMap(slot .getName(), slot.getValue(), false); if (typeValueMap != null) try { return typeValueMap.lessThan(slot.getValue()); } catch (UnsupportedOperationException uoe) { if (LOGGER.isDebugEnabled()) LOGGER.debug(slot.getValue() + " does not have natural ordering"); } return Collections.EMPTY_LIST; } protected Collection<IChunk> greaterThan(ISlot slot) { ITypeValueMap<?, IChunk> typeValueMap = getSlotNameTypeValueMap(slot .getName(), slot.getValue(), false); if (typeValueMap != null) try { return typeValueMap.greaterThan(slot.getValue()); } catch (UnsupportedOperationException uoe) { if (LOGGER.isDebugEnabled()) LOGGER.debug(slot.getValue() + " does not have natural ordering"); } return Collections.EMPTY_LIST; } protected Collection<IChunk> not(ISlot slot) { /* * return values are not only what the approriate typevalue map say they * are, but also all the other type value maps.all() we'll start with the * obvious part first */ HashSet<IChunk> rtn = new HashSet<IChunk>(); ITypeValueMap<?, IChunk> typeValueMap = getSlotNameTypeValueMap(slot .getName(), slot.getValue(), false); if (typeValueMap != null) rtn.addAll(typeValueMap.not(slot.getValue())); // now let's snag all the rest try { getLock().readLock().lock(); for (ITypeValueMap<?, IChunk> tvm : _slotMap.get(slot.getName() .toLowerCase())) { if (tvm != typeValueMap && tvm != null) rtn.addAll(tvm.all()); } return rtn; } finally { getLock().readLock().unlock(); } } public void index(IChunk chunk) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Indexing " + chunk); if (!chunk.isEncoded()) throw new RuntimeException(chunk + " has not been encoded, will not index"); for (ISlot slot : chunk.getSymbolicChunk().getSlots()) addIndexing(chunk, slot.getName(), slot.getValue()); } public void unindex(IChunk chunk) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Unindexing " + chunk); for (ISlot slot : chunk.getSymbolicChunk().getSlots()) removeIndexing(chunk, slot.getName(), slot.getValue()); } public void update(IChunk chunk, String slotName, Object oldValue, Object newValue) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Updating indexing for " + chunk + "." + slotName); if (oldValue == null) oldValue = NullTypeValueMap.NULL; if (newValue == null) newValue = NullTypeValueMap.NULL; removeIndexing(chunk, slotName, oldValue); addIndexing(chunk, slotName, newValue); } protected void removeIndexing(IChunk chunk, String slotName, Object value) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Unindexing " + chunk + "." + slotName + "=" + value); ITypeValueMap<?, IChunk> typeValueMap = getSlotNameTypeValueMap(slotName, value, false); if (typeValueMap != null) typeValueMap.remove(value, chunk); } protected void addIndexing(IChunk chunk, String slotName, Object value) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Indexing " + chunk + "." + slotName + "=" + value); ITypeValueMap<?, IChunk> typeValueMap = getSlotNameTypeValueMap(slotName, value, true); // this is possible if we can't index the data type if (typeValueMap != null) typeValueMap.add(value, chunk); } /** * return the ITypeValueMap for the slot name. if create is true, the write * lock will be acquired and if no map exists, it will be created based on the * value passed * * @param slotName * @param create * @return */ protected ITypeValueMap<?, IChunk> getSlotNameTypeValueMap(String slotName, Object value, boolean create) { slotName = slotName.toLowerCase(); ReentrantReadWriteLock lock = getLock(); Collection<ITypeValueMap<?, IChunk>> typeValueMaps = null; ITypeValueMap<?, IChunk> typeValueMap = null; if (create) try { lock.writeLock().lock(); typeValueMaps = _slotMap.get(slotName); if (typeValueMaps == null) { if (LOGGER.isDebugEnabled()) LOGGER.debug("slot " + slotName + " has no type value map collection, creating"); // create typeValueMaps = instantiateTypeValueMapCollection(); _slotMap.put(slotName, typeValueMaps); } for (ITypeValueMap<?, IChunk> tvm : typeValueMaps) if (tvm.isValueRelevant(value)) { typeValueMap = tvm; continue; } /* * no typevaluemap was found, create */ if (typeValueMap == null) { typeValueMap = instantiateTypeValueMap(value); if (typeValueMap != null) { if (LOGGER.isDebugEnabled()) LOGGER.debug("No type value map exists for current value " + value + ", created " + typeValueMap); typeValueMaps.add(typeValueMap); } } } finally { lock.writeLock().unlock(); } else try { lock.readLock().lock(); typeValueMaps = _slotMap.get(slotName); if (typeValueMaps != null) { for (ITypeValueMap<?, IChunk> tvm : typeValueMaps) if (tvm.isValueRelevant(value)) { typeValueMap = tvm; continue; } else if (LOGGER.isDebugEnabled()) LOGGER.debug(tvm + " is irrelevant to " + value); if (typeValueMap == null) if (LOGGER.isDebugEnabled()) LOGGER.debug("No type value map was found for " + slotName + ", returning"); } else if (LOGGER.isDebugEnabled()) LOGGER.debug("slot " + slotName + " has no type value map collection, returning"); } finally { lock.readLock().unlock(); } if (LOGGER.isDebugEnabled()) LOGGER.debug("Returning " + typeValueMap + " for " + slotName + "=" + value); return typeValueMap; } // public void addListener(ISearchListener listener, Executor executor) // { // _eventDispatcher.addListener(listener, executor); // } // // // public void removeListener(ISearchListener listener) // { // _eventDispatcher.removeListener(listener); // } // // public boolean hasListeners() // { // return _eventDispatcher.hasListeners(); // } }