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

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

Add f0 parsing and f0->Model transformation.

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