1 | package com.framsticks.gui.tree; |
---|
2 | |
---|
3 | import java.lang.ref.WeakReference; |
---|
4 | import java.util.Iterator; |
---|
5 | import java.util.LinkedList; |
---|
6 | |
---|
7 | import org.apache.logging.log4j.LogManager; |
---|
8 | import org.apache.logging.log4j.Logger; |
---|
9 | |
---|
10 | import com.framsticks.gui.Frame; |
---|
11 | import com.framsticks.gui.ImageProvider; |
---|
12 | import com.framsticks.gui.TreeAtFrame; |
---|
13 | import com.framsticks.gui.TreePanel; |
---|
14 | import com.framsticks.params.Access; |
---|
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; |
---|
21 | import com.framsticks.structure.Node; |
---|
22 | import com.framsticks.structure.Path; |
---|
23 | import com.framsticks.structure.SideNoteKey; |
---|
24 | import com.framsticks.structure.Tree; |
---|
25 | import com.framsticks.util.FramsticksException; |
---|
26 | import com.framsticks.util.dispatching.Future; |
---|
27 | import com.framsticks.util.lang.Casting; |
---|
28 | import com.framsticks.util.lang.Containers; |
---|
29 | import com.framsticks.util.swing.TooltipConstructor; |
---|
30 | |
---|
31 | import static com.framsticks.structure.TreeOperations.*; |
---|
32 | |
---|
33 | public class TreeNode extends AbstractNode { |
---|
34 | |
---|
35 | private static final Logger log = LogManager.getLogger(TreeNode.class); |
---|
36 | |
---|
37 | protected final WeakReference<Object> reference; |
---|
38 | protected final int hashCode; |
---|
39 | protected final TreeAtFrame treeAtFrame; |
---|
40 | protected final String textual; |
---|
41 | protected final CompositeParam param; |
---|
42 | protected TreePanel panel; |
---|
43 | |
---|
44 | public TreeModel getTreeModel() { |
---|
45 | return treeAtFrame.getFrame().getTreeModel(); |
---|
46 | } |
---|
47 | |
---|
48 | /** |
---|
49 | * @param reference |
---|
50 | */ |
---|
51 | public TreeNode(TreeAtFrame treeAtFrame, Path path) { |
---|
52 | path.assureResolved(); |
---|
53 | |
---|
54 | this.reference = new WeakReference<Object>(path.getTopObject()); |
---|
55 | this.textual = path.getTextual(); |
---|
56 | this.treeAtFrame = treeAtFrame; |
---|
57 | this.param = path.getTop().getParam(); |
---|
58 | hashCode = System.identityHashCode(path.getTopObject()); |
---|
59 | |
---|
60 | if (isMarked(path.getTree(), path.getTopObject(), getTreeModel().createdTag, false)) { |
---|
61 | return; |
---|
62 | } |
---|
63 | |
---|
64 | // path.getTree().putSideNote(path.getTopObject(), Textual.class, path.getTextual()); |
---|
65 | mark(path.getTree(), path.getTopObject(), getTreeModel().createdTag, true); |
---|
66 | |
---|
67 | /** Iterate over all EventParams and for matching ValueParams register listeners. */ |
---|
68 | if (path.getTop().getParam() instanceof ObjectParam) { |
---|
69 | Access access = bindAccess(path); |
---|
70 | for (EventParam eventParam : Containers.filterInstanceof(access.getParams(), EventParam.class)) { |
---|
71 | if (!eventParam.getId().endsWith("_changed")) { |
---|
72 | continue; |
---|
73 | } |
---|
74 | String valueId = eventParam.getId().substring(0, eventParam.getId().length() - 8); |
---|
75 | final ValueParam valueParam = Casting.tryCast(ValueParam.class, access.getParam(valueId)); |
---|
76 | if (valueParam == null) { |
---|
77 | continue; |
---|
78 | } |
---|
79 | getTreeModel().registerForEventParam(this, path, eventParam, valueParam); |
---|
80 | } |
---|
81 | } |
---|
82 | } |
---|
83 | |
---|
84 | @Override |
---|
85 | public int hashCode() { |
---|
86 | return hashCode; |
---|
87 | } |
---|
88 | |
---|
89 | @Override |
---|
90 | public boolean equals(Object obj) { |
---|
91 | if (obj instanceof TreeNode) { |
---|
92 | return lock() == ((TreeNode) obj).lock(); |
---|
93 | } |
---|
94 | return false; |
---|
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 | } |
---|
103 | Tree tree = getTree(); |
---|
104 | Access access = bindAccessForTreeObject(referent); |
---|
105 | |
---|
106 | final int count = access.getCompositeParamCount(); |
---|
107 | if (number >= count) { |
---|
108 | throw new FramsticksException().msg("invalid state - no child"); |
---|
109 | } |
---|
110 | |
---|
111 | /** textual path may be not valid anymore*/ |
---|
112 | CompositeParam childParam = access.getCompositeParam(number); |
---|
113 | |
---|
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) { |
---|
121 | } |
---|
122 | return new EmptyNode(getFrame(), childParam); |
---|
123 | |
---|
124 | } |
---|
125 | |
---|
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); |
---|
140 | } |
---|
141 | |
---|
142 | public Object lock() { |
---|
143 | return reference.get(); |
---|
144 | } |
---|
145 | |
---|
146 | @Override |
---|
147 | public int getIndexOfChild(Object child) { |
---|
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 | } |
---|
157 | final Access access = bindAccessForTreeObject(parentObject); |
---|
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 | } |
---|
166 | log.debug("{} not found in {}", child, this); |
---|
167 | return -1; |
---|
168 | } |
---|
169 | |
---|
170 | public Frame getFrame() { |
---|
171 | return getTreeAtFrame().getFrame(); |
---|
172 | } |
---|
173 | |
---|
174 | public TreeAtFrame getTreeAtFrame() { |
---|
175 | return treeAtFrame; |
---|
176 | } |
---|
177 | |
---|
178 | public Tree getTree() { |
---|
179 | return getTreeAtFrame().getTree(); |
---|
180 | } |
---|
181 | |
---|
182 | protected Path assurePath() { |
---|
183 | return Path.to(getTree(), getTextual()).assureResolved(); |
---|
184 | } |
---|
185 | |
---|
186 | @Override |
---|
187 | public String toString() { |
---|
188 | return getTextual(); |
---|
189 | } |
---|
190 | |
---|
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; |
---|
202 | } |
---|
203 | |
---|
204 | @Override |
---|
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 |
---|
216 | public boolean isLeaf() { |
---|
217 | Object referent = lock(); |
---|
218 | if (referent == null) { |
---|
219 | return true; |
---|
220 | } |
---|
221 | return bindAccessForTreeObject(referent).getCompositeParamCount() == 0; |
---|
222 | } |
---|
223 | |
---|
224 | protected Access bindAccessForTreeObject(Object child) { |
---|
225 | return bindAccessFromSideNote(getTree(), child); |
---|
226 | } |
---|
227 | |
---|
228 | @Override |
---|
229 | public void render(TreeCellRenderer renderer) { |
---|
230 | |
---|
231 | Object child = lock(); |
---|
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); |
---|
239 | CompositeParam param = getTree().getSideNote(child, Path.OBJECT_PARAM_KEY); |
---|
240 | String name = param.getId(); |
---|
241 | |
---|
242 | StringParam nameParam = Casting.tryCast(StringParam.class, access.getParam("name")); |
---|
243 | |
---|
244 | if (nameParam != null) { |
---|
245 | name = access.get(nameParam, String.class); |
---|
246 | } |
---|
247 | |
---|
248 | renderer.setToolTipText(new TooltipConstructor() |
---|
249 | .append("frams", access.getTypeId()) |
---|
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))) |
---|
255 | .append("size", access.getCompositeParamCount()) |
---|
256 | .build()); |
---|
257 | |
---|
258 | renderer.setIcon(ImageProvider.loadImage(TreeCellRenderer.findIconName(param))); |
---|
259 | renderer.setText(name); |
---|
260 | } |
---|
261 | |
---|
262 | |
---|
263 | public String getTextual() { |
---|
264 | return textual; |
---|
265 | } |
---|
266 | |
---|
267 | @SuppressWarnings("rawtypes") |
---|
268 | protected final SideNoteKey<LinkedList> listenersTag = SideNoteKey.make(LinkedList.class); |
---|
269 | |
---|
270 | protected <A> void tryAddListener(final Path path, final EventParam eventParam, Class<A> argumentType, final EventListener<A> listener) { |
---|
271 | addListener(path, eventParam, listener, argumentType, new Future<Void>(getFrame()) { |
---|
272 | @SuppressWarnings("unchecked") |
---|
273 | @Override |
---|
274 | protected void result(Void result) { |
---|
275 | assert getFrame().isActive(); |
---|
276 | log.debug("registered gui listener for {} at {}", eventParam, path); |
---|
277 | getOrCreateSideNote(getTree(), lock(), listenersTag).add(listener); |
---|
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 | |
---|
291 | } |
---|