Source code: mas_gui/Parser.java
1 /* Copyright 1998 - 2003: Jim Cochrane - see file forum.txt */
2
3 package mas_gui;
4
5 import java.lang.*;
6 import java.util.*;
7 import graph.*;
8
9 /** Market Analysis Parser - parses market and indicator data sent from
10 the Market Analysis server */
11 class Parser {
12 public static final int Date = 1, Open = 2, High = 3, Low = 4, Close = 5,
13 Volume = 6, Open_interest = 7, Not_set = 0;
14
15 // Constructor - fieldspecs specifies the fields format of each tuple -
16 // e.g., date, high, low, close, volume.
17 Parser(int fieldspecs[], String record_sep, String field_sep) {
18 parsetype = fieldspecs;
19 _record_separator = record_sep;
20 _field_separator = field_sep;
21
22 dates = new Vector();
23 times = new Vector();
24 float_field_count = float_fields(fieldspecs);
25 }
26
27 // Set the tuple field specifications.
28 // Precondition: fs != null
29 // Postcondition: field_specifications() == fs
30 void set_field_specifications(int[] fs) {
31 parsetype = fs;
32 }
33
34 // Set the drawer for drawing volume.
35 void set_volume_drawer(BasicDrawer d) {
36 volume_drawer = d;
37 }
38
39 // Set the drawer for open interest.
40 void set_open_interest_drawer(BasicDrawer d) {
41 open_interest_drawer = d;
42 }
43
44 // Specifications of the type (date, open, etc.) and order of each field
45 // in a tuple.
46 int[] field_specifications() {
47 return parsetype;
48 }
49
50 // Parse `s' into a DataSet according to record_separator and
51 // field_separator. `drawer' is the tuple drawer to use for the
52 // DataSet. result() gives the new DataSet.
53 public void parse(String s, BasicDrawer drawer) throws Exception {
54 int rec_count;
55 volumes = null;
56 open_interests = null;
57 is_intraday = contains_time_field(s);
58 StringTokenizer recs = new StringTokenizer(s, _record_separator, false);
59 rec_count = recs.countTokens();
60 clear_vectors();
61 if (has_field_type(Volume)) {
62 volumes = new double[rec_count];
63 }
64 if (has_field_type(Open_interest)) {
65 open_interests = new double[rec_count];
66 }
67 // If there is no open field
68 if (! has_field_type(Open) && has_field_type(High) &&
69 has_field_type(Low)) {
70 // Add 1 to make room for the "fake" open field.
71 value_data = new double[rec_count * (float_field_count + 1)];
72 parse_with_no_open(recs);
73 } else {
74 value_data = new double[rec_count * float_field_count];
75 parse_default(recs);
76 }
77 process_data(drawer);
78 }
79
80 // Parse fields - default routine
81 private void parse_default(StringTokenizer recs) throws Exception {
82 int float_index = 0, volume_index = 0, oi_index = 0;
83 while (recs.hasMoreTokens()) {
84 String s = recs.nextToken();
85 StringTokenizer fields = new StringTokenizer(s,
86 _field_separator, false);
87 for (int j = 0; fields.hasMoreTokens(); ++j) {
88 try {
89 switch (parsetype[j]) {
90 case Date:
91 dates.addElement(fields.nextToken());
92 if (is_intraday) {
93 times.addElement(fields.nextToken());
94 }
95 break;
96 case Open:
97 value_data[float_index++] =
98 parse_double(fields.nextToken());
99 break;
100 case High:
101 value_data[float_index++] =
102 parse_double(fields.nextToken());
103 break;
104 case Low:
105 value_data[float_index++] =
106 parse_double(fields.nextToken());
107 break;
108 case Close:
109 value_data[float_index++] =
110 parse_double(fields.nextToken());
111 break;
112 case Volume:
113 volumes[volume_index++] =
114 parse_double(fields.nextToken());
115 break;
116 case Open_interest:
117 open_interests[oi_index++] =
118 parse_double(fields.nextToken());
119 break;
120 }
121 }
122 catch (Exception e) {
123 System.err.println("Last record processed was dated " +
124 dates.elementAt(dates.size() - 1));
125 throw e;
126 }
127 }
128 }
129 }
130
131 // Parse fields - expecting high, low, and close fields (in that order),
132 // but NO open field.
133 private void parse_with_no_open(StringTokenizer recs) throws Exception {
134 int float_index = 0, volume_index = 0, oi_index = 0;
135 while (recs.hasMoreTokens()) {
136 StringTokenizer fields = new StringTokenizer(recs.nextToken(),
137 _field_separator, false);
138 int open_field_index = -1;
139 for (int j = 0; fields.hasMoreTokens(); ++j) {
140 try {
141 switch (parsetype[j]) {
142 case Date:
143 dates.addElement(fields.nextToken());
144 if (is_intraday) {
145 times.addElement(fields.nextToken());
146 }
147 // Save a place for the open field:
148 value_data[float_index] = 0;
149 open_field_index = float_index;
150 ++float_index;
151 break;
152 case High:
153 value_data[float_index++] =
154 parse_double(fields.nextToken());
155 break;
156 case Low:
157 value_data[float_index++] =
158 parse_double(fields.nextToken());
159 break;
160 case Close:
161 value_data[float_index++] =
162 parse_double(fields.nextToken());
163 if (open_field_index != -1) {
164 // Store the close value into the open field.
165 value_data[open_field_index] =
166 value_data[float_index - 1];
167 }
168 break;
169 case Volume:
170 volumes[volume_index++] =
171 parse_double(fields.nextToken());
172 break;
173 case Open_interest:
174 open_interests[oi_index++] =
175 parse_double(fields.nextToken());
176 break;
177 }
178 }
179 catch (Exception e) {
180 System.err.println("Last record processed was dated " +
181 dates.elementAt(dates.size() - 1));
182 throw e;
183 }
184 }
185 }
186 }
187
188 // Parsed data set result
189 DataSet result() {
190 return processed_data;
191 }
192
193 // Parsed volume
194 DataSet volume_result() {
195 return volume_data;
196 }
197
198 // Parsed open interest
199 DataSet open_interest_result() {
200 return oi_data;
201 }
202
203 public String record_separator() {
204 return _record_separator;
205 }
206
207 public String field_separator() {
208 return _field_separator;
209 }
210
211 // Implementation
212
213 double parse_double(String s) throws Exception {
214 double result;
215 try {
216 result = Float.valueOf(s).floatValue();
217 }
218 catch (Exception e) {
219 if (s.equals("NaN") || s.equals("nan")) {
220 System.err.println("NaN encountered - substituting 0.");
221 result = 0;
222 }
223 else {
224 System.err.println("Error occurred in formatting value " + s +
225 " (" + e + ")");
226 throw e;
227 }
228 }
229 return result;
230 }
231
232 Integer parse_int(String s) {
233 return Integer.valueOf(s);
234 }
235
236 // Put the parsed data into a data set.
237 // Postcondition: result() != null && result().drawer() == drawer
238 private void process_data(BasicDrawer drawer) throws Exception {
239 String[] date_array = null;
240 String[] time_array = null;
241 boolean has_dates = false;
242 boolean has_times = false;
243
244 volume_data = null;
245 oi_data = null;
246
247 try {
248 has_dates = dates != null && ! dates.isEmpty();
249 has_times = times != null && ! times.isEmpty();
250 int length = value_data.length / float_field_count;
251 if (length > 0) {
252 processed_data = new DataSet(value_data, length, drawer);
253 }
254 else {
255 processed_data = new DataSet(drawer);
256 }
257 if (has_dates) {
258 date_array = new String[dates.size()];
259 dates.copyInto(date_array);
260 processed_data.set_dates(date_array);
261 }
262 if (has_times) {
263 time_array = new String[times.size()];
264 times.copyInto(time_array);
265 processed_data.set_times(time_array);
266 }
267 if (volume_drawer != null && volumes != null &&
268 volumes.length > 0) {
269 volume_data = new DataSet(volumes, volumes.length,
270 volume_drawer);
271 if (has_dates) volume_data.set_dates(date_array);
272 if (has_times) volume_data.set_times(time_array);
273 }
274 if (open_interest_drawer != null && open_interests != null &&
275 open_interests.length > 0) {
276 oi_data = new DataSet(open_interests,
277 open_interests.length, open_interest_drawer);
278 if (has_dates) oi_data.set_dates(date_array);
279 if (has_times) oi_data.set_times(time_array);
280 }
281 }
282 catch (Exception e) {
283 System.err.println("DataSet constructor failed - " + e);
284 e.printStackTrace();
285 }
286 }
287
288 // Remove all elements from data vectors - set their sizes to 0.
289 private void clear_vectors() {
290 dates.removeAllElements();
291 times.removeAllElements();
292 }
293
294 // The number of fields in `fieldspecs' that will be of type float
295 private int float_fields(int fieldspecs[]) {
296 int result = 0;
297
298 for (int i = 0; i < fieldspecs.length; ++i) {
299 if (fieldspecs[i] == Open ||
300 fieldspecs[i] == Close ||
301 fieldspecs[i] == High ||
302 fieldspecs[i] == Low) {
303 result = result + 1;
304 }
305 }
306
307 return result;
308 }
309
310 // Does `parsetype' have the specified field type?
311 private boolean has_field_type(int ftype) {
312 boolean result = false;
313 for (int i = 0; i < parsetype.length; ++i) {
314 if (parsetype[i] == ftype) result = true;
315 }
316
317 return result;
318 }
319
320 // Does `s' contain a time field?
321 boolean contains_time_field(String s) {
322 boolean result = false;
323 int valid_parse_fields;
324 StringTokenizer recs = new StringTokenizer(s, _record_separator, false);
325 if (recs.countTokens() > 0) {
326 StringTokenizer fields = new StringTokenizer(recs.nextToken(),
327 _field_separator, false);
328
329 valid_parse_fields = 0;
330 for (int i = 0; i < parsetype.length && parsetype[i] != Not_set;
331 ++i) {
332 ++valid_parse_fields;
333 }
334 // If the number of fields in a record is greater than the number of
335 // parse types, a time field is included.
336 result = fields.countTokens() > valid_parse_fields;
337 }
338 return result;
339 }
340
341 int parsetype[];
342 int float_field_count;
343
344 // Date, time, open, high, low, close, volume, and open interest values -
345 // Some fields may not be used - for those that are, lengths should all
346 // be equal to the number of records in the input.
347 protected Vector dates; // String
348 protected Vector times; // String
349 // Holds open, high, low, close data or, for indicator data, holds
350 // the main value data.
351 protected double[] value_data;
352 protected double[] volumes, open_interests;
353
354 String _record_separator, _field_separator;
355 DataSet processed_data; // the parsed data
356 DataSet volume_data; // the parsed volume data
357 DataSet oi_data; // the parsed open interest data
358 boolean is_intraday;
359 BasicDrawer volume_drawer;
360 BasicDrawer open_interest_drawer;
361 }