/* * Created on Jun 27, 2007 Copyright (C) 2001-2007, 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.modules.pm.aural.audicon.map; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.Executor; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.commonreality.identifier.IIdentifier; import org.commonreality.modalities.aural.DefaultAuralPropertyHandler; import org.commonreality.modalities.aural.IAuralPropertyHandler; import org.commonreality.object.IAfferentObject; import org.commonreality.object.delta.IObjectDelta; import org.jactr.core.chunk.IChunk; import org.jactr.core.production.request.ChunkTypeRequest; import org.jactr.core.runtime.ACTRRuntime; import org.jactr.core.slot.BasicSlot; import org.jactr.core.slot.IConditionalSlot; import org.jactr.modules.pm.aural.IAuralModule; import org.jactr.modules.pm.common.memory.IPerceptualMemory; import org.jactr.modules.pm.common.memory.map.IFeatureMap; import org.jactr.modules.pm.common.memory.map.IFeatureMapListener; /** * @author developer */ public class OffsetFeatureMap implements IAuralFeatureMap<Double> { /** * logger definition */ static private final Log LOGGER = LogFactory .getLog(OffsetFeatureMap.class); private IAuralPropertyHandler _propertyHandler; private TreeMap<Double, Set<IIdentifier>> _offsetMap; private Map<IIdentifier, Double> _activeSounds; private Map<IIdentifier, Double> _identifierOffsetMap; private Map<IIdentifier, Double> _removeAt; private IAuralModule _module; public OffsetFeatureMap(IAuralModule module) { _module = module; _propertyHandler = new DefaultAuralPropertyHandler(); _activeSounds = new HashMap<IIdentifier, Double>(); _offsetMap = new TreeMap<Double, Set<IIdentifier>>(); _identifierOffsetMap = new HashMap<IIdentifier, Double>(); _removeAt = new HashMap<IIdentifier, Double>(); } /** * @see org.jactr.modules.pm.common.memory.map.IFeatureMap#clear() */ public void clear() { _offsetMap.clear(); _identifierOffsetMap.clear(); } /** * @see org.jactr.modules.pm.common.memory.map.IFeatureMap#dispose() */ public void dispose() { clear(); } /** * @see org.jactr.modules.pm.common.memory.map.IFeatureMap#fillSlotValues(ChunkTypeRequest, * org.commonreality.identifier.IIdentifier, * IChunk, ChunkTypeRequest) */ public void fillSlotValues(ChunkTypeRequest mutableRequest, IIdentifier identifier, IChunk encodedChunk, ChunkTypeRequest originalSearchRequest) { Double then = _identifierOffsetMap.get(identifier); if (then == null) return; mutableRequest.addSlot(new BasicSlot(IAuralModule.OFFSET_SLOT, then)); } /** * @see org.jactr.modules.pm.common.memory.map.IFeatureMap#getCandidateRealObjects(ChunkTypeRequest, Set) */ public void getCandidateRealObjects( ChunkTypeRequest request, Set<IIdentifier> container) { Set<IIdentifier> identifiers = new HashSet<IIdentifier>(); boolean firstIteration = true; for (IConditionalSlot cSlot : request.getConditionalSlots()) if (cSlot.getName().equalsIgnoreCase(IAuralModule.OFFSET_SLOT)) { Object value = cSlot.getValue(); if (_module.getLowestChunk().equals(value)) value = _offsetMap.firstKey(); else if (_module.getHighestChunk().equals(value)) value = _offsetMap.lastKey(); Number val = (Number) value; Collection<IIdentifier> eval = new HashSet<IIdentifier>(); if (val == null) { if (IConditionalSlot.NOT_EQUALS == cSlot.getCondition()) eval.addAll(all()); } else switch (cSlot.getCondition()) { case IConditionalSlot.EQUALS: eval.addAll(equal(val.doubleValue())); break; case IConditionalSlot.NOT_EQUALS: eval.addAll(not(val.doubleValue())); break; case IConditionalSlot.GREATER_THAN_EQUALS: eval.addAll(equal(val.doubleValue())); case IConditionalSlot.GREATER_THAN: eval.addAll(greaterThan(val.doubleValue())); break; case IConditionalSlot.LESS_THAN_EQUALS: eval.addAll(equal(val.doubleValue())); case IConditionalSlot.LESS_THAN: eval.addAll(lessThan(val.doubleValue())); break; default: if (LOGGER.isWarnEnabled()) LOGGER.warn(getClass().getSimpleName() + " can only handle =,!=,<,<=,>,>="); break; } if (eval.size() == 0) break; if (firstIteration) { identifiers.addAll(eval); firstIteration = false; } else identifiers.retainAll(eval); } } protected Collection<IIdentifier> all() { Set<IIdentifier> identifiers = new HashSet<IIdentifier>(); for (Set<IIdentifier> ids : _offsetMap.values()) identifiers.addAll(ids); return identifiers; } protected Collection<IIdentifier> not(Double when) { Set<IIdentifier> identifiers = new HashSet<IIdentifier>(); for (Map.Entry<Double, Set<IIdentifier>> entry : _offsetMap.entrySet()) if (!entry.getKey().equals(when)) identifiers.addAll(entry.getValue()); return identifiers; } protected Collection<IIdentifier> equal(Double when) { Set<IIdentifier> identifiers = _offsetMap.get(when); if (identifiers == null) identifiers = Collections.EMPTY_SET; return identifiers; } protected Collection<IIdentifier> lessThan(Double when) { Set<IIdentifier> identifiers = new HashSet<IIdentifier>(); for (Set<IIdentifier> ids : _offsetMap.headMap(when).values()) identifiers.addAll(ids); return identifiers; } protected Collection<IIdentifier> greaterThan(Double when) { Set<IIdentifier> identifiers = new HashSet<IIdentifier>(); for (Set<IIdentifier> ids : _offsetMap.tailMap(when).values()) identifiers.addAll(ids); Set<IIdentifier> equals = _offsetMap.get(when); if (equals != null) identifiers.removeAll(equals); return identifiers; } /** * @see org.jactr.modules.pm.common.memory.map.IFeatureMap#isInterestedIn(ChunkTypeRequest) */ public boolean isInterestedIn(ChunkTypeRequest request) { for (IConditionalSlot cSlot : request.getConditionalSlots()) if (cSlot.getName().equals(IAuralModule.OFFSET_SLOT)) return true; return false; } /** * @see org.jactr.modules.pm.common.afferent.IAfferentObjectListener#afferentObjectAdded(org.commonreality.object.IAfferentObject) */ public void afferentObjectAdded(IAfferentObject object) { _activeSounds.put(object.getIdentifier(), ACTRRuntime.getRuntime() .getClock(_module.getModel()).getTime()); } protected void add(IIdentifier identifier) { Double then = _activeSounds.remove(identifier); if (then == null) return; double now = ACTRRuntime.getRuntime().getClock(_module.getModel()) .getTime(); Double duration = now - then; Set<IIdentifier> identifiers = _offsetMap.get(duration); if (identifiers == null) { identifiers = new HashSet<IIdentifier>(); _offsetMap.put(duration, identifiers); } identifiers.add(identifier); _identifierOffsetMap.put(identifier, duration); _removeAt.put(identifier, now + _module.getAuralDecayTime()); /* * and let's sweep through the list of sounds that need to be removed from * our cache.. */ Iterator<Map.Entry<IIdentifier, Double>> iterator = _removeAt.entrySet() .iterator(); while (iterator.hasNext()) { Map.Entry<IIdentifier, Double> removal = iterator.next(); if (removal.getValue() < now) { remove(removal.getKey()); iterator.remove(); } } } protected void remove(IIdentifier identifier) { Double duration = _identifierOffsetMap.remove(identifier); if (duration == null) return; Set<IIdentifier> identifiers = _offsetMap.get(duration); if (identifiers != null) if (identifiers.remove(identifier)) if (identifiers.size() == 0) _offsetMap.remove(duration); } /** * @see org.jactr.modules.pm.common.afferent.IAfferentObjectListener#afferentObjectRemoved(org.commonreality.object.IAfferentObject) */ public void afferentObjectRemoved(IAfferentObject object) { add(object.getIdentifier()); } /** * @see org.jactr.modules.pm.common.afferent.IAfferentObjectListener#afferentObjectUpdated(org.commonreality.object.IAfferentObject, * org.commonreality.object.delta.IObjectDelta) */ public void afferentObjectUpdated(IAfferentObject object, IObjectDelta delta) { // noop } /** * @see org.jactr.modules.pm.common.afferent.IAfferentObjectListener#isInterestedIn(org.commonreality.object.IAfferentObject) */ public boolean isInterestedIn(IAfferentObject object) { return _identifierOffsetMap.containsKey(object.getIdentifier()) || _propertyHandler.hasModality(object); } /** * @see org.jactr.modules.pm.aural.audicon.map.IAuralFeatureMap#removeFeatureFor(org.commonreality.object.IAfferentObject) */ public void removeFeatureFor(IAfferentObject object) { remove(object.getIdentifier()); } public void addListener(IFeatureMapListener listener, Executor executor) { // TODO Auto-generated method stub } public Double getInformation(IIdentifier identifier) { // TODO Auto-generated method stub return null; } public IPerceptualMemory getPerceptualMemory() { // TODO Auto-generated method stub return null; } public void removeListener(IFeatureMapListener listener) { // TODO Auto-generated method stub } public void setPerceptualMemory(IPerceptualMemory memory) { // TODO Auto-generated method stub } public void normalizeRequest(ChunkTypeRequest request) { // TODO Auto-generated method stub } }