1 package net.sourceforge.jparam.parser;
2
3 import net.sourceforge.jparam.ConstantRegistry;
4 import net.sourceforge.jparam.JParamException;
5 import net.sourceforge.jparam.conversion.ConverterRegistry;
6 import net.sourceforge.jparam.conversion.weights.ScalarConversionWeight;
7 import net.sourceforge.jparam.util.Utils;
8
9 /***
10 * A description of class TreeNode
11 */
12 public class SimpleLeaf implements TreeNode {
13 public static final int CHAR = 0;
14
15 public static final int STRING = 1;
16
17 public static final int TENTATIVE = 2;
18
19 public static final int DECODED_STRING = 3;
20
21 private String value;
22
23 private int type;
24
25 /***
26 * Create a new TreeNode object.
27 */
28 public SimpleLeaf(String value, int type) {
29 this.value = value;
30 this.type = type;
31 }
32
33 public Object getConstructedValue(Class expectedType)
34 throws JParamException {
35 switch (type) {
36 case CHAR:
37 return new Character(decode_char(value));
38 case STRING:
39 return decode_string(value);
40 case DECODED_STRING:
41 return value;
42 case TENTATIVE:
43 return decode_tentative(expectedType, value);
44 default:
45 Utils.assert(false, "Impossible type: " + type);
46 return null;
47 }
48 }
49
50 public ScalarConversionWeight getConversionWeight(Class expectedTypeClass) {
51 ConverterRegistry cr = ConverterRegistry.getInstance();
52 switch (type) {
53 case CHAR:
54 return cr.getConversionWeight(Character.class, expectedTypeClass);
55 case STRING:
56 case DECODED_STRING:
57 return cr.getConversionWeight(String.class, expectedTypeClass);
58 case TENTATIVE:
59 int tentative_type = getTentativeType(value);
60 Class tentativeClass;
61 switch (tentative_type) {
62 case T_INTEGER:
63 tentativeClass = Integer.class;
64 break;
65 case T_DOUBLE:
66 tentativeClass = Double.class;
67 break;
68 case T_FLOAT:
69 tentativeClass = Float.class;
70 break;
71 case T_STRING:
72 Object result = decode_tentative_string(value.substring(1));
73 if (result != null) {
74 tentativeClass = result.getClass();
75 } else {
76 if (expectedTypeClass.isPrimitive()) {
77 return ScalarConversionWeight.CONV_IMPOSSIBLE;
78 }
79
80 return ScalarConversionWeight.CONV_STANDARD;
81 }
82 break;
83 default:
84 return ScalarConversionWeight.CONV_IMPOSSIBLE;
85 }
86 return cr.getConversionWeight(tentativeClass, expectedTypeClass);
87 default:
88 return ScalarConversionWeight.CONV_IMPOSSIBLE;
89 }
90 }
91
92
93
94
95
96 int hex_value(char digit) {
97 if (digit >= '0' && digit <= '9')
98 return digit - '0';
99 if (digit >= 'A' && digit <= 'F')
100 return 10 + digit - 'A';
101 Utils.assert(digit >= 'a' && digit <= 'f',
102 "invalid digit for hex value: " + digit);
103 return 10 + digit - 'f';
104 }
105
106 class StringWithPos {
107 StringWithPos(String str, int pos) {
108 this.str = str;
109 this.pos = pos;
110 }
111
112 public String str;
113
114 public int pos;
115 }
116
117 char decode_next_char(StringWithPos swp) {
118 Utils.assert(swp.pos < swp.str.length(), "Invalid position: " + swp.pos
119 + ", str=" + swp.str);
120
121 char c = swp.str.charAt(swp.pos++);
122 if (c != '//')
123 return c;
124
125 c = swp.str.charAt(swp.pos++);
126 if (c == 'x' || c == 'X') {
127 char most_digit = swp.str.charAt(swp.pos++);
128 char least_digit = swp.str.charAt(swp.pos++);
129 int uc = (hex_value(most_digit) << 4) | hex_value(least_digit);
130 return (char) uc;
131 }
132
133 switch (c) {
134 case 'n':
135 return '\n';
136 case 't':
137 return '\t';
138 case 'v':
139 return 11;
140 case 'b':
141 return '\b';
142 case 'r':
143 return '\r';
144 case 'f':
145 return '\f';
146 case 'a':
147 return 7;
148 default:
149 return c;
150 }
151 }
152
153 char decode_char(String str) {
154 char decoded_char;
155
156 Utils.assert(str.charAt(0) == '\'', "String doesn't start with \':"
157 + str);
158 StringWithPos swp = new StringWithPos(str, 1);
159 decoded_char = decode_next_char(swp);
160 Utils
161 .assert(swp.pos == swp.str.length(),
162 "Pos not at end of string, pos=" + swp.pos + ", str="
163 + swp.str);
164
165
166
167
168
169
170
171
172 return decoded_char;
173 }
174
175 String decode_string(String str) {
176 String decoded_string = "";
177
178 Utils.assert(str.charAt(0) == '"', "String doesn't start with \":"
179 + str);
180 StringWithPos swp = new StringWithPos(str, 1);
181 while (swp.pos < swp.str.length()) {
182 decoded_string += decode_next_char(swp);
183 }
184
185 Utils
186 .assert(swp.pos == swp.str.length(),
187 "Pos not at end of string, pos=" + swp.pos + ", str="
188 + swp.str);
189
190 return decoded_string;
191 }
192
193 private final static int T_INTEGER = 0;
194
195 private final static int T_FLOAT = 1;
196
197 private final static int T_DOUBLE = 2;
198
199 private final static int T_STRING = 3;
200
201 private int getTentativeType(String value) {
202 Utils.assert(!value.equals(""), "Empty string");
203 int retVal;
204 switch (value.charAt(0)) {
205 case 'I':
206 retVal = T_INTEGER;
207 break;
208 case 'R':
209 char c = value.charAt(value.length() - 1);
210 boolean isFloat = (c == 'f' || c == 'F');
211 boolean isLongDouble = (c == 'l' || c == 'L');
212
213 if (isFloat) {
214 retVal = T_FLOAT;
215 } else if (isLongDouble) {
216 retVal = T_DOUBLE;
217 } else {
218 retVal = T_DOUBLE;
219 }
220 break;
221 case 'S':
222 retVal = T_STRING;
223 break;
224 default:
225 Utils.assert(false, "Invalid tentative type: " + value.charAt(0));
226 retVal = -1;
227 }
228
229 return retVal;
230 }
231
232
233
234
235 private Object decode_tentative(Class targetType, String value)
236 throws JParamException {
237 Utils.assert(!value.equals(""), "Empty string");
238 int tentative_type = getTentativeType(value);
239
240 Object retVal;
241 switch (tentative_type) {
242 case T_INTEGER:
243 Utils.assert(value.length() > 4, "invalid integer literal: "
244 + value);
245
246 int base = 0;
247 switch (value.charAt(1)) {
248 case 'D':
249 base = 10;
250 break;
251 case 'H':
252 base = 16;
253 break;
254 case 'O':
255 base = 8;
256 break;
257 case 'B':
258 base = 2;
259 break;
260 default:
261 Utils.assert(false, "Unexpected base: " + value.charAt(1));
262 }
263 boolean isUnsigned = value.charAt(2) == 'U';
264 boolean isLong = value.charAt(3) == 'L';
265 retVal = decode_tentative_int(value.substring(4), base, isUnsigned,
266 isLong);
267 break;
268 case T_FLOAT:
269 case T_DOUBLE:
270 char c = value.charAt(value.length() - 1);
271 boolean isFloat = (c == 'f' || c == 'F');
272 boolean isLongDouble = (c == 'l' || c == 'L');
273
274 if (isFloat || isLongDouble) {
275
276 value = value.substring(1, value.length() - 1);
277
278
279 retVal = decode_tentative_real(value, isFloat);
280 } else {
281 retVal = decode_tentative_real(value.substring(1), isFloat);
282 }
283 break;
284 case T_STRING:
285 retVal = decode_tentative_string(value.substring(1));
286 break;
287 default:
288 Utils.assert(false, "Invalid tentative type: " + value.charAt(0));
289 return null;
290 }
291 if ((retVal != null)
292 && (!targetType.isAssignableFrom(retVal.getClass()))) {
293 return ConverterRegistry.getInstance().convert(targetType, retVal);
294 }
295
296 return retVal;
297 }
298
299 private Object decode_tentative_real(String value, boolean isFloat) {
300 if (isFloat) {
301 return new Float(value);
302 }
303
304 return new Double(value);
305 }
306
307 private Object decode_tentative_int(String value, int base,
308 boolean isUnsigned, boolean isLong) {
309 int nSuffix = 0;
310 if (isLong)
311 nSuffix++;
312 if (isUnsigned)
313 nSuffix++;
314
315 int n = value.length() - nSuffix;
316 char c;
317 int pos = 0;
318
319
320 Utils.assert(n > pos, "Empty integer");
321 boolean isNegative = false;
322 c = value.charAt(pos);
323 if ((c == '-') || (c == '+')) {
324 pos++;
325 if (c == '-')
326 isNegative = true;
327 }
328
329
330 int nPrefix = 0;
331 switch (base) {
332 case 8:
333 case 10:
334 nPrefix = 0;
335 break;
336 case 2:
337 case 16:
338 nPrefix = 2;
339 break;
340 default:
341 Utils.assert(false, "Invalid base: " + base);
342 }
343 pos += nPrefix;
344
345
346 value = value.substring(pos, value.length() - nSuffix);
347
348 long l;
349 try {
350 l = Long.parseLong(value, base);
351 } catch (NumberFormatException e) {
352 throw new RuntimeException("Cannot convert '" + value
353 + "' to an integer.");
354 }
355 if (isNegative)
356 l = -l;
357 if (l > Integer.MAX_VALUE) {
358 throw new RuntimeException("Integer too big - " + value);
359 }
360 return new Integer((int) l);
361 }
362
363 private Object decode_tentative_string(String value) {
364 if (ConstantRegistry.getInstance().isConstant(value)) {
365 return ConstantRegistry.getInstance().getConstant(value);
366 }
367
368 return value;
369 }
370
371 public String toString() {
372 return "SimpleLeaf, value=" + value + ", type=" + type;
373 }
374 }