package org.archstudio.bna.logics.tracking; import java.util.Collection; import java.util.Map; import org.archstudio.bna.BNAModelEvent; import org.archstudio.bna.IBNAModelListener; import org.archstudio.bna.IBNAWorld; import org.archstudio.bna.IThing; import org.archstudio.bna.ThingEvent; import org.archstudio.bna.keys.IThingKey; import org.archstudio.bna.keys.IThingRefKey; import org.archstudio.bna.logics.AbstractThingLogic; import org.archstudio.bna.utils.BNAUtils; import com.google.common.collect.HashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; public class ThingReferenceTrackingLogic extends AbstractThingLogic implements IBNAModelListener { public static final class Reference { private final Object fromThingID; private final IThingRefKey<?> fromKey; public Reference(Object fromThingID, IThingRefKey<?> fromKey) { this.fromThingID = fromThingID; this.fromKey = fromKey; } public Object getFromThingID() { return fromThingID; } public IThingRefKey<?> getFromKey() { return fromKey; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (fromKey == null ? 0 : fromKey.hashCode()); result = prime * result + (fromThingID == null ? 0 : fromThingID.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Reference other = (Reference) obj; if (fromKey == null) { if (other.fromKey != null) { return false; } } else if (!fromKey.equals(other.fromKey)) { return false; } if (fromThingID == null) { if (other.fromThingID != null) { return false; } } else if (!fromThingID.equals(other.fromThingID)) { return false; } return true; } } protected final Multimap<Object, Reference> idToReferences = HashMultimap.create(); public ThingReferenceTrackingLogic(IBNAWorld world) { super(world); for (IThing t : model.getAllThings()) { addReferences(t); } } @Override public void dispose() { BNAUtils.checkLock(); idToReferences.clear(); super.dispose(); } private void addReferences(IThing fromThing) { for (Map.Entry<IThingKey<?>, ?> entry : fromThing.entrySet()) { if (entry.getKey() instanceof IThingRefKey) { IThingRefKey<?> refKey = (IThingRefKey<?>) entry.getKey(); addReference(fromThing, refKey, entry.getValue()); } } } private void removeReferences(IThing fromThing) { for (Map.Entry<IThingKey<?>, ?> entry : fromThing.entrySet()) { if (entry.getKey() instanceof IThingRefKey) { IThingRefKey<?> refKey = (IThingRefKey<?>) entry.getKey(); removeReference(fromThing, refKey, entry.getValue()); } } } private void addReference(IThing fromThing, IThingRefKey<?> fromKey, Object toThingID) { idToReferences.put(toThingID, new Reference(fromThing.getID(), fromKey)); } private void removeReference(IThing fromThing, IThingRefKey<?> fromKey, Object toThingID) { idToReferences.remove(toThingID, new Reference(fromThing.getID(), fromKey)); } public Collection<Reference> getReferences(IThing toThing) { return getReferences(toThing.getID()); } public Collection<Reference> getReferences(Object toThingID) { BNAUtils.checkLock(); if (toThingID != null) { Collection<Reference> references = idToReferences.get(toThingID); if (references != null) { return Lists.newArrayList(idToReferences.get(toThingID)); } } return Lists.newArrayList(); } @Override @SuppressWarnings("rawtypes") public void bnaModelChanged(BNAModelEvent evt) { BNAUtils.checkLock(); switch (evt.getEventType()) { case THING_ADDED: addReferences(evt.getTargetThing()); break; case THING_REMOVED: removeReferences(evt.getTargetThing()); break; case THING_CHANGED: ThingEvent thingEvent = evt.getThingEvent(); IThingKey key = thingEvent.getPropertyName(); if (key instanceof IThingRefKey) { removeReference(evt.getTargetThing(), (IThingRefKey) key, thingEvent.getOldPropertyValue()); addReference(evt.getTargetThing(), (IThingRefKey) key, thingEvent.getNewPropertyValue()); } default: // do nothing } } }