package org.archstudio.bna.logics.coordinating; import org.archstudio.bna.IBNAModelListener; import org.archstudio.bna.IBNAWorld; import org.archstudio.bna.IThing; import org.archstudio.bna.keys.IThingKey; import org.archstudio.bna.keys.IThingRefKey; import org.archstudio.bna.keys.ThingKey; import org.archstudio.bna.keys.ThingRefKey; import org.archstudio.bna.logics.AbstractKeyCoordinatingThingLogic; import org.archstudio.bna.utils.BNAPath; import org.archstudio.bna.utils.BNAUtils; import com.google.common.collect.Lists; /** * This logic re-parents a thing so that it is a child of a given thing id. This is useful, for example, to force * interfaces to always be children of the bricks that have them. Optionally, when re-parenting, a thing can be placed * before or after a sibling specified by a {@link BNAPath} from the parent thing. * * @author sahendrickson@gmail.com (Scott A. Hendrickson) */ public class ReparentToThingIDLogic extends AbstractKeyCoordinatingThingLogic implements IBNAModelListener { /** The key storing the parent thing's id. */ public static final IThingRefKey<IThing> REPARENT_TO_THING_KEY = ThingRefKey.create(Lists.newArrayList("reparent-to-thing", ReparentToThingIDLogic.class)); /** * The key storing optional parameters that indicate which sibling a thing should be placed by. May be * <code>null</code>. */ public static final IThingKey<ReparentParams> REPARENT_TO_THING_PARAMS_KEY = ThingKey.create(Lists.newArrayList("reparent-to-thing-parameters", ReparentToThingIDLogic.class)); /** * Parameters to specify which sibling a thing should be placed before or after. * * @author sahendrickson@gmail.com (Scott A. Hendrickson) */ public static final class ReparentParams { /** The BNA path leading to the sibling under the parent to stack next to. */ private final BNAPath siblingPath; /** Whether the thing should be stacked before (<code>true</code>) or after (<code>false</code>) the sibling. */ private final boolean before; /** * Indicates which sibling a thing should be placed before or after. * * @param siblingPath * The BNA path leading to the sibling under the parent to stack next to. * @param before * Whether the thing should be stacked before (<code>true</code>) or after (<code>false</code>) the * sibling. */ public ReparentParams(BNAPath siblingPath, boolean before) { this.siblingPath = siblingPath; this.before = before; } public BNAPath getSiblingPath() { return siblingPath; } public boolean isBefore() { return before; } } public ReparentToThingIDLogic(IBNAWorld world) { super(world, false); track(REPARENT_TO_THING_KEY); track(REPARENT_TO_THING_PARAMS_KEY); } /** * Returns the key storing the parent thing's id. * * @return the key storing the parent thing's id. */ public IThingRefKey<IThing> getReparentToThingKey() { BNAUtils.checkLock(); return REPARENT_TO_THING_KEY; } /** * Returns the key storing optional parameters that indicate which sibling a thing should be placed by. * * @return the key storing optional parameters that indicate which sibling a thing should be placed by. */ public IThingKey<ReparentParams> getReparentToThingParamsKey() { BNAUtils.checkLock(); return REPARENT_TO_THING_PARAMS_KEY; } @Override protected void update(IThing thing, IThingKey<?> key) { IThing parentThing = REPARENT_TO_THING_KEY.get(thing, model); if (parentThing != null) { ReparentParams params = thing.get(REPARENT_TO_THING_PARAMS_KEY); if (params != null) { IThing sibling = BNAPath.resolve(model, parentThing, params.siblingPath); if (sibling != null) { IThing siblingParent = model.getParentThing(sibling); if (siblingParent != null) { model.reparent(siblingParent, thing); if (params.isBefore()) { model.moveBefore(thing, sibling); } else { model.moveAfter(thing, sibling); } } } } else { model.reparent(parentThing, thing); } } } }