source: java/main/src/main/java/com/framsticks/parsers/MultiParamLoader.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: 9.9 KB
Line 
1package com.framsticks.parsers;
2
3import com.framsticks.params.*;
4import org.apache.logging.log4j.Logger;
5import org.apache.logging.log4j.LogManager;
6
7import java.util.*;
8
9public class MultiParamLoader {
10
11        public interface StatusListener {
12                void onStatusChange();
13        }
14
15        private final static Logger log = LogManager.getLogger(MultiParamLoader.class);
16
17        /**
18         * The class which name was recently found in the file, to which execution
19         * should be passed.
20         */
21        public Access getLastAccess() {
22                return lastAccess;
23        }
24
25        /** TODO does it support multilines? */
26        /**
27         * Specifies the condition to break execution.
28         */
29        public enum Status {
30                None, Finished, BeforeObject, AfterObject, BeforeUnknown, OnComment, OnError, Loading
31        }
32
33        protected final Map<Status, List<StatusListener>> listeners = new HashMap<>();
34
35        /**
36         * Specifies the action that should be taken inside loops.
37         */
38        private enum LoopAction {
39                Nothing, Break, Continue
40        }
41
42        protected Access lastAccess;
43
44        protected static final FramsClass emptyFramsClass = FramsClass.build().idAndName("<empty>").finish();
45        /**
46         * Empty Param representing unknown classes - used to omit unknown
47         * objects in the file.
48         */
49        protected Access emptyParam = new PropertiesAccess(emptyFramsClass);
50
51        /**
52         * Last comment found in the file.
53         */
54        protected String lastComment;
55
56        /**
57         * Set of break conditions.
58         */
59        private EnumSet<Status> breakConditions = EnumSet.of(Status.None);
60
61        /**
62         * Status of current execution.
63         */
64        private Status status = Status.None;
65
66
67        /**
68         * File from which data should be read.
69         */
70        private SourceInterface currentSource;
71
72        protected String currentLine;
73
74
75        /**
76         * All the files read by the loader (there could be many of them because of
77         * '#|include').
78         */
79        private Stack<String> fileStack = new Stack<String>();
80
81        /**
82         * A map that specifies connection between the getName of the file and the
83         * actual reader.
84         */
85        private Map<String, SourceInterface> fileMap = new HashMap<String, SourceInterface>();
86
87        /**
88         * List of known classes.
89         */
90        private Map<String, Access> knownParamInterfaces = new HashMap<String, Access>();
91
92        /**
93         * Last unknown object found in the file.
94         */
95        private String lastUnknownObjectName;
96
97        /**
98         * @return the currentLine
99         */
100        public String getCurrentLine() {
101                return currentLine;
102        }
103
104        public MultiParamLoader() {
105
106        }
107
108        /**
109         * Starts reading the file.
110         */
111        public Status go() {
112                log.trace("go");
113
114                while (!isFinished()) {
115                        // check if we are before some known or unknown object
116                        LoopAction loopAction = tryReadObject();
117                        if (loopAction == LoopAction.Break) {
118                                break;
119                        } else if (loopAction == LoopAction.Continue) {
120                                continue;
121                        }
122
123                        // read data
124                        currentLine = currentSource.readLine();
125
126                        // end of file
127                        if (currentLine == null) {
128                                if (!returnFromIncluded()) {
129                                        finish();
130                                        break;
131                                } else {
132                                        continue;
133                                }
134                        }
135                        log.trace("read line: {}", currentLine);
136
137                        // empty line
138                        if (currentLine.length() == 0) {
139                                continue;
140                        }
141
142                        // check if some file should be included
143                        if (isIncludeLine(currentLine) == LoopAction.Continue) {
144                                continue;
145                        }
146
147                        // check if should break on comment
148                        if (isCommentLine(currentLine) == LoopAction.Break) {
149                                break;
150                        }
151
152                        // get class getName
153                        LoopAction action = changeCurrentParamInterface(currentLine);
154                        if (action == LoopAction.Break) {
155                                break;
156                        }
157                        if (action == LoopAction.Continue) {
158                                continue;
159                        }
160
161                        // log.warn("unknown line: {}", currentLine);
162                        changeStatus(Status.OnError);
163                        if (action == LoopAction.Break) {
164                                break;
165                        }
166                        if (action == LoopAction.Continue) {
167                                continue;
168                        }
169                }
170
171                return status;
172        }
173
174        /**
175         * Checks whether the reader found a known or unknown object and execution
176         * should be passed to it.
177         * @throws Exception
178         */
179        private LoopAction tryReadObject() {
180                if (status == Status.BeforeObject || (status == Status.BeforeUnknown && lastAccess != null)) {
181                        // found object - let it load data
182                        if (lastAccess.getSelected() == null) {
183                                lastAccess.select(lastAccess.createAccessee());
184                        }
185                        log.trace("loading into {}", lastAccess);
186                        lastAccess.load(currentSource);
187
188                        if (changeStatus(Status.AfterObject)) {
189                                return LoopAction.Break;
190                        }
191                        return LoopAction.Continue;
192                } else if (status == Status.BeforeUnknown) {
193                        log.warn("omitting unknown object: {}", lastUnknownObjectName);
194
195                        // found unknown object
196                        emptyParam.load(currentSource);
197                        if (changeStatus(Status.AfterObject)) {
198                                return LoopAction.Break;
199                        }
200                        return LoopAction.Continue;
201                }
202
203                return LoopAction.Nothing;
204        }
205
206        /**
207         * Checks whether some additional file shouldn't be included.
208         */
209        private LoopAction isIncludeLine(String line) {
210                try {
211                        // found comment
212                        if (line.charAt(0) == '#') {
213                                // maybe we should include something
214                                if (line.substring(1, 8).equals("include")) {
215                                        int beg = line.indexOf('\"');
216                                        if (beg == -1) {
217                                                log.info("Wanted to include some file, but the format is incorrect");
218                                                return LoopAction.Continue;
219                                        }
220
221                                        String includeFileName = line.substring(beg + 1);
222                                        int end = includeFileName.indexOf('\"');
223                                        if (end == -1) {
224                                                log.info("Wanted to include some file, but the format is incorrect");
225                                                return LoopAction.Continue;
226                                        }
227
228                                        includeFileName = includeFileName.substring(0, end);
229
230                                        include(includeFileName);
231
232                                        return LoopAction.Continue;
233                                }
234                        }
235                } catch (IndexOutOfBoundsException ex) {
236                        // value after # sign is shorter than expected 7 characters - do
237                        // nothing
238                }
239
240                return LoopAction.Nothing;
241        }
242
243        /**
244         * Checks whether execution shouldn't break on comment.
245         */
246        private LoopAction isCommentLine(String line) {
247                if (line.charAt(0) == '#') {
248                        lastComment = line;
249                        if (changeStatus(Status.OnComment)) {
250                                // it's a simple comment - maybe we should break?
251                                return LoopAction.Break;
252                        }
253                }
254                return LoopAction.Nothing;
255        }
256
257        /**
258         * Gets the getName of the class from line read from file.
259         */
260        private LoopAction changeCurrentParamInterface(String line) {
261                // found key - value line
262                if (line.charAt(line.length() - 1) == ':') {
263                        String typeName = line.substring(0, line.length() - 1);
264                        lastAccess = knownParamInterfaces.get(typeName);
265
266                        if (lastAccess != null) {
267                                if (changeStatus(Status.BeforeObject)) {
268                                        return LoopAction.Break;
269                                } else {
270                                        return LoopAction.Continue;
271                                }
272                        } else {
273                                lastUnknownObjectName = typeName;
274                                if (changeStatus(Status.BeforeUnknown)) {
275                                        return LoopAction.Break;
276                                } else {
277                                        return LoopAction.Continue;
278                                }
279                        }
280                }
281                return LoopAction.Nothing;
282        }
283
284        /**
285         * Adds another break condition.
286         */
287        public void addBreakCondition(Status condition) {
288                breakConditions.add(condition);
289        }
290
291        /**
292         * Removes break condition.
293         */
294        public void removeBreakCondition(Status condition) {
295                breakConditions.remove(condition);
296        }
297
298        /**
299         * Adds another class.
300         */
301        public void addAccess(Access access) {
302                /**TODO: by id or by name? rather by id, because from file is always lowercase*/
303                knownParamInterfaces.put(access.getId(), access);
304        }
305
306        /**
307         * Checks whether execution is finished.
308         */
309        private boolean isFinished() {
310                return (status == Status.Finished);
311        }
312
313        private void finish() {
314                log.trace("finishing");
315                if (currentSource != null) {
316                        currentSource.close();
317                }
318
319                changeStatus(Status.Finished);
320        }
321
322        /**
323         * Opens selected file.
324         */
325
326        public boolean setNewSource(SourceInterface source) {
327                log.debug("switching current source to {}...", source.getFilename());
328
329                currentSource = source;
330                changeStatus(Status.Loading);
331
332                return true;
333        }
334
335        /**
336         * Includes specified file.
337         */
338        private void include(String includeFilename) {
339
340                includeFilename = currentSource.demangleInclude(includeFilename);
341
342                if (includeFilename == null) {
343                        return;
344                }
345                // check if it is already included and break if it is
346                if (isAlreadyIncluded(includeFilename)) {
347                        log.debug("circular reference ignored ({})", includeFilename);
348                        return;
349                }
350
351                log.info("including file {}...", includeFilename);
352
353                SourceInterface newSource = currentSource.openInclude(includeFilename);
354                if (newSource == null) {
355                        return;
356                }
357
358                fileStack.add(currentSource.getFilename());
359                fileMap.put(currentSource.getFilename(), currentSource);
360                setNewSource(newSource);
361
362        }
363
364        /**
365         * Checks whether selected file was already included.
366         */
367        private boolean isAlreadyIncluded(String filename) {
368                for (String file : fileStack) {
369                        if (filename.equals(file)) {
370                                log.warn("file {} was already included", filename);
371                                return true;
372                        }
373                }
374
375                return false;
376        }
377
378        /**
379         * Returns from included file.
380         */
381        private boolean returnFromIncluded() {
382                if (fileStack.size() == 0) {
383                        return false;
384                }
385
386                if (currentSource != null) {
387                        currentSource.close();
388                }
389
390                String filename = fileStack.pop();
391                currentSource = fileMap.get(filename);
392                fileMap.remove(filename);
393
394                return true;
395        }
396
397        /**
398         * Checks whether execution should break on selected condition.
399         */
400        private boolean changeStatus(Status status) {
401                log.trace("changing status: {} -> {}", this.status.toString(), status.toString());
402                this.status = status;
403                if (listeners.containsKey(status)) {
404                        for (StatusListener l : listeners.get(status)) {
405                                l.onStatusChange();
406                        }
407                }
408                return breakConditions.contains(status);
409        }
410
411        public Object returnObject() {
412                assert lastAccess != null;
413                Object result = lastAccess.getSelected();
414                if (result == null) {
415                        return null;
416                }
417                lastAccess.select(null);
418                return result;
419        }
420
421        public void addListener(Status status, StatusListener listener) {
422                if (!listeners.containsKey(status)) {
423                        listeners.put(status, new LinkedList<StatusListener>());
424                }
425                listeners.get(status).add(listener);
426        }
427
428        public static List<Object> loadAll(SourceInterface source, Access access) {
429                final List<Object> result = new LinkedList<>();
430
431                final MultiParamLoader loader = new MultiParamLoader();
432                loader.setNewSource(source);
433                loader.addAccess(access);
434                loader.addListener(MultiParamLoader.Status.AfterObject, new StatusListener() {
435                        @Override
436                        public void onStatusChange() {
437                                result.add(loader.returnObject());
438                        }
439                });
440                loader.go();
441                return result;
442        }
443}
Note: See TracBrowser for help on using the repository browser.