source: java/main/src/main/java/com/framsticks/core/TreeOperations.java @ 100

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

HIGHLIGHTS:

  • add <include/> to configuration
  • add side notes to tree
    • used to store arbitrary information alongside the tree structure
  • migrate to log4j2
    • supports lazy string evaluation of passed arguments
  • improve GUI tree
    • it stays in synchronization with actual state (even in high load test scenario)
  • improve panel management in GUI
  • make loading objects in GUI more lazy
  • offload parsing to connection receiver thread
    • info parsing
    • first step of objects parsing
  • fix connection parsing bug (eof in long values)
  • support zero-arguments procedure in table view

CHANGELOG:
Implement procedure calls from table view.

Refactorization around procedures in tables.

Add table editor for buttons.

Render buttons in the the list view.

Further improve Columns.

Add Column class for TableModel?.

Accept also non-arguments ProcedureParams? in tableView.

Increase maximal TextAreaControl? size.

Add tooltip to ProcedureControl?.

Fix bug of interpreting eofs in long values by connection reader.

Further rework connection parsing.

Simplify client connection processing.

Test ListChange? modification.

Test ListChange? events with java server.

Add TestChild?.

Fix bug with fast deregistering when connecting to running server.

Another minor refactorization in TreeOperations?.

Fix bug in SimpleAbstractAccess? loading routine.

Another minor improvement.

Minor change.

Make reading of List objects two-phase.

Another minor change.

Dispatch parsing into receiver thread.

Another step.

Enclose passing value in ObjectParam? case in closure.

Minor step.

Minor change on way to offload parsing.

Temporarily comment out single ValueParam? get.

It will be generalized to multi ValueParam?.

Process info in receiver thread.

Add DispatchingExceptionHandler?.

Make waits in browser test longer.

Use FETCHED_MARK.

It is honored in GUI, where it used to decide whether to get values

after user action.

It is set in standard algorithm for processing fetched values.

Add remove operation to side notes.

Make loading more lazy.

Improve loading policy.

On node choose load itself, on node expansion, load children.

Minor improvement.

Fix bug with panel interleaving.

Minor improvements.

Improve panel management.

More cleaning around panels.

Reorganize panels.

Further improve tree.

Fix bug in TreeModel?.

Remove children from TreeNode?.

Implement TreeNode? hashCode and equals.

Make TreeNode? delegate equals and hashcode to internal reference.

Move listeners from TreeNode? to side notes.

Store path.textual as a side note.

Side note params instead of accesses for objects.

More refactorizations.

In TreeNode? bindAccess based on side notes.

Minor step.

Hide createAccess.

Rename AccessInterface? to Access.

Minor changes.

Several improvements in high load scenarios.

Change semantics of ArrayListAccess?.set(index, null);

It now removes the element, making list shorter
(it was set to null before).

Add path remove handler.

Handle exceptions in Connection.

Update .gitignore

Configure logging to file.

Move registration to TreeModel?.

Further refactorization.

Minor refactorization.

Minor improvements.

Use specialized event also for Modify action of ListChange?.

Use remove events.

Use the insertion events for tree.

Further improve tree refreshing.

Further improve reacting on events in GUI.

Fix problem with not adding objects on addition list change.

Migrate to log4j lazy String construction interface.

Migrate imports to log4j2.

Drop dependency on adapter to version 1.2.

Switch log4j implementation to log4j2.

Add dirty mark to the NodeAtFrame?.

Make selecting in AccessInterfaces? type safe.

Ignore containers size settings in Model and Genotype.

Use tree side notes to remember local changes and panels.

Add sideNotes to tree.

They will be used to store various accompanying information
right in the tree.

Use ReferenceIdentityMap? from apache in TreeNode?.

It suits the need perfectly (weak semantics on both key and value).

Make ArrayListParam? do not react size changes.

Guard in TableModel? before not yet loaded objects.

Add <include/> clause and AutoInjector?.

Extract common columns configuration to separate xml,
that can be included by other configurations.

File size: 10.8 KB
Line 
1package com.framsticks.core;
2
3
4import java.util.HashSet;
5import java.util.List;
6import java.util.Set;
7
8import javax.annotation.Nonnull;
9
10import org.apache.logging.log4j.Logger;
11import org.apache.logging.log4j.LogManager;
12
13import com.framsticks.communication.File;
14import com.framsticks.params.Access;
15import com.framsticks.params.CompositeParam;
16import com.framsticks.params.EventListener;
17import com.framsticks.params.FramsClass;
18import com.framsticks.params.ListAccess;
19import com.framsticks.params.Param;
20import com.framsticks.params.ParamBuilder;
21import com.framsticks.params.PrimitiveParam;
22import com.framsticks.params.PropertiesAccess;
23import com.framsticks.params.UniqueListAccess;
24import com.framsticks.params.Util;
25import com.framsticks.params.types.EventParam;
26import com.framsticks.params.types.ObjectParam;
27import com.framsticks.params.types.ProcedureParam;
28import com.framsticks.parsers.Loaders;
29import com.framsticks.parsers.MultiParamLoader;
30import com.framsticks.util.FramsticksException;
31import com.framsticks.util.dispatching.Dispatching;
32import com.framsticks.util.dispatching.Future;
33import com.framsticks.util.dispatching.FutureHandler;
34import com.framsticks.util.dispatching.RunAt;
35
36import static com.framsticks.util.dispatching.Dispatching.*;
37
38public final class TreeOperations {
39
40        private static final Logger log = LogManager.getLogger(TreeOperations.class);
41
42        private TreeOperations() {
43        }
44
45        public static final Object FETCHED_MARK = new Object();
46
47        public static @Nonnull FramsClass processFetchedInfo(Tree tree, File file) {
48                assert tree.isActive();
49                FramsClass framsClass = Loaders.loadFramsClass(file.getContent());
50                log.debug("process fetched info for {}: {}", tree, framsClass);
51                tree.putInfoIntoCache(framsClass);
52                return framsClass;
53        }
54
55        public static Path create(Path path) {
56                assert !path.isResolved();
57
58                Access access = path.getTree().prepareAccess(path.getTop().getParam());
59                Object child = createAccessee(path.getTree(), access);
60                assert child != null;
61                if (path.size() == 1) {
62                        path.getTree().assignRootObject(child);
63                } else {
64                        Access parentAccess = bindAccess(path.getUnder());
65
66                        /** this special case is not very good - maybe hide it in createAccessee? */
67                        if (parentAccess instanceof UniqueListAccess) {
68                                access.select(child);
69                                access.set(((UniqueListAccess) parentAccess).getUidName(), path.getTop().getParam().getId());
70                        }
71
72                        parentAccess.set(path.getTop().getParam(), child);
73                }
74                path = path.appendResolution(child);
75                return path;
76        }
77
78        public static void processFetchedValues(final Path path, final List<File> files, final Access access, final Future<Path> future) {
79                assert files.size() == 1;
80                assert path.isTheSame(files.get(0).getPath());
81
82
83                try {
84                        log.debug("process fetched values: {}", path);
85                        final Access parsingAccess = new PropertiesAccess(access.getFramsClass());
86                        final List<Object> results = MultiParamLoader.loadAll(files.get(0).getContent(), parsingAccess);
87
88                        Dispatching.dispatchIfNotActive(path.getTree(), new RunAt<Tree>(future) {
89                                @Override
90                                protected void runAt() {
91
92                                        Path result = path.tryResolveIfNeeded();
93
94                                        if (!result.isResolved()) {
95                                                result = create(result);
96                                        }
97
98                                        if (path.getTop().getParam() instanceof ObjectParam) {
99                                                assert results.size() == 1;
100                                                Util.takeAllNonNullValues(bindAccess(result), parsingAccess.select(results.get(0)));
101                                                mark(result.getTree(), result.getTopObject(), FETCHED_MARK, true);
102                                                future.pass(result);
103                                                return;
104                                        }
105
106
107                                        final ListAccess listAccess = (ListAccess) access;
108
109                                        listAccess.select(result.getTopObject());
110                                        Set<String> oldValuesIds = new HashSet<>();
111                                        for (Param p : listAccess.getParams()) {
112                                                oldValuesIds.add(p.getId());
113                                        }
114
115                                        Access targetAccess = listAccess.getElementAccess();//.cloneAccess();
116
117                                        int number = 0;
118                                        for (Object r : results) {
119
120                                                parsingAccess.select(r);
121                                                String id;
122                                                if (listAccess instanceof UniqueListAccess) {
123                                                        id = parsingAccess.get(((UniqueListAccess) listAccess).getUidName(), String.class);
124                                                } else {
125                                                        id = Integer.toString(number);
126                                                }
127                                                ++number;
128
129                                                Object childTo = listAccess.get(id, Object.class);
130                                                boolean newOne;
131                                                if (childTo == null) {
132                                                        childTo = createAccessee(result.getTree(), targetAccess);
133                                                        newOne = true;
134                                                } else {
135                                                        assert oldValuesIds.contains(id);
136                                                        newOne = false;
137                                                }
138                                                oldValuesIds.remove(id);
139
140                                                targetAccess.select(childTo);
141                                                Util.takeAllNonNullValues(targetAccess, parsingAccess);
142                                                if (newOne) {
143                                                        listAccess.set(id, childTo);
144                                                }
145                                                mark(result.getTree(), childTo, FETCHED_MARK, true);
146
147                                        }
148                                        mark(result.getTree(), result.getTopObject(), FETCHED_MARK, true);
149
150                                        /** It looks tricky for ArrayListAccess but should also work.
151                                         *
152                                         * They should be sorted.
153                                         */
154                                        for (String id : oldValuesIds) {
155                                                listAccess.set(id, null);
156                                        }
157                                        future.pass(result);
158                                }
159                        });
160
161                } catch (FramsticksException e) {
162                        throw new FramsticksException().msg("exception occurred while loading").cause(e);
163                }
164        }
165
166        public static FramsClass getInfo(Path path) {
167                Tree tree = path.getTree();
168                assert tree.isActive();
169                log.debug("get info for: {}", path);
170                final String name = path.getTop().getParam().getContainedTypeName();
171                return tree.getInfoFromCache(name);
172        }
173
174        public static void findInfo(final Path path, final Future<FramsClass> future) {
175                log.debug("find info for: {}", path);
176                try {
177                        Tree tree = path.getTree();
178                        assert tree.isActive();
179                        final FramsClass framsClass = getInfo(path);
180                        if (framsClass != null) {
181                                future.pass(framsClass);
182                                return;
183                        }
184                        tree.info(path, future);
185                } catch (FramsticksException e) {
186                        future.handle(e);
187                }
188        }
189
190
191        public static @Nonnull Access bindAccessFromSideNote(Tree tree, Object object) {
192                CompositeParam param = tree.getSideNote(object, CompositeParam.class, CompositeParam.class);
193                if (param == null) {
194                        throw new FramsticksException().msg("failed to bind access from side node").arg("tree", tree).arg("object", object).arg("type", object.getClass());
195                }
196                return tree.prepareAccess(param).select(object);
197        }
198
199        public static @Nonnull Access bindAccess(Tree tree, String path) {
200                log.debug("bind access for textual: {} in {}", path, tree);
201                return bindAccess(Path.to(tree, path));
202        }
203
204        public static @Nonnull Access bindAccess(Node node) {
205                Tree tree = node.getTree();
206                assert tree.isActive();
207                assert node.getObject() != null;
208
209                try {
210                        Access access = tree.prepareAccess(node.getParam());
211                        tree.putSideNote(node.getObject(), CompositeParam.class, node.getParam());
212
213                        return access.select(node.getObject());
214                } catch (FramsticksException e) {
215                        throw new FramsticksException().msg("failed to prepare access for param").arg("param", node.getParam()).cause(e);
216                        // log.error("failed to bind access for {}: ", node.getParam(), e);
217                }
218        }
219
220        public static @Nonnull Access bindAccess(Path path) {
221                assert path.getTree().isActive();
222                path.assureResolved();
223                log.debug("bind access for: {}", path);
224                return bindAccess(path.getTop());
225        }
226
227        public static void set(final Path path, final PrimitiveParam<?> param, final Object value, final Future<Integer> future) {
228                final Tree tree = path.getTree();
229
230                dispatchIfNotActive(tree, new RunAt<Tree>(future) {
231                        @Override
232                        protected void runAt() {
233                                tree.set(path, param, value, future);
234                        }
235                });
236        }
237
238        public static void call(final Path path, final String procedureName, final Object[] arguments, final Future<Object> future) {
239                final Tree tree = path.getTree();
240
241                dispatchIfNotActive(tree, new RunAt<Tree>(future) {
242                        @Override
243                        protected void runAt() {
244                                path.assureResolved();
245                                tree.call(path, tree.getRegistry().getFramsClass(path.getTop().getParam()).getParamEntry(procedureName, ProcedureParam.class), arguments, future);
246                        }
247                });
248        }
249
250        public static void call(final Path path, final ProcedureParam param, final Object[] arguments, final Future<Object> future) {
251                final Tree tree = path.getTree();
252
253                dispatchIfNotActive(tree, new RunAt<Tree>(future) {
254                        @Override
255                        protected void runAt() {
256                                tree.call(path, param, arguments, future);
257                        }
258                });
259        }
260
261        public static <A> void addListener(final Path path, final EventParam param, final EventListener<A> listener, final Class<A> argument, final Future<Void> future) {
262                final Tree tree = path.getTree();
263
264                dispatchIfNotActive(tree, new RunAt<Tree>(future) {
265                        @Override
266                        protected void runAt() {
267                                tree.addListener(path, param, listener, argument, future);
268                        }
269                });
270        }
271
272        public static void removeListener(final Path path, final EventParam param, final EventListener<?> listener, final Future<Void> future) {
273                final Tree tree = path.getTree();
274
275                dispatchIfNotActive(tree, new RunAt<Tree>(future) {
276                        @Override
277                        protected void runAt() {
278                                tree.removeListener(path, param, listener, future);
279                        }
280                });
281        }
282
283
284        /**
285         *
286         * If StackOverflow occurs in that loop in LocalTree it is probably caused
287         * the by the fact, that get operation may find the object, but Path resolution
288         * cannot.
289         * */
290        public static void tryGet(final Tree tree, final String targetPath, final Future<Path> future) {
291                log.debug("resolve textual: {} for {}", targetPath, tree);
292                dispatchIfNotActive(tree, new RunAt<Tree>(future) {
293
294                        @Override
295                        protected void runAt() {
296                                final Path path = Path.tryTo(tree, targetPath).tryResolveIfNeeded();
297                                log.debug("found: {}", path);
298                                if (path.isResolved()) {
299                                        future.pass(path);
300                                        return;
301                                }
302
303                                tree.get(path, new FutureHandler<Path>(future) {
304                                        @Override
305                                        protected void result(Path result) {
306                                                // if (result.isResolved(targetPath)) {
307                                                //      future.pass(result);
308                                                //      return;
309                                                // }
310                                                log.debug("retrying resolve textual: {} for {} with {}", targetPath, tree, result);
311                                                tryGet(tree, targetPath, future);
312                                        }
313                                });
314                        }
315                });
316        }
317
318        public static FramsClass getInfoFromCache(Path path) {
319                assert path.getTree().isActive();
320                return path.getTree().getInfoFromCache(path.getTop().getParam().getContainedTypeName());
321        }
322
323        public static Object createAccessee(Tree tree, CompositeParam param) {
324                Object object = tree.prepareAccess(param).createAccessee();
325                tree.putSideNote(object, CompositeParam.class, param);
326                return object;
327        }
328
329        public static Object createAccessee(Tree tree, Access access) {
330                Object object = access.createAccessee();
331                tree.putSideNote(object, CompositeParam.class, access.buildParam(new ParamBuilder()).finish(CompositeParam.class));
332                return object;
333        }
334
335        public static boolean isMarked(Tree tree, Object object, Object mark, boolean defValue) {
336                assert tree.isActive();
337                Boolean v = tree.getSideNote(object, mark, Boolean.class);
338                return (v != null ? v : defValue);
339        }
340
341        public static void mark(Tree tree, Object object, Object mark, boolean value) {
342                assert tree.isActive();
343                tree.putSideNote(object, mark, value);
344        }
345
346        public static FramsClass getFramsClass(Path path) {
347                return path.getTree().getRegistry().getFramsClass(path.getTop().getParam());
348        }
349
350}
Note: See TracBrowser for help on using the repository browser.