source: java/main/src/main/java/com/framsticks/parsers/F0Parser.java @ 87

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

HIGHLIGHTS:

  • FramsClass? and contained Param are now immutable classes (like String),

which allows to refer to them concurrently without synchronization
(which for example in turn simplifies GUI management)

  • also make Path immutable (which was earlier only assumed)
  • add global cache for FramsClasses? created solely and automatically

on base of Java classes.

representations basing on given FramsClass?

  • above changes greatly improved GUI responsivness during browsing
  • furtherly improve Param class hierarchy
  • allow to inject actions on state changes into MultiParamLoader?
  • add more tests

CHANGELOG:

Add StatusListener? to MultiParamLoader?.

Minor refactorization in MultiParamLoader?.

First step with auto append.

Add SchemaTest?.

Improve Registry.

Clean up in Registry.

Work out Registry.

Use annotations for Param.

Fix ListChange?.

Improve fluent interface of the FramsClassBuilder?.

Done caching of ReflectionAccess?.Backend

Fix hashCode of Pair.

A step on a way to cache ReflectionAccess?.Backend

Make SimpleAbstractAccess?.framsClass a final field.

Add static cache for FramsClasses? based on java.

Only classes created strictly and automatically
based on java classes are using this cache.

Make all Params immutable.

Many improvement to make Param immutable.

Make PrimitiveParam? generic type.

Several changes to make Param immutable.

Make FramsClass? immutable.

Another improvement to Path immutability.

Several improvements to Path.

Improve PathTest?.

Configurarable MutabilityDetector?.

File size: 7.7 KB
Line 
1package com.framsticks.parsers;
2
3import java.io.BufferedReader;
4import java.io.IOException;
5import java.io.InputStream;
6import java.io.InputStreamReader;
7import java.text.ParseException;
8import java.util.ArrayList;
9import java.util.Collection;
10import java.util.List;
11
12import com.framsticks.model.Model;
13import static com.framsticks.params.SimpleAbstractAccess.*;
14
15import com.framsticks.params.Flags;
16import com.framsticks.params.Param;
17import com.framsticks.params.PrimitiveParam;
18import com.framsticks.util.lang.Containers;
19import com.framsticks.util.lang.Pair;
20import com.framsticks.util.lang.Strings;
21import org.apache.log4j.Logger;
22
23import com.framsticks.params.FramsClass;
24import com.framsticks.params.AccessInterface;
25
26/**
27 * The class Parser is used to parse genotype encoded in f0 representation.
28 */
29public class F0Parser {
30
31        private final static Logger log = Logger.getLogger(F0Parser.class);
32
33        /** The schema proper for f0 representation. */
34        protected final Schema schema;
35        protected final InputStream is;
36        protected final List<AccessInterface> result = new ArrayList<AccessInterface>();
37        int lineNumber = 0;
38
39        public F0Parser(Schema schema, InputStream is) {
40                assert schema != null;
41                assert is != null;
42                this.schema = schema;
43                this.is = is;
44        }
45
46        protected AccessInterface processLine(String line) {
47                try {
48
49                        Pair<String, String> p = Strings.splitIntoPair(line, ':', "");
50                        String classId = p.first.trim();
51                        FramsClass framsClass = schema.getRegistry().getInfoFromCache(classId);
52                        if (framsClass == null) {
53                                throw new Exception("unknown class id: " + classId);
54                        }
55                        AccessInterface access = schema.getRegistry().createAccess(classId, framsClass);
56                        access.select(access.createAccessee());
57                        for (Exception e : loadFromLine(access, p.second)) {
58                                warn(lineNumber, "however entry was added", e);
59                        }
60                        return access;
61                } catch (Exception e) {
62                        warn(lineNumber, "entry was not added", e);
63                }
64                return null;
65        }
66
67        /**
68         * Parses the stream with genotype in f0 representation. The correctness of
69         * genotype is checked. IO and syntax exceptions interrupts parsing and no
70         * result is returned. Other exceptions, connected with schema validation
71         * cause that certain object or it's parameter is ignored (appropriate
72         * communicate informs user about it). Inappropriate values in numeric
73         * fields (bigger than maximum or smaller than minimum values) are
74         * communicated by warnings and set to minimum / maximum value.
75         *
76         * @return the list
77         * @throws IOException
78         *             Signals that an I/O exception has occurred.
79         * @throws ParseException
80         *             the parse exception
81         */
82        public List<AccessInterface> parse() throws IOException,
83                        ParseException {
84
85                try (InputStreamReader reader = new InputStreamReader(is, "UTF-8")) {
86                        BufferedReader br = new BufferedReader(reader);
87                        while (br.ready()) {
88                                ++lineNumber;
89                                String line = br.readLine();
90                                line = (line == null ? "" : line.trim());
91                                if (lineNumber == 1) {
92                                        if (!"//0".equals(line)) {
93                                                log.warn("stream should begin with \"//0\" in the first line");
94                                        } else {
95                                                continue;
96                                        }
97                                }
98                                if (line.equals("")) {
99                                        continue;
100                                }
101                                if (line.startsWith("#")) {
102                                        continue;
103                                }
104                                AccessInterface access = processLine(line);
105                                if (access != null) {
106                                        result.add(access);
107                                }
108                        }
109
110                        /** If no 'm' (Model) line was found, than simulate it on the beginning of the result.*/
111                        if (result.isEmpty() || !(result.get(0) instanceof Model)) {
112                                result.add(0, processLine("m:"));
113                        }
114                }
115
116                return result;
117        }
118
119        private static void warn(int lineNumber, String message, Exception e) {
120                log.warn("in line " + lineNumber + " the following error occurred (" + message + "): " + e);
121        }
122
123        /** Breaks string into entries.*/
124        public List<Entry> breakIntoEntries(String parameters) throws Exception {
125                // tokenize
126                boolean inQuotes = false;
127                char previousChar = ',';
128                List<Entry> result = new ArrayList<Entry>();
129                StringBuilder stringBuilder = new StringBuilder();
130                String key = null;
131                if (parameters.trim().length() > 0) {
132                        for (char currentChar : parameters.toCharArray()) {
133                                if (!inQuotes && (currentChar == '=') && (key == null)) {
134                                        key = stringBuilder.toString().trim();
135                                        stringBuilder = new StringBuilder();
136                                } else if (!inQuotes && currentChar == ',') {
137                                        if (previousChar == ',') {
138                                                result.add(new Entry(key, null));
139                                        } else {
140                                                result.add(new Entry(key, stringBuilder.toString().trim()));
141                                        }
142                                        stringBuilder = new StringBuilder();
143                                        key = null;
144                                } else if (currentChar == '"') {
145                                        if (previousChar == '\\') {
146                                                stringBuilder.deleteCharAt(stringBuilder.length() - 1);
147                                                stringBuilder.append(currentChar);
148                                        } else {
149                                                inQuotes = !inQuotes;
150                                        }
151                                } else {
152                                        stringBuilder.append(currentChar);
153                                }
154
155                                previousChar = currentChar;
156                        }
157
158                        result.add(new Entry(key, stringBuilder.toString().trim()));
159
160                        if (inQuotes) {
161                                throw new Exception("Double quotes expected while end of line met");
162                        }
163                }
164                return result;
165        }
166
167        public List<Exception> loadFromLine(AccessInterface access, String parameters) throws Exception {
168
169                List<Entry> entries = breakIntoEntries(parameters);
170
171                List<Exception> exceptions = new ArrayList<Exception>();
172
173                Collection<Param> paramsC = access.getParams();
174                Param[] params = paramsC.toArray(new Param[] {null});
175                if (params.length == 0) {
176                        return exceptions;
177                }
178                for (PrimitiveParam<?> p : Containers.filterInstanceof(paramsC, PrimitiveParam.class)) {
179                        Object def = p.getDef(Object.class);
180                        if (def != null) {
181                                access.set(p, def);
182                        }
183                }
184
185                int number = -1;
186                Integer nextParamNumber = 0;
187                for (Entry pair : entries) {
188                        ++number;
189                        try {
190                                Param currentParam;
191                                if (pair.key != null) {
192                                        currentParam = access.getParam(pair.key);
193                                        if (currentParam == null) {
194                                                nextParamNumber = null;
195                                                throw new Exception("no parameter with such id: " + pair.key);
196                                        }
197                                } else {
198                                        if (nextParamNumber == null || ((params[nextParamNumber].getFlags() & Flags.CANOMITNAME) == 0)) {
199                                                nextParamNumber = null;
200                                                throw new Exception(
201                                                                "parameter with offset: "
202                                                                                + number
203                                                                                + " is not set, "
204                                                                                + "because it's definition or definition of the previous param "
205                                                                                + "does not contain flag, which allows to skip the name (flag 1024)");
206                                        }
207                                        currentParam = params[nextParamNumber];
208                                }
209                                if (currentParam != null) {
210                                        if (pair.value != null) {
211                                                PrimitiveParam<?> vp = (PrimitiveParam<?>) currentParam;
212                                                int setFlag = access.set(vp, pair.value);
213                                                if ((setFlag & Flags.PSET_HITMIN) != 0) {
214                                                        exceptions.add(createBoundaryHitException(access, vp, pair.value, Flags.PSET_HITMIN));
215                                                }
216
217                                                if ((setFlag & Flags.PSET_HITMAX) != 0) {
218                                                        exceptions.add(createBoundaryHitException(access, vp, pair.value, Flags.PSET_HITMAX));
219                                                }
220
221                                                if ((setFlag & Flags.PSET_RONLY) != 0) {
222                                                        throw (new Exception("tried to set a read-only attribute \""
223                                                                + currentParam.getId()
224                                                                + "\" in class \"" + access.getId() + "\""));
225                                                }
226                                        }
227                                        nextParamNumber = null;
228                                        for (int j = params.length - 1; j > 0; --j) {
229                                                if (params[j - 1] == currentParam) {
230                                                        nextParamNumber = j;
231                                                }
232                                        }
233                                }
234
235                        } catch (Exception e) {
236                                exceptions.add(e);
237                        }
238                }
239                return exceptions;
240        }
241
242        private static Exception createBoundaryHitException(AccessInterface access, PrimitiveParam<?> param, String value, int flag) {
243                boolean minimum = (flag & Flags.PSET_HITMIN) != 0;
244                String boundary = (minimum ? param.getMin(Object.class) : param.getMax(Object.class)).toString();
245                String name =  (minimum ? "minimum" : "maximum");
246                return new Exception("Tried to set attribute \""
247                                + param.getId()
248                                + "\" in class \""
249                                + access.getId()
250                                + "\" to value which exceeds " + name + " ("
251                                + value
252                                + "), truncated to: "
253                                + boundary);
254        }
255}
Note: See TracBrowser for help on using the repository browser.