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