package org.jactr.modules.versioned.declarative;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
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.IChunk;
import org.jactr.core.chunktype.IChunkType;
import org.jactr.core.model.IModel;
import org.jactr.core.module.AbstractModule;
import org.jactr.core.module.declarative.basic.AbstractDeclarativeModule;
import org.jactr.core.slot.ISlot;
import org.jactr.core.utils.parameter.IParameterized;
import org.jactr.modules.declarative.DelegatedDeclarativeModule;
import org.jactr.modules.versioned.procedural.ProductionRewriter;
public class DefaultVersionedDeclarativeModule extends DelegatedDeclarativeModule implements
IVersionedDeclarativeModule, IParameterized
{
static final transient Log LOGGER = LogFactory
.getLog(DefaultVersionedDeclarativeModule.class);
ProductionRewriter _rewriter;
public DefaultVersionedDeclarativeModule()
{
}
@Override
public void install(IModel model)
{
super.install(model);
_rewriter = new ProductionRewriter(_model);
}
public Future<IChunkType> getChunkType(final String name, final double version)
{
return AbstractModule.delayedFuture(new Callable<IChunkType>() {
public IChunkType call() throws Exception
{
IChunkType ct = getChunkTypeInternal(name, version);
if (LOGGER.isDebugEnabled())
LOGGER
.debug("Requesting chunk type " + name + " and returning " + ct);
return ct;
}
}, ((AbstractDeclarativeModule) _delegate).getExecutor());
}
protected IChunkType getChunkTypeInternal(String name, double version)
{
try
{
IChunkType ancestor = _delegate.getChunkType(name).get();
if (ancestor == null) return null; // no chunktype of this name
while (true)
{
String heir = ancestor.getSubsymbolicChunkType().getParameter("heir");
if (heir != null)
{
IChunkType descendant = _delegate.getChunkType(heir).get();
if (Double.parseDouble(descendant.getSubsymbolicChunkType()
.getParameter("version")) > version)
return ancestor;
else
ancestor = descendant;
}
else
return ancestor;
}
}
catch (InterruptedException e)
{
LOGGER.error(e);
}
catch (ExecutionException e)
{
LOGGER.error(e);
}
return null;
}
public Future<IChunk> getChunk(final String name, final double version)
{
return AbstractModule.delayedFuture(new Callable<IChunk>() {
public IChunk call() throws Exception
{
return getChunkInternal(name, version);
}
}, ((AbstractDeclarativeModule) _delegate).getExecutor());
}
protected IChunk getChunkInternal(String name, double version)
{
try
{
IChunk ancestor = _delegate.getChunk(name).get();
if (ancestor == null) return null; // no chunk of this name
while (true)
{
String heir = ancestor.getSubsymbolicChunk().getParameter("heir");
if (heir != null)
{
IChunk descendant = _delegate.getChunk(heir).get();
if (Double.parseDouble(descendant.getSubsymbolicChunk().getParameter(
"version")) > version)
return ancestor;
else
ancestor = descendant;
}
else
return ancestor;
}
}
catch (InterruptedException e)
{
LOGGER.error(e);
}
catch (ExecutionException e)
{
LOGGER.error(e);
}
return null;
}
public Future<IChunkType> refineChunkType(final IChunkType ct,
final int action, final String propName)
{
return AbstractModule.delayedFuture(new Callable<IChunkType>() {
public IChunkType call() throws Exception
{
return refineChunkTypeInternal(ct, action, propName);
}
}, ((AbstractDeclarativeModule) _delegate).getExecutor());
}
private IChunkType refineChunkTypeInternal(IChunkType ct, int action,
String propName)
{
if (action != IVersionedDeclarativeModule.ADD)
{
LOGGER
.error("cannot refine chunk types in ways other than ADD; ignoring request");
return null;
}
IChunkType newParent = null;
try
{
newParent = getChunkType(propName).get();
if (newParent == null)
{
LOGGER.error("CANNOT add parent " + propName
+ " because don't know about this chunk type.");
return null;
}
return refineChunkTypeInternal(ct, action, newParent);
}
catch (InterruptedException e)
{
LOGGER.error(e);
}
catch (ExecutionException e)
{
LOGGER.error(e);
}
return null;
}
private IChunkType refineChunkTypeInternal(IChunkType ct, int action,
IChunkType newParent)
{
try
{
LOGGER.debug("adding " + newParent + " to " + ct);
Collection<IChunkType> newSupertypeParents = Arrays.asList(ct, newParent);
final IChunkType newCT = createChunkType(newSupertypeParents,
ct.getSymbolicChunkType().getName()).get();
ct.getSubsymbolicChunkType().setParameter("heir", newCT.toString());
newCT.getSubsymbolicChunkType().setParameter("version",
Double.toString(_model.getAge()));
addChunkType(newCT);
_rewriter.refineChunkType(ct, newCT);
Collection<IChunk> chunks = new ArrayList<IChunk>();
chunks.addAll(ct.getSymbolicChunkType().getChunks());
for (IChunk chunk : chunks)
{
// create new chunks with the new ct as a parent
IChunk newChunk = _delegate.createChunk(newCT,
chunk.getSymbolicChunk().getName()).get();
for (ISlot slot : chunk.getSymbolicChunk().getSlots())
{
LOGGER.debug("duplicated chunk. adding slot " + slot
+ " to newChunk, class is " + slot.getClass());
newChunk.getSymbolicChunk().addSlot(slot);
LOGGER.debug("new chunk's slot's class is "
+ newChunk.getSymbolicChunk().getSlot(slot.getName()).getClass());
}
chunk.getSubsymbolicChunk().setParameter("heir", newChunk.toString());
newChunk.getSubsymbolicChunk().setParameter("version",
Double.toString(_model.getAge()));
addChunk(newChunk);
/*
* trigger the copy/rewrite of dependent productions
*/
_rewriter.refineChunk(chunk, newChunk);
FastList<IActivationBuffer> containingBuffers = FastList.newInstance();
BufferUtilities.getContainingBuffers(chunk, false, containingBuffers);
for (IActivationBuffer buffer : containingBuffers)
{
buffer.removeSourceChunk(chunk);
buffer.addSourceChunk(newChunk);
}
FastList.recycle(containingBuffers);
}
// for now, give the new chunks the default values of the added parent
for (IChunkType type : ct.getSymbolicChunkType().getChildren())
if (!type.equals(newCT))
refineChunkTypeInternal(ct, action, newParent);
return newCT;
}
catch (InterruptedException e)
{
LOGGER.error(e);
}
catch (ExecutionException e)
{
LOGGER.error(e);
}
return null;
}
}