package org.jactr.core.module.declarative.basic; /* * default logging */ import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import javolution.util.FastList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jactr.core.buffer.BufferUtilities; import org.jactr.core.buffer.IActivationBuffer; import org.jactr.core.chunk.ChunkActivationComparator; import org.jactr.core.chunk.IChunk; import org.jactr.core.chunk.ISubsymbolicChunk; import org.jactr.core.chunk.ISymbolicChunk; import org.jactr.core.chunk.event.ChunkEvent; import org.jactr.core.chunktype.IChunkType; import org.jactr.core.chunktype.ISubsymbolicChunkType; import org.jactr.core.chunktype.ISymbolicChunkType; import org.jactr.core.event.ACTREventDispatcher; import org.jactr.core.logging.Logger; import org.jactr.core.model.IModel; import org.jactr.core.model.event.IModelListener; import org.jactr.core.model.event.ModelEvent; import org.jactr.core.model.event.ModelListenerAdaptor; import org.jactr.core.module.AbstractModule; import org.jactr.core.module.declarative.IDeclarativeModule; import org.jactr.core.module.declarative.associative.IAssociativeLinkageSystem; import org.jactr.core.module.declarative.basic.chunk.ChainedChunkConfigurator; import org.jactr.core.module.declarative.basic.chunk.IChunkConfigurator; import org.jactr.core.module.declarative.basic.chunk.IChunkFactory; import org.jactr.core.module.declarative.basic.chunk.IChunkNamer; import org.jactr.core.module.declarative.basic.chunk.ISubsymbolicChunkFactory; import org.jactr.core.module.declarative.basic.chunk.ISymbolicChunkFactory; import org.jactr.core.module.declarative.basic.chunk.NoOpChunkNamer; import org.jactr.core.module.declarative.basic.type.IChunkTypeConfigurator; import org.jactr.core.module.declarative.basic.type.IChunkTypeFactory; import org.jactr.core.module.declarative.basic.type.IChunkTypeNamer; import org.jactr.core.module.declarative.basic.type.ISubsymbolicChunkTypeFactory; import org.jactr.core.module.declarative.basic.type.ISymbolicChunkTypeFactory; import org.jactr.core.module.declarative.basic.type.NoOpChunkTypeConfigurator; import org.jactr.core.module.declarative.basic.type.NoOpChunkTypeNamer; import org.jactr.core.module.declarative.event.DeclarativeModuleEvent; import org.jactr.core.module.declarative.event.IDeclarativeModuleListener; import org.jactr.core.module.declarative.search.filter.IChunkFilter; import org.jactr.core.production.request.ChunkTypeRequest; import org.jactr.core.runtime.ACTRRuntime; import org.jactr.core.slot.ISlot; import org.jactr.core.utils.StringUtilities; /** * Abstract declarative module that provides most of the functionality required * of the {@link IDeclarativeModule} including creation, merging and disposal of * chunks and types. However, the actual adding to the containers or searching * is left to subclasses. <br/> * The factories used by this class are not set by default. * * @see http://jactr.org/node/120 * @author harrison */ public abstract class AbstractDeclarativeModule extends AbstractModule implements IDeclarativeModule { /** * Logger definition */ static private final transient Log LOGGER = LogFactory .getLog(AbstractDeclarativeModule.class); static public final String SUSPEND_DISPOSAL_KEY = DefaultDeclarativeModule.class + ".suspendDisposal"; /** * there is a grey area between the creation of a chunk and it's use in a * buffer or encoding. Most never encounter it, but it can occur in the time * between a perceptual search (i.e. visual-location) and encoding, where the * system may want to dispose of the chunk (i.e. the underlying percept has * changed too much) in order to create a new one. However, if the encoding * process has already started, it is possible that the system will try to add * the disposed chunk to a buffer.<br> * This mechanism is a recommendation only, that the declarative module can * use to temporarily suspend disposal. * * @param chunk */ public static void setDisposalSuspended(IChunk chunk, boolean suspend) { if (suspend) chunk.setMetaData(SUSPEND_DISPOSAL_KEY, Boolean.TRUE); else chunk.setMetaData(SUSPEND_DISPOSAL_KEY, null); } public static boolean isDisposalSuspended(IChunk chunk) { return Boolean.TRUE.equals(chunk.getMetaData(SUSPEND_DISPOSAL_KEY)); } private IChunk _freeChunk; private IChunk _busyChunk; private IChunk _errorChunk; private IChunk _emptyChunk; private IChunk _fullChunk; private IChunk _newChunk; private IChunk _requestedChunk; private IChunk _unrequestedChunk; private ACTREventDispatcher<IDeclarativeModule, IDeclarativeModuleListener> _eventDispatcher; /** * factory instances */ private IChunkFactory _chunkFactory; private ISymbolicChunkFactory _symbolicChunkFactory; private ISubsymbolicChunkFactory _subsymbolicChunkFactory; private IChunkConfigurator _chunkConfigurator = new ChainedChunkConfigurator(); private IChunkNamer _chunkNamer; private IChunkTypeFactory _chunkTypeFactory; private ISymbolicChunkTypeFactory _symbolicChunkTypeFactory; private ISubsymbolicChunkTypeFactory _subsymbolicChunkTypeFactory; private IChunkTypeNamer _chunkTypeNamer; private IChunkTypeConfigurator _chunkTypeConfigurator; private IAssociativeLinkageSystem _associativeLinkageSystem; /** * list that contains chunks to be disposed by * {@link #processPendingDisposals()} */ private List<IChunk> _chunksToDispose; /** * lock for _chunksToDispose */ private ReentrantLock _disposalLock = new ReentrantLock(); private IModelListener _disposalListener; /** * list for the chunks that should be encoded at the earliest convenience */ private List<IChunk> _deferredEncodings; private ReentrantLock _encodingLock = new ReentrantLock(); protected ChunkActivationComparator _activationSorter = new ChunkActivationComparator(); public AbstractDeclarativeModule(String name) { super(name); _eventDispatcher = new ACTREventDispatcher<IDeclarativeModule, IDeclarativeModuleListener>(); _chunksToDispose = FastList.newInstance(); _deferredEncodings = FastList.newInstance(); _disposalListener = new ModelListenerAdaptor() { @Override public void cycleStarted(ModelEvent event) { flush(); } @Override public void cycleStopped(ModelEvent event) { flush(); } @Override public void modelStopped(ModelEvent event) { flush(); } }; } public void flush() { processPendingEncodingAndDisposals(); } @Override public void install(IModel model) { super.install(model); // inline to the main model model.addListener(_disposalListener, null); } @Override public void uninstall(IModel model) { model.removeListener(_disposalListener); super.uninstall(model); } @SuppressWarnings("unchecked") @Override public void initialize() { } public void addListener(IDeclarativeModuleListener listener, Executor executor) { _eventDispatcher.addListener(listener, executor); } public void removeListener(IDeclarativeModuleListener listener) { _eventDispatcher.removeListener(listener); } protected void fireChunkCreated(IChunk chunk) { if (_eventDispatcher.hasListeners()) _eventDispatcher.fire(new DeclarativeModuleEvent(this, DeclarativeModuleEvent.Type.CHUNK_CREATED, chunk)); } protected void fireChunkTypeCreated(IChunkType chunkType) { if (_eventDispatcher.hasListeners()) _eventDispatcher.fire(new DeclarativeModuleEvent(this, DeclarativeModuleEvent.Type.CHUNK_TYPE_CREATED, chunkType)); } protected void fireChunkAdded(IChunk chunk) { if (_eventDispatcher.hasListeners()) _eventDispatcher.fire(new DeclarativeModuleEvent(this, DeclarativeModuleEvent.Type.CHUNK_ADDED, chunk)); } protected void fireChunkRemoved(IChunk chunk) { if (_eventDispatcher.hasListeners()) _eventDispatcher.fire(new DeclarativeModuleEvent(this, DeclarativeModuleEvent.Type.CHUNK_REMOVED, chunk)); } protected void fireChunkDisposed(IChunk chunk) { if (_eventDispatcher.hasListeners()) _eventDispatcher.fire(new DeclarativeModuleEvent(this, DeclarativeModuleEvent.Type.CHUNK_DISPOSED, chunk)); } protected void fireChunkTypeAdded(IChunkType chunkType) { if (_eventDispatcher.hasListeners()) _eventDispatcher.fire(new DeclarativeModuleEvent(this, DeclarativeModuleEvent.Type.CHUNK_TYPE_ADDED, chunkType)); } protected void fireChunksMerged(IChunk original, IChunk duplicateChunk) { if (_eventDispatcher.hasListeners()) _eventDispatcher.fire(new DeclarativeModuleEvent(this, DeclarativeModuleEvent.Type.CHUNKS_MERGED, original, duplicateChunk)); } protected void fireChunkTypeDisposed(IChunkType chunkType) { if (_eventDispatcher.hasListeners()) _eventDispatcher.fire(new DeclarativeModuleEvent(this, DeclarativeModuleEvent.Type.CHUNK_TYPE_DISPOSED, chunkType)); } protected void fireChunkTypesMerged(IChunkType original, IChunkType duplicate) { if (_eventDispatcher.hasListeners()) _eventDispatcher.fire(new DeclarativeModuleEvent(this, DeclarativeModuleEvent.Type.CHUNK_TYPES_MERGED, original, duplicate)); } protected boolean hasListeners() { return _eventDispatcher.hasListeners(); } protected void dispatch(DeclarativeModuleEvent event) { _eventDispatcher.fire(event); } public IChunk getBusyChunk() { return _busyChunk; } public IChunk getEmptyChunk() { return _emptyChunk; } public IChunk getErrorChunk() { return _errorChunk; } public IChunk getFreeChunk() { return _freeChunk; } public IChunk getFullChunk() { return _fullChunk; } public IChunk getNewChunk() { return _newChunk; } public IChunk getRequestedChunk() { return _requestedChunk; } public IChunk getUnrequestedChunk() { return _unrequestedChunk; } public void setChunkFactory(IChunkFactory chunkFactory) { _chunkFactory = chunkFactory; } public IChunkFactory getChunkFactory() { return _chunkFactory; } public void setSymbolicChunkFactory(ISymbolicChunkFactory factory) { _symbolicChunkFactory = factory; } public ISymbolicChunkFactory getSymbolicChunkFactory() { return _symbolicChunkFactory; } public void setSubsymbolicChunkFactory(ISubsymbolicChunkFactory factory) { _subsymbolicChunkFactory = factory; } public ISubsymbolicChunkFactory getSubsymbolicChunkFactory() { return _subsymbolicChunkFactory; } public void setChunkConfigurator(IChunkConfigurator configurator) { _chunkConfigurator = configurator; } /** * the default from this class is to return a ChainedChunkConfigurator. It is * preferrable to use this configurator and add yours to it, allowing more * than one to be installed and used. * * @return */ public IChunkConfigurator getChunkConfigurator() { if (_chunkConfigurator == null) _chunkConfigurator = new ChainedChunkConfigurator(); return _chunkConfigurator; } public void setChunkNamer(IChunkNamer namer) { _chunkNamer = namer; } public IChunkNamer getChunkNamer() { if (_chunkNamer == null) _chunkNamer = new NoOpChunkNamer(); return _chunkNamer; } public void setChunkTypeFactory(IChunkTypeFactory chunkTypeFactory) { _chunkTypeFactory = chunkTypeFactory; } public IChunkTypeFactory getChunkTypeFactory() { return _chunkTypeFactory; } public void setSymbolicChunkTypeFactory(ISymbolicChunkTypeFactory factory) { _symbolicChunkTypeFactory = factory; } public ISymbolicChunkTypeFactory getSymbolicChunkTypeFactory() { return _symbolicChunkTypeFactory; } public void setSubsymbolicChunkTypeFactory( ISubsymbolicChunkTypeFactory factory) { _subsymbolicChunkTypeFactory = factory; } public ISubsymbolicChunkTypeFactory getSubsymbolicChunkTypeFactory() { return _subsymbolicChunkTypeFactory; } public void setChunkTypeConfigurator(IChunkTypeConfigurator configurator) { _chunkTypeConfigurator = configurator; } public IChunkTypeConfigurator getChunkTypeConfigurator() { if (_chunkTypeConfigurator == null) _chunkTypeConfigurator = new NoOpChunkTypeConfigurator(); return _chunkTypeConfigurator; } public void setChunkTypeNamer(IChunkTypeNamer namer) { _chunkTypeNamer = namer; } public IChunkTypeNamer getChunkTypeNamer() { if (_chunkTypeNamer == null) _chunkTypeNamer = new NoOpChunkTypeNamer(); return _chunkTypeNamer; } /** * create a chunk by delegating to * {@link #createChunkInternal(IChunkType, String)} on {@link #getExecutor()} * * @param parent * @param name * @return * @see org.jactr.core.module.declarative.IDeclarativeModule#createChunk(org.jactr.core.chunktype.IChunkType, * java.lang.String) */ public CompletableFuture<IChunk> createChunk(final IChunkType parent, final String name) { if (parent == null) throw new NullPointerException("IChunkType cannot be null"); return delayedFuture(new Callable<IChunk>() { public IChunk call() throws Exception { return createChunkInternal(parent, name); } }, getExecutor()); } /** * create the chunk * * @param parent * @param name * @return */ protected IChunk createChunkInternal(IChunkType parent, String name) { IChunkFactory cFactory = getChunkFactory(); ISymbolicChunkFactory scFactory = getSymbolicChunkFactory(); ISubsymbolicChunkFactory sscFactory = getSubsymbolicChunkFactory(); IChunk rtn = cFactory.newChunk(getModel()); ISymbolicChunk sc = scFactory.newSymbolicChunk(); ISubsymbolicChunk ssc = sscFactory.newSubsymbolicChunk(); cFactory.bind(rtn, sc, ssc); scFactory.bind(sc, rtn, parent); sscFactory.bind(ssc, rtn, parent); sc.setName(name); configure(rtn); fireChunkCreated(rtn); return rtn; } /** * calls the pluggable IChunkConfigurator * * @param newChunk */ protected void configure(IChunk newChunk) { getChunkConfigurator().configure(newChunk); } /** * add chunk to DM. performs a search for any matches, then merely delegates * to {@link #addChunkInternal(IChunk, Collection)} on {@link #getExecutor()} * * @param chunk * @return * @see org.jactr.core.module.declarative.IDeclarativeModule#addChunk(org.jactr.core.chunk.IChunk) */ public CompletableFuture<IChunk> addChunk(final IChunk chunk) { if (chunk.isEncoded()) { if (LOGGER.isDebugEnabled()) LOGGER.debug(chunk + " has already been encoded, silly"); return immediateReturn(chunk); } ISymbolicChunk sc = chunk.getSymbolicChunk(); Collection<? extends ISlot> slots = sc.getSlots(); CompletableFuture<Collection<IChunk>> matches = null; /* * we don't do merge searches for slotless chunks. */ if (slots.size() == 0) matches = immediateReturn((Collection<IChunk>) new LinkedList<IChunk>()); else { final IChunkType parentType = chunk.getSymbolicChunk().getChunkType(); matches = getModel().getDeclarativeModule().findExactMatches( new ChunkTypeRequest(sc.getChunkType(), slots), _activationSorter, new IChunkFilter() { /* * for the merge test, we need to do an isAStrict test. * (non-Javadoc) * @see * org.jactr.core.module.declarative.search.filter.IChunkFilter# * accept(org.jactr.core.chunk.IChunk) */ public boolean accept(IChunk chunkToFilter) { return chunkToFilter.isAStrict(parentType); } }); } final CompletableFuture<Collection<IChunk>> fMatches = matches; return delayedFuture(new Callable<IChunk>() { public IChunk call() throws Exception { try { IChunk rtn = addChunkInternal(chunk, fMatches.get()); String name = rtn.getSymbolicChunk().getName(); if (_busyChunk == null && name.equals("busy")) _busyChunk = rtn; else if (_emptyChunk == null && name.equals("empty")) _emptyChunk = rtn; else if (_errorChunk == null && name.equals("error")) _errorChunk = rtn; else if (_freeChunk == null && name.equals("free")) _freeChunk = rtn; else if (_fullChunk == null && name.equals("full")) _fullChunk = rtn; else if (_newChunk == null && name.equals("new")) _newChunk = rtn; else if (_requestedChunk == null && name.equals("requested")) _requestedChunk = rtn; else if (_unrequestedChunk == null && name.equals("unrequested")) _unrequestedChunk = rtn; return rtn; } catch (Exception e) { LOGGER.error("Error while encoding chunk " + chunk + " ", e); throw e; } } }, getExecutor()); } /** * add the chunk to DM on the module's executor. If the executor is INLINE or * multiply threaded, thread safety is a must. * * @param chunkToAdd * @param possibleMatches * TODO * @param * @return */ abstract protected IChunk addChunkInternal(IChunk chunkToAdd, Collection<IChunk> possibleMatches); /** * copy the specified chunk, by default this will also copy subsymbolics */ public CompletableFuture<IChunk> copyChunk(IChunk sourceChunk) { return copyChunk(sourceChunk, true); } public CompletableFuture<IChunk> copyChunk(final IChunk sourceChunk, final boolean copySubsymbolics) { if (sourceChunk == null) throw new NullPointerException("sourceChunk cannot be null"); return delayedFuture(new Callable<IChunk>() { public IChunk call() throws Exception { String name = sourceChunk.getSymbolicChunk().getName(); IChunkType parent = sourceChunk.getSymbolicChunk().getChunkType(); IChunk copy = createChunkInternal(parent, name); copyChunkInternal(sourceChunk, copy, copySubsymbolics); return copy; } }, getExecutor()); } /** * copy source to copy * * @param sourceChunk * @param copy */ protected void copyChunkInternal(IChunk sourceChunk, IChunk destination, boolean copySubsymbolics) { getChunkFactory().copy(sourceChunk, destination); /* * set the symbolic contents */ ISymbolicChunk sourceSC = sourceChunk.getSymbolicChunk(); ISymbolicChunk destinationSC = destination.getSymbolicChunk(); getSymbolicChunkFactory().copy(sourceSC, destinationSC); if (copySubsymbolics) { ISubsymbolicChunk destinationSSC = destination.getSubsymbolicChunk(); ISubsymbolicChunk sourceSSC = sourceChunk.getSubsymbolicChunk(); getSubsymbolicChunkFactory().copy(sourceSSC, destinationSSC); } else destination.setMetaData(ISubsymbolicChunkFactory.SUBSYMBOLICS_COPIED_KEY, Boolean.FALSE); if (Logger.hasLoggers(getModel())) Logger.log(getModel(), Logger.Stream.DECLARATIVE, "Copied " + StringUtilities.toString(destination)); } /** * delegated * * @param name * @return * @see org.jactr.core.module.declarative.IDeclarativeModule#getChunk(java.lang.String) */ public CompletableFuture<IChunk> getChunk(final String name) { Callable<IChunk> callable = new Callable<IChunk>() { public IChunk call() throws Exception { return getChunkInternal(name); } }; return delayedFuture(callable, getExecutor()); } abstract protected IChunk getChunkInternal(String chunkName); /** * @return * @see org.jactr.core.module.declarative.IDeclarativeModule#getChunks() */ public CompletableFuture<Collection<IChunk>> getChunks() { Callable<Collection<IChunk>> callable = new Callable<Collection<IChunk>>() { public Collection<IChunk> call() throws Exception { return getChunksInternal(); } }; return delayedFuture(callable, getExecutor()); } abstract protected Collection<IChunk> getChunksInternal(); /** * create chunktype, delegates to * {@link #createChunkTypeInternal(IChunkType, String)} on * {@link #getExecutor()} * * @param parent * @param name * @return * @see org.jactr.core.module.declarative.IDeclarativeModule#createChunkType(org.jactr.core.chunktype.IChunkType, * java.lang.String) */ public CompletableFuture<IChunkType> createChunkType( final Collection<IChunkType> parents, final String name) { return delayedFuture(new Callable<IChunkType>() { public IChunkType call() throws Exception { return createChunkTypeInternal(parents, name); } }, getExecutor()); } public CompletableFuture<IChunkType> createChunkType(final IChunkType parent, final String name) { if (parent != null) return createChunkType(Collections.singleton(parent), name); else return createChunkType(Collections.EMPTY_LIST, name); } protected IChunkType createChunkTypeInternal(Collection<IChunkType> parents, String name) { IChunkTypeFactory cFactory = getChunkTypeFactory(); ISymbolicChunkTypeFactory scFactory = getSymbolicChunkTypeFactory(); ISubsymbolicChunkTypeFactory sscFactory = getSubsymbolicChunkTypeFactory(); IChunkType rtn = cFactory.newChunkType(getModel()); ISymbolicChunkType sct = scFactory.newSymbolicChunkType(); ISubsymbolicChunkType ssct = sscFactory.newSubsymbolicChunkType(); cFactory.bind(rtn, sct, ssct); scFactory.bind(sct, rtn, parents); sscFactory.bind(ssct, rtn, parents); sct.setName(name); configure(rtn); fireChunkTypeCreated(rtn); return rtn; } /** * calls {@link IChunkTypeConfigurator} * * @param newChunkType */ protected void configure(IChunkType newChunkType) { getChunkTypeConfigurator().configure(newChunkType); } /** * add chunktype to DM, delegated to {@link #addChunkTypeInternal(IChunkType)} * on {@link #getExecutor()} * * @param chunkType * @return * @see org.jactr.core.module.declarative.IDeclarativeModule#addChunkType(org.jactr.core.chunktype.IChunkType) */ public CompletableFuture<IChunkType> addChunkType(final IChunkType chunkType) { if (chunkType.isEncoded()) { if (LOGGER.isDebugEnabled()) LOGGER.debug(chunkType + " has already been encoded silly"); return immediateReturn(chunkType); } return delayedFuture(new Callable<IChunkType>() { public IChunkType call() throws Exception { return addChunkTypeInternal(chunkType); } }, getExecutor()); } /** * add the chunktype DM on the module's executor. If the executor is INLINE or * multithreaded, thread safety is a must. * * @param chunkType * @return */ abstract protected IChunkType addChunkTypeInternal(IChunkType chunkType); /** * delegated * * @param name * @return * @see org.jactr.core.module.declarative.IDeclarativeModule#getChunkType(java.lang.String) */ public CompletableFuture<IChunkType> getChunkType(final String name) { Callable<IChunkType> callable = new Callable<IChunkType>() { public IChunkType call() throws Exception { return getChunkTypeInternal(name); } }; return delayedFuture(callable, getExecutor()); } abstract protected IChunkType getChunkTypeInternal(String name); /** * delegated * * @return * @see org.jactr.core.module.declarative.IDeclarativeModule#getChunkTypes() */ public CompletableFuture<Collection<IChunkType>> getChunkTypes() { Callable<Collection<IChunkType>> callable = new Callable<Collection<IChunkType>>() { public Collection<IChunkType> call() throws Exception { return getChunkTypesInternal(); } }; return delayedFuture(callable, getExecutor()); } abstract protected Collection<IChunkType> getChunkTypesInternal(); public void setAssociativeLinkageSystem( IAssociativeLinkageSystem linkageSystem) { if (_associativeLinkageSystem != null) _associativeLinkageSystem.uninstall(getModel()); _associativeLinkageSystem = linkageSystem; if (_associativeLinkageSystem != null) _associativeLinkageSystem.install(getModel()); } public IAssociativeLinkageSystem getAssociativeLinkageSystem() { return _associativeLinkageSystem; } /** * actually perform the disposal * * @param chunk */ protected void disposeInternal(IChunk chunk) { try { fireChunkDisposed(chunk); if (LOGGER.isDebugEnabled()) LOGGER.debug("Disposing of " + chunk); IChunkFactory cFactory = getChunkFactory(); ISymbolicChunkFactory scFactory = getSymbolicChunkFactory(); ISubsymbolicChunkFactory sscFactory = getSubsymbolicChunkFactory(); ISymbolicChunk sc = chunk.getSymbolicChunk(); ISubsymbolicChunk ssc = chunk.getSubsymbolicChunk(); // let the linkage system clean itself up IAssociativeLinkageSystem linkage = getAssociativeLinkageSystem(); if (linkage != null) linkage.chunkWillBeDisposed(chunk); cFactory.unbind(chunk, sc, ssc); scFactory.unbind(sc); sscFactory.unbind(ssc); scFactory.dispose(sc); sscFactory.dispose(ssc); cFactory.dispose(chunk); } catch (Exception e) { LOGGER.error("Failed to dispose of chunk " + chunk + " ", e); } } /** * entry point for merging chunks. This assumes that originalChunk is encoded, * newChunk was going to be encoded, but was determined to be merged into * originalChunk instead. newChunk may or maynot have been encoded. <br/> * This method handles the event dispatching, logging and ultimately calls * {@link #mergeChunksInternal(IChunk, IChunk)} to do the real work. * * @param originalChunk * @param newChunk * @return */ protected IChunk merge(IChunk originalChunk, IChunk newChunk) { /** * double check */ if (originalChunk.equals(newChunk)) return originalChunk; if (LOGGER.isDebugEnabled()) LOGGER.debug("Merging new chunk " + newChunk + " into existing chunk " + originalChunk); originalChunk.dispatch(new ChunkEvent(originalChunk, ChunkEvent.Type.MERGING_WITH, newChunk)); newChunk.dispatch(new ChunkEvent(newChunk, ChunkEvent.Type.MERGING_INTO, originalChunk)); mergeChunksInternal(originalChunk, newChunk); String msg = null; if (Logger.hasLoggers(getModel())) msg = String.format("Merged %s into %s (%.2f)", newChunk, StringUtilities .toString(originalChunk), originalChunk.getSubsymbolicChunk() .getActivation()); if (msg != null) Logger.log(getModel(), Logger.Stream.DECLARATIVE, msg); fireChunksMerged(originalChunk, newChunk); return originalChunk; } /** * actually do the work or merging newChunk into originalChunk. * * @param originalChunk * @param newChunk */ protected void mergeChunksInternal(IChunk originalChunk, IChunk newChunk) { if (LOGGER.isDebugEnabled()) LOGGER.debug("Replacing the guts of " + newChunk + "(with slots" + newChunk.getSymbolicChunk().getSlots() + ") with " + originalChunk + "(with slots" + originalChunk.getSymbolicChunk().getSlots() + ")"); ISymbolicChunk osc = originalChunk.getSymbolicChunk(); ISubsymbolicChunk ossc = originalChunk.getSubsymbolicChunk(); ISymbolicChunk nsc = newChunk.getSymbolicChunk(); ISubsymbolicChunk nssc = newChunk.getSubsymbolicChunk(); IChunkFactory chunkFactory = getChunkFactory(); ISymbolicChunkFactory scFactory = getSymbolicChunkFactory(); ISubsymbolicChunkFactory sscFactory = getSubsymbolicChunkFactory(); /** * we need to review the locks here. We aren't using them at all, and that's * bad. Can we assume that the factories are locking correctly? probably * not.. */ Lock chunkLock = originalChunk.getWriteLock(); /** * first, unbind. to hell with locking this one */ chunkFactory.unbind(newChunk, nsc, nssc); try { chunkLock.lock(); // merge at the toplevel chunkFactory.merge(originalChunk, newChunk); // merge symbolic, not strictly necessary, but good for completeness scFactory.merge(osc, nsc); // and subsymbolic sscFactory.merge(ossc, nssc); // rebind to the new contents. newChunk and masterChunk are now the same. chunkFactory.bind(newChunk, osc, ossc); } finally { chunkLock.unlock(); } // dispose of the old pieces scFactory.dispose(nsc); sscFactory.dispose(nssc); // old code // /* // * ok, here we do something that is a little strange.. think of meta data // - // * who's metadata (which may be different) should take precedence? the new // * or the old? Because the perceptual modules use the metadata to keep // track // * of connections to actual objects in the environment, we actually want // to // * copy the new metadata over the existing metadata // */ // for (String meta : newChunk.getMetaDataKeys()) // originalChunk.setMetaData(meta, newChunk.getMetaData(meta)); // // /* // * should this use adaptable pattern? // */ // if (newChunk instanceof DefaultChunk) // ((DefaultChunk) newChunk).replaceContents(originalChunk); } /** * schedule this chunk to be disposed, at the module's earliest convenience */ public void dispose(IChunk chunk) { try { _disposalLock.lock(); _chunksToDispose.add(chunk); } finally { _disposalLock.unlock(); } } /** * encode this chunk at some later time (top/bottom of the cycle). * * @param chunk */ protected void deferredEncode(IChunk chunk) { try { _encodingLock.lock(); _deferredEncodings.add(chunk); } finally { _encodingLock.unlock(); } } protected void deferredEncode(Collection<IChunk> chunks) { try { _encodingLock.lock(); _deferredEncodings.addAll(chunks); } finally { _encodingLock.unlock(); } } public boolean willEncode(IChunk chunk) { try { _encodingLock.lock(); return chunk.isEncoded() || _deferredEncodings.contains(chunk); } finally { _encodingLock.unlock(); } } protected void processPendingEncodingAndDisposals() { FastList<IChunk> chunkContainer = FastList.newInstance(); FastList<IActivationBuffer> bufferContainer = FastList.newInstance(); try { processPendingEncodings(chunkContainer, bufferContainer); processPendingDisposals(chunkContainer, bufferContainer); } finally { FastList.recycle(chunkContainer); FastList.recycle(bufferContainer); } } protected void processPendingEncodings(FastList<IChunk> chunkContainer, FastList<IActivationBuffer> bufferContainer) { /* * even though we are a declarative module, we may not be THE declarative * module. That is, the actual decM might be delegating to us, if so, we * should use it's addChunk and not ours (even though, ours will eventually * be called) */ IDeclarativeModule decM = getModel().getDeclarativeModule(); chunkContainer.clear(); bufferContainer.clear(); try { _encodingLock.lock(); chunkContainer.addAll(_deferredEncodings); _deferredEncodings.clear(); } finally { _encodingLock.unlock(); } if (chunkContainer.size() == 0) return; double currentTime = ACTRRuntime.getRuntime().getClock(getModel()) .getTime(); if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("Deferred encoding %d chunks", chunkContainer.size())); // fast, destructive iterator where processing order does not matter for (IChunk chunk = null; !chunkContainer.isEmpty() && (chunk = chunkContainer.removeLast()) != null;) { /* * because this chunk might get merged, effectively changing the lock * instance, we do grab a reference to the lock temporarily */ Lock lock = chunk.getWriteLock(); try { lock.lock(); if (chunk.hasBeenDisposed()) continue; if (chunk.isEncoded()) chunk.getSubsymbolicChunk().accessed(currentTime); else { BufferUtilities.getContainingBuffers(chunk, true, bufferContainer); if (bufferContainer.size() == 0) decM.addChunk(chunk); } } finally { lock.unlock(); bufferContainer.clear(); } } } /** * called internally at the top & bottom of the cycle and at the end of a run. */ protected void processPendingDisposals(FastList<IChunk> chunkContainer, FastList<IActivationBuffer> bufferContainer) { chunkContainer.clear(); bufferContainer.clear(); /* * disposals */ try { _disposalLock.lock(); chunkContainer.addAll(_chunksToDispose); _chunksToDispose.clear(); } finally { _disposalLock.unlock(); } if (LOGGER.isDebugEnabled()) LOGGER.debug(String.format("Disposing of %d chunks", chunkContainer.size())); // fast, destructive iterator where processing order does not matter for (IChunk chunk = null; !chunkContainer.isEmpty() && (chunk = chunkContainer.removeLast()) != null;) { Lock chunkLock = chunk.getWriteLock(); try { chunkLock.lock(); if (chunk.isEncoded()) continue; if (chunk.hasBeenDisposed()) continue; // requeue if (isDisposalSuspended(chunk)) dispose(chunk); else { BufferUtilities.getContainingBuffers(chunk, true, bufferContainer); if (bufferContainer.size() == 0) disposeInternal(chunk); } } finally { chunkLock.unlock(); bufferContainer.clear(); } } } }