source: java/main/src/main/java/com/framsticks/gui/TreeNode.java @ 96

Last change on this file since 96 was 96, checked in by psniegowski, 11 years ago

HIGHLIGHTS:

  • cleanup Instance management
    • extract Instance interface
    • extract Instance common algorithms to InstanceUtils?
  • fix closing issues: Ctrl+C or window close button

properly shutdown whole program

by Java Framsticks framework

  • fix parsing and printing of all request types
  • hide exception passing in special handle method of closures
    • substantially improve readability of closures
    • basically enable use of exception in asynchronous closures

(thrown exception is transported back to the caller)

  • implement call request on both sides

CHANGELOG:
Further improve calling.

Improve instance calling.

Calling is working on both sides.

Improve exception handling in testing.

Waiters do not supercede other apllication exception being thrown.

Finished parsing and printing of all request types (with tests).

Move implementation and tests of request parsing to Request.

Add tests for Requests.

Improve waits in asynchronours tests.

Extract more algorithms to InstanceUtils?.

Extract Instance.resolve to InstanceUtils?.

Improve naming.

Improve passing exception in InstanceClient?.

Hide calling of passed functor in StateCallback?.

Hide Exception passing in asynchronous closures.

Hide exception passing in Future.

Make ResponseCallback? an abstract class.

Make Future an abstract class.

Minor change.

Move getPath to Path.to()

Move bindAccess to InstanceUtils?.

Extract common things to InstanceUtils?.

Fix synchronization bug in Connection.

Move resolve to InstanceUtils?.

Allow names of Joinable to be dynamic.

Add support for set request server side.

More fixes in communication.

Fix issues with parsing in connection.

Cut new line characters when reading.

More improvements.

Migrate closures to FramsticksException?.

Several changes.

Extract resolveAndFetch to InstanceUtils? algorithms.

Test resolving and fetching.

More fixes with function signature deduction.

Do not print default values in SimpleAbstractAccess?.

Add test of FramsClass? printing.

Improve FramsticksException? messages.

Add explicit dispatcher synchronization feature.

Rework assertions in tests.

Previous solution was not generic enough.

Allow addition of joinables to collection after start.

Extract SimulatorInstance? from RemoteInstance?.

Remove PrivateJoinableCollection?.

Improve connections.

Move shutdown hook to inside the Monitor.

It should work in TestNG tests, but it seems that
hooks are not called.

In ServerTest? client connects to testing server.

Move socket initialization to receiver thread.

Add proper closing on Ctrl+C (don't use signals).

Fix bugs with server accepting connections.

Merge Entity into Joinable.

Reworking ServerInstance?.

Extract more algorithm to InstanceUtils?.

Extract some common functionality from AbstractInstance?.

Functions were placed in InstanceUtils?.

Hide registry of Instance.

Use ValueParam? in Instance interface.

Minor change.

Extract Instance interface.

Old Instance is now AbstractInstance?.

File size: 13.6 KB
Line 
1package com.framsticks.gui;
2
3import com.framsticks.communication.Connection;
4import com.framsticks.communication.Subscription;
5import com.framsticks.communication.util.LoggingStateCallback;
6import com.framsticks.core.Instance;
7import com.framsticks.core.InstanceUtils;
8import com.framsticks.core.ListChange;
9import com.framsticks.core.Path;
10import com.framsticks.gui.controls.ValueControl;
11import com.framsticks.gui.view.TreeCellRenderer;
12import com.framsticks.params.AccessInterface;
13import com.framsticks.params.CompositeParam;
14import com.framsticks.params.FramsClass;
15import com.framsticks.params.types.*;
16import com.framsticks.remote.*;
17import com.framsticks.util.lang.Casting;
18import com.framsticks.util.lang.Holder;
19import com.framsticks.util.swing.TooltipConstructor;
20import com.framsticks.util.dispatching.Future;
21import com.framsticks.util.AbstractStateFunctor;
22import com.framsticks.util.Logging;
23import org.apache.log4j.Logger;
24import com.framsticks.util.dispatching.RunAt;
25
26import javax.swing.tree.DefaultMutableTreeNode;
27import java.util.HashMap;
28import java.util.LinkedList;
29import java.util.List;
30import java.util.Map;
31import static com.framsticks.util.lang.Containers.filterInstanceof;
32import com.framsticks.util.swing.TreeNodeUtils;
33import static com.framsticks.core.InstanceUtils.*;
34
35/**
36 * @author Piotr Sniegowski
37 */
38@SuppressWarnings("serial")
39public class TreeNode extends DefaultMutableTreeNode implements NodeListener {
40
41        private static final Logger log = Logger.getLogger(TreeNode.class.getName());
42
43        Panel panel;
44
45        final protected Frame frame;
46        final protected InstanceAtFrame instanceAtFrame;
47
48        final protected Map<EventParam, Subscription<?>> userSubscriptions = new HashMap<>();
49        protected Map<ValueControl, Object> localChanges = null;
50        protected String tooltip;
51        protected String name;
52        protected final String paramId;
53        protected String iconName;
54        protected Path path;
55
56        public TreeNode(InstanceAtFrame instanceAtFrame, final Path path) {
57                this.frame = instanceAtFrame.getFrame();
58                assert frame.isActive();
59                this.paramId = path.getLastElement();
60                this.name = this.paramId;
61                log.debug("creating treenode " + name + ": " + path);
62                this.path = path;
63                this.instanceAtFrame = instanceAtFrame;
64
65                iconName = TreeCellRenderer.findIconName(name, path.getTextual());
66                tooltip = "?";
67                path.getInstance().dispatch(new RunAt<Instance>() {
68                        @Override
69                        public void run() {
70                                updateDescriptions(path);
71                        }
72                });
73        }
74
75        public void fetch(final Path p) {
76                assert p.getInstance().isActive();
77                log.debug("fetching: " + p);
78                p.getInstance().fetchValues(p, new AbstractStateFunctor() {
79                        @Override
80                        public void call() {
81                                // reactForFetchResult(p, e);
82                        }
83                });
84        }
85
86        protected void reactForFetchResult(final Path p, Exception e) {
87                assert p.getInstance().isActive();
88                if (Logging.log(log, "fetch", TreeNode.this, e)) {
89                        frame.dispatch(new RunAt<Frame>() {
90                                @Override
91                                public void run() {
92                                        log.debug("removing node from tree: " + p);
93                                        frame.treeModel.removeNodeFromParent(TreeNode.this);
94                                }
95                        });
96                        return;
97                }
98                updateChildren(p);
99                frame.dispatch(new RunAt<Frame>() {
100                        @Override
101                        public void run() {
102                                //TODO maybe this should be called from some better place
103                                useOrCreatePanel();
104                        }
105                });
106        }
107
108        protected void postUpdatePath(final Path newPath) {
109                assert !frame.isActive();
110                /** TODO those two actions could be merged into single closure */
111                frame.dispatch(new RunAt<Frame>() {
112                        @Override
113                        public void run() {
114                                updatePath(newPath);
115                        }
116                });
117                updateDescriptions(newPath);
118        }
119
120        protected void updatePath(Path newPath) {
121                assert frame.isActive();
122                if (!path.isResolved()) {
123                        path = newPath;
124                        log.debug("updated treenode's path: " + path);
125                        return;
126                }
127                if (path.matches(newPath)) {
128                        return;
129                }
130                log.debug("removing all children due to path update");
131                this.removeAllChildren();
132                frame.treeModel.nodeStructureChanged(this);
133        }
134
135        public Iterable<TreeNode> childrenIterable() {
136                return filterInstanceof(TreeNodeUtils.children(TreeNode.this, frame.treeModel), TreeNode.class);
137        }
138
139        /** Update children, by removing non existing and adding new ones.
140         */
141        protected void updateChildren(final Path p) {
142                assert p.getInstance().isActive();
143                log.debug("updating children of " + this);
144                AccessInterface access = InstanceUtils.bindAccess(p.getInstance(), p.getTop());
145                if (access == null) {
146                        return;
147                }
148                final List<Path> childrenPaths = new LinkedList<Path>();
149                /**Prepare path for each child.*/
150                for (CompositeParam param : filterInstanceof(access.getParams(), CompositeParam.class)) {
151                        childrenPaths.add(p.appendParam(param).tryFindResolution());
152                }
153
154                /**If some child were found, update in frame context.*/
155                if (childrenPaths.size() > 0) {
156                        frame.dispatch(new RunAt<Frame>() {
157                                @Override
158                                public void run() {
159                                        // TreePath treePath = frame.startChange();
160                                        updatePath(p);
161                                        Map<String, TreeNode> current = new HashMap<String, TreeNode>();
162                                        for (TreeNode n : childrenIterable()) {
163                                                current.put(n.path.getLastElement(), n);
164                                        }
165                                        assert current.size() == TreeNode.this.getChildCount();
166                                        assert TreeNode.this.getChildCount() == frame.treeModel.getChildCount(TreeNode.this);
167                                        log.trace("current " + TreeNode.this.getChildCount() + " children: " + current.values());
168                                        for (Path childPath : childrenPaths) {
169                                                String e = childPath.getLastElement();
170                                                if (current.containsKey(e)) {
171                                                        current.remove(e);
172                                                        continue;
173                                                }
174                                                log.debug("update: treenode for " + p);
175                                                TreeNode childNode = new TreeNode(instanceAtFrame, childPath);
176
177                                                frame.addNode(childNode, TreeNode.this);
178                                        }
179                                        for (TreeNode n : current.values()) {
180                                                frame.treeModel.removeNodeFromParent(n);
181                                        }
182                                        log.debug("updated children of " + TreeNode.this + ": " + TreeNode.this.getChildCount());
183                                }
184                        });
185                } else {
186                        log.debug("no children in " + TreeNode.this);
187                }
188        }
189
190        public void select() {
191                final Path p = path;
192
193                p.getInstance().dispatch(new RunAt<Instance>() {
194                        @Override
195                        public void run() {
196                                final Path updated = (p.isResolved()) ? p : p.tryFindResolution();
197                                if (updated.isResolved()) {
198                                        Logging.log(log, "update", updated, null);
199                                        fetch(updated);
200                                        postUpdatePath(updated);
201                                        return;
202                                }
203                                p.getInstance().resolve(updated, new Future<Path>(Logging.logger(log, "resolve and select", TreeNode.this)) {
204                                        @Override
205                                        protected void result(Path result) {
206                                                fetch(result);
207                                                postUpdatePath(result);
208                                        }
209                                });
210                        }
211                });
212
213        }
214
215        @Override
216        public String toString() {
217                return path.toString();
218        }
219
220        public final Panel getPanel() {
221                assert frame.isActive();
222                return panel;
223        }
224
225        protected void updateDescriptions(Path p) {
226                assert p.getInstance().isActive();
227
228                if (!p.isResolved()) {
229                        return;
230                }
231                AccessInterface access = InstanceUtils.bindAccess(p);
232
233                final String tooltip = new TooltipConstructor()
234                        .append("frams", access.getId())
235                        .append("java", p.getTopObject().getClass().getCanonicalName())
236                        .append("access", access.getClass().getSimpleName())
237                        .append("name", name)
238                        .append("id", paramId)
239                        .append("object", Integer.toHexString(System.identityHashCode(this)))
240                        .build()
241                        ;
242
243                StringParam nameParam = Casting.tryCast(StringParam.class, access.getParam("name"));
244                final String name = (nameParam != null ? access.get(nameParam, String.class) : path.getTop().getParam().getId());
245
246                frame.dispatch(new RunAt<Frame>() {
247                        @Override
248                        public void run() {
249                                TreeNode.this.tooltip = tooltip;
250                                TreeNode.this.name = name;
251
252                                log.debug("updated descriptions for " + path + ": " + name);
253                        }
254                });
255        }
256
257/*
258        public void updateData() {
259                assert browser.isActive();
260                final Node node = getNode();
261                browser.manager.invokeLater(new Runnable() {
262                        @Override
263                        public void run() {
264                                node.fetchValues(new StateFunctor() {
265                                        @Override
266                                        public void call(Exception e) {
267                                                if (e != null) {
268                                                        return;
269                                                }
270                                                browser.invokeLater(new Runnable() {
271                                                        @Override
272                                                        public void run() {
273                                                                assert browser.isActive();
274                                                                if (panel.getCurrentTreeNode() == TreeNode.this) {
275                                                                        panel.refreshComponents();
276                                                                }
277
278                                                                browser.tree.repaint();
279                                                                panel.refreshComponents();
280                                                        }
281                                                });
282                                        }
283                                });
284                        }
285                });
286        }
287*/
288
289        public void showPanel() {
290                assert frame.isActive();
291                assert panel != null;
292                frame.showPanel(panel);
293        }
294
295        public void fillPanelWithValues() {
296                assert frame.isActive();
297                if (panel == null) {
298                        return;
299                }
300                final Path p = path;
301                assert p.isResolved();
302                panel.setCurrentTreeNode(this);
303                p.getInstance().dispatch(new RunAt<Instance>() {
304                        @Override
305                        public void run() {
306                                AccessInterface access = InstanceUtils.bindAccess(p);
307                                panel.pullValuesFromLocalToUser(access);
308
309                                frame.dispatch(new RunAt<Frame>() {
310                                        @Override
311                                        public void run() {
312                                                showPanel();
313                                        }
314                                });
315                        }
316                });
317
318        }
319
320        public void useOrCreatePanel() {
321                assert frame.isActive();
322                if (panel != null) {
323                        log.trace("panel is already attached: " + path);
324                        fillPanelWithValues();
325                        return;
326                }
327                if (!path.isResolved()) {
328                        log.trace("path is not resolved: " + path);
329                        return;
330                }
331
332                CompositeParam param = path.getTop().getParam();
333                panel = instanceAtFrame.findPanel(param.computeAccessId());
334                if (panel != null) {
335                        log.debug("found prepared panel for: " + path);
336                        fillPanelWithValues();
337                        return;
338                }
339                final Path p = path;
340                log.debug("preparing panel: " + p);
341                p.getInstance().dispatch(new RunAt<Instance>() {
342                        @Override
343                        public void run() {
344                                assert p.getInstance().isActive();
345                                final CompositeParam param = p.getTop().getParam();
346                                final FramsClass framsClass = p.getInstance().getInfoFromCache(param.getContainedTypeName());
347                                frame.dispatch(new RunAt<Frame>() {
348                                        @Override
349                                        public void run() {
350                                                panel = instanceAtFrame.preparePanel(param, framsClass);
351                                                fillPanelWithValues();
352                                        }
353                                });
354                        }
355                });
356        }
357
358        public String getTooltip() {
359                assert frame.isActive();
360                return tooltip;
361        }
362
363
364
365
366        /*public void subscribe(final EventParam eventParam) {
367                assert browser.isActive();
368                if (hasSubscribed(eventParam)) {
369                        log.error(eventParam + " is already subscribed for " + this);
370                        return;
371                }
372                Node node = getNode();
373                node.getConnection().subscribe(node.getPath() + "/" + eventParam.getId(), new SubscriptionCallback() {
374                        @Override
375                        public EventCallback subscribed(final Subscription subscription) {
376                                if (subscription == null) {
377                                        log.error("subscription failed");
378                                        return null;
379                                }
380                                if (hasSubscribed(eventParam)) {
381                                        //abort subscription
382                                        return null;
383                                }
384                                userSubscriptions.put(eventParam, subscription);
385                                log.debug("subscription succeeded: " + subscription);
386                                subscription.setDispatcher(browser);
387                                return new EventCallback() {
388                                        @Override
389                                        public void call(SourceInterface content) {
390                                                assert browser.isActive();
391                                                log.info("event " + subscription + " occurred");
392                                        }
393                                };
394                        }
395                });
396
397        }*/
398
399        public boolean hasSubscribed(EventParam param) {
400                assert frame.isActive();
401                return userSubscriptions.containsKey(param);
402        }
403
404        public void unsubscribe(EventParam eventParam) {
405                assert frame.isActive();
406                if (!hasSubscribed(eventParam)) {
407                        log.error("could not unsubscribe from " + eventParam);
408                        return;
409                }
410                userSubscriptions.get(eventParam).unsubscribe(new LoggingStateCallback<Connection>(log, "unsubscribed " + eventParam));
411                userSubscriptions.remove(eventParam);
412        }
413
414/*
415
416        @Override
417        public void onChildChange(final Child child, ListChange.Action action) {
418                assert browser.manager.isActive();
419
420                switch (action) {
421                        case Remove: {
422                                Dispatching.invokeDispatch(browser, browser.manager, new Runnable() {
423                                        @Override
424                                        public void run() {
425                                                assert browser.manager.isActive();
426                                                final TreeNode treeNode = (TreeNode) child.getUserObject();
427                                                if (treeNode == null) {
428                                                        log.error("child " + child + " had no tree node attached");
429                                                        return;
430                                                }
431                                                browser.invokeLater(new Runnable() {
432                                                        @Override
433                                                        public void run() {
434                                                                assert browser.isActive();
435                                                                TreePath path = browser.startChange();
436                                                                //assert treeNode.getParent() == TreeNode.this;
437                                                                if (treeNode.getParent() != null) {
438                                                                        browser.treeModel.removeNodeFromParent(treeNode);
439                                                                }
440                                                                //remove(treeNode);
441                                                                browser.markNodeChanged(TreeNode.this, path);
442                                                        }
443                                                });
444                                        }
445                                });
446                                break;
447                        }
448                }
449
450        }
451*/
452
453        public String getName() {
454                return name;
455        }
456
457        public String getIconName() {
458                return iconName;
459        }
460
461        @Override
462        public void onUpgrade(Path path) {
463        }
464
465        @Override
466        public void onChange(Path path) {
467        }
468
469        @Override
470        public void onChildChange(Path path, ListChange.Action action) {
471        }
472
473        public final Frame getFrame() {
474                return frame;
475        }
476
477        public boolean changeValue(ValueControl component, Object newValue) {
478                log.debug("changing value of " + component + " to '" + newValue + "'");
479
480                if (localChanges == null) {
481                        localChanges = new HashMap<ValueControl, Object>();
482                }
483                localChanges.put(component, newValue);
484
485                return true;
486        }
487
488        public Path getInstancePath() {
489                assert frame.isActive();
490                return path;
491        }
492
493        public void pushLocalChanges() {
494                assert frame.isActive();
495                if (localChanges == null) {
496                        return;
497                }
498                Map<ValueControl, Object> changes = localChanges;
499                localChanges = null;
500                final Holder<Integer> counter = new Holder<>(changes.size());
501                final Path p = path;
502
503                for (Map.Entry<ValueControl, Object> e : localChanges.entrySet()) {
504                        storeValue(p, e.getKey().getParam(), e.getValue(), new AbstractStateFunctor() {
505                                @Override
506                                public void call() {
507                                        counter.set(counter.get() - 1);
508                                        if (counter.get() != 0) {
509                                                return;
510                                        }
511                                        log.debug("applied changes for: " + p);
512                                        frame.dispatch(new RunAt<Frame>() {
513                                                @Override
514                                                public void run() {
515                                                        fillPanelWithValues();
516                                                }
517                                        });
518                                }
519                        });
520                }
521        }
522}
Note: See TracBrowser for help on using the repository browser.