package com.framsticks.gui.tree; import java.lang.ref.WeakReference; import java.util.Iterator; import java.util.LinkedList; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.framsticks.core.Node; import com.framsticks.core.Path; import com.framsticks.core.SideNoteKey; import com.framsticks.core.Tree; import com.framsticks.gui.Frame; import com.framsticks.gui.ImageProvider; import com.framsticks.gui.TreeAtFrame; import com.framsticks.gui.TreePanel; import com.framsticks.params.Access; import com.framsticks.params.CompositeParam; import com.framsticks.params.EventListener; import com.framsticks.params.FramsClass; import com.framsticks.params.ValueParam; import com.framsticks.params.types.EventParam; import com.framsticks.params.types.ObjectParam; import com.framsticks.params.types.StringParam; import com.framsticks.util.FramsticksException; import com.framsticks.util.dispatching.FutureHandler; import com.framsticks.util.lang.Casting; import com.framsticks.util.lang.Containers; import com.framsticks.util.swing.TooltipConstructor; import static com.framsticks.core.TreeOperations.*; public class TreeNode extends AbstractNode { private static final Logger log = LogManager.getLogger(TreeNode.class); protected final WeakReference reference; protected final int hashCode; protected final TreeAtFrame treeAtFrame; protected final String textual; protected final CompositeParam param; protected TreePanel panel; public TreeModel getTreeModel() { return treeAtFrame.getFrame().getTreeModel(); } /** * @param reference */ public TreeNode(TreeAtFrame treeAtFrame, Path path) { path.assureResolved(); this.reference = new WeakReference(path.getTopObject()); this.textual = path.getTextual(); this.treeAtFrame = treeAtFrame; this.param = path.getTop().getParam(); hashCode = System.identityHashCode(path.getTopObject()); if (isMarked(path.getTree(), path.getTopObject(), getTreeModel().createdTag, false)) { return; } // path.getTree().putSideNote(path.getTopObject(), Textual.class, path.getTextual()); mark(path.getTree(), path.getTopObject(), getTreeModel().createdTag, true); /** Iterate over all EventParams and for matching ValueParams register listeners. */ if (path.getTop().getParam() instanceof ObjectParam) { Access access = bindAccess(path); FramsClass framsClass = access.getFramsClass(); for (EventParam eventParam : Containers.filterInstanceof(framsClass.getParamEntries(), EventParam.class)) { if (!eventParam.getId().endsWith("_changed")) { continue; } String valueId = eventParam.getId().substring(0, eventParam.getId().length() - 8); final ValueParam valueParam = Casting.tryCast(ValueParam.class, framsClass.getParam(valueId)); if (valueParam == null) { continue; } getTreeModel().registerForEventParam(this, path, eventParam, valueParam); } } } @Override public int hashCode() { return hashCode; } @Override public boolean equals(Object obj) { if (obj instanceof TreeNode) { return lock() == ((TreeNode) obj).lock(); } return false; } @Override public AbstractNode getChild(int number) { Object referent = lock(); if (referent == null) { throw new FramsticksException().msg("invalid state - missing referent"); } Tree tree = getTree(); Access access = bindAccessForTreeObject(referent); final int count = access.getCompositeParamCount(); if (number >= count) { throw new FramsticksException().msg("invalid state - no child"); } /** textual path may be not valid anymore*/ CompositeParam childParam = access.getCompositeParam(number); try { Path path = Path.to(tree, getTextual()).appendParam(childParam).tryFindResolution(); if (!path.isResolved()) { path = create(path); } return prepareTreeNodeForChild(path); } catch (FramsticksException e) { } return new EmptyNode(getFrame(), childParam); } public TreeNode prepareTreeNodeForChild(Path path) { assert path.getTree() == getTree(); Object parent = lock(); Iterator n = path.getNodes().iterator(); while (n.hasNext()) { if (n.next().getObject() == parent) { break; } } if (!n.hasNext()) { return null; // throw new FramsticksException().msg("tree node is not on path (or is last)").arg("path", path).arg("node", this); } return new TreeNode(treeAtFrame, path); } public Object lock() { return reference.get(); } @Override public int getIndexOfChild(Object child) { final TreeNode treeChild = Casting.tryCast(TreeNode.class, child); if (treeChild == null) { return -1; } final Object childObject = treeChild.lock(); final Object parentObject = lock(); if (childObject == null || parentObject == null) { return -1; } final Access access = bindAccessForTreeObject(parentObject); final int count = access.getCompositeParamCount(); for (int i = 0; i < count; ++i) { Object c = access.get(access.getCompositeParam(i), Object.class); if (c == childObject) { return i; } } log.debug("{} not found in {}", child, this); return -1; } public Frame getFrame() { return getTreeAtFrame().getFrame(); } public TreeAtFrame getTreeAtFrame() { return treeAtFrame; } public Tree getTree() { return getTreeAtFrame().getTree(); } protected Path assurePath() { return Path.to(getTree(), getTextual()).assureResolved(); } @Override public String toString() { return getTextual(); } public Node tryCreateNode() { Object child = lock(); if (child == null) { return null; } String textual = getTextual(); Path path = Path.tryTo(getTree(), textual); if (path.isResolved(textual)) { return path.getTop(); } return null; } @Override public int getChildCount() { Object referent = lock(); if (referent == null) { return 0; } Access access = bindAccessForTreeObject(referent); final int count = access.getCompositeParamCount(); return count; } @Override public boolean isLeaf() { Object referent = lock(); if (referent == null) { return true; } return bindAccessForTreeObject(referent).getCompositeParamCount() == 0; } protected Access bindAccessForTreeObject(Object child) { return bindAccessFromSideNote(getTree(), child); } @Override public void render(TreeCellRenderer renderer) { Object child = lock(); if (child == null) { renderer.setToolTipText("?"); renderer.setText("?"); renderer.setIcon(ImageProvider.loadImage(ImageProvider.FOLDER_CLOSED)); return; } Access access = bindAccessForTreeObject(child); CompositeParam param = getTree().getSideNote(child, Path.OBJECT_PARAM_KEY); String name = param.getId(); StringParam nameParam = Casting.tryCast(StringParam.class, access.getParam("name")); if (nameParam != null) { name = access.get(nameParam, String.class); } renderer.setToolTipText(new TooltipConstructor() .append("frams", access.getId()) .append("java", child.getClass().getCanonicalName()) .append("access", access.getClass().getSimpleName()) .append("name", name) .append("id", param.getId()) .append("object", Integer.toHexString(System.identityHashCode(child))) .append("size", access.getCompositeParamCount()) .build()); renderer.setIcon(ImageProvider.loadImage(TreeCellRenderer.findIconName(param))); renderer.setText(name); } public String getTextual() { return textual; } @SuppressWarnings("rawtypes") protected final SideNoteKey listenersTag = SideNoteKey.make(LinkedList.class); protected void tryAddListener(final Path path, final EventParam eventParam, Class argumentType, final EventListener listener) { getTree().addListener(path, eventParam, listener, argumentType, new FutureHandler(getFrame()) { @SuppressWarnings("unchecked") @Override protected void result(Void result) { assert getFrame().isActive(); log.debug("registered gui listener for {} at {}", eventParam, path); getOrCreateSideNote(getTree(), lock(), listenersTag).add(listener); } }); } @Override public TreePanel getPanel() { if (panel != null) { return panel; } panel = getTreeAtFrame().preparePanel(param); return panel; } }