package org.jactr.modules.pm.aural.memory.impl.map; /* * default logging */ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javolution.util.FastSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.commonreality.agents.IAgent; import org.commonreality.identifier.IIdentifier; import org.commonreality.object.IAfferentObject; import org.commonreality.object.manager.event.IAfferentListener; import org.commonreality.object.manager.event.IObjectEvent; import org.jactr.core.concurrent.ExecutorServices; import org.jactr.core.production.request.ChunkTypeRequest; import org.jactr.core.slot.IConditionalSlot; import org.jactr.modules.pm.IPerceptualModule; import org.jactr.modules.pm.aural.IAuralModule; import org.jactr.modules.pm.common.memory.impl.INeedsAgent; /** * consider using sorted but use supers. and override the defaults * * @author harrison */ public class OffsetFeatureMap extends AbstractAuralFeatureMap<Double> implements INeedsAgent { /** * Logger definition */ static private final transient Log LOGGER = LogFactory .getLog(OffsetFeatureMap.class); /** * used to record the actual end time of a sound */ private IAfferentListener _trueRemoveListener; private IAgent _agent; private Map<IIdentifier, Double> _completedSounds; private Set<IIdentifier> _activeSounds; public OffsetFeatureMap() { this(IAuralModule.OFFSET_SLOT); } public OffsetFeatureMap(String requestSlotName) { super(requestSlotName, null); _activeSounds = new HashSet<IIdentifier>(); _completedSounds = new HashMap<IIdentifier, Double>(); _trueRemoveListener = new IAfferentListener() { public void objectsAdded(IObjectEvent<IAfferentObject, ?> addEvent) { } public void objectsRemoved(IObjectEvent<IAfferentObject, ?> removeEvent) { double now = getCurrentTime(); /* * record the time the sound actually stopped */ try { getLock().lock(); for (IAfferentObject object : removeEvent.getObjects()) { IIdentifier id = object.getIdentifier(); _completedSounds.put(id, now); if (LOGGER.isDebugEnabled()) LOGGER.debug("Sound : " + id + " has actually ended at " + now); } } finally { getLock().unlock(); } } public void objectsUpdated(IObjectEvent<IAfferentObject, ?> updateEvent) { } }; } public void setAgent(IAgent agent) { if (agent == null) { if (_agent != null) _agent.getAfferentObjectManager().removeListener(_trueRemoveListener); _agent = null; } else { _agent = agent; /* * attach inline so that we are notified of removal before the perceptual * memory. this allows us to handle the perceptual delay w/ no collisions * when delay is 0. */ _agent.getAfferentObjectManager().addListener(_trueRemoveListener, ExecutorServices.INLINE_EXECUTOR); } } protected IAgent getAgent() { return _agent; } protected double getCurrentTime() { return _agent.getClock().getTime(); } @Override protected void addInformation(IIdentifier identifier, Double data) { _activeSounds.add(identifier); } @Override protected void clearInternal() { _activeSounds.clear(); _completedSounds.clear(); } @Override protected Double extractInformation(IAfferentObject afferentObject) { return _agent.getClock().getTime(); } @Override protected void getCandidates(ChunkTypeRequest request, Set<IIdentifier> results) { boolean firstInsertion = true; String slotName = getRelevantSlotName(); FastSet<IIdentifier> tmp = FastSet.newInstance(); for (IConditionalSlot slot : request.getConditionalSlots()) if (slot.getName().equalsIgnoreCase(slotName)) if (slot.getValue() instanceof Number) { tmp.clear(); double value = ((Number) slot.getValue()).doubleValue(); switch (slot.getCondition()) { case IConditionalSlot.LESS_THAN_EQUALS: equals(value, tmp); case IConditionalSlot.LESS_THAN: lessThan(value, tmp); break; case IConditionalSlot.GREATER_THAN_EQUALS: equals(value, tmp); case IConditionalSlot.GREATER_THAN: greaterThan(value, tmp); break; case IConditionalSlot.NOT_EQUALS: not(value, tmp); break; case IConditionalSlot.WITHIN: LOGGER.warn("within is not implemented"); default: equals(value, tmp); break; } if (firstInsertion) results.addAll(tmp); else results.retainAll(tmp); firstInsertion = false; if (results.size() == 0) { if (LOGGER.isDebugEnabled()) LOGGER.debug(this + " No possible results, returning early"); break; } } else LOGGER.warn(this + " " + slot + " value is not a number"); FastSet.recycle(tmp); } protected void not(double time, Set<IIdentifier> results) { double now = getCurrentTime(); if (now != time) results.addAll(_activeSounds); for (Map.Entry<IIdentifier, Double> entry : _completedSounds.entrySet()) if (entry.getValue() != time) results.add(entry.getKey()); } protected void equals(double time, Set<IIdentifier> results) { // ideally we should use a tolerance.. double now = getCurrentTime(); if (now == time) results.addAll(_activeSounds); for (Map.Entry<IIdentifier, Double> entry : _completedSounds.entrySet()) if (entry.getValue() == time) results.add(entry.getKey()); } protected void lessThan(double time, Set<IIdentifier> results) { double now = getCurrentTime(); if (now < time) results.addAll(_activeSounds); for (Map.Entry<IIdentifier, Double> entry : _completedSounds.entrySet()) if (entry.getValue() < time) results.add(entry.getKey()); } protected void greaterThan(double time, Set<IIdentifier> results) { double now = getCurrentTime(); if (now > time) results.addAll(_activeSounds); for (Map.Entry<IIdentifier, Double> entry : _completedSounds.entrySet()) if (entry.getValue() > time) results.add(entry.getKey()); } /** * returns current time or end time if available * * @param identifier * @return * @see org.jactr.modules.pm.common.memory.map.AbstractFeatureMap#getCurrentValue(org.commonreality.identifier.IIdentifier) */ @Override protected Double getCurrentValue(IIdentifier identifier) { Double completedTime = _completedSounds.get(identifier); if (completedTime == null) completedTime = getCurrentTime(); return completedTime; } @Override protected Double removeInformation(IIdentifier identifier) { Double rtn = getCurrentValue(identifier); _activeSounds.remove(identifier); return rtn; } @Override protected void objectRemoved(IAfferentObject object, Double data) { super.objectRemoved(object, data); IIdentifier id = object.getIdentifier(); if (_completedSounds.remove(id) != null && LOGGER.isDebugEnabled()) LOGGER.debug("Sound: " + id + " has been removed from audicon"); } public void normalizeRequest(ChunkTypeRequest request) { for (IConditionalSlot cSlot : request.getConditionalSlots()) { String name = cSlot.getName(); if (IPerceptualModule.LOWEST_CHUNK.equalsIgnoreCase(name)) { if (LOGGER.isWarnEnabled()) LOGGER.warn(name+" filtering not implemented (yet)"); } else if(IPerceptualModule.HIGHEST_CHUNK.equalsIgnoreCase(name)) if (LOGGER.isWarnEnabled()) LOGGER.warn(name+" filtering not implemented (yet)"); } } }