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

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

HIGHLIGHTS:

  • use java annotations to mark classes and fields to be used when:
    • using java classes with ReflectionAccess? to represent remote objects with FramsClass? description found by "info ..." requests
    • to build up FramsClass? representation of objects not present at remote server
  • allow using primitive types (instead of wraping counterparts) in reflected classes
  • rework FramsClass? creation process (add FramsClassBuilder?)
  • add more tests

CHANGELOG:
Prepare model.World class.

Minor change.

Use primitive types for Genotype and Creature classes.

Use primitive types in model.Neuro* classes.

Use primitive types in model.Joint* classes.

Use primitive types in model.Part* classes.

Fix primitive values.

Extract FramsClassBuilder?.

Add tests of Model classes.

More fixes.

Refactorize out ParamCandidate?.

Several fixes.

Fix all regressions after introducing annotations.

Use annotations throughout the project.

Add exception classes.

Improve creation of FramsClass?.

More changes.

Many changes regarding annotations.

Annotate classes in com.framsticks.model package.

Remove manual FramsClass? constructor.

Construct FramsClass? for Creature. Add test.

Add default values to the ParamAnnotation?.

Add ParamBuilderTest? and ParamAnnotation?.

Add FramsClassAnnotation?.

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.