source: java/main/src/main/java/com/framsticks/gui/tree/TreeModel.java @ 101

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

HIGHLIGHTS:

  • improve tree side notes
  • improve GUI layout
  • add foldable list of occured events to EventControl?
  • improve automatic type conversion in proxy listeners
  • implement several Access functionalities as algorithms independent of Access type
  • introduce draft base classes for distributed experiments
  • automatically register dependant Java classes to FramsClass? registry
  • add testing prime experiment and configuration
  • simplify and improve task dispatching

CHANGELOG:
Improve task dispatching in RemoteTree?.

GUI no longer hangs on connection problems.

Make all dispatchers joinables.

Refactorize Thread dispatcher.

Remove Task and PeriodicTask?.

Use Java utilities in those situations.

Reworking tasks dispatching.

Fix bug in EventControl? listener dispatching.

Minor improvements.

Add testing configuration for ExternalProcess? in GUI.

More improvement to prime.

Support for USERREADONLY in GUI.

Add that flag to various params in Java classes.

Remove redundant register clauses from several FramsClassAnnotations?.

Automatically gather and register dependant classes.

Add configuration for prime.

Improve Simulator class.

Add prime.xml configuration.

Introduce draft Experiment and Simulator classes.

Add prime experiment tests.

Enclose typical map with listeners into SimpleUniqueList?.

Needfile works in GUI.

Improve needfile handling in Browser.

More improvement with NeedFile?.

Implementing needfile.

Update test.

Rename ChangeEvent? to TestChangeEvent?.

Automatic argument type search in RemoteTree? listeners.

MultiParamLoader? uses AccessProvider?. By default old implementation
enclosed in AccessStash? or Registry.

Minor changes.

Rename SourceInterface? to Source.

Also improve toString of File and ListSource?.

Remove unused SimpleSource? class.

Add clearing in HistoryControl?.

Show entries in table at EventControl?.

Improve EventControl?.

Add listeners registration to EventControl?.

Add foldable table to HistoryControl?.

Add control row to Procedure and Event controls.

Improve layout of controls.

Another minor change to gui layout.

Minor improvement in the SliderControl?.

Minor changes.

Move ReflectionAccess?.Backend to separate file.

It was to cluttered.

Cleanup in ReflectionAccess?.

Move setMin, setMax, setDef to AccessOperations?.

Extract loading operation into AccessOperations?.

Append Framsticks to name of UnsupportedOperationException?.

The java.lang.UnsupportedOperationException? was shadowing this class.

Rename params.Util to params.ParamsUtil?.

Several improvements.

Minor changes.

Implement revert functionality.

Improve local changes management.

Minor improvement.

Remove methods rendered superfluous after SideNoteKey? improvement.

Improve SideNoteKey?.

It is now generic type, so explicit type specification at
call site is no more needed.

Introduce SideNoteKey? interface.

Only Objects implementing that key may be used as side note keys.

Minor improvements.

Use strings instead of ValueControls? in several gui mappings.

File size: 12.2 KB
Line 
1package com.framsticks.gui.tree;
2
3import java.util.Enumeration;
4import java.util.Iterator;
5import java.util.LinkedList;
6import java.util.List;
7
8import javax.annotation.Nullable;
9import javax.swing.event.TreeModelEvent;
10import javax.swing.event.TreeModelListener;
11import javax.swing.tree.TreePath;
12
13import org.apache.logging.log4j.Logger;
14import org.apache.logging.log4j.LogManager;
15
16import com.framsticks.core.ListChange;
17import com.framsticks.core.Node;
18import com.framsticks.core.Path;
19import com.framsticks.core.SideNoteKey;
20import com.framsticks.core.TreeOperations;
21import com.framsticks.gui.Frame;
22import com.framsticks.params.Access;
23import com.framsticks.params.CompositeParam;
24import com.framsticks.params.EventListener;
25import com.framsticks.params.ListAccess;
26import com.framsticks.params.PrimitiveParam;
27import com.framsticks.params.ParamsUtil;
28import com.framsticks.params.ValueParam;
29import com.framsticks.params.types.EventParam;
30import com.framsticks.util.FramsticksException;
31import com.framsticks.util.Misc;
32import com.framsticks.util.FramsticksUnsupportedOperationException;
33import com.framsticks.util.dispatching.FutureHandler;
34import com.framsticks.util.lang.Casting;
35
36import static com.framsticks.core.TreeOperations.*;
37
38public class TreeModel implements javax.swing.tree.TreeModel {
39        private static final Logger log = LogManager.getLogger(TreeModel.class);
40
41
42        protected List<TreeModelListener> listeners = new LinkedList<>();
43
44
45        protected final Frame frame;
46
47        /**
48         * @param frame
49         */
50        public TreeModel(Frame frame) {
51                this.frame = frame;
52        }
53
54        @Override
55        public void addTreeModelListener(TreeModelListener listener) {
56                listeners.add(listener);
57        }
58
59        @Override
60        public Object getChild(Object parent, int number) {
61                return Casting.throwCast(AbstractNode.class, parent).getChild(number);
62        }
63
64        @Override
65        public int getChildCount(Object parent) {
66                return Casting.throwCast(AbstractNode.class, parent).getChildCount();
67        }
68
69        @Override
70        public int getIndexOfChild(Object parent, Object child) {
71                if ((parent == null) || (child == null)) {
72                        return -1;
73                }
74                return Casting.throwCast(AbstractNode.class, parent).getIndexOfChild(child);
75        }
76
77        @Override
78        public MetaNode getRoot() {
79                return frame.getRootNode();
80        }
81
82        @Override
83        public boolean isLeaf(Object node) {
84                return Casting.throwCast(AbstractNode.class, node).isLeaf();
85        }
86
87        @Override
88        public void removeTreeModelListener(TreeModelListener listener) {
89                listeners.remove(listener);
90        }
91
92        @Override
93        public void valueForPathChanged(TreePath path, Object value) {
94                throw new FramsticksUnsupportedOperationException().msg("changing value of tree node");
95        }
96
97
98        protected boolean changing = false;
99
100        public void treeNodesInserted(TreeModelEvent event) {
101                assert frame.isActive();
102                try {
103                        for (TreeModelListener listener : listeners) {
104                                listener.treeNodesInserted(event);
105                        }
106                } catch (ArrayIndexOutOfBoundsException e) {
107                }
108        }
109
110        public void treeNodesRemoved(TreeModelEvent event) {
111                assert frame.isActive();
112                try {
113                        for (TreeModelListener listener : listeners) {
114                                listener.treeNodesRemoved(event);
115                        }
116                } catch (ArrayIndexOutOfBoundsException e) {
117                }
118        }
119
120        public void treeNodesChanged(TreeModelEvent event) {
121                try {
122                        for (TreeModelListener listener : listeners) {
123                                listener.treeNodesChanged(event);
124                        }
125                } catch (ArrayIndexOutOfBoundsException e) {
126                }
127        }
128
129        public TreeModelEvent prepareModelEvent(TreePath treePath, int number, TreeNode node) {
130                return new TreeModelEvent(this, treePath, new int[] {number}, new Object[] { node });
131        }
132
133
134        public TreeModelEvent prepareModelEventRegarding(Access access, String id, TreePath treeListPath) {
135
136                int number = ParamsUtil.getNumberOfCompositeParamChild(access, access.get(id, Object.class));
137                if (number == -1) {
138                        log.debug("encountered minor tree inconsistency in {}", treeListPath);
139                        return null;
140                }
141                TreeNode node = Casting.throwCast(TreeNode.class, Casting.throwCast(TreeNode.class, treeListPath.getLastPathComponent()).getChild(number));
142                return prepareModelEvent(treeListPath, number, node);
143        }
144
145        public void treeStructureChanged(TreePath treePath) {
146
147                if (treePath == null) {
148                        return;
149                }
150                assert frame.isActive();
151
152                changing = true;
153                log.debug("changing structure: {}", treePath);
154                Enumeration<TreePath> expanded = frame.getJtree().getExpandedDescendants(treePath);
155                TreePath selection = frame.getJtree().getSelectionPath();
156
157                try {
158                        for (TreeModelListener listener : listeners) {
159                                listener.treeStructureChanged(new TreeModelEvent(this, treePath));
160                        }
161                } catch (ArrayIndexOutOfBoundsException e) {
162                }
163
164
165                if (expanded != null) {
166                        while (expanded.hasMoreElements()) {
167                                TreePath expansion = expanded.nextElement();
168                                // log.info("reexpanding: {}", expansion);
169                                frame.getJtree().expandPath(expansion);
170                        }
171                }
172
173                if (selection != null) {
174                        frame.getJtree().setSelectionPath(selection);
175                }
176                changing = false;
177        }
178
179        /**
180         *
181         * This method may return null on conversion failure, which may happen in highload situations.
182         */
183        public @Nullable Path convertToPath(TreePath treePath) {
184                final Object[] components = treePath.getPath();
185                assert components[0] == frame.getRootNode();
186                if (components.length == 1) {
187                        return null;
188                }
189                Path.PathBuilder builder = Path.build();
190                builder.tree(Casting.assertCast(TreeNode.class, components[1]).getTree());
191                List<Node> nodes = new LinkedList<>();
192                for (int i = 1; i < components.length; ++i) {
193                        TreeNode treeNode = Casting.tryCast(TreeNode.class, components[i]);
194                        if (treeNode == null) {
195                                return null;
196                        }
197                        Node node = treeNode.tryCreateNode();
198                        if (node == null) {
199                                return null;
200                                // throw new FramsticksException().msg("failed to recreate path").arg("treePath", treePath);
201                        }
202                        nodes.add(node);
203                }
204                builder.buildUpTo(nodes, null);
205
206                return builder.finish();
207        }
208
209        public TreePath convertToTreePath(Path path, boolean forceComplete) {
210                assert frame.isActive();
211
212                List<Object> accumulator = new LinkedList<Object>();
213                accumulator.add(getRoot());
214
215                for (Object r : getRoot().getChildren()) {
216                        if (r instanceof TreeNode) {
217                                TreeNode root = (TreeNode) r;
218                                if (root.getTree() == path.getTree()) {
219                                        Iterator<Node> n = path.getNodes().iterator();
220                                        TreeNode treeNode = root;
221                                        accumulator.add(root);
222                                        n.next();
223                                        while (n.hasNext()) {
224                                                Node node = n.next();
225                                                treeNode = treeNode.prepareTreeNodeForChild(Path.build().tree(path.getTree()).buildUpTo(path.getNodes(), node).finish());
226                                                if (treeNode == null) {
227                                                        break;
228                                                }
229                                                accumulator.add(treeNode);
230                                        }
231                                        break;
232                                }
233                        }
234                }
235                return new TreePath(accumulator.toArray());
236        }
237
238        /**
239         * @return the listeners
240         */
241        public List<TreeModelListener> getListeners() {
242                return listeners;
243        }
244
245        /**
246         * @return the changing
247         */
248        public boolean isChanging() {
249                return changing;
250        }
251
252        public void loadChildren(Path path, boolean reload) {
253                if (path == null) {
254                        return;
255                }
256                Access access = TreeOperations.bindAccess(path);
257
258                int count = access.getCompositeParamCount();
259                for (int i = 0; i < count; ++i) {
260                        Path childPath = path.appendParam(access.getCompositeParam(i)).tryFindResolution();
261                        loadPath(childPath, reload);
262                }
263        }
264
265        public void loadPath(Path path, boolean reload) {
266                if (path == null) {
267                        return;
268                }
269                if (!reload && path.isResolved() && isMarked(path.getTree(), path.getTopObject(), FETCHED_MARK, false)) {
270                        return;
271                }
272                path.getTree().get(path, new FutureHandler<Path>(frame) {
273                        @Override
274                        protected void result(Path result) {
275                                final TreePath treePath = convertToTreePath(result, true);
276
277
278                                if (treePath != null) {
279                                        treeStructureChanged(treePath);
280                                        frame.updatePanelIfIsLeadSelection(result);
281                                }
282                        }
283                });
284        }
285
286        public void expandTreeNode(TreePath treePath) {
287                assert frame.isActive();
288                if (treePath == null) {
289                        return;
290                }
291                if (isChanging()) {
292                        return;
293                }
294                Path path = convertToPath(treePath);
295                if (path == null) {
296                        return;
297                }
298                loadChildren(path.assureResolved(), false);
299        }
300
301        public void chooseTreeNode(final TreePath treePath) {
302                assert frame.isActive();
303                if (treePath == null) {
304                        return;
305                }
306                if (isChanging()) {
307                        return;
308                }
309
310                Path path = convertToPath(treePath);
311                if (path == null) {
312                        return;
313                }
314                path = path.assureResolved();
315
316                log.debug("choosing {}", path);
317                frame.showPanelForTreePath(treePath);
318                loadPath(path, false);
319
320        }
321
322
323        protected void registerForEventParam(final TreeNode treeNode, Path path, final EventParam eventParam, ValueParam valueParam) {
324                /** TODO make this listener not bind hold the reference to this TreeNode, maybe hold WeakReference internally */
325                if (valueParam instanceof PrimitiveParam) {
326
327                        treeNode.tryAddListener(path, eventParam, Object.class, new EventListener<Object>() {
328                                @Override
329                                public void action(Object argument) {
330                                        loadPath(treeNode.assurePath(), true);
331                                }
332                        });
333
334                } else if (valueParam instanceof CompositeParam) {
335
336                        final CompositeParam compositeParam = (CompositeParam) valueParam;
337
338                        treeNode.tryAddListener(path, eventParam, ListChange.class, new EventListener<ListChange>() {
339                                @Override
340                                public void action(ListChange listChange) {
341                                        assert treeNode.getTree().isActive();
342
343                                        Path parentPath = treeNode.assurePath();
344                                        final Path listPath = parentPath.appendParam(compositeParam).tryFindResolution();
345                                        if (!listPath.isResolved()) {
346                                                /** that situation is quietly ignored - it may happen if first event comes before the container was resolved */
347                                                return;
348                                        }
349
350                                        log.debug("reacting to change {} in {}", listChange, listPath);
351                                        final TreePath treeListPath = convertToTreePath(listPath, true);
352                                        if (treeListPath == null) {
353                                                throw new FramsticksException().msg("path was not fully converted").arg("path", listPath);
354                                        }
355
356                                        if ((listChange.getAction().equals(ListChange.Action.Modify)) && (listChange.getPosition() == -1)) {
357                                                // get(listPath, future);
358                                                // treeModel.nodeStructureChanged(treePath);
359                                                // frame.updatePanelIfIsLeadSelection(treePath, result);
360                                                return;
361                                        }
362                                        final String id = listChange.getBestIdentifier();
363
364                                        final ListAccess access = (ListAccess) bindAccess(listPath);
365                                        switch (listChange.getAction()) {
366                                                case Add: {
367                                                        Path childPath = listPath.appendParam(access.prepareParamFor(id)).tryFindResolution();
368                                                        if (!childPath.isResolved()) {
369                                                                childPath = create(childPath);
370
371                                                                TreeModelEvent event = prepareModelEventRegarding(access, id, treeListPath);
372                                                                if (event != null) {
373                                                                        treeNodesInserted(event);
374                                                                } else {
375                                                                        treeStructureChanged(treeListPath);
376                                                                }
377                                                                frame.updatePanelIfIsLeadSelection(listPath);
378                                                        }
379
380                                                        listPath.getTree().get(childPath, new FutureHandler<Path>(frame) {
381                                                                @Override
382                                                                protected void result(Path result) {
383                                                                        if (!result.isResolved()) {
384                                                                                log.warn("inconsistency after addition list change: {}", result);
385                                                                        }
386                                                                        assert frame.isActive();
387                                                                        final TreePath treePath = Misc.throwIfNull(frame.getTreeModel().convertToTreePath(result, true));
388
389                                                                        // treeModel.nodeStructureChanged(treePath);
390                                                                        frame.updatePanelIfIsLeadSelection(result);
391
392                                                                        log.debug("added {}({}) updated {}", id, result, treePath);
393                                                                }
394                                                        });
395                                                        break;
396                                                }
397                                                case Remove: {
398
399                                                        TreeModelEvent event = prepareModelEventRegarding(access, id, treeListPath);
400                                                        access.set(id, null);
401                                                        if (event != null) {
402                                                                treeNodesRemoved(event);
403                                                        } else {
404                                                                treeStructureChanged(treeListPath);
405                                                        }
406
407                                                        frame.updatePanelIfIsLeadSelection(listPath);
408
409                                                        break;
410                                                }
411                                                case Modify: {
412                                                        Path childPath = listPath.appendParam(access.prepareParamFor(id)).tryResolveIfNeeded();
413                                                        listPath.getTree().get(childPath, new FutureHandler<Path>(frame) {
414                                                                @Override
415                                                                protected void result(Path result) {
416                                                                        assert frame.isActive();
417                                                                        // final TreePath treePath = frame.getTreeModel().convertToTreePath(result, true);
418
419                                                                        TreeModelEvent event = prepareModelEventRegarding(access, id, treeListPath);
420                                                                        if (event != null) {
421                                                                                treeNodesChanged(event);
422                                                                        } else {
423                                                                                treeStructureChanged(treeListPath);
424                                                                        }
425
426                                                                        frame.updatePanelIfIsLeadSelection(listPath);
427                                                                        frame.updatePanelIfIsLeadSelection(result);
428                                                                }
429                                                        });
430                                                        break;
431                                                }
432                                        }
433                                }
434                        });
435                }
436
437        }
438
439
440
441        protected final SideNoteKey<Boolean> createdTag = SideNoteKey.make(Boolean.class);
442
443
444
445}
Note: See TracBrowser for help on using the repository browser.