001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.xml.internal.bind.v2.model.impl;
027:
028: import java.awt.*;
029: import java.awt.image.BufferedImage;
030: import java.io.ByteArrayInputStream;
031: import java.io.File;
032: import java.io.IOException;
033: import java.io.InputStream;
034: import java.io.OutputStreamWriter;
035: import java.io.UnsupportedEncodingException;
036: import java.lang.reflect.Type;
037: import java.math.BigDecimal;
038: import java.math.BigInteger;
039: import java.net.MalformedURLException;
040: import java.net.URI;
041: import java.net.URISyntaxException;
042: import java.net.URL;
043: import java.util.ArrayList;
044: import java.util.Calendar;
045: import java.util.Collections;
046: import java.util.Date;
047: import java.util.GregorianCalendar;
048: import java.util.HashMap;
049: import java.util.Iterator;
050: import java.util.List;
051: import java.util.Map;
052: import java.util.UUID;
053:
054: import javax.activation.DataHandler;
055: import javax.activation.DataSource;
056: import javax.activation.MimeType;
057: import javax.activation.MimeTypeParseException;
058: import javax.imageio.ImageIO;
059: import javax.imageio.ImageWriter;
060: import javax.xml.bind.ValidationEvent;
061: import javax.xml.bind.helpers.ValidationEventImpl;
062: import javax.xml.datatype.DatatypeConfigurationException;
063: import javax.xml.datatype.DatatypeConstants;
064: import javax.xml.datatype.DatatypeFactory;
065: import javax.xml.datatype.Duration;
066: import javax.xml.datatype.XMLGregorianCalendar;
067: import javax.xml.namespace.QName;
068: import javax.xml.stream.XMLStreamException;
069: import javax.xml.transform.Source;
070: import javax.xml.transform.TransformerException;
071: import javax.xml.transform.stream.StreamResult;
072:
073: import com.sun.istack.internal.ByteArrayDataSource;
074: import com.sun.xml.internal.bind.DatatypeConverterImpl;
075: import com.sun.xml.internal.bind.WhiteSpaceProcessor;
076: import com.sun.xml.internal.bind.api.AccessorException;
077: import com.sun.xml.internal.bind.v2.TODO;
078: import com.sun.xml.internal.bind.v2.WellKnownNamespace;
079: import com.sun.xml.internal.bind.v2.model.runtime.RuntimeBuiltinLeafInfo;
080: import com.sun.xml.internal.bind.v2.runtime.Name;
081: import com.sun.xml.internal.bind.v2.runtime.Transducer;
082: import com.sun.xml.internal.bind.v2.runtime.XMLSerializer;
083: import com.sun.xml.internal.bind.v2.runtime.output.Pcdata;
084: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data;
085: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext;
086: import com.sun.xml.internal.bind.v2.util.ByteArrayOutputStreamEx;
087: import com.sun.xml.internal.bind.v2.util.DataSourceSource;
088:
089: import org.xml.sax.SAXException;
090:
091: /**
092: * {@link BuiltinLeafInfoImpl} with a support for runtime.
093: *
094: * <p>
095: * In particular this class defines {@link Transducer}s for the built-in types.
096: *
097: * @author Kohsuke Kawaguchi
098: */
099: public abstract class RuntimeBuiltinLeafInfoImpl<T> extends
100: BuiltinLeafInfoImpl<Type, Class> implements
101: RuntimeBuiltinLeafInfo, Transducer<T> {
102:
103: private RuntimeBuiltinLeafInfoImpl(Class type, QName... typeNames) {
104: super (type, typeNames);
105: LEAVES.put(type, this );
106: }
107:
108: public final Class getClazz() {
109: return (Class) getType();
110: }
111:
112: public final Transducer getTransducer() {
113: return this ;
114: }
115:
116: public boolean useNamespace() {
117: return false;
118: }
119:
120: public final boolean isDefault() {
121: return true;
122: }
123:
124: public void declareNamespace(T o, XMLSerializer w)
125: throws AccessorException {
126: }
127:
128: public QName getTypeName(T instance) {
129: return null;
130: }
131:
132: /**
133: * Those built-in types that print to {@link String}.
134: */
135: private static abstract class StringImpl<T> extends
136: RuntimeBuiltinLeafInfoImpl<T> {
137: protected StringImpl(Class type, QName... typeNames) {
138: super (type, typeNames);
139: }
140:
141: public abstract String print(T o) throws AccessorException;
142:
143: public void writeText(XMLSerializer w, T o, String fieldName)
144: throws IOException, SAXException, XMLStreamException,
145: AccessorException {
146: w.text(print(o), fieldName);
147: }
148:
149: public void writeLeafElement(XMLSerializer w, Name tagName,
150: T o, String fieldName) throws IOException,
151: SAXException, XMLStreamException, AccessorException {
152: w.leafElement(tagName, print(o), fieldName);
153: }
154: }
155:
156: /**
157: * Those built-in types that print to {@link Pcdata}.
158: */
159: private static abstract class PcdataImpl<T> extends
160: RuntimeBuiltinLeafInfoImpl<T> {
161: protected PcdataImpl(Class type, QName... typeNames) {
162: super (type, typeNames);
163: }
164:
165: public abstract Pcdata print(T o) throws AccessorException;
166:
167: public final void writeText(XMLSerializer w, T o,
168: String fieldName) throws IOException, SAXException,
169: XMLStreamException, AccessorException {
170: w.text(print(o), fieldName);
171: }
172:
173: public final void writeLeafElement(XMLSerializer w,
174: Name tagName, T o, String fieldName)
175: throws IOException, SAXException, XMLStreamException,
176: AccessorException {
177: w.leafElement(tagName, print(o), fieldName);
178: }
179:
180: }
181:
182: /**
183: * All instances of {@link RuntimeBuiltinLeafInfoImpl}s keyed by their type.
184: */
185: public static final Map<Type, RuntimeBuiltinLeafInfoImpl<?>> LEAVES = new HashMap<Type, RuntimeBuiltinLeafInfoImpl<?>>();
186:
187: public static final RuntimeBuiltinLeafInfoImpl<String> STRING = new StringImpl<String>(
188: String.class, createXS("string"),
189: createXS("normalizedString"), createXS("token"),
190: createXS("language"), createXS("Name"), createXS("NCName"),
191: createXS("NMTOKEN"), createXS("ENTITY")) {
192: public String parse(CharSequence text) {
193: return text.toString();
194: }
195:
196: public String print(String s) {
197: return s;
198: }
199:
200: @Override
201: public final void writeText(XMLSerializer w, String o,
202: String fieldName) throws IOException, SAXException,
203: XMLStreamException {
204: w.text(o, fieldName);
205: }
206:
207: @Override
208: public final void writeLeafElement(XMLSerializer w,
209: Name tagName, String o, String fieldName)
210: throws IOException, SAXException, XMLStreamException {
211: w.leafElement(tagName, o, fieldName);
212: }
213: };
214:
215: /**
216: * List of all {@link RuntimeBuiltinLeafInfoImpl}s.
217: *
218: * <p>
219: * This corresponds to the built-in Java classes that are specified to be
220: * handled differently than ordinary classes. See table 8-2 "Mapping of Standard Java classes".
221: */
222: public static final List<RuntimeBuiltinLeafInfoImpl<?>> builtinBeanInfos;
223:
224: static {
225: RuntimeBuiltinLeafInfoImpl[] secondary = new RuntimeBuiltinLeafInfoImpl[] {
226: /*
227: There are cases where more than one Java classes map to the same XML type.
228: But when we see the same XML type in an incoming document, we only pick
229: one of those Java classes to unmarshal. This Java class is called 'primary'.
230: The rest are called 'secondary'.
231:
232: Currently we lack the proper infrastructure to handle those nicely.
233: For now, we rely on a hack.
234:
235: We define secondary mappings first, then primary ones later. GrammarInfo
236: builds a map from type name to BeanInfo. By defining primary ones later,
237: those primary bindings will overwrite the secondary ones.
238: */
239:
240: /*
241: secondary bindings
242: */
243: new StringImpl<Character>(Character.class,
244: createXS("unsignedShort")) {
245: public Character parse(CharSequence text) {
246: // TODO.checkSpec("default mapping for char is not defined yet");
247: return (char) DatatypeConverterImpl
248: ._parseInt(text);
249: }
250:
251: public String print(Character v) {
252: return Integer.toString(v);
253: }
254: },
255: new StringImpl<Calendar>(Calendar.class,
256: DatatypeConstants.DATETIME) {
257: public Calendar parse(CharSequence text) {
258: return DatatypeConverterImpl
259: ._parseDateTime(text.toString());
260: }
261:
262: public String print(Calendar v) {
263: return DatatypeConverterImpl._printDateTime(v);
264: }
265: },
266: new StringImpl<GregorianCalendar>(
267: GregorianCalendar.class,
268: DatatypeConstants.DATETIME) {
269: public GregorianCalendar parse(CharSequence text) {
270: return DatatypeConverterImpl
271: ._parseDateTime(text.toString());
272: }
273:
274: public String print(GregorianCalendar v) {
275: return DatatypeConverterImpl._printDateTime(v);
276: }
277: },
278: new StringImpl<Date>(Date.class,
279: DatatypeConstants.DATETIME) {
280: public Date parse(CharSequence text) {
281: return DatatypeConverterImpl._parseDateTime(
282: text.toString()).getTime();
283: }
284:
285: public String print(Date v) {
286: GregorianCalendar cal = new GregorianCalendar(
287: 0, 0, 0);
288: cal.setTime(v);
289: return DatatypeConverterImpl
290: ._printDateTime(cal);
291: }
292: },
293: new StringImpl<File>(File.class, createXS("string")) {
294: public File parse(CharSequence text) {
295: return new File(WhiteSpaceProcessor.trim(text)
296: .toString());
297: }
298:
299: public String print(File v) {
300: return v.getPath();
301: }
302: },
303: new StringImpl<URL>(URL.class, createXS("anyURI")) {
304: public URL parse(CharSequence text)
305: throws SAXException {
306: TODO.checkSpec("JSR222 Issue #42");
307: try {
308: return new URL(WhiteSpaceProcessor.trim(
309: text).toString());
310: } catch (MalformedURLException e) {
311: UnmarshallingContext.getInstance()
312: .handleError(e);
313: return null;
314: }
315: }
316:
317: public String print(URL v) {
318: return v.toExternalForm();
319: }
320: },
321: new StringImpl<Class>(Class.class, createXS("string")) {
322: public Class parse(CharSequence text)
323: throws SAXException {
324: TODO.checkSpec("JSR222 Issue #42");
325: try {
326: String name = WhiteSpaceProcessor
327: .trim(text).toString();
328: ClassLoader cl = Thread.currentThread()
329: .getContextClassLoader();
330: if (cl != null)
331: return cl.loadClass(name);
332: else
333: return Class.forName(name);
334: } catch (ClassNotFoundException e) {
335: UnmarshallingContext.getInstance()
336: .handleError(e);
337: return null;
338: }
339: }
340:
341: public String print(Class v) {
342: return v.getName();
343: }
344: },
345:
346: /*
347: classes that map to base64Binary / MTOM related classes.
348: a part of the secondary binding.
349: */
350: new PcdataImpl<Image>(Image.class,
351: createXS("base64Binary")) {
352: public Image parse(CharSequence text)
353: throws SAXException {
354: try {
355: InputStream is;
356: if (text instanceof Base64Data)
357: is = ((Base64Data) text)
358: .getInputStream();
359: else
360: is = new ByteArrayInputStream(
361: decodeBase64(text)); // TODO: buffering is inefficient
362:
363: // technically we should check the MIME type here, but
364: // normally images can be content-sniffed.
365: // so the MIME type check will only make us slower and draconian, both of which
366: // JAXB 2.0 isn't interested.
367: return ImageIO.read(is);
368: } catch (IOException e) {
369: UnmarshallingContext.getInstance()
370: .handleError(e);
371: return null;
372: }
373: }
374:
375: private BufferedImage convertToBufferedImage(
376: Image image) throws IOException {
377: if (image instanceof BufferedImage) {
378: return (BufferedImage) image;
379:
380: } else {
381: MediaTracker tracker = new MediaTracker(
382: new Component() {
383: }); // not sure if this is the right thing to do.
384: tracker.addImage(image, 0);
385: try {
386: tracker.waitForAll();
387: } catch (InterruptedException e) {
388: throw new IOException(e.getMessage());
389: }
390: BufferedImage bufImage = new BufferedImage(
391: image.getWidth(null), image
392: .getHeight(null),
393: BufferedImage.TYPE_INT_ARGB);
394:
395: Graphics g = bufImage.createGraphics();
396: g.drawImage(image, 0, 0, null);
397: return bufImage;
398: }
399: }
400:
401: public Base64Data print(Image v) {
402: ByteArrayOutputStreamEx imageData = new ByteArrayOutputStreamEx();
403: XMLSerializer xs = XMLSerializer.getInstance();
404:
405: String mimeType = xs.getXMIMEContentType();
406: if (mimeType == null
407: || mimeType.startsWith("image/*"))
408: // because PNG is lossless, it's a good default
409: //
410: // mime type can be a range, in which case we can't just pass that
411: // to ImageIO.getImageWritersByMIMEType, so here I'm just assuming
412: // the default of PNG. Not sure if this is complete.
413: mimeType = "image/png";
414:
415: try {
416: Iterator<ImageWriter> itr = ImageIO
417: .getImageWritersByMIMEType(mimeType);
418: if (itr.hasNext()) {
419: ImageWriter w = itr.next();
420: w
421: .setOutput(ImageIO
422: .createImageOutputStream(imageData));
423: w.write(convertToBufferedImage(v));
424: w.dispose();
425: } else {
426: // no encoder
427: xs.handleEvent(new ValidationEventImpl(
428: ValidationEvent.ERROR,
429: Messages.NO_IMAGE_WRITER
430: .format(mimeType),
431: xs.getCurrentLocation(null)));
432: // TODO: proper error reporting
433: throw new RuntimeException(
434: "no encoder for MIME type "
435: + mimeType);
436: }
437: } catch (IOException e) {
438: xs.handleError(e);
439: // TODO: proper error reporting
440: throw new RuntimeException(e);
441: }
442: Base64Data bd = xs
443: .getCachedBase64DataInstance();
444: imageData.set(bd, mimeType);
445: return bd;
446: }
447: },
448: new PcdataImpl<DataHandler>(DataHandler.class,
449: createXS("base64Binary")) {
450: public DataHandler parse(CharSequence text) {
451: if (text instanceof Base64Data)
452: return ((Base64Data) text).getDataHandler();
453: else
454: return new DataHandler(
455: new ByteArrayDataSource(
456: decodeBase64(text),
457: UnmarshallingContext
458: .getInstance()
459: .getXMIMEContentType()));
460: }
461:
462: public Base64Data print(DataHandler v) {
463: Base64Data bd = XMLSerializer.getInstance()
464: .getCachedBase64DataInstance();
465: bd.set(v);
466: return bd;
467: }
468: },
469: new PcdataImpl<Source>(Source.class,
470: createXS("base64Binary")) {
471: public Source parse(CharSequence text)
472: throws SAXException {
473: try {
474: if (text instanceof Base64Data)
475: return new DataSourceSource(
476: ((Base64Data) text)
477: .getDataHandler());
478: else
479: return new DataSourceSource(
480: new ByteArrayDataSource(
481: decodeBase64(text),
482: UnmarshallingContext
483: .getInstance()
484: .getXMIMEContentType()));
485: } catch (MimeTypeParseException e) {
486: UnmarshallingContext.getInstance()
487: .handleError(e);
488: return null;
489: }
490: }
491:
492: public Base64Data print(Source v) {
493: XMLSerializer xs = XMLSerializer.getInstance();
494: Base64Data bd = xs
495: .getCachedBase64DataInstance();
496:
497: String contentType = xs.getXMIMEContentType();
498: MimeType mt = null;
499: if (contentType != null)
500: try {
501: mt = new MimeType(contentType);
502: } catch (MimeTypeParseException e) {
503: xs.handleError(e);
504: // recover by ignoring the content type specification
505: }
506:
507: if (v instanceof DataSourceSource) {
508: // if so, we already have immutable DataSource so
509: // this can be done efficiently
510: DataSource ds = ((DataSourceSource) v)
511: .getDataSource();
512:
513: String dsct = ds.getContentType();
514: if (dsct != null
515: && (contentType == null || contentType
516: .equals(dsct))) {
517: bd.set(new DataHandler(ds));
518: return bd;
519: }
520: }
521:
522: // general case. slower.
523:
524: // find out the encoding
525: String charset = null;
526: if (mt != null)
527: charset = mt.getParameter("charset");
528: if (charset == null)
529: charset = "UTF-8";
530:
531: try {
532: ByteArrayOutputStreamEx baos = new ByteArrayOutputStreamEx();
533: xs.getIdentityTransformer().transform(
534: v,
535: new StreamResult(
536: new OutputStreamWriter(
537: baos, charset)));
538: baos.set(bd, "application/xml; charset="
539: + charset);
540: return bd;
541: } catch (TransformerException e) {
542: // TODO: marshaller error handling
543: xs.handleError(e);
544: } catch (UnsupportedEncodingException e) {
545: xs.handleError(e);
546: }
547:
548: // error recoverly
549: bd.set(new byte[0], "application/xml");
550: return bd;
551: }
552: },
553: new StringImpl<XMLGregorianCalendar>(
554: XMLGregorianCalendar.class,
555: createXS("anySimpleType"),
556: DatatypeConstants.DATE,
557: DatatypeConstants.DATETIME,
558: DatatypeConstants.TIME,
559: DatatypeConstants.GMONTH,
560: DatatypeConstants.GDAY,
561: DatatypeConstants.GYEAR,
562: DatatypeConstants.GYEARMONTH,
563: DatatypeConstants.GMONTHDAY) {
564: public String print(XMLGregorianCalendar cal) {
565: XMLSerializer xs = XMLSerializer.getInstance();
566:
567: QName type = xs.getSchemaType();
568: if (type != null) {
569: String format = xmlGregorianCalendarFormatString
570: .get(type);
571: if (format != null)
572: return format(format, cal);
573: // TODO:
574: // we need to think about how to report an error where @XmlSchemaType
575: // didn't take effect. a general case is when a transducer isn't even
576: // written to look at that value.
577: }
578:
579: return cal.toXMLFormat();
580: }
581:
582: public XMLGregorianCalendar parse(
583: CharSequence lexical) throws SAXException {
584: try {
585: return datatypeFactory
586: .newXMLGregorianCalendar(lexical
587: .toString());
588: } catch (Exception e) {
589: UnmarshallingContext.getInstance()
590: .handleError(e);
591: return null;
592: }
593: }
594:
595: // code duplicated from JAXP RI 1.3. See 6277586
596: private String format(String format,
597: XMLGregorianCalendar value) {
598: StringBuilder buf = new StringBuilder();
599: int fidx = 0, flen = format.length();
600:
601: while (fidx < flen) {
602: char fch = format.charAt(fidx++);
603: if (fch != '%') {// not a meta char
604: buf.append(fch);
605: continue;
606: }
607:
608: switch (format.charAt(fidx++)) {
609: case 'Y':
610: printNumber(buf, value.getEonAndYear(),
611: 4);
612: break;
613: case 'M':
614: printNumber(buf, value.getMonth(), 2);
615: break;
616: case 'D':
617: printNumber(buf, value.getDay(), 2);
618: break;
619: case 'h':
620: printNumber(buf, value.getHour(), 2);
621: break;
622: case 'm':
623: printNumber(buf, value.getMinute(), 2);
624: break;
625: case 's':
626: printNumber(buf, value.getSecond(), 2);
627: if (value.getFractionalSecond() != null) {
628: String frac = value
629: .getFractionalSecond()
630: .toString();
631: //skip leading zero.
632: buf.append(frac.substring(1, frac
633: .length()));
634: }
635: break;
636: case 'z':
637: int offset = value.getTimezone();
638: if (offset == 0) {
639: buf.append('Z');
640: } else if (offset != DatatypeConstants.FIELD_UNDEFINED) {
641: if (offset < 0) {
642: buf.append('-');
643: offset *= -1;
644: } else {
645: buf.append('+');
646: }
647: printNumber(buf, offset / 60, 2);
648: buf.append(':');
649: printNumber(buf, offset % 60, 2);
650: }
651: break;
652: default:
653: throw new InternalError(); // impossible
654: }
655: }
656:
657: return buf.toString();
658: }
659:
660: private void printNumber(StringBuilder out,
661: BigInteger number, int nDigits) {
662: String s = number.toString();
663: for (int i = s.length(); i < nDigits; i++)
664: out.append('0');
665: out.append(s);
666: }
667:
668: private void printNumber(StringBuilder out,
669: int number, int nDigits) {
670: String s = String.valueOf(number);
671: for (int i = s.length(); i < nDigits; i++)
672: out.append('0');
673: out.append(s);
674: }
675:
676: @Override
677: public QName getTypeName(XMLGregorianCalendar cal) {
678: return cal.getXMLSchemaType();
679: }
680: } };
681:
682: RuntimeBuiltinLeafInfoImpl[] primary = new RuntimeBuiltinLeafInfoImpl[] {
683: /*
684: primary bindings
685: */
686: STRING,
687: new StringImpl<Boolean>(Boolean.class,
688: createXS("boolean")) {
689: public Boolean parse(CharSequence text) {
690: return DatatypeConverterImpl
691: ._parseBoolean(text);
692: }
693:
694: public String print(Boolean v) {
695: return v.toString();
696: }
697: },
698: new PcdataImpl<byte[]>(byte[].class,
699: createXS("base64Binary"), createXS("hexBinary")) {
700: public byte[] parse(CharSequence text) {
701: return decodeBase64(text);
702: }
703:
704: public Base64Data print(byte[] v) {
705: XMLSerializer w = XMLSerializer.getInstance();
706: Base64Data bd = w.getCachedBase64DataInstance();
707: String mimeType = w.getXMIMEContentType();
708: bd.set(v, mimeType);
709: return bd;
710: }
711: },
712: new StringImpl<Byte>(Byte.class, createXS("byte")) {
713: public Byte parse(CharSequence text) {
714: return DatatypeConverterImpl._parseByte(text);
715: }
716:
717: public String print(Byte v) {
718: return DatatypeConverterImpl._printByte(v);
719: }
720: },
721: new StringImpl<Short>(Short.class, createXS("short"),
722: createXS("unsignedByte")) {
723: public Short parse(CharSequence text) {
724: return DatatypeConverterImpl._parseShort(text);
725: }
726:
727: public String print(Short v) {
728: return DatatypeConverterImpl._printShort(v);
729: }
730: },
731: new StringImpl<Integer>(Integer.class, createXS("int"),
732: createXS("unsignedShort")) {
733: public Integer parse(CharSequence text) {
734: return DatatypeConverterImpl._parseInt(text);
735: }
736:
737: public String print(Integer v) {
738: return DatatypeConverterImpl._printInt(v);
739: }
740: },
741: new StringImpl<Long>(Long.class, createXS("long"),
742: createXS("unsignedInt")) {
743: public Long parse(CharSequence text) {
744: return DatatypeConverterImpl._parseLong(text);
745: }
746:
747: public String print(Long v) {
748: return DatatypeConverterImpl._printLong(v);
749: }
750: },
751: new StringImpl<Float>(Float.class, createXS("float")) {
752: public Float parse(CharSequence text) {
753: return DatatypeConverterImpl._parseFloat(text
754: .toString());
755: }
756:
757: public String print(Float v) {
758: return DatatypeConverterImpl._printFloat(v);
759: }
760: },
761: new StringImpl<Double>(Double.class, createXS("double")) {
762: public Double parse(CharSequence text) {
763: return DatatypeConverterImpl._parseDouble(text);
764: }
765:
766: public String print(Double v) {
767: return DatatypeConverterImpl._printDouble(v);
768: }
769: },
770: new StringImpl<BigInteger>(BigInteger.class,
771: createXS("integer"),
772: createXS("positiveInteger"),
773: createXS("negativeInteger"),
774: createXS("nonPositiveInteger"),
775: createXS("nonNegativeInteger"),
776: createXS("unsignedLong")) {
777: public BigInteger parse(CharSequence text) {
778: return DatatypeConverterImpl
779: ._parseInteger(text);
780: }
781:
782: public String print(BigInteger v) {
783: return DatatypeConverterImpl._printInteger(v);
784: }
785: },
786: new StringImpl<BigDecimal>(BigDecimal.class,
787: createXS("decimal")) {
788: public BigDecimal parse(CharSequence text) {
789: return DatatypeConverterImpl._parseDecimal(text
790: .toString());
791: }
792:
793: public String print(BigDecimal v) {
794: return DatatypeConverterImpl._printDecimal(v);
795: }
796: },
797: new StringImpl<QName>(QName.class, createXS("QName")) {
798: public QName parse(CharSequence text)
799: throws SAXException {
800: try {
801: return DatatypeConverterImpl._parseQName(
802: text.toString(),
803: UnmarshallingContext.getInstance());
804: } catch (IllegalArgumentException e) {
805: UnmarshallingContext.getInstance()
806: .handleError(e);
807: return null;
808: }
809: }
810:
811: public String print(QName v) {
812: return DatatypeConverterImpl._printQName(v,
813: XMLSerializer.getInstance()
814: .getNamespaceContext());
815: }
816:
817: public boolean useNamespace() {
818: return true;
819: }
820:
821: public void declareNamespace(QName v,
822: XMLSerializer w) {
823: w.getNamespaceContext().declareNamespace(
824: v.getNamespaceURI(), v.getPrefix(),
825: false);
826: }
827: },
828: new StringImpl<URI>(URI.class, createXS("anyURI")) {
829: public URI parse(CharSequence text)
830: throws SAXException {
831: TODO.checkSpec("JSR222 Issue #42");
832: try {
833: return new URI(text.toString());
834: } catch (URISyntaxException e) {
835: UnmarshallingContext.getInstance()
836: .handleError(e);
837: return null;
838: }
839: }
840:
841: public String print(URI v) {
842: return v.toString();
843: }
844: },
845: new StringImpl<Duration>(Duration.class,
846: createXS("duration")) {
847: public String print(Duration duration) {
848: return duration.toString();
849: }
850:
851: public Duration parse(CharSequence lexical) {
852: TODO.checkSpec("JSR222 Issue #42");
853: return datatypeFactory.newDuration(lexical
854: .toString());
855: }
856: }, new StringImpl<Void>(Void.class) {
857: // 'void' binding isn't defined by the spec, but when the JAX-RPC processes user-defined
858: // methods like "int actionFoo()", they need this pseudo-void property.
859:
860: public String print(Void value) {
861: return "";
862: }
863:
864: public Void parse(CharSequence lexical) {
865: return null;
866: }
867: } };
868:
869: List<RuntimeBuiltinLeafInfoImpl<?>> l = new ArrayList<RuntimeBuiltinLeafInfoImpl<?>>(
870: secondary.length + primary.length + 1);
871: for (RuntimeBuiltinLeafInfoImpl<?> item : secondary)
872: l.add(item);
873:
874: // UUID may fail to load if we are running on JDK 1.4. Handle gracefully
875: try {
876: l.add(new UUIDImpl());
877: } catch (LinkageError e) {
878: // ignore
879: }
880:
881: for (RuntimeBuiltinLeafInfoImpl<?> item : primary)
882: l.add(item);
883:
884: builtinBeanInfos = Collections.unmodifiableList(l);
885: }
886:
887: private static byte[] decodeBase64(CharSequence text) {
888: if (text instanceof Base64Data) {
889: Base64Data base64Data = (Base64Data) text;
890: return base64Data.getExact();
891: } else {
892: return DatatypeConverterImpl._parseBase64Binary(text
893: .toString());
894: }
895: }
896:
897: private static QName createXS(String typeName) {
898: return new QName(WellKnownNamespace.XML_SCHEMA, typeName);
899: }
900:
901: /**
902: * Cached instance of {@link DatatypeFactory} to create
903: * {@link XMLGregorianCalendar} and {@link Duration}.
904: */
905: private static final DatatypeFactory datatypeFactory = init();
906:
907: private static DatatypeFactory init() {
908: try {
909: return DatatypeFactory.newInstance();
910: } catch (DatatypeConfigurationException e) {
911: throw new Error(
912: Messages.FAILED_TO_INITIALE_DATATYPE_FACTORY
913: .format(), e);
914: }
915: }
916:
917: /**
918: * Format string for the {@link XMLGregorianCalendar}.
919: */
920: private static final Map<QName, String> xmlGregorianCalendarFormatString = new HashMap<QName, String>();
921:
922: static {
923: Map<QName, String> m = xmlGregorianCalendarFormatString;
924: // See 4971612: be careful for SCCS substitution
925: m.put(DatatypeConstants.DATETIME, "%Y-%M-%DT%h:%m:%s" + "%z");
926: m.put(DatatypeConstants.DATE, "%Y-%M-%D" + "%z");
927: m.put(DatatypeConstants.TIME, "%h:%m:%s" + "%z");
928: m.put(DatatypeConstants.GMONTH, "--%M--%z");
929: m.put(DatatypeConstants.GDAY, "---%D" + "%z");
930: m.put(DatatypeConstants.GYEAR, "%Y" + "%z");
931: m.put(DatatypeConstants.GYEARMONTH, "%Y-%M" + "%z");
932: m.put(DatatypeConstants.GMONTHDAY, "--%M-%D" + "%z");
933: }
934:
935: /**
936: * {@link RuntimeBuiltinLeafInfoImpl} for {@link UUID}.
937: *
938: * This class is given a name so that failing to load this class won't cause a fatal problem.
939: */
940: private static class UUIDImpl extends StringImpl<UUID> {
941: public UUIDImpl() {
942: super (UUID.class, RuntimeBuiltinLeafInfoImpl
943: .createXS("string"));
944: }
945:
946: public UUID parse(CharSequence text) throws SAXException {
947: TODO.checkSpec("JSR222 Issue #42");
948: try {
949: return UUID.fromString(WhiteSpaceProcessor.trim(text)
950: .toString());
951: } catch (IllegalArgumentException e) {
952: UnmarshallingContext.getInstance().handleError(e);
953: return null;
954: }
955: }
956:
957: public String print(UUID v) {
958: return v.toString();
959: }
960: }
961: }
|