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: }