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

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

HIGHLIGHTS:

  • import refactorization: move Tree, Path, etc.

from core to structure package

  • initial serialization implementation
  • improve PrimeExperiment? test
  • many organizational changes and convenience improvements

CHANGELOG:
Make registry in AbstractTree? final.

Move most classes from core to structure package.

Minor changes.

Switch names of Future and FutureHandler?.

Rename ExceptionResultHandler? to ExceptionHandler?.

Rename ExceptionHandler? to ExceptionDispatcherHandler?.

Fix bug in ParamCandidate? cache.

Add missing synchronization to the BufferedDispatcher?.

Develop @Serialized support.

Rework serialization further.

Add serialization/deserialization interface to ValueParam?.

Move getStorageType and isNumeric from Param down to params hierarchy.

Minor changes.

Improve param type induction.

Add TestSerializedClass? for testing new serialization.

Add info files gor GenePool? and Population.

Add standard.expt exemplary netfile.

Add type name field to PropertiesObject?.

Use PropertiesObject? for PropertiesAccess? instead of ordinary map.

Hide getFramsClass is several more places.

More unification accross FramsClass?, Access and Path.

Add ParamCollection?.

Simplify interface for getting params from FramsClass?, Access
or Path.

Make Access.call() interface variadic.

Add arguments(args) convenience wrapper around new Object[] {args}.

Upgrade to apache.commons.lang version 3.1

Minor improvement with Response constructors.

Develop proper result printing in ClientAtServer?.

Add experimentNetsave to PrimeExperiment?.

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