package org.jactr.tools.tracer.listeners;
/*
* default logging
*/
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.Executor;
import javolution.util.FastList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.commonreality.identifier.IIdentifier;
import org.jactr.core.concurrent.ExecutorServices;
import org.jactr.core.event.IParameterEvent;
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.IModule;
import org.jactr.modules.pm.common.event.IPerceptualMemoryModuleEvent;
import org.jactr.modules.pm.common.memory.map.FeatureMapEvent;
import org.jactr.modules.pm.common.memory.map.IFeatureMap;
import org.jactr.modules.pm.common.memory.map.IFeatureMapListener;
import org.jactr.modules.pm.visual.IVisualModule;
import org.jactr.modules.pm.visual.event.IVisualModuleListener;
import org.jactr.modules.pm.visual.event.VisualModuleEvent;
import org.jactr.tools.tracer.transformer.visual.TransformedVisualEvent;
import org.jactr.tools.tracer.transformer.visual.VisualEventTransformer;
public class VisualModuleTracer extends BaseTraceListener
{
/**
* Logger definition
*/
static private final transient Log LOGGER = LogFactory
.getLog(VisualModuleTracer.class);
private IModelListener _outputListener;
private IVisualModuleListener _visualListener;
private IFeatureMapListener _featureListener;
private Executor _traceExecutor;
private Map<IModel, Collection<TransformedVisualEvent>> _pendingEvents;
private Map<IModel, Set<IIdentifier>> _addedIds;
private Map<IModel, Set<IIdentifier>> _removedIds;
private Map<IModel, Map<IIdentifier, Map<String, Object>>> _updates;
public VisualModuleTracer()
{
setEventTransformer(new VisualEventTransformer());
_pendingEvents = new HashMap<IModel, Collection<TransformedVisualEvent>>();
_addedIds = new HashMap<IModel, Set<IIdentifier>>();
_removedIds = new HashMap<IModel, Set<IIdentifier>>();
_updates = new HashMap<IModel, Map<IIdentifier, Map<String, Object>>>();
_outputListener = new ModelListenerAdaptor() {
@Override
public void cycleStopped(ModelEvent me)
{
dumpPendingEvents(me.getSource());
}
};
_visualListener = new IVisualModuleListener() {
public void perceptAttended(IPerceptualMemoryModuleEvent event)
{
TransformedVisualEvent tve = (TransformedVisualEvent) getEventTransformer()
.transform(event);
if (tve != null)
getEvents(((IModule) event.getSource()).getModel()).add(tve);
}
public void perceptIndexFound(IPerceptualMemoryModuleEvent event)
{
TransformedVisualEvent tve = (TransformedVisualEvent) getEventTransformer()
.transform(event);
if (tve != null)
getEvents(((IModule) event.getSource()).getModel()).add(tve);
}
public void trackedObjectMoved(VisualModuleEvent event)
{
}
public void trackingObjectStarted(VisualModuleEvent event)
{
}
public void trackingObjectStopped(VisualModuleEvent event)
{
}
public void parameterChanged(IParameterEvent pe)
{
}
public void moduleReset(IPerceptualMemoryModuleEvent event)
{
}
};
_featureListener = new IFeatureMapListener() {
public void featureAdded(FeatureMapEvent event)
{
if (LOGGER.isDebugEnabled()) LOGGER.debug("Got feature add " + event);
IModel model = event.getSource().getPerceptualMemory().getModule()
.getModel();
IIdentifier id = event.getIdentifiers().iterator().next();
Set<IIdentifier> added = getAdded(model);
if (added.contains(id)) return;
TransformedVisualEvent tve = (TransformedVisualEvent) getEventTransformer()
.transform(event);
if (tve != null) getEvents(model).add(tve);
added.add(id);
}
public void featureRemoved(FeatureMapEvent event)
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("got feature remove " + event);
IModel model = event.getSource().getPerceptualMemory().getModule()
.getModel();
IIdentifier id = event.getIdentifiers().iterator().next();
Set<IIdentifier> removed = getRemoved(model);
if (removed.contains(id)) return;
TransformedVisualEvent tve = (TransformedVisualEvent) getEventTransformer()
.transform(event);
if (tve != null) getEvents(model).add(tve);
removed.add(id);
}
public void featureUpdated(FeatureMapEvent event)
{
if (LOGGER.isDebugEnabled())
LOGGER.debug("got feature update " + event);
IModel model = event.getSource().getPerceptualMemory().getModule()
.getModel();
IIdentifier id = event.getIdentifiers().iterator().next();
Map<IIdentifier, Map<String, Object>> updates = getUpdates(model);
Map<String, Object> data = updates.get(id);
if (data == null)
{
data = new TreeMap<String, Object>();
TransformedVisualEvent tve = (TransformedVisualEvent) getEventTransformer()
.transform(event);
if (tve != null) getEvents(model).add(tve);
updates.put(id, data);
}
updateData(event.getSource(), id, data);
}
};
}
public void install(IModel model, Executor executor)
{
_traceExecutor = executor;
// we dump to sink async..
model.addListener(_outputListener, _traceExecutor);
// but since these come in on CR thread, we want to create
// the transformed event immediately, and let the async dump
executor = ExecutorServices.INLINE_EXECUTOR;
IVisualModule vis = (IVisualModule) model.getModule(IVisualModule.class);
if (vis != null)
{
vis.addListener(_visualListener, executor);
FastList<IFeatureMap> featureMaps = FastList.newInstance();
vis.getVisualMemory().getFeatureMaps(featureMaps);
for (IFeatureMap map : featureMaps)
{
if (LOGGER.isDebugEnabled()) LOGGER.debug("Attaching to " + map);
map.addListener(_featureListener, executor);
}
FastList.recycle(featureMaps);
}
}
public void uninstall(IModel model)
{
model.removeListener(_outputListener);
IVisualModule vis = (IVisualModule) model.getModule(IVisualModule.class);
if (vis != null)
{
vis.removeListener(_visualListener);
FastList<IFeatureMap> featureMaps = FastList.newInstance();
vis.getVisualMemory().getFeatureMaps(featureMaps);
for (IFeatureMap map : featureMaps)
map.removeListener(_featureListener);
FastList.recycle(featureMaps);
}
}
synchronized protected Collection<TransformedVisualEvent> getEvents(
IModel model)
{
Collection<TransformedVisualEvent> events = _pendingEvents.get(model);
if (events == null)
{
events = Collections
.synchronizedList(new ArrayList<TransformedVisualEvent>(100));
_pendingEvents.put(model, events);
}
return events;
}
synchronized protected Set<IIdentifier> getAdded(IModel model)
{
Set<IIdentifier> added = _addedIds.get(model);
if (added == null)
{
added = Collections.synchronizedSet(new HashSet<IIdentifier>());
_addedIds.put(model, added);
}
return added;
}
synchronized protected void getAdded(IModel model, Set<IIdentifier> container)
{
Set<IIdentifier> source = getAdded(model);
synchronized (source)
{
container.addAll(source);
}
}
synchronized protected Set<IIdentifier> getRemoved(IModel model)
{
Set<IIdentifier> removed = _removedIds.get(model);
if (removed == null)
{
removed = Collections.synchronizedSet(new HashSet<IIdentifier>());
_removedIds.put(model, removed);
}
return removed;
}
synchronized protected void getRemoved(IModel model,
Set<IIdentifier> container)
{
Set<IIdentifier> source = getRemoved(model);
synchronized (source)
{
container.addAll(source);
}
}
synchronized protected Map<IIdentifier, Map<String, Object>> getUpdates(
IModel model)
{
Map<IIdentifier, Map<String, Object>> updates = _updates.get(model);
if (updates == null)
{
updates = Collections
.synchronizedMap(new HashMap<IIdentifier, Map<String, Object>>());
_updates.put(model, updates);
}
return updates;
}
protected Map<String, Object> getUpdates(
Map<IIdentifier, Map<String, Object>> updates, IIdentifier object,
boolean create)
{
Map<String, Object> map = updates.get(object);
if (map == null) if (create)
{
map = new TreeMap<String, Object>();
updates.put(object, map);
}
else
map = Collections.emptyMap();
return map;
}
protected void updateAllData(IModel model, IIdentifier identifier,
Map<String, Object> data)
{
FastList<IFeatureMap> featureMaps = FastList.newInstance();
try
{
((IVisualModule) model.getModule(IVisualModule.class)).getVisualMemory()
.getFeatureMaps(featureMaps);
for (IFeatureMap featureMap : featureMaps)
updateData(featureMap, identifier, data);
}
finally
{
FastList.recycle(featureMaps);
}
}
protected void updateData(IFeatureMap featureMap, IIdentifier identifier,
Map<String, Object> data)
{
Object info = featureMap.getInformation(identifier);
if (info instanceof Serializable)
data.put(featureMap.getClass().getSimpleName(), info);
}
synchronized protected void dumpPendingEvents(IModel model)
{
Map<IIdentifier, Map<String, Object>> updates = getUpdates(model);
/*
* make sure the added have all their data
*/
// need to make thread safe.
Collection<IIdentifier> added = getAdded(model);
if (LOGGER.isDebugEnabled())
LOGGER.debug("Getting full data for " + added.size() + " add events");
for (IIdentifier add : added)
updateAllData(model, add, getUpdates(updates, add, true));
Collection<TransformedVisualEvent> events = getEvents(model);
if (LOGGER.isDebugEnabled())
LOGGER.debug("Processing " + events.size() + " visual events");
for (TransformedVisualEvent event : events)
{
TransformedVisualEvent.Type type = event.getType();
/*
* if it is an add or update, we need to get the collated properties
*/
if (type == TransformedVisualEvent.Type.ADDED
|| type == TransformedVisualEvent.Type.UPDATED)
event.getData().putAll(
getUpdates(updates, event.getIdentifier(), false));
sink(event);
}
added.clear();
getRemoved(model).clear();
updates.clear();
events.clear();
}
}