325N/A/*
325N/A * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
325N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
325N/A *
325N/A * This code is free software; you can redistribute it and/or modify it
325N/A * under the terms of the GNU General Public License version 2 only, as
325N/A * published by the Free Software Foundation. Oracle designates this
325N/A * particular file as subject to the "Classpath" exception as provided
325N/A * by Oracle in the LICENSE file that accompanied this code.
325N/A *
325N/A * This code is distributed in the hope that it will be useful, but WITHOUT
325N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
325N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
325N/A * version 2 for more details (a copy is included in the LICENSE file that
325N/A * accompanied this code).
325N/A *
325N/A * You should have received a copy of the GNU General Public License version
325N/A * 2 along with this work; if not, write to the Free Software Foundation,
325N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
325N/A *
325N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
325N/A * or visit www.oracle.com if you need additional information or have any
325N/A * questions.
325N/A */
325N/A
325N/Apackage com.sun.xml.internal.bind.v2.model.impl;
325N/A
325N/Aimport java.awt.*;
325N/Aimport java.awt.image.BufferedImage;
325N/Aimport java.io.ByteArrayInputStream;
325N/Aimport java.io.File;
325N/Aimport java.io.IOException;
325N/Aimport java.io.InputStream;
325N/Aimport java.io.OutputStreamWriter;
325N/Aimport java.io.UnsupportedEncodingException;
325N/Aimport java.lang.reflect.Type;
325N/Aimport java.math.BigDecimal;
325N/Aimport java.math.BigInteger;
325N/Aimport java.net.MalformedURLException;
325N/Aimport java.net.URI;
325N/Aimport java.net.URISyntaxException;
325N/Aimport java.net.URL;
325N/Aimport java.util.ArrayList;
325N/Aimport java.util.Calendar;
325N/Aimport java.util.Collections;
325N/Aimport java.util.Date;
325N/Aimport java.util.GregorianCalendar;
325N/Aimport java.util.HashMap;
325N/Aimport java.util.Iterator;
325N/Aimport java.util.List;
325N/Aimport java.util.Map;
325N/Aimport java.util.UUID;
325N/A
325N/Aimport javax.activation.DataHandler;
325N/Aimport javax.activation.DataSource;
325N/Aimport javax.activation.MimeType;
325N/Aimport javax.activation.MimeTypeParseException;
325N/Aimport javax.imageio.ImageIO;
325N/Aimport javax.imageio.ImageWriter;
325N/Aimport javax.imageio.stream.ImageOutputStream;
325N/Aimport javax.xml.bind.ValidationEvent;
325N/Aimport javax.xml.bind.helpers.ValidationEventImpl;
325N/Aimport javax.xml.datatype.DatatypeConfigurationException;
325N/Aimport javax.xml.datatype.DatatypeConstants;
325N/Aimport javax.xml.datatype.DatatypeFactory;
325N/Aimport javax.xml.datatype.Duration;
325N/Aimport javax.xml.datatype.XMLGregorianCalendar;
325N/Aimport javax.xml.namespace.QName;
325N/Aimport javax.xml.stream.XMLStreamException;
325N/Aimport javax.xml.transform.OutputKeys;
325N/Aimport javax.xml.transform.Source;
325N/Aimport javax.xml.transform.Transformer;
325N/Aimport javax.xml.transform.TransformerException;
325N/Aimport javax.xml.transform.stream.StreamResult;
325N/A
325N/Aimport com.sun.istack.internal.ByteArrayDataSource;
325N/Aimport com.sun.xml.internal.bind.DatatypeConverterImpl;
325N/Aimport com.sun.xml.internal.bind.WhiteSpaceProcessor;
325N/Aimport com.sun.xml.internal.bind.api.AccessorException;
325N/Aimport com.sun.xml.internal.bind.v2.TODO;
325N/Aimport com.sun.xml.internal.bind.v2.WellKnownNamespace;
325N/Aimport com.sun.xml.internal.bind.v2.model.runtime.RuntimeBuiltinLeafInfo;
325N/Aimport com.sun.xml.internal.bind.v2.runtime.Name;
325N/Aimport com.sun.xml.internal.bind.v2.runtime.Transducer;
325N/Aimport com.sun.xml.internal.bind.v2.runtime.XMLSerializer;
325N/Aimport com.sun.xml.internal.bind.v2.runtime.output.Pcdata;
325N/Aimport com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data;
325N/Aimport com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext;
325N/Aimport com.sun.xml.internal.bind.v2.util.ByteArrayOutputStreamEx;
325N/Aimport com.sun.xml.internal.bind.v2.util.DataSourceSource;
325N/A
325N/Aimport org.xml.sax.SAXException;
325N/A
325N/A/**
325N/A * {@link BuiltinLeafInfoImpl} with a support for runtime.
325N/A *
325N/A * <p>
325N/A * In particular this class defines {@link Transducer}s for the built-in types.
325N/A *
325N/A * @author Kohsuke Kawaguchi
325N/A */
325N/Apublic abstract class RuntimeBuiltinLeafInfoImpl<T> extends BuiltinLeafInfoImpl<Type,Class>
325N/A implements RuntimeBuiltinLeafInfo, Transducer<T> {
325N/A
325N/A private RuntimeBuiltinLeafInfoImpl(Class type, QName... typeNames) {
325N/A super(type, typeNames);
325N/A LEAVES.put(type,this);
325N/A }
325N/A
325N/A public final Class getClazz() {
325N/A return (Class)getType();
325N/A }
325N/A
325N/A
325N/A public final Transducer getTransducer() {
325N/A return this;
325N/A }
325N/A
325N/A public boolean useNamespace() {
325N/A return false;
325N/A }
325N/A
325N/A public final boolean isDefault() {
325N/A return true;
325N/A }
325N/A
325N/A public void declareNamespace(T o, XMLSerializer w) throws AccessorException {
325N/A }
325N/A
325N/A public QName getTypeName(T instance) {
325N/A return null;
325N/A }
325N/A
325N/A /**
325N/A * Those built-in types that print to {@link String}.
325N/A */
325N/A private static abstract class StringImpl<T> extends RuntimeBuiltinLeafInfoImpl<T> {
325N/A protected StringImpl(Class type, QName... typeNames) {
325N/A super(type,typeNames);
325N/A }
325N/A
325N/A public abstract String print(T o) throws AccessorException;
325N/A
325N/A public void writeText(XMLSerializer w, T o, String fieldName) throws IOException, SAXException, XMLStreamException, AccessorException {
325N/A w.text(print(o),fieldName);
325N/A }
325N/A
325N/A public void writeLeafElement(XMLSerializer w, Name tagName, T o, String fieldName) throws IOException, SAXException, XMLStreamException, AccessorException {
325N/A w.leafElement(tagName,print(o),fieldName);
325N/A }
325N/A }
325N/A
325N/A /**
325N/A * Those built-in types that print to {@link Pcdata}.
325N/A */
325N/A private static abstract class PcdataImpl<T> extends RuntimeBuiltinLeafInfoImpl<T> {
325N/A protected PcdataImpl(Class type, QName... typeNames) {
325N/A super(type,typeNames);
325N/A }
325N/A
325N/A public abstract Pcdata print(T o) throws AccessorException;
325N/A
325N/A public final void writeText(XMLSerializer w, T o, String fieldName) throws IOException, SAXException, XMLStreamException, AccessorException {
325N/A w.text(print(o),fieldName);
325N/A }
325N/A
325N/A public final void writeLeafElement(XMLSerializer w, Name tagName, T o, String fieldName) throws IOException, SAXException, XMLStreamException, AccessorException {
325N/A w.leafElement(tagName,print(o),fieldName);
325N/A }
325N/A
325N/A }
325N/A
325N/A /**
325N/A * All instances of {@link RuntimeBuiltinLeafInfoImpl}s keyed by their type.
325N/A */
325N/A public static final Map<Type,RuntimeBuiltinLeafInfoImpl<?>> LEAVES = new HashMap<Type, RuntimeBuiltinLeafInfoImpl<?>>();
325N/A
325N/A private static QName createXS(String typeName) {
325N/A return new QName(WellKnownNamespace.XML_SCHEMA,typeName);
325N/A }
325N/A
325N/A public static final RuntimeBuiltinLeafInfoImpl<String> STRING;
325N/A
325N/A private static final String DATE = "date";
325N/A
325N/A /**
325N/A * List of all {@link RuntimeBuiltinLeafInfoImpl}s.
325N/A *
325N/A * <p>
325N/A * This corresponds to the built-in Java classes that are specified to be
325N/A * handled differently than ordinary classes. See table 8-2 "Mapping of Standard Java classes".
325N/A */
325N/A public static final List<RuntimeBuiltinLeafInfoImpl<?>> builtinBeanInfos;
325N/A
325N/A public static final String MAP_ANYURI_TO_URI = "mapAnyUriToUri";
325N/A
325N/A static {
325N/A
325N/A QName[] qnames = (System.getProperty(MAP_ANYURI_TO_URI) == null) ? new QName[] {
325N/A createXS("string"),
325N/A createXS("anySimpleType"),
325N/A createXS("normalizedString"),
325N/A createXS("anyURI"),
325N/A createXS("token"),
325N/A createXS("language"),
325N/A createXS("Name"),
325N/A createXS("NCName"),
325N/A createXS("NMTOKEN"),
325N/A createXS("ENTITY")}
325N/A :
325N/A new QName[] {
325N/A createXS("string"),
325N/A createXS("anySimpleType"),
325N/A createXS("normalizedString"),
325N/A createXS("token"),
325N/A createXS("language"),
325N/A createXS("Name"),
325N/A createXS("NCName"),
325N/A createXS("NMTOKEN"),
325N/A createXS("ENTITY")};
325N/A
325N/A STRING = new StringImplImpl(String.class, qnames);
325N/A
325N/A ArrayList<RuntimeBuiltinLeafInfoImpl<?>> secondaryList = new ArrayList<RuntimeBuiltinLeafInfoImpl<?>>();
325N/A /*
325N/A There are cases where more than one Java classes map to the same XML type.
325N/A But when we see the same XML type in an incoming document, we only pick
325N/A one of those Java classes to unmarshal. This Java class is called 'primary'.
325N/A The rest are called 'secondary'.
325N/A
325N/A Currently we lack the proper infrastructure to handle those nicely.
325N/A For now, we rely on a hack.
325N/A
325N/A We define secondary mappings first, then primary ones later. GrammarInfo
325N/A builds a map from type name to BeanInfo. By defining primary ones later,
325N/A those primary bindings will overwrite the secondary ones.
325N/A */
325N/A
325N/A /*
325N/A secondary bindings
325N/A */
325N/A secondaryList.add(
325N/A new StringImpl<Character>(Character.class, createXS("unsignedShort")) {
325N/A public Character parse(CharSequence text) {
325N/A // TODO.checkSpec("default mapping for char is not defined yet");
325N/A return (char)DatatypeConverterImpl._parseInt(text);
325N/A }
325N/A public String print(Character v) {
325N/A return Integer.toString(v);
325N/A }
325N/A });
325N/A secondaryList.add(
325N/A new StringImpl<Calendar>(Calendar.class, DatatypeConstants.DATETIME) {
325N/A public Calendar parse(CharSequence text) {
325N/A return DatatypeConverterImpl._parseDateTime(text.toString());
325N/A }
325N/A public String print(Calendar v) {
325N/A return DatatypeConverterImpl._printDateTime(v);
325N/A }
325N/A });
325N/A secondaryList.add(
325N/A new StringImpl<GregorianCalendar>(GregorianCalendar.class, DatatypeConstants.DATETIME) {
325N/A public GregorianCalendar parse(CharSequence text) {
325N/A return DatatypeConverterImpl._parseDateTime(text.toString());
325N/A }
325N/A public String print(GregorianCalendar v) {
325N/A return DatatypeConverterImpl._printDateTime(v);
325N/A }
325N/A });
325N/A secondaryList.add(
325N/A new StringImpl<Date>(Date.class, DatatypeConstants.DATETIME) {
325N/A public Date parse(CharSequence text) {
325N/A return DatatypeConverterImpl._parseDateTime(text.toString()).getTime();
325N/A }
325N/A public String print(Date v) {
325N/A XMLSerializer xs = XMLSerializer.getInstance();
325N/A QName type = xs.getSchemaType();
325N/A GregorianCalendar cal = new GregorianCalendar(0,0,0);
325N/A cal.setTime(v);
325N/A if ((type != null) && (WellKnownNamespace.XML_SCHEMA.equals(type.getNamespaceURI())) &&
325N/A DATE.equals(type.getLocalPart())) {
325N/A return DatatypeConverterImpl._printDate(cal);
325N/A } else {
325N/A return DatatypeConverterImpl._printDateTime(cal);
325N/A }
325N/A }
325N/A });
325N/A secondaryList.add(
325N/A new StringImpl<File>(File.class, createXS("string")) {
325N/A public File parse(CharSequence text) {
325N/A return new File(WhiteSpaceProcessor.trim(text).toString());
325N/A }
325N/A public String print(File v) {
325N/A return v.getPath();
325N/A }
325N/A });
325N/A secondaryList.add(
325N/A new StringImpl<URL>(URL.class, createXS("anyURI")) {
325N/A public URL parse(CharSequence text) throws SAXException {
325N/A TODO.checkSpec("JSR222 Issue #42");
325N/A try {
325N/A return new URL(WhiteSpaceProcessor.trim(text).toString());
325N/A } catch (MalformedURLException e) {
325N/A UnmarshallingContext.getInstance().handleError(e);
325N/A return null;
325N/A }
325N/A }
325N/A public String print(URL v) {
325N/A return v.toExternalForm();
325N/A }
325N/A });
325N/A if (System.getProperty(MAP_ANYURI_TO_URI) == null) {
325N/A secondaryList.add(
325N/A new StringImpl<URI>(URI.class, createXS("string")) {
325N/A public URI parse(CharSequence text) throws SAXException {
325N/A try {
325N/A return new URI(text.toString());
325N/A } catch (URISyntaxException e) {
325N/A UnmarshallingContext.getInstance().handleError(e);
325N/A return null;
325N/A }
325N/A }
325N/A
325N/A public String print(URI v) {
325N/A return v.toString();
325N/A }
325N/A });
325N/A }
325N/A secondaryList.add(
325N/A new StringImpl<Class>(Class.class, createXS("string")) {
325N/A public Class parse(CharSequence text) throws SAXException {
325N/A TODO.checkSpec("JSR222 Issue #42");
325N/A try {
325N/A String name = WhiteSpaceProcessor.trim(text).toString();
325N/A ClassLoader cl = UnmarshallingContext.getInstance().classLoader;
325N/A if(cl==null)
325N/A cl = Thread.currentThread().getContextClassLoader();
325N/A
325N/A if(cl!=null)
325N/A return cl.loadClass(name);
325N/A else
325N/A return Class.forName(name);
325N/A } catch (ClassNotFoundException e) {
325N/A UnmarshallingContext.getInstance().handleError(e);
325N/A return null;
325N/A }
325N/A }
325N/A public String print(Class v) {
325N/A return v.getName();
325N/A }
325N/A });
325N/A
325N/A /*
325N/A classes that map to base64Binary / MTOM related classes.
325N/A a part of the secondary binding.
325N/A */
325N/A secondaryList.add(
325N/A new PcdataImpl<Image>(Image.class, createXS("base64Binary")) {
325N/A public Image parse(CharSequence text) throws SAXException {
325N/A try {
325N/A InputStream is;
325N/A if(text instanceof Base64Data)
325N/A is = ((Base64Data)text).getInputStream();
325N/A else
325N/A is = new ByteArrayInputStream(decodeBase64(text)); // TODO: buffering is inefficient
325N/A
325N/A // technically we should check the MIME type here, but
325N/A // normally images can be content-sniffed.
325N/A // so the MIME type check will only make us slower and draconian, both of which
325N/A // JAXB 2.0 isn't interested.
325N/A try {
325N/A return ImageIO.read(is);
325N/A } finally {
325N/A is.close();
325N/A }
325N/A } catch (IOException e) {
325N/A UnmarshallingContext.getInstance().handleError(e);
325N/A return null;
325N/A }
325N/A }
325N/A
325N/A private BufferedImage convertToBufferedImage(Image image) throws IOException {
325N/A if (image instanceof BufferedImage) {
325N/A return (BufferedImage)image;
325N/A
325N/A } else {
325N/A MediaTracker tracker = new MediaTracker(new Component(){}); // not sure if this is the right thing to do.
325N/A tracker.addImage(image, 0);
325N/A try {
325N/A tracker.waitForAll();
325N/A } catch (InterruptedException e) {
325N/A throw new IOException(e.getMessage());
325N/A }
325N/A BufferedImage bufImage = new BufferedImage(
325N/A image.getWidth(null),
325N/A image.getHeight(null),
325N/A BufferedImage.TYPE_INT_ARGB);
325N/A
325N/A Graphics g = bufImage.createGraphics();
325N/A g.drawImage(image, 0, 0, null);
325N/A return bufImage;
325N/A }
325N/A }
325N/A
325N/A public Base64Data print(Image v) {
325N/A ByteArrayOutputStreamEx imageData = new ByteArrayOutputStreamEx();
325N/A XMLSerializer xs = XMLSerializer.getInstance();
325N/A
325N/A String mimeType = xs.getXMIMEContentType();
325N/A if(mimeType==null || mimeType.startsWith("image/*"))
325N/A // because PNG is lossless, it's a good default
325N/A //
325N/A // mime type can be a range, in which case we can't just pass that
325N/A // to ImageIO.getImageWritersByMIMEType, so here I'm just assuming
325N/A // the default of PNG. Not sure if this is complete.
325N/A mimeType = "image/png";
325N/A
325N/A try {
325N/A Iterator<ImageWriter> itr = ImageIO.getImageWritersByMIMEType(mimeType);
325N/A if(itr.hasNext()) {
325N/A ImageWriter w = itr.next();
325N/A ImageOutputStream os = ImageIO.createImageOutputStream(imageData);
325N/A w.setOutput(os);
325N/A w.write(convertToBufferedImage(v));
325N/A os.close();
325N/A w.dispose();
325N/A } else {
325N/A // no encoder
325N/A xs.handleEvent(new ValidationEventImpl(
325N/A ValidationEvent.ERROR,
325N/A Messages.NO_IMAGE_WRITER.format(mimeType),
325N/A xs.getCurrentLocation(null) ));
325N/A // TODO: proper error reporting
325N/A throw new RuntimeException("no encoder for MIME type "+mimeType);
325N/A }
325N/A } catch (IOException e) {
325N/A xs.handleError(e);
325N/A // TODO: proper error reporting
325N/A throw new RuntimeException(e);
325N/A }
325N/A Base64Data bd = new Base64Data();
325N/A imageData.set(bd,mimeType);
325N/A return bd;
325N/A }
325N/A });
325N/A secondaryList.add(
325N/A new PcdataImpl<DataHandler>(DataHandler.class, createXS("base64Binary")) {
325N/A public DataHandler parse(CharSequence text) {
325N/A if(text instanceof Base64Data)
325N/A return ((Base64Data)text).getDataHandler();
325N/A else
325N/A return new DataHandler(new ByteArrayDataSource(decodeBase64(text),
325N/A UnmarshallingContext.getInstance().getXMIMEContentType()));
325N/A }
325N/A
325N/A public Base64Data print(DataHandler v) {
325N/A Base64Data bd = new Base64Data();
325N/A bd.set(v);
325N/A return bd;
325N/A }
325N/A });
325N/A secondaryList.add(
325N/A new PcdataImpl<Source>(Source.class, createXS("base64Binary")) {
325N/A public Source parse(CharSequence text) throws SAXException {
325N/A try {
325N/A if(text instanceof Base64Data)
325N/A return new DataSourceSource( ((Base64Data)text).getDataHandler() );
325N/A else
325N/A return new DataSourceSource(new ByteArrayDataSource(decodeBase64(text),
325N/A UnmarshallingContext.getInstance().getXMIMEContentType()));
325N/A } catch (MimeTypeParseException e) {
325N/A UnmarshallingContext.getInstance().handleError(e);
325N/A return null;
325N/A }
325N/A }
325N/A
325N/A public Base64Data print(Source v) {
325N/A XMLSerializer xs = XMLSerializer.getInstance();
325N/A Base64Data bd = new Base64Data();
325N/A
325N/A String contentType = xs.getXMIMEContentType();
325N/A MimeType mt = null;
325N/A if(contentType!=null)
325N/A try {
325N/A mt = new MimeType(contentType);
325N/A } catch (MimeTypeParseException e) {
325N/A xs.handleError(e);
325N/A // recover by ignoring the content type specification
325N/A }
325N/A
325N/A if( v instanceof DataSourceSource ) {
325N/A // if so, we already have immutable DataSource so
325N/A // this can be done efficiently
325N/A DataSource ds = ((DataSourceSource)v).getDataSource();
325N/A
325N/A String dsct = ds.getContentType();
325N/A if(dsct!=null && (contentType==null || contentType.equals(dsct))) {
325N/A bd.set(new DataHandler(ds));
325N/A return bd;
325N/A }
325N/A }
325N/A
325N/A // general case. slower.
325N/A
325N/A // find out the encoding
325N/A String charset=null;
325N/A if(mt!=null)
325N/A charset = mt.getParameter("charset");
325N/A if(charset==null)
325N/A charset = "UTF-8";
325N/A
325N/A try {
325N/A ByteArrayOutputStreamEx baos = new ByteArrayOutputStreamEx();
325N/A Transformer tr = xs.getIdentityTransformer();
325N/A String defaultEncoding = tr.getOutputProperty(OutputKeys.ENCODING);
325N/A tr.setOutputProperty(OutputKeys.ENCODING, charset);
325N/A tr.transform(v, new StreamResult(new OutputStreamWriter(baos,charset)));
325N/A tr.setOutputProperty(OutputKeys.ENCODING, defaultEncoding);
325N/A baos.set(bd,"application/xml; charset="+charset);
325N/A return bd;
325N/A } catch (TransformerException e) {
325N/A // TODO: marshaller error handling
325N/A xs.handleError(e);
325N/A } catch (UnsupportedEncodingException e) {
325N/A xs.handleError(e);
325N/A }
325N/A
325N/A // error recoverly
325N/A bd.set(new byte[0],"application/xml");
325N/A return bd;
325N/A }
325N/A });
325N/A secondaryList.add(
325N/A new StringImpl<XMLGregorianCalendar>(XMLGregorianCalendar.class,
325N/A createXS("anySimpleType"),
325N/A DatatypeConstants.DATE,
325N/A DatatypeConstants.DATETIME,
325N/A DatatypeConstants.TIME,
325N/A DatatypeConstants.GMONTH,
325N/A DatatypeConstants.GDAY,
325N/A DatatypeConstants.GYEAR,
325N/A DatatypeConstants.GYEARMONTH,
325N/A DatatypeConstants.GMONTHDAY
325N/A ) {
325N/A public String print(XMLGregorianCalendar cal) {
325N/A XMLSerializer xs = XMLSerializer.getInstance();
325N/A
325N/A QName type = xs.getSchemaType();
325N/A if (type != null) {
325N/A try {
325N/A checkXmlGregorianCalendarFieldRef(type, cal);
325N/A String format = xmlGregorianCalendarFormatString.get(type);
325N/A if (format != null) {
325N/A return format(format, cal);
325N/A }
325N/A } catch (javax.xml.bind.MarshalException e) {
325N/A // see issue 649
325N/A xs.handleEvent(new ValidationEventImpl(ValidationEvent.WARNING, e.getMessage(),
325N/A xs.getCurrentLocation(null) ));
325N/A return "";
325N/A }
325N/A }
325N/A return cal.toXMLFormat();
325N/A }
325N/A
325N/A public XMLGregorianCalendar parse(CharSequence lexical) throws SAXException {
325N/A try {
325N/A return datatypeFactory.newXMLGregorianCalendar(lexical.toString().trim()); // (.trim() - issue 396)
325N/A } catch (Exception e) {
325N/A UnmarshallingContext.getInstance().handleError(e);
325N/A return null;
325N/A }
325N/A }
325N/A
325N/A // code duplicated from JAXP RI 1.3. See 6277586
325N/A private String format( String format, XMLGregorianCalendar value ) {
325N/A StringBuilder buf = new StringBuilder();
325N/A int fidx=0,flen=format.length();
325N/A
325N/A while(fidx<flen) {
325N/A char fch = format.charAt(fidx++);
325N/A if(fch!='%') {// not a meta char
325N/A buf.append(fch);
325N/A continue;
325N/A }
325N/A
325N/A switch(format.charAt(fidx++)) {
325N/A case 'Y':
325N/A printNumber(buf,value.getEonAndYear(), 4);
325N/A break;
325N/A case 'M':
325N/A printNumber(buf,value.getMonth(),2);
325N/A break;
325N/A case 'D':
325N/A printNumber(buf,value.getDay(),2);
325N/A break;
325N/A case 'h':
325N/A printNumber(buf,value.getHour(),2);
325N/A break;
325N/A case 'm':
325N/A printNumber(buf,value.getMinute(),2);
325N/A break;
325N/A case 's':
325N/A printNumber(buf,value.getSecond(),2);
325N/A if (value.getFractionalSecond() != null) {
325N/A String frac = value.getFractionalSecond().toPlainString();
325N/A //skip leading zero.
325N/A buf.append(frac.substring(1, frac.length()));
325N/A }
325N/A break;
325N/A case 'z':
325N/A int offset = value.getTimezone();
325N/A if(offset == 0) {
325N/A buf.append('Z');
325N/A } else if (offset != DatatypeConstants.FIELD_UNDEFINED) {
325N/A if(offset<0) {
325N/A buf.append('-');
325N/A offset *= -1;
325N/A } else {
325N/A buf.append('+');
325N/A }
325N/A printNumber(buf,offset/60,2);
325N/A buf.append(':');
325N/A printNumber(buf,offset%60,2);
325N/A }
325N/A break;
325N/A default:
325N/A throw new InternalError(); // impossible
325N/A }
325N/A }
325N/A
325N/A return buf.toString();
325N/A }
325N/A private void printNumber( StringBuilder out, BigInteger number, int nDigits) {
325N/A String s = number.toString();
325N/A for( int i=s.length(); i<nDigits; i++ )
325N/A out.append('0');
325N/A out.append(s);
325N/A }
325N/A private void printNumber( StringBuilder out, int number, int nDigits ) {
325N/A String s = String.valueOf(number);
325N/A for( int i=s.length(); i<nDigits; i++ )
325N/A out.append('0');
325N/A out.append(s);
325N/A }
325N/A @Override
325N/A public QName getTypeName(XMLGregorianCalendar cal) {
325N/A return cal.getXMLSchemaType();
325N/A }
325N/A });
325N/A
325N/A ArrayList<RuntimeBuiltinLeafInfoImpl<?>> primaryList = new ArrayList<RuntimeBuiltinLeafInfoImpl<?>>();
325N/A
325N/A /*
325N/A primary bindings
325N/A */
325N/A primaryList.add(STRING);
325N/A primaryList.add(new StringImpl<Boolean>(Boolean.class,
325N/A createXS("boolean")
325N/A ) {
325N/A public Boolean parse(CharSequence text) {
325N/A return DatatypeConverterImpl._parseBoolean(text);
325N/A }
325N/A
325N/A public String print(Boolean v) {
325N/A return v.toString();
325N/A }
325N/A });
325N/A primaryList.add(new PcdataImpl<byte[]>(byte[].class,
325N/A createXS("base64Binary"),
325N/A createXS("hexBinary")
325N/A ) {
325N/A public byte[] parse(CharSequence text) {
325N/A return decodeBase64(text);
325N/A }
325N/A
325N/A public Base64Data print(byte[] v) {
325N/A XMLSerializer w = XMLSerializer.getInstance();
325N/A Base64Data bd = new Base64Data();
325N/A String mimeType = w.getXMIMEContentType();
325N/A bd.set(v,mimeType);
325N/A return bd;
325N/A }
325N/A });
325N/A primaryList.add(new StringImpl<Byte>(Byte.class,
325N/A createXS("byte")
325N/A ) {
325N/A public Byte parse(CharSequence text) {
325N/A return DatatypeConverterImpl._parseByte(text);
325N/A }
325N/A
325N/A public String print(Byte v) {
325N/A return DatatypeConverterImpl._printByte(v);
325N/A }
325N/A });
325N/A primaryList.add(new StringImpl<Short>(Short.class,
325N/A createXS("short"),
325N/A createXS("unsignedByte")
325N/A ) {
325N/A public Short parse(CharSequence text) {
325N/A return DatatypeConverterImpl._parseShort(text);
325N/A }
325N/A
325N/A public String print(Short v) {
325N/A return DatatypeConverterImpl._printShort(v);
325N/A }
325N/A });
325N/A primaryList.add(new StringImpl<Integer>(Integer.class,
325N/A createXS("int"),
325N/A createXS("unsignedShort")
325N/A ) {
325N/A public Integer parse(CharSequence text) {
325N/A return DatatypeConverterImpl._parseInt(text);
325N/A }
325N/A
325N/A public String print(Integer v) {
325N/A return DatatypeConverterImpl._printInt(v);
325N/A }
325N/A });
325N/A primaryList.add(
325N/A new StringImpl<Long>(Long.class,
325N/A createXS("long"),
325N/A createXS("unsignedInt")
325N/A ) {
325N/A public Long parse(CharSequence text) {
325N/A return DatatypeConverterImpl._parseLong(text);
325N/A }
325N/A
325N/A public String print(Long v) {
325N/A return DatatypeConverterImpl._printLong(v);
325N/A }
325N/A });
325N/A primaryList.add(
325N/A new StringImpl<Float>(Float.class,
325N/A createXS("float")
325N/A ) {
325N/A public Float parse(CharSequence text) {
325N/A return DatatypeConverterImpl._parseFloat(text.toString());
325N/A }
325N/A
325N/A public String print(Float v) {
325N/A return DatatypeConverterImpl._printFloat(v);
325N/A }
325N/A });
325N/A primaryList.add(
325N/A new StringImpl<Double>(Double.class,
325N/A createXS("double")
325N/A ) {
325N/A public Double parse(CharSequence text) {
325N/A return DatatypeConverterImpl._parseDouble(text);
325N/A }
325N/A
325N/A public String print(Double v) {
325N/A return DatatypeConverterImpl._printDouble(v);
325N/A }
325N/A });
325N/A primaryList.add(
325N/A new StringImpl<BigInteger>(BigInteger.class,
325N/A createXS("integer"),
325N/A createXS("positiveInteger"),
325N/A createXS("negativeInteger"),
325N/A createXS("nonPositiveInteger"),
325N/A createXS("nonNegativeInteger"),
325N/A createXS("unsignedLong")
325N/A ) {
325N/A public BigInteger parse(CharSequence text) {
325N/A return DatatypeConverterImpl._parseInteger(text);
325N/A }
325N/A
325N/A public String print(BigInteger v) {
325N/A return DatatypeConverterImpl._printInteger(v);
325N/A }
325N/A });
325N/A primaryList.add(
325N/A new StringImpl<BigDecimal>(BigDecimal.class,
325N/A createXS("decimal")
325N/A ) {
325N/A public BigDecimal parse(CharSequence text) {
325N/A return DatatypeConverterImpl._parseDecimal(text.toString());
325N/A }
325N/A
325N/A public String print(BigDecimal v) {
325N/A return DatatypeConverterImpl._printDecimal(v);
325N/A }
325N/A });
325N/A primaryList.add(
325N/A new StringImpl<QName>(QName.class,
325N/A createXS("QName")
325N/A ) {
325N/A public QName parse(CharSequence text) throws SAXException {
325N/A try {
325N/A return DatatypeConverterImpl._parseQName(text.toString(),UnmarshallingContext.getInstance());
325N/A } catch (IllegalArgumentException e) {
325N/A UnmarshallingContext.getInstance().handleError(e);
325N/A return null;
325N/A }
325N/A }
325N/A
325N/A public String print(QName v) {
325N/A return DatatypeConverterImpl._printQName(v,XMLSerializer.getInstance().getNamespaceContext());
325N/A }
325N/A
325N/A @Override
325N/A public boolean useNamespace() {
325N/A return true;
325N/A }
325N/A
325N/A @Override
325N/A public void declareNamespace(QName v, XMLSerializer w) {
325N/A w.getNamespaceContext().declareNamespace(v.getNamespaceURI(),v.getPrefix(),false);
325N/A }
325N/A });
325N/A if (System.getProperty(MAP_ANYURI_TO_URI) != null) {
325N/A primaryList.add(
325N/A new StringImpl<URI>(URI.class, createXS("anyURI")) {
325N/A public URI parse(CharSequence text) throws SAXException {
325N/A try {
325N/A return new URI(text.toString());
325N/A } catch (URISyntaxException e) {
325N/A UnmarshallingContext.getInstance().handleError(e);
325N/A return null;
325N/A }
325N/A }
325N/A
325N/A public String print(URI v) {
325N/A return v.toString();
325N/A }
325N/A });
325N/A }
325N/A primaryList.add(
325N/A new StringImpl<Duration>(Duration.class, createXS("duration")) {
325N/A public String print(Duration duration) {
325N/A return duration.toString();
325N/A }
325N/A
325N/A public Duration parse(CharSequence lexical) {
325N/A TODO.checkSpec("JSR222 Issue #42");
325N/A return datatypeFactory.newDuration(lexical.toString());
325N/A }
325N/A });
325N/A primaryList.add(
325N/A new StringImpl<Void>(Void.class) {
325N/A // 'void' binding isn't defined by the spec, but when the JAX-RPC processes user-defined
325N/A // methods like "int actionFoo()", they need this pseudo-void property.
325N/A
325N/A public String print(Void value) {
325N/A return "";
325N/A }
325N/A
325N/A public Void parse(CharSequence lexical) {
325N/A return null;
325N/A }
325N/A });
325N/A
325N/A List<RuntimeBuiltinLeafInfoImpl<?>> l = new ArrayList<RuntimeBuiltinLeafInfoImpl<?>>(secondaryList.size()+primaryList.size()+1);
325N/A l.addAll(secondaryList);
325N/A
325N/A // UUID may fail to load if we are running on JDK 1.4. Handle gracefully
325N/A try {
325N/A l.add(new UUIDImpl());
325N/A } catch (LinkageError e) {
325N/A // ignore
325N/A }
325N/A
325N/A l.addAll(primaryList);
325N/A
325N/A builtinBeanInfos = Collections.unmodifiableList(l);
325N/A }
325N/A
325N/A private static byte[] decodeBase64(CharSequence text) {
325N/A if (text instanceof Base64Data) {
325N/A Base64Data base64Data = (Base64Data) text;
325N/A return base64Data.getExact();
325N/A } else {
325N/A return DatatypeConverterImpl._parseBase64Binary(text.toString());
325N/A }
325N/A }
325N/A
325N/A
325N/A /**
325N/A * Cached instance of {@link DatatypeFactory} to create
325N/A * {@link XMLGregorianCalendar} and {@link Duration}.
325N/A */
325N/A private static final DatatypeFactory datatypeFactory = init();
325N/A
325N/A private static DatatypeFactory init() {
325N/A try {
325N/A return DatatypeFactory.newInstance();
325N/A } catch (DatatypeConfigurationException e) {
325N/A throw new Error(Messages.FAILED_TO_INITIALE_DATATYPE_FACTORY.format(),e);
325N/A }
325N/A }
325N/A
325N/A private static void checkXmlGregorianCalendarFieldRef(QName type,
325N/A XMLGregorianCalendar cal)throws javax.xml.bind.MarshalException{
325N/A StringBuilder buf = new StringBuilder();
325N/A int bitField = xmlGregorianCalendarFieldRef.get(type);
325N/A final int l = 0x1;
325N/A int pos = 0;
325N/A while (bitField != 0x0){
325N/A int bit = bitField & l;
325N/A bitField >>>= 4;
325N/A pos++;
325N/A
325N/A if (bit == 1) {
325N/A switch(pos){
325N/A case 1:
325N/A if (cal.getSecond() == DatatypeConstants.FIELD_UNDEFINED){
325N/A buf.append(" ").append(Messages.XMLGREGORIANCALENDAR_SEC);
325N/A }
325N/A break;
325N/A case 2:
325N/A if (cal.getMinute() == DatatypeConstants.FIELD_UNDEFINED){
325N/A buf.append(" ").append(Messages.XMLGREGORIANCALENDAR_MIN);
325N/A }
325N/A break;
325N/A case 3:
325N/A if (cal.getHour() == DatatypeConstants.FIELD_UNDEFINED){
325N/A buf.append(" ").append(Messages.XMLGREGORIANCALENDAR_HR);
325N/A }
325N/A break;
325N/A case 4:
325N/A if (cal.getDay() == DatatypeConstants.FIELD_UNDEFINED){
325N/A buf.append(" ").append(Messages.XMLGREGORIANCALENDAR_DAY);
325N/A }
325N/A break;
325N/A case 5:
325N/A if (cal.getMonth() == DatatypeConstants.FIELD_UNDEFINED){
325N/A buf.append(" ").append(Messages.XMLGREGORIANCALENDAR_MONTH);
325N/A }
325N/A break;
325N/A case 6:
325N/A if (cal.getYear() == DatatypeConstants.FIELD_UNDEFINED){
325N/A buf.append(" ").append(Messages.XMLGREGORIANCALENDAR_YEAR);
325N/A }
325N/A break;
325N/A case 7: // ignore timezone setting
325N/A break;
325N/A }
325N/A }
325N/A }
325N/A if (buf.length() > 0){
325N/A throw new javax.xml.bind.MarshalException(
325N/A Messages.XMLGREGORIANCALENDAR_INVALID.format(type.getLocalPart())
325N/A + buf.toString());
325N/A }
325N/A }
325N/A
325N/A /**
325N/A * Format string for the {@link XMLGregorianCalendar}.
325N/A */
325N/A private static final Map<QName,String> xmlGregorianCalendarFormatString = new HashMap<QName, String>();
325N/A
325N/A static {
325N/A Map<QName,String> m = xmlGregorianCalendarFormatString;
325N/A // See 4971612: be careful for SCCS substitution
325N/A m.put(DatatypeConstants.DATETIME, "%Y-%M-%DT%h:%m:%s"+ "%z");
325N/A m.put(DatatypeConstants.DATE, "%Y-%M-%D" +"%z");
325N/A m.put(DatatypeConstants.TIME, "%h:%m:%s"+ "%z");
325N/A m.put(DatatypeConstants.GMONTH, "--%M--%z");
325N/A m.put(DatatypeConstants.GDAY, "---%D" + "%z");
325N/A m.put(DatatypeConstants.GYEAR, "%Y" + "%z");
325N/A m.put(DatatypeConstants.GYEARMONTH, "%Y-%M" + "%z");
325N/A m.put(DatatypeConstants.GMONTHDAY, "--%M-%D" +"%z");
325N/A }
325N/A
325N/A /**
325N/A * Field designations for XMLGregorianCalendar format string.
325N/A * sec 0x0000001
325N/A * min 0x0000010
325N/A * hrs 0x0000100
325N/A * day 0x0001000
325N/A * month 0x0010000
325N/A * year 0x0100000
325N/A * timezone 0x1000000
325N/A */
325N/A private static final Map<QName, Integer> xmlGregorianCalendarFieldRef =
325N/A new HashMap<QName, Integer>();
325N/A static {
325N/A Map<QName, Integer> f = xmlGregorianCalendarFieldRef;
325N/A f.put(DatatypeConstants.DATETIME, 0x1111111);
325N/A f.put(DatatypeConstants.DATE, 0x1111000);
325N/A f.put(DatatypeConstants.TIME, 0x1000111);
325N/A f.put(DatatypeConstants.GDAY, 0x1001000);
325N/A f.put(DatatypeConstants.GMONTH, 0x1010000);
325N/A f.put(DatatypeConstants.GYEAR, 0x1100000);
325N/A f.put(DatatypeConstants.GYEARMONTH, 0x1110000);
325N/A f.put(DatatypeConstants.GMONTHDAY, 0x1011000);
325N/A }
325N/A
325N/A /**
325N/A * {@link RuntimeBuiltinLeafInfoImpl} for {@link UUID}.
325N/A *
325N/A * This class is given a name so that failing to load this class won't cause a fatal problem.
325N/A */
325N/A private static class UUIDImpl extends StringImpl<UUID> {
325N/A public UUIDImpl() {
325N/A super(UUID.class, RuntimeBuiltinLeafInfoImpl.createXS("string"));
325N/A }
325N/A
325N/A public UUID parse(CharSequence text) throws SAXException {
325N/A TODO.checkSpec("JSR222 Issue #42");
325N/A try {
325N/A return UUID.fromString(WhiteSpaceProcessor.trim(text).toString());
325N/A } catch (IllegalArgumentException e) {
325N/A UnmarshallingContext.getInstance().handleError(e);
325N/A return null;
325N/A }
325N/A }
325N/A
325N/A public String print(UUID v) {
325N/A return v.toString();
325N/A }
325N/A }
325N/A
325N/A private static class StringImplImpl extends StringImpl<String> {
325N/A
325N/A public StringImplImpl(Class type, QName[] typeNames) {
325N/A super(type, typeNames);
325N/A }
325N/A
325N/A public String parse(CharSequence text) {
325N/A return text.toString();
325N/A }
325N/A
325N/A public String print(String s) {
325N/A return s;
325N/A }
325N/A
325N/A @Override
325N/A public final void writeText(XMLSerializer w, String o, String fieldName) throws IOException, SAXException, XMLStreamException {
325N/A w.text(o, fieldName);
325N/A }
325N/A
325N/A @Override
325N/A public final void writeLeafElement(XMLSerializer w, Name tagName, String o, String fieldName) throws IOException, SAXException, XMLStreamException {
325N/A w.leafElement(tagName, o, fieldName);
325N/A }
325N/A }
325N/A}