001: // Copyright (c) 2006 Per M.A. Bothner.
002: // This is free software; for specifics see ../../../COPYING.
003:
004: package gnu.kawa.xml;
005:
006: import gnu.bytecode.*;
007: import gnu.mapping.Procedure;
008: import gnu.mapping.Values;
009: import gnu.expr.*;
010: import gnu.text.Printable;
011: import gnu.math.*;
012: import java.math.BigDecimal;
013: import gnu.lists.SeqPosition;
014: import gnu.lists.Consumer;
015: import gnu.xml.TextUtils;
016: import gnu.text.URIPath;
017:
018: /** An atomic type as used in XML Schema and related languages.
019: * For example the {code xs:decimal} type is {@code XDataType.decimalType}.
020: */
021:
022: public class XDataType extends Type implements TypeValue {
023: Type implementationType;
024:
025: Object name;
026:
027: /** The "parent" type. */
028: XDataType baseType;
029:
030: /** One of the {@code XXXX_TYPE_CODE} constants. */
031: int typeCode;
032:
033: public static final int ANY_ATOMIC_TYPE_CODE = 1;
034: public static final int DECIMAL_TYPE_CODE = 2;
035: public static final int INTEGER_TYPE_CODE = 3;
036: public static final int NON_POSITIVE_INTEGER_TYPE_CODE = 4;
037: public static final int NEGATIVE_INTEGER_TYPE_CODE = 5;
038: public static final int LONG_TYPE_CODE = 6;
039: public static final int INT_TYPE_CODE = 7;
040: public static final int SHORT_TYPE_CODE = 8;
041: public static final int BYTE_TYPE_CODE = 9;
042: public static final int NONNEGATIVE_INTEGER_TYPE_CODE = 10;
043: public static final int UNSIGNED_LONG_TYPE_CODE = 11;
044: public static final int UNSIGNED_INT_TYPE_CODE = 12;
045: public static final int UNSIGNED_SHORT_TYPE_CODE = 13;
046: public static final int UNSIGNED_BYTE_TYPE_CODE = 14;
047: public static final int POSITIVE_INTEGER_TYPE_CODE = 15;
048:
049: public static final int FLOAT_TYPE_CODE = 16;
050: public static final int DOUBLE_TYPE_CODE = 17;
051:
052: public static final int DATE_TIME_TYPE_CODE = 18;
053: public static final int DATE_TYPE_CODE = 19;
054: public static final int TIME_TYPE_CODE = 20;
055: public static final int G_YEAR_MONTH_TYPE_CODE = 21;
056: public static final int G_YEAR_TYPE_CODE = 22;
057: public static final int G_MONTH_DAY_TYPE_CODE = 23;
058: public static final int G_DAY_TYPE_CODE = 24;
059: public static final int G_MONTH_TYPE_CODE = 25;
060: public static final int DURATION_TYPE_CODE = 26;
061: public static final int YEAR_MONTH_DURATION_TYPE_CODE = 27;
062: public static final int DAY_TIME_DURATION_TYPE_CODE = 28;
063:
064: public static final int BOOLEAN_TYPE_CODE = 29;
065:
066: public static final int QNAME_TYPE_CODE = 30;
067: public static final int ANY_URI_TYPE_CODE = 31;
068: public static final int BASE64_BINARY_TYPE_CODE = 32;
069: public static final int HEX_BINARY_TYPE_CODE = 33;
070: public static final int NOTATION_TYPE_CODE = 34;
071:
072: public static final int UNTYPED_ATOMIC_TYPE_CODE = 35;
073:
074: public static final int STRING_TYPE_CODE = 36;
075:
076: public static final int NORMALIZED_STRING_TYPE_CODE = 37;
077: public static final int TOKEN_TYPE_CODE = 38;
078: public static final int LANGUAGE_TYPE_CODE = 39;
079: public static final int NMTOKEN_TYPE_CODE = 40;
080: public static final int NAME_TYPE_CODE = 41;
081: public static final int NCNAME_TYPE_CODE = 42;
082: public static final int ID_TYPE_CODE = 43;
083: public static final int IDREF_TYPE_CODE = 44;
084: public static final int ENTITY_TYPE_CODE = 45;
085:
086: public XDataType(Object name, Type implementationType, int typeCode) {
087: super (implementationType);
088: this .name = name;
089: if (name != null)
090: setName(name.toString());
091: this .implementationType = implementationType;
092: this .typeCode = typeCode;
093: }
094:
095: public static final XDataType anyAtomicType = new XDataType(
096: "anyAtomicType", Type.pointer_type, ANY_ATOMIC_TYPE_CODE);
097:
098: public static final XDataType stringType = new XDataType("string",
099: /* #ifdef use:java.lang.CharSequence */
100: ClassType.make("java.lang.CharSequence"),
101: /* #else */
102: // ClassType.make("java.lang.String"),
103: /* #endif */
104: STRING_TYPE_CODE);
105:
106: /** A value implemented as java.lang.String.
107: * Can be cast from CharSequence.
108: */
109: public static final XDataType stringStringType = new XDataType(
110: "String", ClassType.make("java.lang.String"),
111: STRING_TYPE_CODE);
112:
113: public static final XDataType untypedAtomicType = new XDataType(
114: "string", ClassType.make("gnu.kawa.xml.UntypedAtomic"),
115: UNTYPED_ATOMIC_TYPE_CODE);
116:
117: public static final XDataType base64BinaryType = new XDataType(
118: "base64Binary",
119: ClassType.make("gnu.kawa.xml.Base64Binary"),
120: BASE64_BINARY_TYPE_CODE);
121:
122: public static final XDataType hexBinaryType = new XDataType(
123: "hexBinary", ClassType.make("gnu.kawa.xml.HexBinary"),
124: HEX_BINARY_TYPE_CODE);
125:
126: public static final XDataType booleanType = new XDataType(
127: "boolean", Type.boolean_type, BOOLEAN_TYPE_CODE);
128:
129: public static final XDataType anyURIType = new XDataType("anyURI",
130: ClassType.make("gnu.text.Path"), ANY_URI_TYPE_CODE);
131:
132: public static final XDataType NotationType = new XDataType(
133: "NOTATION", ClassType.make("gnu.kawa.xml.Notation"),
134: NOTATION_TYPE_CODE);
135:
136: public static final XDataType decimalType =
137: // A decimal value is implemented using java.math.BigDecimal.
138: // However, the integer sub-type is implemented using gnu.math.IntNum.
139: // So we use their common supertype as the implementationType.
140: new XDataType("decimal", ClassType.make("java.lang.Number"),
141: DECIMAL_TYPE_CODE);
142:
143: public static final XDataType floatType = new XDataType("float",
144: ClassType.make("java.lang.Float"), FLOAT_TYPE_CODE);
145:
146: public static final XDataType doubleType = new XDataType("double",
147: ClassType.make("java.lang.Double"), DOUBLE_TYPE_CODE);
148:
149: public static final XDataType durationType = new XDataType(
150: "duration", ClassType.make("gnu.math.Duration"),
151: DURATION_TYPE_CODE);
152:
153: public static final XDataType yearMonthDurationType = new XDataType(
154: "yearMonthDuration", ClassType.make("gnu.math.Duration"),
155: YEAR_MONTH_DURATION_TYPE_CODE);
156:
157: public static final XDataType dayTimeDurationType = new XDataType(
158: "dayTimeDuration", ClassType.make("gnu.math.Duration"),
159: DAY_TIME_DURATION_TYPE_CODE);
160:
161: public java.lang.Class getReflectClass() {
162: return implementationType.getReflectClass();
163: }
164:
165: public Type getImplementationType() {
166: return implementationType;
167: }
168:
169: public void emitCoerceFromObject(CodeAttr code) {
170: Compilation comp = Compilation.getCurrent();
171: comp.compileConstant(this , Target.pushObject);
172: Method meth = ClassType.make("gnu.kawa.xml.XDataType")
173: .getDeclaredMethod("coerceFromObject", 1);
174: code.emitSwap();
175: code.emitInvokeVirtual(meth);
176: // Needed to avoid VerifyErrors.
177: implementationType.emitCoerceFromObject(code);
178: }
179:
180: public void emitCoerceToObject(CodeAttr code) {
181: if (typeCode == BOOLEAN_TYPE_CODE)
182: implementationType.emitCoerceToObject(code);
183: else
184: super .emitCoerceToObject(code);
185: }
186:
187: public void emitTestIf(Variable incoming, Declaration decl,
188: Compilation comp) {
189: CodeAttr code = comp.getCode();
190: if (typeCode == BOOLEAN_TYPE_CODE) {
191: if (incoming != null)
192: code.emitLoad(incoming);
193: Type.boolean_ctype.emitIsInstance(code);
194: code.emitIfIntNotZero();
195: if (decl != null) {
196: // Error if incoming is null.
197: code.emitLoad(incoming);
198: Type.boolean_type.emitCoerceFromObject(code);
199: decl.compileStore(comp);
200: }
201: return;
202: }
203:
204: comp.compileConstant(this , Target.pushObject);
205: if (incoming == null)
206: code.emitSwap();
207: else
208: code.emitLoad(incoming);
209: if (decl != null) {
210: code.emitDup();
211: decl.compileStore(comp);
212: }
213: code.emitInvokeVirtual(Compilation.typeType.getDeclaredMethod(
214: "isInstance", 1));
215: code.emitIfIntNotZero();
216: }
217:
218: public boolean isInstance(Object obj) {
219: switch (typeCode) {
220: case ANY_ATOMIC_TYPE_CODE:
221: return !(obj instanceof Values || obj instanceof SeqPosition);
222: case STRING_TYPE_CODE:
223: /* #ifdef use:java.lang.CharSequence */
224: return obj instanceof java.lang.CharSequence;
225: /* #else */
226: // return obj instanceof java.lang.String;
227: /* #endif */
228: case UNTYPED_ATOMIC_TYPE_CODE:
229: return obj instanceof gnu.kawa.xml.UntypedAtomic;
230: case ANY_URI_TYPE_CODE:
231: return obj instanceof gnu.text.Path;
232: case BOOLEAN_TYPE_CODE:
233: return obj instanceof java.lang.Boolean;
234: case FLOAT_TYPE_CODE:
235: return obj instanceof java.lang.Float;
236: case DOUBLE_TYPE_CODE:
237: return obj instanceof java.lang.Double;
238: case DECIMAL_TYPE_CODE:
239: return obj instanceof java.math.BigDecimal
240: || obj instanceof gnu.math.IntNum;
241: case DURATION_TYPE_CODE:
242: return obj instanceof Duration;
243: case YEAR_MONTH_DURATION_TYPE_CODE:
244: return obj instanceof Duration
245: && ((Duration) obj).unit() == Unit.month;
246: case DAY_TIME_DURATION_TYPE_CODE:
247: return obj instanceof Duration
248: && ((Duration) obj).unit() == Unit.second;
249: default:
250: return super .isInstance(obj);
251: }
252: }
253:
254: public void emitIsInstance(Variable incoming, Compilation comp,
255: Target target) {
256: gnu.kawa.reflect.InstanceOf.emitIsInstance(this , incoming,
257: comp, target);
258: }
259:
260: public String toString(Object value) {
261: return value.toString();
262: }
263:
264: public void print(Object value, Consumer out) {
265: if (value instanceof Printable)
266: ((Printable) value).print(out);
267: else
268: out.write(toString(value));
269: }
270:
271: public boolean castable(Object value) {
272: try {
273: // FIXME - inefficient!
274: cast(value);
275: return true;
276: } catch (Throwable ex) {
277: return false;
278: }
279: }
280:
281: public Object cast(Object value) {
282: value = KNode.atomicValue(value);
283: if (value instanceof UntypedAtomic) {
284: if (typeCode == UNTYPED_ATOMIC_TYPE_CODE)
285: return value;
286: return valueOf(value.toString());
287: }
288: if (value instanceof String)
289: return valueOf(value.toString());
290: switch (typeCode) {
291: case STRING_TYPE_CODE:
292: return TextUtils.asString(value);
293: case UNTYPED_ATOMIC_TYPE_CODE:
294: return new UntypedAtomic(TextUtils.stringValue(value));
295: case ANY_URI_TYPE_CODE:
296: return URIPath.makeURI(value);
297: case BOOLEAN_TYPE_CODE:
298: if (value instanceof Boolean)
299: return (((Boolean) value).booleanValue() ? Boolean.TRUE
300: : Boolean.FALSE);
301: if (value instanceof Number) {
302: double d = ((Number) value).doubleValue();
303: return d == 0.0 || Double.isNaN(d) ? Boolean.FALSE
304: : Boolean.TRUE;
305: }
306: break;
307: case DECIMAL_TYPE_CODE:
308: // Partly duplicates Arithmetic asBigDecimal.
309: if (value instanceof java.math.BigDecimal)
310: return value;
311: if (value instanceof gnu.math.RealNum)
312: return ((gnu.math.RealNum) value).asBigDecimal();
313: if (value instanceof Float || value instanceof Double) {
314: double d = ((Number) value).doubleValue();
315: /* #ifdef JAVA5 */
316: // return BigDecimal.valueOf(d);
317: /* #else */
318: return new BigDecimal(d);
319: /* #endif */
320: }
321: if (value instanceof Boolean)
322: return cast(((Boolean) value).booleanValue() ? IntNum
323: .one() : IntNum.zero());
324: break;
325: case FLOAT_TYPE_CODE:
326: if (value instanceof java.lang.Float)
327: return value;
328: if (value instanceof java.lang.Number)
329: // Wrong for complex numbers with non-zero imaginary part. FIXME.
330: return makeFloat(((Number) value).floatValue());
331: if (value instanceof Boolean)
332: return ((Boolean) value).booleanValue() ? FLOAT_ONE
333: : FLOAT_ZERO;
334: break;
335: case DOUBLE_TYPE_CODE:
336: if (value instanceof java.lang.Double)
337: return value;
338: if (value instanceof java.lang.Number)
339: // Wrong for complex numbers with non-zero imaginary part. FIXME.
340: return makeDouble(((Number) value).doubleValue());
341: if (value instanceof Boolean)
342: return ((Boolean) value).booleanValue() ? DOUBLE_ONE
343: : DOUBLE_ZERO;
344: break;
345: case G_YEAR_TYPE_CODE:
346: case G_YEAR_MONTH_TYPE_CODE:
347: case G_MONTH_TYPE_CODE:
348: case G_MONTH_DAY_TYPE_CODE:
349: case G_DAY_TYPE_CODE:
350: if (value instanceof DateTime) {
351: int dstMask = XTimeType
352: .components(((XTimeType) this ).typeCode);
353: DateTime dt = (DateTime) value;
354: int srcMask = dt.components();
355: if (dstMask == srcMask
356: || (srcMask & DateTime.DATE_MASK) == DateTime.DATE_MASK)
357: return dt.cast(dstMask);
358: throw new ClassCastException();
359: }
360: break;
361: case DATE_TYPE_CODE:
362: case TIME_TYPE_CODE:
363: case DATE_TIME_TYPE_CODE:
364: if (value instanceof DateTime) {
365: int mask = XTimeType
366: .components(((XTimeType) this ).typeCode);
367: return ((DateTime) value).cast(mask);
368: }
369: break;
370: case DURATION_TYPE_CODE:
371: return castToDuration(value, Unit.duration);
372: case YEAR_MONTH_DURATION_TYPE_CODE:
373: return castToDuration(value, Unit.month);
374: case DAY_TIME_DURATION_TYPE_CODE:
375: return castToDuration(value, Unit.second);
376: case BASE64_BINARY_TYPE_CODE:
377: if (value instanceof BinaryObject)
378: return new Base64Binary(((BinaryObject) value)
379: .getBytes());
380: case HEX_BINARY_TYPE_CODE:
381: if (value instanceof BinaryObject)
382: return new HexBinary(((BinaryObject) value).getBytes());
383: }
384: return coerceFromObject(value);
385: }
386:
387: Duration castToDuration(Object value, Unit unit) {
388: if (value instanceof Duration) {
389: Duration dur = (Duration) value;
390: if (dur.unit() == unit)
391: return dur;
392: int months = dur.getTotalMonths();
393: long seconds = dur.getTotalSeconds();
394: int nanos = dur.getNanoSecondsOnly();
395: if (unit == Unit.second)
396: months = 0;
397: if (unit == Unit.month) {
398: seconds = 0;
399: nanos = 0;
400: }
401: return Duration.make(months, seconds, nanos, unit);
402: }
403: return (Duration) coerceFromObject(value);
404: }
405:
406: public Object coerceFromObject(Object obj) {
407: if (!isInstance(obj))
408: throw new ClassCastException("cannot cast " + obj + " to "
409: + name);
410: return obj;
411: }
412:
413: public int compare(Type other) {
414: if (this == other
415: || (this == stringStringType && other == stringType)
416: || (this == stringType && other == stringStringType))
417: return 0;
418: return implementationType.compare(other); // FIXME
419: }
420:
421: public Object valueOf(String value) {
422: switch (typeCode) {
423: case STRING_TYPE_CODE:
424: return value;
425: case UNTYPED_ATOMIC_TYPE_CODE:
426: return new UntypedAtomic(value);
427: case ANY_URI_TYPE_CODE:
428: return URIPath.makeURI(TextUtils.replaceWhitespace(value,
429: true));
430: case BOOLEAN_TYPE_CODE:
431: value = value.trim();
432: if (value.equals("true") || value.equals("1"))
433: return Boolean.TRUE;
434: if (value.equals("false") || value.equals("0"))
435: return Boolean.FALSE;
436: throw new IllegalArgumentException("not a valid boolean: '"
437: + value + "'");
438: case FLOAT_TYPE_CODE:
439: case DOUBLE_TYPE_CODE:
440: value = value.trim();
441: if ("INF".equals(value))
442: value = "Infinity";
443: else if ("-INF".equals(value))
444: value = "-Infinity";
445: return typeCode == FLOAT_TYPE_CODE ? (Object) Float
446: .valueOf(value) : (Object) Double.valueOf(value);
447: case DECIMAL_TYPE_CODE:
448: value = value.trim();
449: // The BigDecimal constructor accepts an exponent.
450: // So check and complain if that exists.
451: for (int i = value.length(); --i >= 0;) {
452: char ch = value.charAt(i);
453: if (ch == 'e' || ch == 'E')
454: throw new IllegalArgumentException(
455: "not a valid decimal: '" + value + "'");
456: }
457: return new java.math.BigDecimal(value);
458: case DURATION_TYPE_CODE:
459: return Duration.parseDuration(value);
460: case YEAR_MONTH_DURATION_TYPE_CODE:
461: return Duration.parseYearMonthDuration(value);
462: case DAY_TIME_DURATION_TYPE_CODE:
463: return Duration.parseDayTimeDuration(value);
464: case BASE64_BINARY_TYPE_CODE:
465: return Base64Binary.valueOf(value);
466: case HEX_BINARY_TYPE_CODE:
467: return HexBinary.valueOf(value);
468: default:
469: throw new RuntimeException("valueOf not implemented for "
470: + name);
471: }
472: }
473:
474: public static Float makeFloat(float value) {
475: /* #ifdef JAVA5 */
476: // return Float.valueOf(value);
477: /* #else */
478: return new Float(value);
479: /* #endif */
480: }
481:
482: public static Double makeDouble(double value) {
483: /* #ifdef JAVA5 */
484: // return Double.valueOf(value);
485: /* #else */
486: return new Double(value);
487: /* #endif */
488: }
489:
490: public static final Double DOUBLE_ZERO = makeDouble(0);
491: public static final Double DOUBLE_ONE = makeDouble(1);
492: public static final Float FLOAT_ZERO = makeFloat(0);
493: public static final Float FLOAT_ONE = makeFloat(1);
494: public static final BigDecimal DECIMAL_ONE = BigDecimal.valueOf(1);
495:
496: public Procedure getConstructor() {
497: return null;
498: }
499: }
|