source: java/main/src/main/java/com/framsticks/parsers/MultiParamLoader.java @ 193

Last change on this file since 193 was 193, checked in by Maciej Komosinski, 10 years ago

Set svn:eol-style native for all textual files

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