[99] | 1 | package com.framsticks.gui.tree; |
---|
| 2 | |
---|
| 3 | import java.lang.ref.WeakReference; |
---|
| 4 | import java.util.Iterator; |
---|
| 5 | import java.util.LinkedList; |
---|
| 6 | |
---|
[100] | 7 | import org.apache.logging.log4j.LogManager; |
---|
| 8 | import org.apache.logging.log4j.Logger; |
---|
[99] | 9 | |
---|
| 10 | import com.framsticks.gui.Frame; |
---|
| 11 | import com.framsticks.gui.ImageProvider; |
---|
| 12 | import com.framsticks.gui.TreeAtFrame; |
---|
[100] | 13 | import com.framsticks.gui.TreePanel; |
---|
| 14 | import com.framsticks.params.Access; |
---|
[99] | 15 | import com.framsticks.params.CompositeParam; |
---|
| 16 | import com.framsticks.params.EventListener; |
---|
| 17 | import com.framsticks.params.ValueParam; |
---|
| 18 | import com.framsticks.params.types.EventParam; |
---|
| 19 | import com.framsticks.params.types.ObjectParam; |
---|
| 20 | import com.framsticks.params.types.StringParam; |
---|
[105] | 21 | import com.framsticks.structure.Node; |
---|
| 22 | import com.framsticks.structure.Path; |
---|
| 23 | import com.framsticks.structure.SideNoteKey; |
---|
| 24 | import com.framsticks.structure.Tree; |
---|
[99] | 25 | import com.framsticks.util.FramsticksException; |
---|
[105] | 26 | import com.framsticks.util.dispatching.Future; |
---|
[99] | 27 | import com.framsticks.util.lang.Casting; |
---|
| 28 | import com.framsticks.util.lang.Containers; |
---|
| 29 | import com.framsticks.util.swing.TooltipConstructor; |
---|
[100] | 30 | |
---|
[105] | 31 | import static com.framsticks.structure.TreeOperations.*; |
---|
[99] | 32 | |
---|
| 33 | public class TreeNode extends AbstractNode { |
---|
| 34 | |
---|
[100] | 35 | private static final Logger log = LogManager.getLogger(TreeNode.class); |
---|
[99] | 36 | |
---|
| 37 | protected final WeakReference<Object> reference; |
---|
[100] | 38 | protected final int hashCode; |
---|
| 39 | protected final TreeAtFrame treeAtFrame; |
---|
| 40 | protected final String textual; |
---|
[99] | 41 | protected final CompositeParam param; |
---|
[100] | 42 | protected TreePanel panel; |
---|
[99] | 43 | |
---|
[100] | 44 | public TreeModel getTreeModel() { |
---|
| 45 | return treeAtFrame.getFrame().getTreeModel(); |
---|
| 46 | } |
---|
[99] | 47 | |
---|
[100] | 48 | /** |
---|
| 49 | * @param reference |
---|
| 50 | */ |
---|
[99] | 51 | public TreeNode(TreeAtFrame treeAtFrame, Path path) { |
---|
| 52 | path.assureResolved(); |
---|
[100] | 53 | |
---|
| 54 | this.reference = new WeakReference<Object>(path.getTopObject()); |
---|
| 55 | this.textual = path.getTextual(); |
---|
| 56 | this.treeAtFrame = treeAtFrame; |
---|
[99] | 57 | this.param = path.getTop().getParam(); |
---|
[100] | 58 | hashCode = System.identityHashCode(path.getTopObject()); |
---|
[99] | 59 | |
---|
[101] | 60 | if (isMarked(path.getTree(), path.getTopObject(), getTreeModel().createdTag, false)) { |
---|
[100] | 61 | return; |
---|
| 62 | } |
---|
[99] | 63 | |
---|
[100] | 64 | // path.getTree().putSideNote(path.getTopObject(), Textual.class, path.getTextual()); |
---|
[101] | 65 | mark(path.getTree(), path.getTopObject(), getTreeModel().createdTag, true); |
---|
[100] | 66 | |
---|
[99] | 67 | /** Iterate over all EventParams and for matching ValueParams register listeners. */ |
---|
[100] | 68 | if (path.getTop().getParam() instanceof ObjectParam) { |
---|
| 69 | Access access = bindAccess(path); |
---|
[105] | 70 | for (EventParam eventParam : Containers.filterInstanceof(access.getParams(), EventParam.class)) { |
---|
[99] | 71 | if (!eventParam.getId().endsWith("_changed")) { |
---|
| 72 | continue; |
---|
| 73 | } |
---|
| 74 | String valueId = eventParam.getId().substring(0, eventParam.getId().length() - 8); |
---|
[105] | 75 | final ValueParam valueParam = Casting.tryCast(ValueParam.class, access.getParam(valueId)); |
---|
[99] | 76 | if (valueParam == null) { |
---|
| 77 | continue; |
---|
| 78 | } |
---|
[100] | 79 | getTreeModel().registerForEventParam(this, path, eventParam, valueParam); |
---|
[99] | 80 | } |
---|
| 81 | } |
---|
| 82 | } |
---|
| 83 | |
---|
[100] | 84 | @Override |
---|
| 85 | public int hashCode() { |
---|
| 86 | return hashCode; |
---|
[99] | 87 | } |
---|
| 88 | |
---|
| 89 | @Override |
---|
[100] | 90 | public boolean equals(Object obj) { |
---|
| 91 | if (obj instanceof TreeNode) { |
---|
| 92 | return lock() == ((TreeNode) obj).lock(); |
---|
[99] | 93 | } |
---|
[100] | 94 | return false; |
---|
[99] | 95 | } |
---|
| 96 | |
---|
| 97 | @Override |
---|
| 98 | public AbstractNode getChild(int number) { |
---|
| 99 | Object referent = lock(); |
---|
| 100 | if (referent == null) { |
---|
| 101 | throw new FramsticksException().msg("invalid state - missing referent"); |
---|
| 102 | } |
---|
[100] | 103 | Tree tree = getTree(); |
---|
| 104 | Access access = bindAccessForTreeObject(referent); |
---|
[99] | 105 | |
---|
| 106 | final int count = access.getCompositeParamCount(); |
---|
| 107 | if (number >= count) { |
---|
| 108 | throw new FramsticksException().msg("invalid state - no child"); |
---|
| 109 | } |
---|
| 110 | |
---|
[100] | 111 | /** textual path may be not valid anymore*/ |
---|
[99] | 112 | CompositeParam childParam = access.getCompositeParam(number); |
---|
| 113 | |
---|
[100] | 114 | try { |
---|
| 115 | Path path = Path.to(tree, getTextual()).appendParam(childParam).tryFindResolution(); |
---|
| 116 | if (!path.isResolved()) { |
---|
| 117 | path = create(path); |
---|
| 118 | } |
---|
| 119 | return prepareTreeNodeForChild(path); |
---|
| 120 | } catch (FramsticksException e) { |
---|
[99] | 121 | } |
---|
[100] | 122 | return new EmptyNode(getFrame(), childParam); |
---|
[99] | 123 | |
---|
[100] | 124 | } |
---|
[99] | 125 | |
---|
[100] | 126 | public TreeNode prepareTreeNodeForChild(Path path) { |
---|
| 127 | assert path.getTree() == getTree(); |
---|
| 128 | Object parent = lock(); |
---|
| 129 | Iterator<Node> n = path.getNodes().iterator(); |
---|
| 130 | while (n.hasNext()) { |
---|
| 131 | if (n.next().getObject() == parent) { |
---|
| 132 | break; |
---|
| 133 | } |
---|
| 134 | } |
---|
| 135 | if (!n.hasNext()) { |
---|
| 136 | return null; |
---|
| 137 | // throw new FramsticksException().msg("tree node is not on path (or is last)").arg("path", path).arg("node", this); |
---|
| 138 | } |
---|
| 139 | return new TreeNode(treeAtFrame, path); |
---|
[99] | 140 | } |
---|
| 141 | |
---|
| 142 | public Object lock() { |
---|
| 143 | return reference.get(); |
---|
| 144 | } |
---|
| 145 | |
---|
| 146 | @Override |
---|
[100] | 147 | public int getIndexOfChild(Object child) { |
---|
[99] | 148 | final TreeNode treeChild = Casting.tryCast(TreeNode.class, child); |
---|
| 149 | if (treeChild == null) { |
---|
| 150 | return -1; |
---|
| 151 | } |
---|
| 152 | final Object childObject = treeChild.lock(); |
---|
| 153 | final Object parentObject = lock(); |
---|
| 154 | if (childObject == null || parentObject == null) { |
---|
| 155 | return -1; |
---|
| 156 | } |
---|
[100] | 157 | final Access access = bindAccessForTreeObject(parentObject); |
---|
[99] | 158 | |
---|
| 159 | final int count = access.getCompositeParamCount(); |
---|
| 160 | for (int i = 0; i < count; ++i) { |
---|
| 161 | Object c = access.get(access.getCompositeParam(i), Object.class); |
---|
| 162 | if (c == childObject) { |
---|
| 163 | return i; |
---|
| 164 | } |
---|
| 165 | } |
---|
[100] | 166 | log.debug("{} not found in {}", child, this); |
---|
[99] | 167 | return -1; |
---|
| 168 | } |
---|
| 169 | |
---|
[100] | 170 | public Frame getFrame() { |
---|
| 171 | return getTreeAtFrame().getFrame(); |
---|
[99] | 172 | } |
---|
| 173 | |
---|
[100] | 174 | public TreeAtFrame getTreeAtFrame() { |
---|
| 175 | return treeAtFrame; |
---|
| 176 | } |
---|
| 177 | |
---|
[99] | 178 | public Tree getTree() { |
---|
[100] | 179 | return getTreeAtFrame().getTree(); |
---|
[99] | 180 | } |
---|
| 181 | |
---|
[100] | 182 | protected Path assurePath() { |
---|
| 183 | return Path.to(getTree(), getTextual()).assureResolved(); |
---|
| 184 | } |
---|
[99] | 185 | |
---|
| 186 | @Override |
---|
| 187 | public String toString() { |
---|
[100] | 188 | return getTextual(); |
---|
[99] | 189 | } |
---|
| 190 | |
---|
[100] | 191 | public Node tryCreateNode() { |
---|
| 192 | Object child = lock(); |
---|
| 193 | if (child == null) { |
---|
| 194 | return null; |
---|
| 195 | } |
---|
| 196 | String textual = getTextual(); |
---|
| 197 | Path path = Path.tryTo(getTree(), textual); |
---|
| 198 | if (path.isResolved(textual)) { |
---|
| 199 | return path.getTop(); |
---|
| 200 | } |
---|
| 201 | return null; |
---|
[99] | 202 | } |
---|
| 203 | |
---|
| 204 | @Override |
---|
[100] | 205 | public int getChildCount() { |
---|
| 206 | Object referent = lock(); |
---|
| 207 | if (referent == null) { |
---|
| 208 | return 0; |
---|
| 209 | } |
---|
| 210 | Access access = bindAccessForTreeObject(referent); |
---|
| 211 | final int count = access.getCompositeParamCount(); |
---|
| 212 | return count; |
---|
| 213 | } |
---|
| 214 | |
---|
| 215 | @Override |
---|
[99] | 216 | public boolean isLeaf() { |
---|
| 217 | Object referent = lock(); |
---|
| 218 | if (referent == null) { |
---|
| 219 | return true; |
---|
| 220 | } |
---|
[100] | 221 | return bindAccessForTreeObject(referent).getCompositeParamCount() == 0; |
---|
[99] | 222 | } |
---|
| 223 | |
---|
[100] | 224 | protected Access bindAccessForTreeObject(Object child) { |
---|
| 225 | return bindAccessFromSideNote(getTree(), child); |
---|
[99] | 226 | } |
---|
| 227 | |
---|
| 228 | @Override |
---|
| 229 | public void render(TreeCellRenderer renderer) { |
---|
| 230 | |
---|
| 231 | Object child = lock(); |
---|
[100] | 232 | if (child == null) { |
---|
| 233 | renderer.setToolTipText("?"); |
---|
| 234 | renderer.setText("?"); |
---|
| 235 | renderer.setIcon(ImageProvider.loadImage(ImageProvider.FOLDER_CLOSED)); |
---|
| 236 | return; |
---|
| 237 | } |
---|
| 238 | Access access = bindAccessForTreeObject(child); |
---|
[101] | 239 | CompositeParam param = getTree().getSideNote(child, Path.OBJECT_PARAM_KEY); |
---|
[100] | 240 | String name = param.getId(); |
---|
[99] | 241 | |
---|
[100] | 242 | StringParam nameParam = Casting.tryCast(StringParam.class, access.getParam("name")); |
---|
[99] | 243 | |
---|
[100] | 244 | if (nameParam != null) { |
---|
| 245 | name = access.get(nameParam, String.class); |
---|
| 246 | } |
---|
[99] | 247 | |
---|
[100] | 248 | renderer.setToolTipText(new TooltipConstructor() |
---|
[105] | 249 | .append("frams", access.getTypeId()) |
---|
[99] | 250 | .append("java", child.getClass().getCanonicalName()) |
---|
| 251 | .append("access", access.getClass().getSimpleName()) |
---|
| 252 | .append("name", name) |
---|
| 253 | .append("id", param.getId()) |
---|
| 254 | .append("object", Integer.toHexString(System.identityHashCode(child))) |
---|
[100] | 255 | .append("size", access.getCompositeParamCount()) |
---|
[99] | 256 | .build()); |
---|
[100] | 257 | |
---|
| 258 | renderer.setIcon(ImageProvider.loadImage(TreeCellRenderer.findIconName(param))); |
---|
[99] | 259 | renderer.setText(name); |
---|
[100] | 260 | } |
---|
[99] | 261 | |
---|
[100] | 262 | |
---|
| 263 | public String getTextual() { |
---|
| 264 | return textual; |
---|
[99] | 265 | } |
---|
| 266 | |
---|
[101] | 267 | @SuppressWarnings("rawtypes") |
---|
| 268 | protected final SideNoteKey<LinkedList> listenersTag = SideNoteKey.make(LinkedList.class); |
---|
[100] | 269 | |
---|
| 270 | protected <A> void tryAddListener(final Path path, final EventParam eventParam, Class<A> argumentType, final EventListener<A> listener) { |
---|
[105] | 271 | addListener(path, eventParam, listener, argumentType, new Future<Void>(getFrame()) { |
---|
[101] | 272 | @SuppressWarnings("unchecked") |
---|
[100] | 273 | @Override |
---|
| 274 | protected void result(Void result) { |
---|
| 275 | assert getFrame().isActive(); |
---|
| 276 | log.debug("registered gui listener for {} at {}", eventParam, path); |
---|
[101] | 277 | getOrCreateSideNote(getTree(), lock(), listenersTag).add(listener); |
---|
[100] | 278 | } |
---|
| 279 | }); |
---|
| 280 | } |
---|
| 281 | |
---|
| 282 | @Override |
---|
| 283 | public TreePanel getPanel() { |
---|
| 284 | if (panel != null) { |
---|
| 285 | return panel; |
---|
| 286 | } |
---|
| 287 | panel = getTreeAtFrame().preparePanel(param); |
---|
| 288 | return panel; |
---|
| 289 | } |
---|
| 290 | |
---|
[99] | 291 | } |
---|