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

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

HIGHLIGHTS:

  • add proper exception passing between communication sides:

if exception occur during handling client request, it is
automatically passed as comment to error response.

it may be used to snoop communication between peers

  • fix algorithm choosing text controls in GUI
  • allow GUI testing in virtual frame buffer (xvfb)

FEST had some problem with xvfb but workaround was found

supports tab-completion based on requests history

CHANGELOG:
Further improve handling of exceptions in GUI.

Add StatusBar? implementing ExceptionResultHandler?.

Make completion processing asynchronous.

Minor changes.

Improve completion in console.

Improve history in InteractiveConsole?.

First working version of DirectConsole?.

Minor changes.

Make Connection.address non final.

It is more suitable to use in configuration.

Improvement of consoles.

Improve PopupMenu? and closing of FrameJoinable?.

Fix BrowserTest?.

Found bug with FEST running under xvfb.

JButtonFixture.click() is not working under xvfb.
GuiTest? has wrapper which uses JButton.doClick() directly.

Store CompositeParam? param in TreeNode?.

Simplify ClientSideManagedConnection? connecting.

There is now connectedFunctor needed, ApplicationRequests? can be
send right after creation. They are buffered until the version
and features are negotiated.

Narow down interface of ClientSideManagedConnection?.

Allow that connection specialization send only
ApplicationRequests?.

Improve policy of text control choosing.

Change name of Genotype in BrowserTest?.

Make BrowserTest? change name of Genotype.

Minor change.

First working draft of TrackConsole?.

Simplify Consoles.

More improvements with gui joinables.

Unify initialization on gui joinables.

More rework of Frame based entities.

Refactorize structure of JFrames based entities.

Extract GuiTest? from BrowserBaseTest?.

Reorganize Console classes structure.

Add Collection view to JoinableCollection?.

Configure timeout in testing.

Minor changes.

Rework connections hierarchy.

Add Mode to the get operation.

Make get and set in Tree take PrimitiveParam?.

Unify naming of operations.

Make RunAt? use the given ExceptionHandler?.

It wraps the virtual runAt() method call with
try-catch passing exception to handler.

Force RunAt? to include ExceptionHandler?.

Improve ClientAtServer?.

Minor change.

Another sweep with FindBugs?.

Rename Instance to Tree.

Minor changes.

Minor changes.

Further clarify semantics of Futures.

Add FutureHandler?.

FutureHandler? is refinement of Future, that proxifies
exception handling to ExceptionResultHandler? given
at construction time.

Remove StateFunctor? (use Future<Void> instead).

Make Connection use Future<Void>.

Unparametrize *ResponseFuture?.

Remove StateCallback? not needed anymore.

Distinguish between sides of ResponseFuture?.

Base ResponseCallback? on Future (now ResponseFuture?).

Make asynchronous store taking Future for flags.

Implement storeValue in ObjectInstance?.

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