001: package org.jicengine.element;
002:
003: import org.jicengine.element.impl.CdataConverterInvocationOperation;
004: import org.jicengine.expression.ClassParser;
005: import org.jicengine.io.Resource;
006: import org.jicengine.operation.Operation;
007: import org.jicengine.operation.OperationException;
008: import org.jicengine.operation.StaticValue;
009: import org.jicengine.operation.Context;
010:
011: import java.math.BigDecimal;
012: import java.math.BigInteger;
013: import java.net.*;
014: import java.util.Arrays;
015: import java.util.Collection;
016: import java.util.HashMap;
017: import java.util.HashSet;
018: import java.util.Locale;
019: import java.util.Map;
020: import java.util.TimeZone;
021: import java.awt.Color;
022: import java.awt.Dimension;
023: import java.awt.Font;
024: import java.awt.Point;
025: import java.io.*;
026: import org.jicengine.util.StringConverter;
027:
028: /**
029: * <p> </p>
030: *
031: * <p> </p>
032: *
033: * <p> </p>
034: *
035: * <p> </p>
036: *
037: * todo: the exception types are not specific enough..
038: *
039: * @author timo laitinen
040: */
041: public class CdataHandler {
042:
043: private static final String CDATA_TRUE = "true";
044: private static final String CDATA_FALSE = "false";
045:
046: /**
047: *
048: *
049: * @param cdata
050: * @return
051: */
052: public static Class resolveInstanceClassFromCdata(String cdata) {
053: Class instanceClass = null;
054:
055: char[] chars = cdata.toCharArray();
056:
057: if (chars.length == 0) {
058: instanceClass = String.class;
059: }
060:
061: if (instanceClass == null) {
062: // not an empty string, try number
063: instanceClass = getNumberClass(chars);
064: }
065:
066: if (instanceClass == null) {
067: // not a number, try boolean
068: if (cdata.equals(CDATA_TRUE)) {
069: instanceClass = Boolean.TYPE;
070: } else if (cdata.equals(CDATA_FALSE)) {
071: instanceClass = Boolean.TYPE;
072: }
073: }
074:
075: if (instanceClass == null) {
076: // it must be a string
077: instanceClass = String.class;
078: }
079:
080: return instanceClass;
081: }
082:
083: private static Class getNumberClass(char[] cdata) {
084: int decimalIndex = -1;
085: for (int i = 0; i < cdata.length; i++) {
086: if (Character.isDigit(cdata[i])) {
087: continue;
088: } else if (cdata[i] == '.' && (decimalIndex == -1 && i > 0)) {
089: // valid decimal index
090: decimalIndex = i;
091: continue;
092: } else if (cdata[i] == '-' && i == 0) {
093: continue;
094: } else {
095: // illegal character, stop and return null.
096: return null;
097: }
098: }
099:
100: if (decimalIndex == -1) {
101: return Integer.TYPE;
102: } else {
103: return Double.TYPE;
104: }
105: }
106:
107: private static Collection defaultTargetClasses;
108: private static Map constructorFactories = new HashMap();
109:
110: static {
111: defaultTargetClasses = new HashSet();
112: defaultTargetClasses.addAll(Arrays.asList(new Object[] {
113: String.class, Integer.TYPE, Integer.class,
114: Double.class, Double.TYPE, Boolean.class, Boolean.TYPE,
115: Long.class, Long.TYPE, Float.class, Float.TYPE,
116: Short.class, Short.TYPE, Resource.class,
117: java.awt.Color.class, Font.class,
118: java.awt.Dimension.class, java.awt.Point.class,
119: java.net.URL.class, java.util.TimeZone.class,
120: java.util.Locale.class, java.lang.Class.class }));
121:
122: constructorFactories.put(String.class,
123: new StringConstructorFactory());
124: constructorFactories.put(Integer.class,
125: new IntConstructorFactory());
126: constructorFactories.put(Integer.TYPE,
127: new IntConstructorFactory());
128: constructorFactories.put(Double.class,
129: new DoubleConstructorFactory());
130: constructorFactories.put(Double.TYPE,
131: new DoubleConstructorFactory());
132: constructorFactories.put(Boolean.class,
133: new BooleanConstructorFactory());
134: constructorFactories.put(Boolean.TYPE,
135: new BooleanConstructorFactory());
136: constructorFactories.put(Character.class,
137: new CharacterConstructorFactory());
138: constructorFactories.put(Character.TYPE,
139: new CharacterConstructorFactory());
140: constructorFactories.put(Long.class,
141: new LongConstructorFactory());
142: constructorFactories.put(Long.TYPE,
143: new LongConstructorFactory());
144: constructorFactories.put(Short.class,
145: new ShortConstructorFactory());
146: constructorFactories.put(Short.TYPE,
147: new ShortConstructorFactory());
148: constructorFactories.put(Float.class,
149: new FloatConstructorFactory());
150: constructorFactories.put(Float.TYPE,
151: new FloatConstructorFactory());
152: constructorFactories.put(BigDecimal.class,
153: new BigDecimalConstructorFactory());
154: constructorFactories.put(BigInteger.class,
155: new BigIntegerConstructorFactory());
156: constructorFactories.put(Resource.class,
157: new ResourceConstructorFactory());
158: constructorFactories.put(Color.class,
159: new ColorConstructorFactory());
160: constructorFactories.put(Font.class,
161: new FontConstructorFactory());
162: constructorFactories.put(Dimension.class,
163: new DimensionConstructorFactory());
164: constructorFactories.put(Point.class,
165: new PointConstructorFactory());
166: constructorFactories
167: .put(URL.class, new UrlConstructorFactory());
168: constructorFactories.put(TimeZone.class,
169: new TimeZoneConstructorFactory());
170: constructorFactories.put(Locale.class,
171: new LocaleConstructorFactory());
172: constructorFactories.put(Class.class,
173: new ClassConstructorFactory());
174: }
175:
176: public static boolean isDefaultCdataConversionType(Class targetClass) {
177: return defaultTargetClasses.contains(targetClass.getName());
178: }
179:
180: /**
181: * Returns a constructor that converts the CDATA value into
182: * the instance of the proper class i.e. the class stated
183: * in the class attribute.
184: *
185: * @param targetClass Class may be null.
186: * @param cdata String
187: * @return Operation
188: * @throws Exception if the CDATA can not be converted to an instance of the particular
189: * class.
190: */
191: public static Operation getClassBasedCdataConversionConstructor(
192: Class targetClass, String cdata) throws Exception {
193: if (targetClass == null) {
194: throw new IllegalArgumentException(
195: "Target class required in CDATA conversions");
196: }
197:
198: ConstructorFactory factory = (ConstructorFactory) constructorFactories
199: .get(targetClass);
200:
201: if (factory != null) {
202: return factory.getConstructor(cdata);
203: } else {
204: // no match found, but the JIC file may have an
205: // user-defined CDATA conversion for the class:
206: return new CdataConverterInvocationOperation(targetClass,
207: cdata);
208: }
209: }
210:
211: static interface ConstructorFactory {
212: public Operation getConstructor(String stringValue)
213: throws OperationException;
214: }
215:
216: static class StringConstructorFactory implements ConstructorFactory {
217: public Operation getConstructor(String stringValue)
218: throws OperationException {
219: return new StaticValue(stringValue);
220: }
221: }
222:
223: static class ClassConstructorFactory implements ConstructorFactory {
224: public Operation getConstructor(String stringValue)
225: throws OperationException {
226: try {
227: return new StaticValue(ClassParser.toClass(stringValue));
228: } catch (ClassNotFoundException e) {
229: throw new OperationException("Failed to load class '"
230: + stringValue + "'");
231: }
232: }
233: }
234:
235: static class IntConstructorFactory implements ConstructorFactory {
236:
237: public Operation getConstructor(String stringValue)
238: throws OperationException {
239: try {
240: return new StaticValue(Integer.valueOf(stringValue));
241: } catch (NumberFormatException e) {
242: throw new OperationException("Failed to convert '"
243: + stringValue + "' to int");
244: }
245: }
246: }
247:
248: static class DoubleConstructorFactory implements ConstructorFactory {
249:
250: public Operation getConstructor(String stringValue)
251: throws OperationException {
252: try {
253: return new StaticValue(Double.valueOf(stringValue));
254: } catch (NumberFormatException e) {
255: throw new OperationException("Failed to convert '"
256: + stringValue + "' to double.");
257: }
258: }
259: }
260:
261: static class BooleanConstructorFactory implements
262: ConstructorFactory {
263: public Operation getConstructor(String stringValue)
264: throws OperationException {
265: try {
266: return new StaticValue(Boolean.valueOf(stringValue));
267: } catch (NumberFormatException e) {
268: throw new OperationException("Failed to convert '"
269: + stringValue + "' to boolean.");
270: }
271: }
272: }
273:
274: static class CharacterConstructorFactory implements
275: ConstructorFactory {
276: public Operation getConstructor(String stringValue)
277: throws OperationException {
278: if (stringValue.length() == 1) {
279: return new StaticValue(new Character(stringValue
280: .charAt(0)));
281: } else {
282: throw new OperationException("Too many characters in '"
283: + stringValue + "'");
284: }
285: }
286: }
287:
288: static class LongConstructorFactory implements ConstructorFactory {
289: public Operation getConstructor(String stringValue)
290: throws OperationException {
291: try {
292: return new StaticValue(Long.valueOf(stringValue));
293: } catch (NumberFormatException e) {
294: throw new OperationException("Failed to convert '"
295: + stringValue + "' to long.");
296: }
297: }
298: }
299:
300: static class ShortConstructorFactory implements ConstructorFactory {
301: public Operation getConstructor(String stringValue)
302: throws OperationException {
303: try {
304: return new StaticValue(Short.valueOf(stringValue));
305: } catch (NumberFormatException e) {
306: throw new OperationException("Failed to convert '"
307: + stringValue + "' to short.");
308: }
309: }
310: }
311:
312: static class FloatConstructorFactory implements ConstructorFactory {
313:
314: public Operation getConstructor(String stringValue)
315: throws OperationException {
316: try {
317: return new StaticValue(Float.valueOf(stringValue));
318: } catch (NumberFormatException e) {
319: throw new OperationException("Failed to convert '"
320: + stringValue + "' to float.");
321: }
322: }
323: }
324:
325: static class BigDecimalConstructorFactory implements
326: ConstructorFactory {
327:
328: public Operation getConstructor(String stringValue)
329: throws OperationException {
330: try {
331: return new StaticValue(new BigDecimal(stringValue));
332: } catch (NumberFormatException e) {
333: throw new OperationException("Failed to convert '"
334: + stringValue + "' to BigDecimal", e);
335: }
336: }
337: }
338:
339: static class BigIntegerConstructorFactory implements
340: ConstructorFactory {
341: private BigInteger value;
342:
343: public Operation getConstructor(String stringValue)
344: throws OperationException {
345: try {
346: return new StaticValue(new BigInteger(stringValue));
347: } catch (NumberFormatException e) {
348: throw new OperationException("Failed to convert '"
349: + stringValue + "' to BigInteger", e);
350: }
351: }
352: }
353:
354: static class ColorConstructorFactory implements ConstructorFactory {
355: public Operation getConstructor(String stringValue)
356: throws OperationException {
357: try {
358: return new StaticValue(StringConverter
359: .toColor(stringValue));
360: } catch (IllegalArgumentException e) {
361: throw new OperationException(e.getMessage(), e);
362: }
363: }
364: }
365:
366: static class FontConstructorFactory implements ConstructorFactory {
367: public Operation getConstructor(String stringValue)
368: throws OperationException {
369: return new FontConstructor(stringValue);
370: }
371: }
372:
373: static class FontConstructor implements Operation {
374: private String stringValue;
375:
376: /**
377: * the Font is not created in the constructor, because.. I guess
378: * fonts can be installed to the system at any time? so we must not be
379: * hasty..
380: *
381: * @param stringValue String
382: */
383: public FontConstructor(String stringValue) {
384: this .stringValue = stringValue;
385: }
386:
387: public boolean needsParameters() {
388: return false;
389: }
390:
391: public boolean needsParameter(String name) {
392: return false;
393: }
394:
395: public Object execute(Context context)
396: throws OperationException {
397: return java.awt.Font.decode(this .stringValue);
398: }
399: }
400:
401: static class UrlConstructorFactory implements ConstructorFactory {
402: public Operation getConstructor(String stringValue)
403: throws OperationException {
404: try {
405: return new StaticValue(new java.net.URL(stringValue));
406: } catch (MalformedURLException ex) {
407: throw new OperationException(ex.getMessage(), ex);
408: }
409: }
410: }
411:
412: static class DimensionConstructorFactory implements
413: ConstructorFactory {
414: public Operation getConstructor(String stringValue)
415: throws OperationException {
416: return new DimensionConstructor(stringValue);
417: }
418: }
419:
420: static class DimensionConstructor implements Operation {
421: private String stringValue;
422:
423: /**
424: * Dimension is not created in the constructor because
425: * Dimensions are modifiable!
426: *
427: * @param stringValue String
428: */
429: public DimensionConstructor(String stringValue) {
430: this .stringValue = stringValue;
431: }
432:
433: public boolean needsParameters() {
434: return false;
435: }
436:
437: public boolean needsParameter(String name) {
438: return false;
439: }
440:
441: public Object execute(Context context)
442: throws OperationException {
443: try {
444: return StringConverter.toDimension(this .stringValue);
445: } catch (IllegalArgumentException e) {
446: throw new OperationException(e.getMessage(), e);
447: }
448: }
449: }
450:
451: static class PointConstructorFactory implements ConstructorFactory {
452: public Operation getConstructor(String stringValue)
453: throws OperationException {
454: return new PointConstructor(stringValue);
455: }
456: }
457:
458: static class PointConstructor implements Operation {
459: private String stringValue;
460:
461: /**
462: * Point IS NOT created in the constructor because Points
463: * are modifiable in Java..
464: *
465: * @param stringValue String
466: */
467: public PointConstructor(String stringValue) {
468: this .stringValue = stringValue;
469: }
470:
471: public boolean needsParameters() {
472: return false;
473: }
474:
475: public boolean needsParameter(String name) {
476: return false;
477: }
478:
479: public Object execute(Context context)
480: throws OperationException {
481: try {
482: return StringConverter.toPoint(this .stringValue);
483: } catch (IllegalArgumentException e) {
484: throw new OperationException(e.getMessage(), e);
485: }
486: }
487: }
488:
489: static class LocaleConstructorFactory implements ConstructorFactory {
490: public Operation getConstructor(String stringValue)
491: throws OperationException {
492: return new LocaleConstructor(stringValue);
493: }
494: }
495:
496: static class LocaleConstructor implements Operation {
497: private String stringValue;
498:
499: public LocaleConstructor(String stringValue) {
500: this .stringValue = stringValue;
501: }
502:
503: public boolean needsParameters() {
504: return false;
505: }
506:
507: public boolean needsParameter(String name) {
508: return false;
509: }
510:
511: public Object execute(Context context)
512: throws OperationException {
513: try {
514: return StringConverter.toLocale(this .stringValue);
515: } catch (IllegalArgumentException e) {
516: throw new OperationException(e.getMessage(), e);
517: }
518: }
519: }
520:
521: static class TimeZoneConstructorFactory implements
522: ConstructorFactory {
523: public Operation getConstructor(String stringValue)
524: throws OperationException {
525: return new TimeZoneConstructor(stringValue);
526: }
527: }
528:
529: static class TimeZoneConstructor implements Operation {
530: private String stringValue;
531:
532: public TimeZoneConstructor(String stringValue) {
533: this .stringValue = stringValue;
534: }
535:
536: public boolean needsParameters() {
537: return false;
538: }
539:
540: public boolean needsParameter(String name) {
541: return false;
542: }
543:
544: public Object execute(Context context)
545: throws OperationException {
546: return java.util.TimeZone.getTimeZone(this .stringValue);
547: }
548: }
549:
550: static class ResourceConstructorFactory implements
551: ConstructorFactory {
552: public Operation getConstructor(String stringValue)
553: throws OperationException {
554: return new ResourceConstructor(stringValue);
555: }
556: }
557:
558: static class ResourceConstructor implements Operation {
559: private String stringValue;
560:
561: public ResourceConstructor(String stringValue) {
562: this .stringValue = stringValue;
563: }
564:
565: public boolean needsParameters() {
566: return false;
567: }
568:
569: public boolean needsParameter(String name) {
570: return false;
571: }
572:
573: public Object execute(Context context)
574: throws OperationException {
575: org.jicengine.BuildContext buildContext = (org.jicengine.BuildContext) context
576: .getObject(org.jicengine.BuildContext.VARIABLE_NAME);
577:
578: try {
579: return buildContext.getCurrentFile().getResource(
580: this .stringValue);
581: } catch (IOException ex) {
582: throw new OperationException(ex.getMessage(), ex);
583: }
584: }
585: }
586:
587: }
|