/*
* Created on Oct 24, 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.production.basic;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.Executor;
import javolution.util.FastList;
import javolution.util.FastMap;
import javolution.util.FastSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jactr.core.buffer.IActivationBuffer;
import org.jactr.core.event.ACTREventDispatcher;
import org.jactr.core.model.IModel;
import org.jactr.core.production.CannotInstantiateException;
import org.jactr.core.production.IInstantiation;
import org.jactr.core.production.IProduction;
import org.jactr.core.production.ISubsymbolicProduction;
import org.jactr.core.production.ISymbolicProduction;
import org.jactr.core.production.IllegalProductionStateException;
import org.jactr.core.production.condition.CannotMatchException;
import org.jactr.core.production.condition.IBufferCondition;
import org.jactr.core.production.condition.ICondition;
import org.jactr.core.production.event.IProductionListener;
import org.jactr.core.production.event.ProductionEvent;
public abstract class AbstractProduction implements IProduction
{
/**
* logger definition
*/
static private final Log LOGGER = LogFactory
.getLog(AbstractProduction.class);
protected ACTREventDispatcher<IProduction, IProductionListener> _eventDispatcher;
protected ISymbolicProduction _symbolicProduction;
protected ISubsymbolicProduction _subsymbolicProduction;
protected IModel _model;
protected String _comment;
protected boolean _encoded;
public AbstractProduction(IModel model)
{
_eventDispatcher = new ACTREventDispatcher<IProduction, IProductionListener>();
_model = model;
_symbolicProduction = createSymbolicProduction(this, model);
_subsymbolicProduction = createSubsymbolicProduction(this, model);
}
abstract protected ISymbolicProduction createSymbolicProduction(
AbstractProduction production, IModel model);
abstract protected ISubsymbolicProduction createSubsymbolicProduction(
AbstractProduction production, IModel model);
public void addListener(IProductionListener pl, Executor executor)
{
_eventDispatcher.addListener(pl, executor);
}
public void dispose()
{
_eventDispatcher.clear();
if (getSymbolicProduction() != null) getSymbolicProduction().dispose();
if (getSubsymbolicProduction() != null)
getSubsymbolicProduction().dispose();
}
public IModel getModel()
{
return _model;
}
public ISubsymbolicProduction getSubsymbolicProduction()
{
return _subsymbolicProduction;
}
public ISymbolicProduction getSymbolicProduction()
{
return _symbolicProduction;
}
// public IInstantiation instantiate() throws CannotInstantiateException
// {
// if (!isEncoded())
// throw new CannotInstantiateException(
// "Cannot instantiate an unencoded production");
//
// HashMap<String, Object> bindings = new HashMap<String, Object>();
// ISymbolicProduction sp = getSymbolicProduction();
//
// IModel m = getModel();
//
// bindings.put("=model", m);
// bindings.put("=production", this);
// Collection<ICondition> conditions = sp.getConditions();
//
// ArrayList<ICondition> boundConditions = new ArrayList<ICondition>(
// conditions.size());
//
// if (LOGGER.isDebugEnabled()) LOGGER.debug("Instantiating " + this);
//
// for (ICondition condition : conditions)
// try
// {
// condition = condition.bind(m, bindings);
// boundConditions.add(condition);
// }
// catch (CannotMatchException cme)
// {
// // cleanup
// for (ICondition bound : boundConditions)
// bound.dispose();
//
// if (LOGGER.isDebugEnabled())
// LOGGER.debug("Cannot instantiate " + this
// + ": Failed to match condition " + condition, cme);
// throw new CannotInstantiateException(cme.getMessage(), cme);
// }
//
// IInstantiation instance = createInstantiation(this, boundConditions,
// bindings);
//
// // event
// if (hasListeners())
// dispatch(new ProductionEvent(this, ProductionEvent.Type.INSTANTIATED,
// instance));
//
// return instance;
// }
public Collection<IInstantiation> instantiateAll(
Collection<Map<String, Object>> provisionalBindings)
throws CannotInstantiateException
{
if (!isEncoded())
throw new CannotInstantiateException(
"Cannot instantiate an unencoded production");
ISymbolicProduction sp = getSymbolicProduction();
IModel m = getModel();
Collection<IInstantiation> instantiations = Collections.EMPTY_LIST;
CannotMatchException exception = null;
int totalIterations = 0;
FastSet<String> missingBuffers = FastSet.newInstance();
for (IActivationBuffer buffer : m.getActivationBuffers())
missingBuffers.add("=" + buffer.getName());
Collection<ICondition> originals = sp.getConditions();
for (ICondition condition : originals)
if (condition instanceof IBufferCondition)
missingBuffers.remove("="
+ ((IBufferCondition) condition).getBufferName());
/*
* missingBuffers now contains the variable names bound to buffers that this
* production should not be able to see so we will remove them from the
* copied bindings
*/
// Collection<Map<String, Object>> attemptedMappings = new
// ArrayList<Map<String, Object>>(
// provisionalBindings.size());
FastList<ICondition> cloned = FastList.newInstance();
// Collection<ICondition> cloned = new
// ArrayList<ICondition>(originals.size());
for (Map<String, Object> variableBindings : provisionalBindings)
{
FastMap<String, Object> tmpBindings = FastMap.newInstance();
try
{
tmpBindings.putAll(variableBindings);
tmpBindings.put("=production", this);
for (String missing : missingBuffers)
tmpBindings.remove(missing);
/*
* now we check to see if this particular mapping has already been
* attempted, if so, skip.
*/
// boolean alreadyAttempted = false;
// for (Map<String, Object> attempted : attemptedMappings)
// if (attempted.equals(variableBindings))
// {
// alreadyAttempted = true;
// break;
// }
//
// if (alreadyAttempted) continue;
//
// attemptedMappings.add(new TreeMap<String, Object>(variableBindings));
if (LOGGER.isDebugEnabled())
LOGGER.debug("Attempting resolution of " + sp.getName()
+ " with provisional binding: " + tmpBindings);
/*
* first we need to duplicate the conditions
*/
cloned.clear();
/*
* clone them, they may throw CMEs if they can determine that binding is
* impossible.
*/
for (ICondition original : originals)
cloned.add(original.clone(m, tmpBindings));
/*
* Now we iteratively zip through the conditions, keeping track of the
* number of unresolved bindings, if the total reaches 0, we are golden
* and can fully instantiate. If the total fails to decrease after any
* iteration, it is stuck, and will fail. OR a CME can be thrown.
*/
int lastTotalUnresolved = Integer.MAX_VALUE;
int iterations = 0;
while (lastTotalUnresolved > 0)
{
totalIterations++;
iterations++;
int totalUnresolved = 0;
for (ICondition condition : cloned)
totalUnresolved += condition.bind(m, tmpBindings, true);
/*
* no change.. we turn off iterative and attempt to resolve again. we
* know it will fail, but we're doing this to get the CME
*/
if (totalUnresolved == lastTotalUnresolved)
for (ICondition condition : cloned)
condition.bind(m, tmpBindings, false);
lastTotalUnresolved = totalUnresolved;
}
if (LOGGER.isDebugEnabled())
LOGGER.debug("Instantiated " + sp.getName() + " after " + iterations
+ " iterations, bindings : " + tmpBindings);
/*
* we can instantiate
*/
if (instantiations.size() == 0)
instantiations = new ArrayList<IInstantiation>(provisionalBindings
.size());
IInstantiation instance = createInstantiation(this, cloned,
new TreeMap<String, Object>(tmpBindings));
instantiations.add(instance);
}
catch (CannotMatchException cme)
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("Could not instantiate " + sp.getName() + " after "
+ totalIterations + " iterations with binding: " + tmpBindings
+ " ", cme);
exception = cme;
}
finally
{
FastMap.recycle(tmpBindings);
}
}
/*
* clean up before exit..
*/
FastList.recycle(cloned);
FastSet.recycle(missingBuffers);
if (instantiations.size() == 0)
throw new CannotInstantiateException(sp.getName()
+ " couldn't instantiate because : " + exception.getMessage() + ". ["
+ provisionalBindings.size() + "/" + totalIterations + "]");
// event
if (hasListeners())
for (IInstantiation instance : instantiations)
dispatch(new ProductionEvent(this, ProductionEvent.Type.INSTANTIATED,
instance));
return instantiations;
}
/**
* return a new instantiation
*
* @param parent
* @param boundConditions
* @param bindings
* @return
*/
abstract protected IInstantiation createInstantiation(
AbstractProduction parent, Collection<ICondition> boundConditions,
Map<String, Object> bindings) throws CannotInstantiateException;
@Override
public String toString()
{
if (_symbolicProduction != null) return _symbolicProduction.getName();
return super.toString();
}
public boolean hasListeners()
{
return _eventDispatcher.hasListeners();
}
public void dispatch(ProductionEvent pe)
{
_eventDispatcher.fire(pe);
}
public void removeListener(IProductionListener pl)
{
_eventDispatcher.removeListener(pl);
}
public int compareTo(IProduction arg0)
{
if (arg0 == this) return 0;
// else lexical
return getSymbolicProduction().getName().compareTo(
arg0.getSymbolicProduction().getName());
}
public String getComment()
{
return _comment;
}
public void setComment(String comment)
{
_comment = comment;
}
public void encode()
{
if (isEncoded())
throw new IllegalProductionStateException(
"Cannot encoded an encoded production");
getSymbolicProduction().encode();
getSubsymbolicProduction().encode();
_encoded = true;
}
public boolean isEncoded()
{
return _encoded;
}
}