package org.archstudio.bna.logics; import static com.google.common.base.Preconditions.checkNotNull; import java.util.Set; 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.IThingMetakey; import org.archstudio.bna.keys.IThingRefKey; import org.archstudio.bna.logics.tracking.ThingReferenceTrackingLogic; import org.archstudio.bna.logics.tracking.ThingReferenceTrackingLogic.Reference; import org.archstudio.bna.logics.tracking.ThingValueTrackingLogic; import org.archstudio.bna.utils.BNAUtils; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; public abstract class AbstractKeyCoordinatingThingLogic extends AbstractThingLogic implements IBNAModelListener { protected final ThingReferenceTrackingLogic referenceLogic; protected final ThingValueTrackingLogic valueLogic; private final Set<IThingKey<?>> trackedKeys = Sets.newHashSet(); private final Set<IThingRefKey<?>> trackedRefKeys = Sets.newHashSet(); boolean updating = false; public AbstractKeyCoordinatingThingLogic(IBNAWorld world, boolean ignoreUpdateEvents) { super(world); referenceLogic = logics.addThingLogic(ThingReferenceTrackingLogic.class); valueLogic = logics.addThingLogic(ThingValueTrackingLogic.class); } protected void track(IThingKey<?> key) { checkNotNull(key); trackedKeys.add(key); if (key instanceof IThingRefKey) { trackedRefKeys.add((IThingRefKey<?>) key); } } private void _update(IThing thing, IThingKey<?> key) { if (updating) { return; } updating = true; try { update(thing, key); } catch (Throwable t) { t.printStackTrace(); } finally { updating = false; } } @Override public void bnaModelChanged(BNAModelEvent evt) { BNAUtils.checkLock(); switch (evt.getEventType()) { case THING_ADDED: { IThing thing = evt.getTargetThing(); for (IThingKey<?> key : trackedKeys) { if (thing.has(key)) { _update(thing, key); } } for (Reference reference : referenceLogic.getReferences(thing)) { IThingRefKey<?> fromKey = reference.getFromKey(); if (trackedRefKeys.contains(fromKey)) { IThing fromThing = model.getThing(reference.getFromThingID()); if (fromThing != null) { _update(fromThing, fromKey); } } } } break; case THING_REMOVED: IThing thing = evt.getTargetThing(); for (IThingMetakey<?, ?, ?> metaKey : Iterables.filter(trackedKeys, IThingMetakey.class)) { IThingKey<?> refKey = metaKey.getKey(); if (refKey instanceof IThingRefKey<?>) { for (IThing fromThing : valueLogic.getThings((IThingRefKey<?>) refKey, thing.getID())) { _update(fromThing, metaKey); } } } for (IThingRefKey<?> fromKey : trackedRefKeys) { for (IThing fromThing : valueLogic.getThings(fromKey, thing.getID())) { _update(fromThing, fromKey); } } break; case THING_CHANGED: ThingEvent thingEvent = evt.getThingEvent(); if (trackedKeys.contains(thingEvent.getPropertyName())) { _update(thingEvent.getTargetThing(), thingEvent.getPropertyName()); } break; default: // do nothing } } protected abstract void update(IThing thing, IThingKey<?> key); }