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
RevLine 
[77]1package com.framsticks.parsers;
2
3import com.framsticks.params.*;
[103]4import com.framsticks.util.Misc;
5
[100]6import org.apache.logging.log4j.Logger;
7import org.apache.logging.log4j.LogManager;
[77]8
9import java.util.*;
10
11public class MultiParamLoader {
[87]12
13        public interface StatusListener {
14                void onStatusChange();
15        }
16
[100]17        private final static Logger log = LogManager.getLogger(MultiParamLoader.class);
[77]18
19        /**
20         * The class which name was recently found in the file, to which execution
21         * should be passed.
22         */
[100]23        public Access getLastAccess() {
24                return lastAccess;
[77]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
[87]34        protected final Map<Status, List<StatusListener>> listeners = new HashMap<>();
35
[77]36        /**
37         * Specifies the action that should be taken inside loops.
38         */
39        private enum LoopAction {
40                Nothing, Break, Continue
41        }
42
[100]43        protected Access lastAccess;
[77]44
[88]45        protected static final FramsClass emptyFramsClass = FramsClass.build().idAndName("<empty>").finish();
[77]46        /**
47         * Empty Param representing unknown classes - used to omit unknown
48         * objects in the file.
49         */
[100]50        protected Access emptyParam = new PropertiesAccess(emptyFramsClass);
[77]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         */
[86]65        private Status status = Status.None;
[77]66
[86]67
[77]68        /**
69         * File from which data should be read.
70         */
[101]71        private Source currentSource;
[77]72
[86]73        protected String currentLine;
[77]74
[86]75
[77]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         */
[101]86        private Map<String, Source> fileMap = new HashMap<String, Source>();
[77]87
88        /**
89         * List of known classes.
90         */
[103]91        protected AccessProvider accessProvider = null;
[77]92
93        /**
94         * Last unknown object found in the file.
95         */
96        private String lastUnknownObjectName;
97
[86]98        /**
99         * @return the currentLine
100         */
101        public String getCurrentLine() {
102                return currentLine;
103        }
104
[101]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
[77]119        public MultiParamLoader() {
120
121        }
122
123        /**
124         * Starts reading the file.
125         */
[97]126        public Status go() {
[103]127                Misc.throwIfNull(accessProvider);
[86]128                log.trace("go");
[77]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
[86]140                        currentLine = currentSource.readLine();
[77]141
142                        // end of file
[86]143                        if (currentLine == null) {
[77]144                                if (!returnFromIncluded()) {
145                                        finish();
146                                        break;
147                                } else {
148                                        continue;
149                                }
150                        }
[100]151                        log.trace("read line: {}", currentLine);
[77]152
153                        // empty line
[86]154                        if (currentLine.length() == 0) {
[77]155                                continue;
156                        }
157
158                        // check if some file should be included
[86]159                        if (isIncludeLine(currentLine) == LoopAction.Continue) {
[77]160                                continue;
161                        }
162
163                        // check if should break on comment
[86]164                        if (isCommentLine(currentLine) == LoopAction.Break) {
[77]165                                break;
166                        }
167
168                        // get class getName
[86]169                        LoopAction action = changeCurrentParamInterface(currentLine);
170                        if (action == LoopAction.Break) {
[77]171                                break;
172                        }
[86]173                        if (action == LoopAction.Continue) {
174                                continue;
175                        }
[97]176
[100]177                        // log.warn("unknown line: {}", currentLine);
[97]178                        changeStatus(Status.OnError);
179                        if (action == LoopAction.Break) {
180                                break;
181                        }
182                        if (action == LoopAction.Continue) {
183                                continue;
184                        }
[77]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.
[86]193         * @throws Exception
[77]194         */
[97]195        private LoopAction tryReadObject() {
[100]196                if (status == Status.BeforeObject || (status == Status.BeforeUnknown && lastAccess != null)) {
[77]197                        // found object - let it load data
[100]198                        if (lastAccess.getSelected() == null) {
199                                lastAccess.select(lastAccess.createAccessee());
[86]200                        }
[100]201                        log.trace("loading into {}", lastAccess);
[101]202                        AccessOperations.load(lastAccess, currentSource);
[77]203
[87]204                        if (changeStatus(Status.AfterObject)) {
[77]205                                return LoopAction.Break;
206                        }
207                        return LoopAction.Continue;
208                } else if (status == Status.BeforeUnknown) {
[100]209                        log.warn("omitting unknown object: {}", lastUnknownObjectName);
[77]210
211                        // found unknown object
[101]212                        AccessOperations.load(emptyParam, currentSource);
[87]213                        if (changeStatus(Status.AfterObject)) {
214                                return LoopAction.Break;
215                        }
[77]216                        return LoopAction.Continue;
217                }
218
219                return LoopAction.Nothing;
220        }
221
222        /**
223         * Checks whether some additional file shouldn't be included.
224         */
[97]225        private LoopAction isIncludeLine(String line) {
[77]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) {
[86]233                                                log.info("Wanted to include some file, but the format is incorrect");
[77]234                                                return LoopAction.Continue;
235                                        }
236
237                                        String includeFileName = line.substring(beg + 1);
238                                        int end = includeFileName.indexOf('\"');
239                                        if (end == -1) {
[86]240                                                log.info("Wanted to include some file, but the format is incorrect");
[77]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) {
[87]263                if (line.charAt(0) == '#') {
[77]264                        lastComment = line;
[87]265                        if (changeStatus(Status.OnComment)) {
266                                // it's a simple comment - maybe we should break?
267                                return LoopAction.Break;
268                        }
[77]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);
[101]280                        lastAccess = accessProvider.getAccess(typeName);
[77]281
[100]282                        if (lastAccess != null) {
[87]283                                if (changeStatus(Status.BeforeObject)) {
[77]284                                        return LoopAction.Break;
[86]285                                } else {
286                                        return LoopAction.Continue;
[77]287                                }
288                        } else {
289                                lastUnknownObjectName = typeName;
[87]290                                if (changeStatus(Status.BeforeUnknown)) {
[77]291                                        return LoopAction.Break;
[86]292                                } else {
293                                        return LoopAction.Continue;
[77]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() {
[86]322                log.trace("finishing");
[77]323                if (currentSource != null) {
324                        currentSource.close();
325                }
326
[87]327                changeStatus(Status.Finished);
[77]328        }
329
330        /**
331         * Opens selected file.
332         */
333
[101]334        public boolean setNewSource(Source source) {
[100]335                log.debug("switching current source to {}...", source.getFilename());
[77]336
337                currentSource = source;
[87]338                changeStatus(Status.Loading);
[77]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)) {
[100]355                        log.debug("circular reference ignored ({})", includeFilename);
[77]356                        return;
357                }
358
[100]359                log.info("including file {}...", includeFilename);
[77]360
[101]361                Source newSource = currentSource.openInclude(includeFilename);
[77]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)) {
[100]378                                log.warn("file {} was already included", filename);
[77]379                                return true;
380                        }
381                }
382
383                return false;
384        }
385
386        /**
387         * Returns from included file.
388         */
[97]389        private boolean returnFromIncluded() {
[77]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         */
[87]408        private boolean changeStatus(Status status) {
[100]409                log.trace("changing status: {} -> {}", this.status.toString(), status.toString());
[87]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);
[77]417        }
418
419        public Object returnObject() {
[100]420                assert lastAccess != null;
421                Object result = lastAccess.getSelected();
[77]422                if (result == null) {
423                        return null;
424                }
[100]425                lastAccess.select(null);
[77]426                return result;
427        }
[87]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        }
[100]435
[101]436        public static List<Object> loadAll(Source source, Access access) {
[100]437                final List<Object> result = new LinkedList<>();
438
439                final MultiParamLoader loader = new MultiParamLoader();
440                loader.setNewSource(source);
[103]441                loader.setAccessProvider(new AccessStash().add(access));
[100]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        }
[77]451}
Note: See TracBrowser for help on using the repository browser.