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

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

HIGHLIGHTS:

  • add auto loading and saving algorithms between

frams files format and Java classes

  • respect ValueChange? events in GUI (do not reload object)
  • support results of procedures in Java server
  • make Experiment automatically convert between frams file and NetFile? object
  • add MessageLogger? (compatible with original frams server messages)
  • WorkPackageLogic? now validates results, is able to discard them, reschedule

whole package, or only uncomputed remainder

CHANGELOG:
Show just a short description in PrimeExperiment?.

Add primes_changed event to the PrimeExperiment?.

Make WorkPackageLogic? robust to frams server returning invalid results.

Add MessageLogger? to logics.

Add NetFile? interface. Support Messages from server.

Minor changes to connections.

Merge results in the PrimeExperiment?.

More netload class->file conversion to Simulator.

Move netsave parsing to Simulator.

Fix bug with inverted ordering of events firing in Experiment.

Minor changes.

Minor logging changes.

Use AccessOperations?.convert in NetLoadSaveLogic?

NetLoadSaveLogic? now encloses the conversion.

Use more generic AccessOperations? saveAll and loadAll in PrimePackage?.

Add Result class for enclosing of call invocations' results.

Improve feature request handling in Connections.

Use AccessOperations?.convert in RemoteTree? events parsing.

Minor change.

Add some information params to Java server root and CLI objects.

A draft implementation of loadAll algorithm.

That algorithm tries to load objects into a tree structure.

Add AccessOperationsTest? test.

Develop WorkPackageLogic?.

  • add state tracking fields
  • add work package generation

Add utility class SimplePrimitive?.

Meant for Java backend classes, enclose a single primitive value
and set of listeners.

Improve primitive value refresh in GUI.

When ValueChange? found in called event, do not reload whole
object, but only update GUI (no communication is performed).

Use ValueChange? in the TestClass? test.

Minor changes.

Sending all packages in PrimeExperiment? to the frams servers.

Develop AccessOperations?.loadComposites().

Remove addAccess from MultiParamLoader? interface.

There is now no default AccessProvider? in MultiParamLoader?.
User must explicitely set AccessStash? or Registry.

Improve saving algorithms in AccessOperations?.

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