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