0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common Development
0008: * and Distribution License("CDDL") (collectively, the "License"). You
0009: * may not use this file except in compliance with the License. You can obtain
0010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
0011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
0012: * language governing permissions and limitations under the License.
0013: *
0014: * When distributing the software, include this License Header Notice in each
0015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
0016: * Sun designates this particular file as subject to the "Classpath" exception
0017: * as provided by Sun in the GPL Version 2 section of the License file that
0018: * accompanied this code. If applicable, add the following below the License
0019: * Header, with the fields enclosed by brackets [] replaced by your own
0020: * identifying information: "Portions Copyrighted [year]
0021: * [name of copyright owner]"
0022: *
0023: * Contributor(s):
0024: *
0025: * If you wish your version of this file to be governed by only the CDDL or
0026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
0027: * elects to include this software in this distribution under the [CDDL or GPL
0028: * Version 2] license." If you don't indicate a single choice of license, a
0029: * recipient has the option to distribute your version of this file under
0030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
0031: * its licensees as provided above. However, if you add GPL Version 2 code
0032: * and therefore, elected the GPL Version 2 license, then the option applies
0033: * only if the new code is made subject to such option by the copyright
0034: * holder.
0035: */
0036:
0037: package com.sun.xml.bind.v2.model.impl;
0038:
0039: import java.awt.*;
0040: import java.awt.image.BufferedImage;
0041: import java.io.ByteArrayInputStream;
0042: import java.io.File;
0043: import java.io.IOException;
0044: import java.io.InputStream;
0045: import java.io.OutputStreamWriter;
0046: import java.io.UnsupportedEncodingException;
0047: import java.lang.reflect.Type;
0048: import java.math.BigDecimal;
0049: import java.math.BigInteger;
0050: import java.net.MalformedURLException;
0051: import java.net.URI;
0052: import java.net.URISyntaxException;
0053: import java.net.URL;
0054: import java.util.ArrayList;
0055: import java.util.Calendar;
0056: import java.util.Collections;
0057: import java.util.Date;
0058: import java.util.GregorianCalendar;
0059: import java.util.HashMap;
0060: import java.util.Iterator;
0061: import java.util.List;
0062: import java.util.Map;
0063: import java.util.UUID;
0064:
0065: import javax.activation.DataHandler;
0066: import javax.activation.DataSource;
0067: import javax.activation.MimeType;
0068: import javax.activation.MimeTypeParseException;
0069: import javax.imageio.ImageIO;
0070: import javax.imageio.ImageWriter;
0071: import javax.imageio.stream.ImageOutputStream;
0072: import javax.xml.bind.ValidationEvent;
0073: import javax.xml.bind.helpers.ValidationEventImpl;
0074: import javax.xml.datatype.DatatypeConfigurationException;
0075: import javax.xml.datatype.DatatypeConstants;
0076: import javax.xml.datatype.DatatypeFactory;
0077: import javax.xml.datatype.Duration;
0078: import javax.xml.datatype.XMLGregorianCalendar;
0079: import javax.xml.namespace.QName;
0080: import javax.xml.stream.XMLStreamException;
0081: import javax.xml.transform.Source;
0082: import javax.xml.transform.TransformerException;
0083: import javax.xml.transform.stream.StreamResult;
0084:
0085: import com.sun.istack.ByteArrayDataSource;
0086: import com.sun.xml.bind.DatatypeConverterImpl;
0087: import com.sun.xml.bind.WhiteSpaceProcessor;
0088: import com.sun.xml.bind.api.AccessorException;
0089: import com.sun.xml.bind.v2.TODO;
0090: import com.sun.xml.bind.v2.WellKnownNamespace;
0091: import com.sun.xml.bind.v2.model.runtime.RuntimeBuiltinLeafInfo;
0092: import com.sun.xml.bind.v2.runtime.Name;
0093: import com.sun.xml.bind.v2.runtime.Transducer;
0094: import com.sun.xml.bind.v2.runtime.XMLSerializer;
0095: import com.sun.xml.bind.v2.runtime.output.Pcdata;
0096: import com.sun.xml.bind.v2.runtime.unmarshaller.Base64Data;
0097: import com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext;
0098: import com.sun.xml.bind.v2.util.ByteArrayOutputStreamEx;
0099: import com.sun.xml.bind.v2.util.DataSourceSource;
0100:
0101: import org.xml.sax.SAXException;
0102:
0103: /**
0104: * {@link BuiltinLeafInfoImpl} with a support for runtime.
0105: *
0106: * <p>
0107: * In particular this class defines {@link Transducer}s for the built-in types.
0108: *
0109: * @author Kohsuke Kawaguchi
0110: */
0111: public abstract class RuntimeBuiltinLeafInfoImpl<T> extends
0112: BuiltinLeafInfoImpl<Type, Class> implements
0113: RuntimeBuiltinLeafInfo, Transducer<T> {
0114:
0115: private RuntimeBuiltinLeafInfoImpl(Class type, QName... typeNames) {
0116: super (type, typeNames);
0117: LEAVES.put(type, this );
0118: }
0119:
0120: public final Class getClazz() {
0121: return (Class) getType();
0122: }
0123:
0124: public final Transducer getTransducer() {
0125: return this ;
0126: }
0127:
0128: public boolean useNamespace() {
0129: return false;
0130: }
0131:
0132: public final boolean isDefault() {
0133: return true;
0134: }
0135:
0136: public void declareNamespace(T o, XMLSerializer w)
0137: throws AccessorException {
0138: }
0139:
0140: public QName getTypeName(T instance) {
0141: return null;
0142: }
0143:
0144: /**
0145: * Those built-in types that print to {@link String}.
0146: */
0147: private static abstract class StringImpl<T> extends
0148: RuntimeBuiltinLeafInfoImpl<T> {
0149: protected StringImpl(Class type, QName... typeNames) {
0150: super (type, typeNames);
0151: }
0152:
0153: public abstract String print(T o) throws AccessorException;
0154:
0155: public void writeText(XMLSerializer w, T o, String fieldName)
0156: throws IOException, SAXException, XMLStreamException,
0157: AccessorException {
0158: w.text(print(o), fieldName);
0159: }
0160:
0161: public void writeLeafElement(XMLSerializer w, Name tagName,
0162: T o, String fieldName) throws IOException,
0163: SAXException, XMLStreamException, AccessorException {
0164: w.leafElement(tagName, print(o), fieldName);
0165: }
0166: }
0167:
0168: /**
0169: * Those built-in types that print to {@link Pcdata}.
0170: */
0171: private static abstract class PcdataImpl<T> extends
0172: RuntimeBuiltinLeafInfoImpl<T> {
0173: protected PcdataImpl(Class type, QName... typeNames) {
0174: super (type, typeNames);
0175: }
0176:
0177: public abstract Pcdata print(T o) throws AccessorException;
0178:
0179: public final void writeText(XMLSerializer w, T o,
0180: String fieldName) throws IOException, SAXException,
0181: XMLStreamException, AccessorException {
0182: w.text(print(o), fieldName);
0183: }
0184:
0185: public final void writeLeafElement(XMLSerializer w,
0186: Name tagName, T o, String fieldName)
0187: throws IOException, SAXException, XMLStreamException,
0188: AccessorException {
0189: w.leafElement(tagName, print(o), fieldName);
0190: }
0191:
0192: }
0193:
0194: /**
0195: * All instances of {@link RuntimeBuiltinLeafInfoImpl}s keyed by their type.
0196: */
0197: public static final Map<Type, RuntimeBuiltinLeafInfoImpl<?>> LEAVES = new HashMap<Type, RuntimeBuiltinLeafInfoImpl<?>>();
0198:
0199: public static final RuntimeBuiltinLeafInfoImpl<String> STRING = new StringImpl<String>(
0200: String.class, createXS("string"),
0201: createXS("normalizedString"), createXS("anyURI"),
0202: createXS("token"), createXS("language"), createXS("Name"),
0203: createXS("NCName"), createXS("NMTOKEN"), createXS("ENTITY")) {
0204: public String parse(CharSequence text) {
0205: return text.toString();
0206: }
0207:
0208: public String print(String s) {
0209: return s;
0210: }
0211:
0212: @Override
0213: public final void writeText(XMLSerializer w, String o,
0214: String fieldName) throws IOException, SAXException,
0215: XMLStreamException {
0216: w.text(o, fieldName);
0217: }
0218:
0219: @Override
0220: public final void writeLeafElement(XMLSerializer w,
0221: Name tagName, String o, String fieldName)
0222: throws IOException, SAXException, XMLStreamException {
0223: w.leafElement(tagName, o, fieldName);
0224: }
0225: };
0226:
0227: /**
0228: * List of all {@link RuntimeBuiltinLeafInfoImpl}s.
0229: *
0230: * <p>
0231: * This corresponds to the built-in Java classes that are specified to be
0232: * handled differently than ordinary classes. See table 8-2 "Mapping of Standard Java classes".
0233: */
0234: public static final List<RuntimeBuiltinLeafInfoImpl<?>> builtinBeanInfos;
0235:
0236: static {
0237: RuntimeBuiltinLeafInfoImpl[] secondary = new RuntimeBuiltinLeafInfoImpl[] {
0238: /*
0239: There are cases where more than one Java classes map to the same XML type.
0240: But when we see the same XML type in an incoming document, we only pick
0241: one of those Java classes to unmarshal. This Java class is called 'primary'.
0242: The rest are called 'secondary'.
0243:
0244: Currently we lack the proper infrastructure to handle those nicely.
0245: For now, we rely on a hack.
0246:
0247: We define secondary mappings first, then primary ones later. GrammarInfo
0248: builds a map from type name to BeanInfo. By defining primary ones later,
0249: those primary bindings will overwrite the secondary ones.
0250: */
0251:
0252: /*
0253: secondary bindings
0254: */
0255: new StringImpl<Character>(Character.class,
0256: createXS("unsignedShort")) {
0257: public Character parse(CharSequence text) {
0258: // TODO.checkSpec("default mapping for char is not defined yet");
0259: return (char) DatatypeConverterImpl
0260: ._parseInt(text);
0261: }
0262:
0263: public String print(Character v) {
0264: return Integer.toString(v);
0265: }
0266: },
0267: new StringImpl<Calendar>(Calendar.class,
0268: DatatypeConstants.DATETIME) {
0269: public Calendar parse(CharSequence text) {
0270: return DatatypeConverterImpl
0271: ._parseDateTime(text.toString());
0272: }
0273:
0274: public String print(Calendar v) {
0275: return DatatypeConverterImpl._printDateTime(v);
0276: }
0277: },
0278: new StringImpl<GregorianCalendar>(
0279: GregorianCalendar.class,
0280: DatatypeConstants.DATETIME) {
0281: public GregorianCalendar parse(CharSequence text) {
0282: return DatatypeConverterImpl
0283: ._parseDateTime(text.toString());
0284: }
0285:
0286: public String print(GregorianCalendar v) {
0287: return DatatypeConverterImpl._printDateTime(v);
0288: }
0289: },
0290: new StringImpl<Date>(Date.class,
0291: DatatypeConstants.DATETIME) {
0292: public Date parse(CharSequence text) {
0293: return DatatypeConverterImpl._parseDateTime(
0294: text.toString()).getTime();
0295: }
0296:
0297: public String print(Date v) {
0298: GregorianCalendar cal = new GregorianCalendar(
0299: 0, 0, 0);
0300: cal.setTime(v);
0301: return DatatypeConverterImpl
0302: ._printDateTime(cal);
0303: }
0304: },
0305: new StringImpl<File>(File.class, createXS("string")) {
0306: public File parse(CharSequence text) {
0307: return new File(WhiteSpaceProcessor.trim(text)
0308: .toString());
0309: }
0310:
0311: public String print(File v) {
0312: return v.getPath();
0313: }
0314: },
0315: new StringImpl<URL>(URL.class, createXS("anyURI")) {
0316: public URL parse(CharSequence text)
0317: throws SAXException {
0318: TODO.checkSpec("JSR222 Issue #42");
0319: try {
0320: return new URL(WhiteSpaceProcessor.trim(
0321: text).toString());
0322: } catch (MalformedURLException e) {
0323: UnmarshallingContext.getInstance()
0324: .handleError(e);
0325: return null;
0326: }
0327: }
0328:
0329: public String print(URL v) {
0330: return v.toExternalForm();
0331: }
0332: },
0333: new StringImpl<URI>(URI.class, createXS("string")) {
0334: public URI parse(CharSequence text)
0335: throws SAXException {
0336: try {
0337: return new URI(text.toString());
0338: } catch (URISyntaxException e) {
0339: UnmarshallingContext.getInstance()
0340: .handleError(e);
0341: return null;
0342: }
0343: }
0344:
0345: public String print(URI v) {
0346: return v.toString();
0347: }
0348: },
0349: new StringImpl<Class>(Class.class, createXS("string")) {
0350: public Class parse(CharSequence text)
0351: throws SAXException {
0352: TODO.checkSpec("JSR222 Issue #42");
0353: try {
0354: String name = WhiteSpaceProcessor
0355: .trim(text).toString();
0356: ClassLoader cl = Thread.currentThread()
0357: .getContextClassLoader();
0358: if (cl != null)
0359: return cl.loadClass(name);
0360: else
0361: return Class.forName(name);
0362: } catch (ClassNotFoundException e) {
0363: UnmarshallingContext.getInstance()
0364: .handleError(e);
0365: return null;
0366: }
0367: }
0368:
0369: public String print(Class v) {
0370: return v.getName();
0371: }
0372: },
0373:
0374: /*
0375: classes that map to base64Binary / MTOM related classes.
0376: a part of the secondary binding.
0377: */
0378: new PcdataImpl<Image>(Image.class,
0379: createXS("base64Binary")) {
0380: public Image parse(CharSequence text)
0381: throws SAXException {
0382: try {
0383: InputStream is;
0384: if (text instanceof Base64Data)
0385: is = ((Base64Data) text)
0386: .getInputStream();
0387: else
0388: is = new ByteArrayInputStream(
0389: decodeBase64(text)); // TODO: buffering is inefficient
0390:
0391: // technically we should check the MIME type here, but
0392: // normally images can be content-sniffed.
0393: // so the MIME type check will only make us slower and draconian, both of which
0394: // JAXB 2.0 isn't interested.
0395: try {
0396: return ImageIO.read(is);
0397: } finally {
0398: is.close();
0399: }
0400: } catch (IOException e) {
0401: UnmarshallingContext.getInstance()
0402: .handleError(e);
0403: return null;
0404: }
0405: }
0406:
0407: private BufferedImage convertToBufferedImage(
0408: Image image) throws IOException {
0409: if (image instanceof BufferedImage) {
0410: return (BufferedImage) image;
0411:
0412: } else {
0413: MediaTracker tracker = new MediaTracker(
0414: new Component() {
0415: }); // not sure if this is the right thing to do.
0416: tracker.addImage(image, 0);
0417: try {
0418: tracker.waitForAll();
0419: } catch (InterruptedException e) {
0420: throw new IOException(e.getMessage());
0421: }
0422: BufferedImage bufImage = new BufferedImage(
0423: image.getWidth(null), image
0424: .getHeight(null),
0425: BufferedImage.TYPE_INT_ARGB);
0426:
0427: Graphics g = bufImage.createGraphics();
0428: g.drawImage(image, 0, 0, null);
0429: return bufImage;
0430: }
0431: }
0432:
0433: public Base64Data print(Image v) {
0434: ByteArrayOutputStreamEx imageData = new ByteArrayOutputStreamEx();
0435: XMLSerializer xs = XMLSerializer.getInstance();
0436:
0437: String mimeType = xs.getXMIMEContentType();
0438: if (mimeType == null
0439: || mimeType.startsWith("image/*"))
0440: // because PNG is lossless, it's a good default
0441: //
0442: // mime type can be a range, in which case we can't just pass that
0443: // to ImageIO.getImageWritersByMIMEType, so here I'm just assuming
0444: // the default of PNG. Not sure if this is complete.
0445: mimeType = "image/png";
0446:
0447: try {
0448: Iterator<ImageWriter> itr = ImageIO
0449: .getImageWritersByMIMEType(mimeType);
0450: if (itr.hasNext()) {
0451: ImageWriter w = itr.next();
0452: ImageOutputStream os = ImageIO
0453: .createImageOutputStream(imageData);
0454: w.setOutput(os);
0455: w.write(convertToBufferedImage(v));
0456: os.close();
0457: w.dispose();
0458: } else {
0459: // no encoder
0460: xs.handleEvent(new ValidationEventImpl(
0461: ValidationEvent.ERROR,
0462: Messages.NO_IMAGE_WRITER
0463: .format(mimeType),
0464: xs.getCurrentLocation(null)));
0465: // TODO: proper error reporting
0466: throw new RuntimeException(
0467: "no encoder for MIME type "
0468: + mimeType);
0469: }
0470: } catch (IOException e) {
0471: xs.handleError(e);
0472: // TODO: proper error reporting
0473: throw new RuntimeException(e);
0474: }
0475: Base64Data bd = new Base64Data();
0476: imageData.set(bd, mimeType);
0477: return bd;
0478: }
0479: },
0480: new PcdataImpl<DataHandler>(DataHandler.class,
0481: createXS("base64Binary")) {
0482: public DataHandler parse(CharSequence text) {
0483: if (text instanceof Base64Data)
0484: return ((Base64Data) text).getDataHandler();
0485: else
0486: return new DataHandler(
0487: new ByteArrayDataSource(
0488: decodeBase64(text),
0489: UnmarshallingContext
0490: .getInstance()
0491: .getXMIMEContentType()));
0492: }
0493:
0494: public Base64Data print(DataHandler v) {
0495: Base64Data bd = new Base64Data();
0496: bd.set(v);
0497: return bd;
0498: }
0499: },
0500: new PcdataImpl<Source>(Source.class,
0501: createXS("base64Binary")) {
0502: public Source parse(CharSequence text)
0503: throws SAXException {
0504: try {
0505: if (text instanceof Base64Data)
0506: return new DataSourceSource(
0507: ((Base64Data) text)
0508: .getDataHandler());
0509: else
0510: return new DataSourceSource(
0511: new ByteArrayDataSource(
0512: decodeBase64(text),
0513: UnmarshallingContext
0514: .getInstance()
0515: .getXMIMEContentType()));
0516: } catch (MimeTypeParseException e) {
0517: UnmarshallingContext.getInstance()
0518: .handleError(e);
0519: return null;
0520: }
0521: }
0522:
0523: public Base64Data print(Source v) {
0524: XMLSerializer xs = XMLSerializer.getInstance();
0525: Base64Data bd = new Base64Data();
0526:
0527: String contentType = xs.getXMIMEContentType();
0528: MimeType mt = null;
0529: if (contentType != null)
0530: try {
0531: mt = new MimeType(contentType);
0532: } catch (MimeTypeParseException e) {
0533: xs.handleError(e);
0534: // recover by ignoring the content type specification
0535: }
0536:
0537: if (v instanceof DataSourceSource) {
0538: // if so, we already have immutable DataSource so
0539: // this can be done efficiently
0540: DataSource ds = ((DataSourceSource) v)
0541: .getDataSource();
0542:
0543: String dsct = ds.getContentType();
0544: if (dsct != null
0545: && (contentType == null || contentType
0546: .equals(dsct))) {
0547: bd.set(new DataHandler(ds));
0548: return bd;
0549: }
0550: }
0551:
0552: // general case. slower.
0553:
0554: // find out the encoding
0555: String charset = null;
0556: if (mt != null)
0557: charset = mt.getParameter("charset");
0558: if (charset == null)
0559: charset = "UTF-8";
0560:
0561: try {
0562: ByteArrayOutputStreamEx baos = new ByteArrayOutputStreamEx();
0563: xs.getIdentityTransformer().transform(
0564: v,
0565: new StreamResult(
0566: new OutputStreamWriter(
0567: baos, charset)));
0568: baos.set(bd, "application/xml; charset="
0569: + charset);
0570: return bd;
0571: } catch (TransformerException e) {
0572: // TODO: marshaller error handling
0573: xs.handleError(e);
0574: } catch (UnsupportedEncodingException e) {
0575: xs.handleError(e);
0576: }
0577:
0578: // error recoverly
0579: bd.set(new byte[0], "application/xml");
0580: return bd;
0581: }
0582: },
0583: new StringImpl<XMLGregorianCalendar>(
0584: XMLGregorianCalendar.class,
0585: createXS("anySimpleType"),
0586: DatatypeConstants.DATE,
0587: DatatypeConstants.DATETIME,
0588: DatatypeConstants.TIME,
0589: DatatypeConstants.GMONTH,
0590: DatatypeConstants.GDAY,
0591: DatatypeConstants.GYEAR,
0592: DatatypeConstants.GYEARMONTH,
0593: DatatypeConstants.GMONTHDAY) {
0594: public String print(XMLGregorianCalendar cal) {
0595: XMLSerializer xs = XMLSerializer.getInstance();
0596:
0597: QName type = xs.getSchemaType();
0598: if (type != null) {
0599: try {
0600: checkXmlGregorianCalendarFieldRef(type,
0601: cal);
0602: String format = xmlGregorianCalendarFormatString
0603: .get(type);
0604: if (format != null)
0605: return format(format, cal);
0606: // TODO:
0607: // we need to think about how to report an error where @XmlSchemaType
0608: // didn't take effect. a general case is when a transducer isn't even
0609: // written to look at that value.
0610: } catch (javax.xml.bind.MarshalException e) {
0611: //-xs.handleError(e);
0612: System.out.println(e.toString());
0613: return "";
0614: }
0615: }
0616: return cal.toXMLFormat();
0617: }
0618:
0619: public XMLGregorianCalendar parse(
0620: CharSequence lexical) throws SAXException {
0621: try {
0622: return datatypeFactory
0623: .newXMLGregorianCalendar(lexical
0624: .toString());
0625: } catch (Exception e) {
0626: UnmarshallingContext.getInstance()
0627: .handleError(e);
0628: return null;
0629: }
0630: }
0631:
0632: // code duplicated from JAXP RI 1.3. See 6277586
0633: private String format(String format,
0634: XMLGregorianCalendar value) {
0635: StringBuilder buf = new StringBuilder();
0636: int fidx = 0, flen = format.length();
0637:
0638: while (fidx < flen) {
0639: char fch = format.charAt(fidx++);
0640: if (fch != '%') {// not a meta char
0641: buf.append(fch);
0642: continue;
0643: }
0644:
0645: switch (format.charAt(fidx++)) {
0646: case 'Y':
0647: printNumber(buf, value.getEonAndYear(),
0648: 4);
0649: break;
0650: case 'M':
0651: printNumber(buf, value.getMonth(), 2);
0652: break;
0653: case 'D':
0654: printNumber(buf, value.getDay(), 2);
0655: break;
0656: case 'h':
0657: printNumber(buf, value.getHour(), 2);
0658: break;
0659: case 'm':
0660: printNumber(buf, value.getMinute(), 2);
0661: break;
0662: case 's':
0663: printNumber(buf, value.getSecond(), 2);
0664: if (value.getFractionalSecond() != null) {
0665: String frac = value
0666: .getFractionalSecond()
0667: .toString();
0668: //skip leading zero.
0669: buf.append(frac.substring(1, frac
0670: .length()));
0671: }
0672: break;
0673: case 'z':
0674: int offset = value.getTimezone();
0675: if (offset == 0) {
0676: buf.append('Z');
0677: } else if (offset != DatatypeConstants.FIELD_UNDEFINED) {
0678: if (offset < 0) {
0679: buf.append('-');
0680: offset *= -1;
0681: } else {
0682: buf.append('+');
0683: }
0684: printNumber(buf, offset / 60, 2);
0685: buf.append(':');
0686: printNumber(buf, offset % 60, 2);
0687: }
0688: break;
0689: default:
0690: throw new InternalError(); // impossible
0691: }
0692: }
0693:
0694: return buf.toString();
0695: }
0696:
0697: private void printNumber(StringBuilder out,
0698: BigInteger number, int nDigits) {
0699: String s = number.toString();
0700: for (int i = s.length(); i < nDigits; i++)
0701: out.append('0');
0702: out.append(s);
0703: }
0704:
0705: private void printNumber(StringBuilder out,
0706: int number, int nDigits) {
0707: String s = String.valueOf(number);
0708: for (int i = s.length(); i < nDigits; i++)
0709: out.append('0');
0710: out.append(s);
0711: }
0712:
0713: @Override
0714: public QName getTypeName(XMLGregorianCalendar cal) {
0715: return cal.getXMLSchemaType();
0716: }
0717: } };
0718:
0719: RuntimeBuiltinLeafInfoImpl[] primary = new RuntimeBuiltinLeafInfoImpl[] {
0720: /*
0721: primary bindings
0722: */
0723: STRING,
0724: new StringImpl<Boolean>(Boolean.class,
0725: createXS("boolean")) {
0726: public Boolean parse(CharSequence text) {
0727: return DatatypeConverterImpl
0728: ._parseBoolean(text);
0729: }
0730:
0731: public String print(Boolean v) {
0732: return v.toString();
0733: }
0734: },
0735: new PcdataImpl<byte[]>(byte[].class,
0736: createXS("base64Binary"), createXS("hexBinary")) {
0737: public byte[] parse(CharSequence text) {
0738: return decodeBase64(text);
0739: }
0740:
0741: public Base64Data print(byte[] v) {
0742: XMLSerializer w = XMLSerializer.getInstance();
0743: Base64Data bd = new Base64Data();
0744: String mimeType = w.getXMIMEContentType();
0745: bd.set(v, mimeType);
0746: return bd;
0747: }
0748: },
0749: new StringImpl<Byte>(Byte.class, createXS("byte")) {
0750: public Byte parse(CharSequence text) {
0751: return DatatypeConverterImpl._parseByte(text);
0752: }
0753:
0754: public String print(Byte v) {
0755: return DatatypeConverterImpl._printByte(v);
0756: }
0757: },
0758: new StringImpl<Short>(Short.class, createXS("short"),
0759: createXS("unsignedByte")) {
0760: public Short parse(CharSequence text) {
0761: return DatatypeConverterImpl._parseShort(text);
0762: }
0763:
0764: public String print(Short v) {
0765: return DatatypeConverterImpl._printShort(v);
0766: }
0767: },
0768: new StringImpl<Integer>(Integer.class, createXS("int"),
0769: createXS("unsignedShort")) {
0770: public Integer parse(CharSequence text) {
0771: return DatatypeConverterImpl._parseInt(text);
0772: }
0773:
0774: public String print(Integer v) {
0775: return DatatypeConverterImpl._printInt(v);
0776: }
0777: },
0778: new StringImpl<Long>(Long.class, createXS("long"),
0779: createXS("unsignedInt")) {
0780: public Long parse(CharSequence text) {
0781: return DatatypeConverterImpl._parseLong(text);
0782: }
0783:
0784: public String print(Long v) {
0785: return DatatypeConverterImpl._printLong(v);
0786: }
0787: },
0788: new StringImpl<Float>(Float.class, createXS("float")) {
0789: public Float parse(CharSequence text) {
0790: return DatatypeConverterImpl._parseFloat(text
0791: .toString());
0792: }
0793:
0794: public String print(Float v) {
0795: return DatatypeConverterImpl._printFloat(v);
0796: }
0797: },
0798: new StringImpl<Double>(Double.class, createXS("double")) {
0799: public Double parse(CharSequence text) {
0800: return DatatypeConverterImpl._parseDouble(text);
0801: }
0802:
0803: public String print(Double v) {
0804: return DatatypeConverterImpl._printDouble(v);
0805: }
0806: },
0807: new StringImpl<BigInteger>(BigInteger.class,
0808: createXS("integer"),
0809: createXS("positiveInteger"),
0810: createXS("negativeInteger"),
0811: createXS("nonPositiveInteger"),
0812: createXS("nonNegativeInteger"),
0813: createXS("unsignedLong")) {
0814: public BigInteger parse(CharSequence text) {
0815: return DatatypeConverterImpl
0816: ._parseInteger(text);
0817: }
0818:
0819: public String print(BigInteger v) {
0820: return DatatypeConverterImpl._printInteger(v);
0821: }
0822: },
0823: new StringImpl<BigDecimal>(BigDecimal.class,
0824: createXS("decimal")) {
0825: public BigDecimal parse(CharSequence text) {
0826: return DatatypeConverterImpl._parseDecimal(text
0827: .toString());
0828: }
0829:
0830: public String print(BigDecimal v) {
0831: return DatatypeConverterImpl._printDecimal(v);
0832: }
0833: },
0834: new StringImpl<QName>(QName.class, createXS("QName")) {
0835: public QName parse(CharSequence text)
0836: throws SAXException {
0837: try {
0838: return DatatypeConverterImpl._parseQName(
0839: text.toString(),
0840: UnmarshallingContext.getInstance());
0841: } catch (IllegalArgumentException e) {
0842: UnmarshallingContext.getInstance()
0843: .handleError(e);
0844: return null;
0845: }
0846: }
0847:
0848: public String print(QName v) {
0849: return DatatypeConverterImpl._printQName(v,
0850: XMLSerializer.getInstance()
0851: .getNamespaceContext());
0852: }
0853:
0854: public boolean useNamespace() {
0855: return true;
0856: }
0857:
0858: public void declareNamespace(QName v,
0859: XMLSerializer w) {
0860: w.getNamespaceContext().declareNamespace(
0861: v.getNamespaceURI(), v.getPrefix(),
0862: false);
0863: }
0864: },
0865: new StringImpl<Duration>(Duration.class,
0866: createXS("duration")) {
0867: public String print(Duration duration) {
0868: return duration.toString();
0869: }
0870:
0871: public Duration parse(CharSequence lexical) {
0872: TODO.checkSpec("JSR222 Issue #42");
0873: return datatypeFactory.newDuration(lexical
0874: .toString());
0875: }
0876: }, new StringImpl<Void>(Void.class) {
0877: // 'void' binding isn't defined by the spec, but when the JAX-RPC processes user-defined
0878: // methods like "int actionFoo()", they need this pseudo-void property.
0879:
0880: public String print(Void value) {
0881: return "";
0882: }
0883:
0884: public Void parse(CharSequence lexical) {
0885: return null;
0886: }
0887: } };
0888:
0889: List<RuntimeBuiltinLeafInfoImpl<?>> l = new ArrayList<RuntimeBuiltinLeafInfoImpl<?>>(
0890: secondary.length + primary.length + 1);
0891: for (RuntimeBuiltinLeafInfoImpl<?> item : secondary)
0892: l.add(item);
0893:
0894: // UUID may fail to load if we are running on JDK 1.4. Handle gracefully
0895: try {
0896: l.add(new UUIDImpl());
0897: } catch (LinkageError e) {
0898: // ignore
0899: }
0900:
0901: for (RuntimeBuiltinLeafInfoImpl<?> item : primary)
0902: l.add(item);
0903:
0904: builtinBeanInfos = Collections.unmodifiableList(l);
0905: }
0906:
0907: private static byte[] decodeBase64(CharSequence text) {
0908: if (text instanceof Base64Data) {
0909: Base64Data base64Data = (Base64Data) text;
0910: return base64Data.getExact();
0911: } else {
0912: return DatatypeConverterImpl._parseBase64Binary(text
0913: .toString());
0914: }
0915: }
0916:
0917: private static QName createXS(String typeName) {
0918: return new QName(WellKnownNamespace.XML_SCHEMA, typeName);
0919: }
0920:
0921: /**
0922: * Cached instance of {@link DatatypeFactory} to create
0923: * {@link XMLGregorianCalendar} and {@link Duration}.
0924: */
0925: private static final DatatypeFactory datatypeFactory = init();
0926:
0927: private static DatatypeFactory init() {
0928: try {
0929: return DatatypeFactory.newInstance();
0930: } catch (DatatypeConfigurationException e) {
0931: throw new Error(
0932: Messages.FAILED_TO_INITIALE_DATATYPE_FACTORY
0933: .format(), e);
0934: }
0935: }
0936:
0937: private static void checkXmlGregorianCalendarFieldRef(QName type,
0938: XMLGregorianCalendar cal)
0939: throws javax.xml.bind.MarshalException {
0940: StringBuffer buf = new StringBuffer();
0941: int bitField = xmlGregorianCalendarFieldRef.get(type);
0942: final int l = 0x1;
0943: int pos = 0;
0944: while (bitField != 0x0) {
0945: int bit = bitField & l;
0946: bitField >>>= 4;
0947: pos++;
0948:
0949: if (bit == 1) {
0950: switch (pos) {
0951: case 1:
0952: if (cal.getSecond() == DatatypeConstants.FIELD_UNDEFINED) {
0953: buf.append(" "
0954: + Messages.XMLGREGORIANCALENDAR_SEC);
0955: }
0956: break;
0957: case 2:
0958: if (cal.getMinute() == DatatypeConstants.FIELD_UNDEFINED) {
0959: buf.append(" "
0960: + Messages.XMLGREGORIANCALENDAR_MIN);
0961: }
0962: break;
0963: case 3:
0964: if (cal.getHour() == DatatypeConstants.FIELD_UNDEFINED) {
0965: buf.append(" "
0966: + Messages.XMLGREGORIANCALENDAR_HR);
0967: }
0968: break;
0969: case 4:
0970: if (cal.getDay() == DatatypeConstants.FIELD_UNDEFINED) {
0971: buf.append(" "
0972: + Messages.XMLGREGORIANCALENDAR_DAY);
0973: }
0974: break;
0975: case 5:
0976: if (cal.getMonth() == DatatypeConstants.FIELD_UNDEFINED) {
0977: buf.append(" "
0978: + Messages.XMLGREGORIANCALENDAR_MONTH);
0979: }
0980: break;
0981: case 6:
0982: if (cal.getYear() == DatatypeConstants.FIELD_UNDEFINED) {
0983: buf.append(" "
0984: + Messages.XMLGREGORIANCALENDAR_YEAR);
0985: }
0986: break;
0987: case 7: // ignore timezone setting
0988: break;
0989: }
0990: }
0991: }
0992: if (buf.length() > 0) {
0993: throw new javax.xml.bind.MarshalException(
0994: Messages.XMLGREGORIANCALENDAR_INVALID.format(type
0995: .getLocalPart())
0996: + buf.toString());
0997: }
0998: }
0999:
1000: /**
1001: * Format string for the {@link XMLGregorianCalendar}.
1002: */
1003: private static final Map<QName, String> xmlGregorianCalendarFormatString = new HashMap<QName, String>();
1004:
1005: static {
1006: Map<QName, String> m = xmlGregorianCalendarFormatString;
1007: // See 4971612: be careful for SCCS substitution
1008: m.put(DatatypeConstants.DATETIME, "%Y-%M-%DT%h:%m:%s" + "%z");
1009: m.put(DatatypeConstants.DATE, "%Y-%M-%D" + "%z");
1010: m.put(DatatypeConstants.TIME, "%h:%m:%s" + "%z");
1011: m.put(DatatypeConstants.GMONTH, "--%M--%z");
1012: m.put(DatatypeConstants.GDAY, "---%D" + "%z");
1013: m.put(DatatypeConstants.GYEAR, "%Y" + "%z");
1014: m.put(DatatypeConstants.GYEARMONTH, "%Y-%M" + "%z");
1015: m.put(DatatypeConstants.GMONTHDAY, "--%M-%D" + "%z");
1016: }
1017:
1018: /**
1019: * Field designations for XMLGregorianCalendar format string.
1020: * sec 0x0000001
1021: * min 0x0000010
1022: * hrs 0x0000100
1023: * day 0x0001000
1024: * month 0x0010000
1025: * year 0x0100000
1026: * timezone 0x1000000
1027: */
1028: private static final Map<QName, Integer> xmlGregorianCalendarFieldRef = new HashMap<QName, Integer>();
1029: static {
1030: Map<QName, Integer> f = xmlGregorianCalendarFieldRef;
1031: f.put(DatatypeConstants.DATETIME, 0x1111111);
1032: f.put(DatatypeConstants.DATE, 0x1111000);
1033: f.put(DatatypeConstants.TIME, 0x1000111);
1034: f.put(DatatypeConstants.GDAY, 0x1001000);
1035: f.put(DatatypeConstants.GMONTH, 0x1010000);
1036: f.put(DatatypeConstants.GYEAR, 0x1100000);
1037: f.put(DatatypeConstants.GYEARMONTH, 0x1110000);
1038: f.put(DatatypeConstants.GMONTHDAY, 0x1011000);
1039: }
1040:
1041: /**
1042: * {@link RuntimeBuiltinLeafInfoImpl} for {@link UUID}.
1043: *
1044: * This class is given a name so that failing to load this class won't cause a fatal problem.
1045: */
1046: private static class UUIDImpl extends StringImpl<UUID> {
1047: public UUIDImpl() {
1048: super (UUID.class, RuntimeBuiltinLeafInfoImpl
1049: .createXS("string"));
1050: }
1051:
1052: public UUID parse(CharSequence text) throws SAXException {
1053: TODO.checkSpec("JSR222 Issue #42");
1054: try {
1055: return UUID.fromString(WhiteSpaceProcessor.trim(text)
1056: .toString());
1057: } catch (IllegalArgumentException e) {
1058: UnmarshallingContext.getInstance().handleError(e);
1059: return null;
1060: }
1061: }
1062:
1063: public String print(UUID v) {
1064: return v.toString();
1065: }
1066: }
1067: }
|