src/share/jaxws_classes/com/sun/xml/internal/bind/v2/model/impl/RuntimeBuiltinLeafInfoImpl.java

Fri, 14 Feb 2014 10:53:55 +0100

author
mkos
date
Fri, 14 Feb 2014 10:53:55 +0100
changeset 514
29a761eaff0d
parent 450
b0c2840e2513
child 637
9c07ef4934dd
child 721
06807f9a6835
permissions
-rw-r--r--

8025030: Enhance stream handling
Summary: Avoiding caching data initialized via TCCL in static context; fix also reviewed by Iaroslav Savytskyi, Alexander Fomin
Reviewed-by: ahgross, mgrebac, skoivu

     1 /*
     2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package com.sun.xml.internal.bind.v2.model.impl;
    28 import java.awt.Component;
    29 import java.awt.Graphics;
    30 import java.awt.Image;
    31 import java.awt.MediaTracker;
    32 import java.awt.image.BufferedImage;
    33 import java.io.ByteArrayInputStream;
    34 import java.io.File;
    35 import java.io.IOException;
    36 import java.io.InputStream;
    37 import java.io.OutputStreamWriter;
    38 import java.io.UnsupportedEncodingException;
    39 import java.lang.reflect.Type;
    40 import java.math.BigDecimal;
    41 import java.math.BigInteger;
    42 import java.net.MalformedURLException;
    43 import java.net.URI;
    44 import java.net.URISyntaxException;
    45 import java.net.URL;
    46 import java.util.ArrayList;
    47 import java.util.Calendar;
    48 import java.util.Collections;
    49 import java.util.Date;
    50 import java.util.GregorianCalendar;
    51 import java.util.HashMap;
    52 import java.util.Iterator;
    53 import java.util.List;
    54 import java.util.Map;
    55 import java.util.UUID;
    57 import javax.activation.DataHandler;
    58 import javax.activation.DataSource;
    59 import javax.activation.MimeType;
    60 import javax.activation.MimeTypeParseException;
    61 import javax.imageio.ImageIO;
    62 import javax.imageio.ImageWriter;
    63 import javax.imageio.stream.ImageOutputStream;
    64 import javax.xml.bind.ValidationEvent;
    65 import javax.xml.bind.helpers.ValidationEventImpl;
    66 import javax.xml.datatype.DatatypeConstants;
    67 import javax.xml.datatype.Duration;
    68 import javax.xml.datatype.XMLGregorianCalendar;
    69 import javax.xml.namespace.QName;
    70 import javax.xml.stream.XMLStreamException;
    71 import javax.xml.transform.OutputKeys;
    72 import javax.xml.transform.Source;
    73 import javax.xml.transform.Transformer;
    74 import javax.xml.transform.TransformerException;
    75 import javax.xml.transform.stream.StreamResult;
    77 import com.sun.istack.internal.ByteArrayDataSource;
    78 import com.sun.xml.internal.bind.DatatypeConverterImpl;
    79 import com.sun.xml.internal.bind.WhiteSpaceProcessor;
    80 import com.sun.xml.internal.bind.api.AccessorException;
    81 import com.sun.xml.internal.bind.v2.TODO;
    82 import com.sun.xml.internal.bind.v2.WellKnownNamespace;
    83 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeBuiltinLeafInfo;
    84 import com.sun.xml.internal.bind.v2.runtime.Name;
    85 import com.sun.xml.internal.bind.v2.runtime.Transducer;
    86 import com.sun.xml.internal.bind.v2.runtime.XMLSerializer;
    87 import com.sun.xml.internal.bind.v2.runtime.output.Pcdata;
    88 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data;
    89 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext;
    90 import com.sun.xml.internal.bind.v2.util.ByteArrayOutputStreamEx;
    91 import com.sun.xml.internal.bind.v2.util.DataSourceSource;
    93 import org.xml.sax.SAXException;
    95 /**
    96  * {@link BuiltinLeafInfoImpl} with a support for runtime.
    97  *
    98  * <p>
    99  * In particular this class defines {@link Transducer}s for the built-in types.
   100  *
   101  * @author Kohsuke Kawaguchi
   102  */
   103 public abstract class RuntimeBuiltinLeafInfoImpl<T> extends BuiltinLeafInfoImpl<Type,Class>
   104     implements RuntimeBuiltinLeafInfo, Transducer<T> {
   106     private RuntimeBuiltinLeafInfoImpl(Class type, QName... typeNames) {
   107         super(type, typeNames);
   108         LEAVES.put(type,this);
   109     }
   111     public final Class getClazz() {
   112         return (Class)getType();
   113     }
   116     public final Transducer getTransducer() {
   117         return this;
   118     }
   120     public boolean useNamespace() {
   121         return false;
   122     }
   124     public final boolean isDefault() {
   125         return true;
   126     }
   128     public void declareNamespace(T o, XMLSerializer w) throws AccessorException {
   129     }
   131     public QName getTypeName(T instance) {
   132         return null;
   133     }
   135     /**
   136      * Those built-in types that print to {@link String}.
   137      */
   138     private static abstract class StringImpl<T> extends RuntimeBuiltinLeafInfoImpl<T> {
   139         protected StringImpl(Class type, QName... typeNames) {
   140             super(type,typeNames);
   141         }
   143         public abstract String print(T o) throws AccessorException;
   145         public void writeText(XMLSerializer w, T o, String fieldName) throws IOException, SAXException, XMLStreamException, AccessorException {
   146             w.text(print(o),fieldName);
   147         }
   149         public void writeLeafElement(XMLSerializer w, Name tagName, T o, String fieldName) throws IOException, SAXException, XMLStreamException, AccessorException {
   150             w.leafElement(tagName,print(o),fieldName);
   151         }
   152     }
   154     /**
   155      * Those built-in types that print to {@link Pcdata}.
   156      */
   157     private static abstract class PcdataImpl<T> extends RuntimeBuiltinLeafInfoImpl<T> {
   158         protected PcdataImpl(Class type, QName... typeNames) {
   159             super(type,typeNames);
   160         }
   162         public abstract Pcdata print(T o) throws AccessorException;
   164         public final void writeText(XMLSerializer w, T o, String fieldName) throws IOException, SAXException, XMLStreamException, AccessorException {
   165             w.text(print(o),fieldName);
   166         }
   168         public final void writeLeafElement(XMLSerializer w, Name tagName, T o, String fieldName) throws IOException, SAXException, XMLStreamException, AccessorException {
   169             w.leafElement(tagName,print(o),fieldName);
   170         }
   172     }
   174     /**
   175      * All instances of {@link RuntimeBuiltinLeafInfoImpl}s keyed by their type.
   176      */
   177     public static final Map<Type,RuntimeBuiltinLeafInfoImpl<?>> LEAVES = new HashMap<Type, RuntimeBuiltinLeafInfoImpl<?>>();
   179     private static QName createXS(String typeName) {
   180         return new QName(WellKnownNamespace.XML_SCHEMA,typeName);
   181     }
   183     public static final RuntimeBuiltinLeafInfoImpl<String> STRING;
   185     private static final String DATE = "date";
   187     /**
   188      * List of all {@link RuntimeBuiltinLeafInfoImpl}s.
   189      *
   190      * <p>
   191      * This corresponds to the built-in Java classes that are specified to be
   192      * handled differently than ordinary classes. See table 8-2 "Mapping of Standard Java classes".
   193      */
   194     public static final List<RuntimeBuiltinLeafInfoImpl<?>> builtinBeanInfos;
   196     public static final String MAP_ANYURI_TO_URI = "mapAnyUriToUri";
   198     static {
   200         QName[] qnames = (System.getProperty(MAP_ANYURI_TO_URI) == null) ? new QName[] {
   201                                 createXS("string"),
   202                                 createXS("anySimpleType"),
   203                                 createXS("normalizedString"),
   204                                 createXS("anyURI"),
   205                                 createXS("token"),
   206                                 createXS("language"),
   207                                 createXS("Name"),
   208                                 createXS("NCName"),
   209                                 createXS("NMTOKEN"),
   210                                 createXS("ENTITY")}
   211                                     :
   212                          new QName[] {
   213                                 createXS("string"),
   214                                 createXS("anySimpleType"),
   215                                 createXS("normalizedString"),
   216                                 createXS("token"),
   217                                 createXS("language"),
   218                                 createXS("Name"),
   219                                 createXS("NCName"),
   220                                 createXS("NMTOKEN"),
   221                                 createXS("ENTITY")};
   223         STRING = new StringImplImpl(String.class, qnames);
   225         ArrayList<RuntimeBuiltinLeafInfoImpl<?>> secondaryList = new ArrayList<RuntimeBuiltinLeafInfoImpl<?>>();
   226             /*
   227                 There are cases where more than one Java classes map to the same XML type.
   228                 But when we see the same XML type in an incoming document, we only pick
   229                 one of those Java classes to unmarshal. This Java class is called 'primary'.
   230                 The rest are called 'secondary'.
   232                 Currently we lack the proper infrastructure to handle those nicely.
   233                 For now, we rely on a hack.
   235                 We define secondary mappings first, then primary ones later. GrammarInfo
   236                 builds a map from type name to BeanInfo. By defining primary ones later,
   237                 those primary bindings will overwrite the secondary ones.
   238             */
   240             /*
   241                 secondary bindings
   242             */
   243         secondaryList.add(
   244             new StringImpl<Character>(Character.class, createXS("unsignedShort")) {
   245                 public Character parse(CharSequence text) {
   246                     // TODO.checkSpec("default mapping for char is not defined yet");
   247                     return (char)DatatypeConverterImpl._parseInt(text);
   248                 }
   249                 public String print(Character v) {
   250                     return Integer.toString(v);
   251                 }
   252             });
   253         secondaryList.add(
   254             new StringImpl<Calendar>(Calendar.class, DatatypeConstants.DATETIME) {
   255                 public Calendar parse(CharSequence text) {
   256                     return DatatypeConverterImpl._parseDateTime(text.toString());
   257                 }
   258                 public String print(Calendar v) {
   259                     return DatatypeConverterImpl._printDateTime(v);
   260                 }
   261             });
   262         secondaryList.add(
   263             new StringImpl<GregorianCalendar>(GregorianCalendar.class, DatatypeConstants.DATETIME) {
   264                 public GregorianCalendar parse(CharSequence text) {
   265                     return DatatypeConverterImpl._parseDateTime(text.toString());
   266                 }
   267                 public String print(GregorianCalendar v) {
   268                     return DatatypeConverterImpl._printDateTime(v);
   269                 }
   270             });
   271         secondaryList.add(
   272             new StringImpl<Date>(Date.class, DatatypeConstants.DATETIME) {
   273                 public Date parse(CharSequence text) {
   274                     return DatatypeConverterImpl._parseDateTime(text.toString()).getTime();
   275                 }
   276                 public String print(Date v) {
   277                     XMLSerializer xs = XMLSerializer.getInstance();
   278                     QName type = xs.getSchemaType();
   279                     GregorianCalendar cal = new GregorianCalendar(0,0,0);
   280                     cal.setTime(v);
   281                     if ((type != null) && (WellKnownNamespace.XML_SCHEMA.equals(type.getNamespaceURI())) &&
   282                             DATE.equals(type.getLocalPart())) {
   283                         return DatatypeConverterImpl._printDate(cal);
   284                     } else {
   285                         return DatatypeConverterImpl._printDateTime(cal);
   286                     }
   287                 }
   288             });
   289         secondaryList.add(
   290             new StringImpl<File>(File.class, createXS("string")) {
   291                 public File parse(CharSequence text) {
   292                     return new File(WhiteSpaceProcessor.trim(text).toString());
   293                 }
   294                 public String print(File v) {
   295                     return v.getPath();
   296                 }
   297             });
   298         secondaryList.add(
   299             new StringImpl<URL>(URL.class, createXS("anyURI")) {
   300                 public URL parse(CharSequence text) throws SAXException {
   301                     TODO.checkSpec("JSR222 Issue #42");
   302                     try {
   303                         return new URL(WhiteSpaceProcessor.trim(text).toString());
   304                     } catch (MalformedURLException e) {
   305                         UnmarshallingContext.getInstance().handleError(e);
   306                         return null;
   307                     }
   308                 }
   309                 public String print(URL v) {
   310                     return v.toExternalForm();
   311                 }
   312             });
   313         if (System.getProperty(MAP_ANYURI_TO_URI) == null) {
   314             secondaryList.add(
   315                 new StringImpl<URI>(URI.class, createXS("string")) {
   316                     public URI parse(CharSequence text) throws SAXException {
   317                         try {
   318                             return new URI(text.toString());
   319                         } catch (URISyntaxException e) {
   320                             UnmarshallingContext.getInstance().handleError(e);
   321                             return null;
   322                         }
   323                     }
   325                     public String print(URI v) {
   326                         return v.toString();
   327                     }
   328                 });
   329         }
   330         secondaryList.add(
   331             new StringImpl<Class>(Class.class, createXS("string")) {
   332                 public Class parse(CharSequence text) throws SAXException {
   333                     TODO.checkSpec("JSR222 Issue #42");
   334                     try {
   335                         String name = WhiteSpaceProcessor.trim(text).toString();
   336                         ClassLoader cl = UnmarshallingContext.getInstance().classLoader;
   337                         if(cl==null)
   338                             cl = Thread.currentThread().getContextClassLoader();
   340                         if(cl!=null)
   341                             return cl.loadClass(name);
   342                         else
   343                             return Class.forName(name);
   344                     } catch (ClassNotFoundException e) {
   345                         UnmarshallingContext.getInstance().handleError(e);
   346                         return null;
   347                     }
   348                 }
   349                 public String print(Class v) {
   350                     return v.getName();
   351                 }
   352             });
   354             /*
   355                 classes that map to base64Binary / MTOM related classes.
   356                 a part of the secondary binding.
   357             */
   358         secondaryList.add(
   359             new PcdataImpl<Image>(Image.class, createXS("base64Binary")) {
   360                 public Image parse(CharSequence text) throws SAXException  {
   361                     try {
   362                         InputStream is;
   363                         if(text instanceof Base64Data)
   364                             is = ((Base64Data)text).getInputStream();
   365                         else
   366                             is = new ByteArrayInputStream(decodeBase64(text)); // TODO: buffering is inefficient
   368                         // technically we should check the MIME type here, but
   369                         // normally images can be content-sniffed.
   370                         // so the MIME type check will only make us slower and draconian, both of which
   371                         // JAXB 2.0 isn't interested.
   372                         try {
   373                             return ImageIO.read(is);
   374                         } finally {
   375                             is.close();
   376                         }
   377                     } catch (IOException e) {
   378                         UnmarshallingContext.getInstance().handleError(e);
   379                         return null;
   380                     }
   381                 }
   383                 private BufferedImage convertToBufferedImage(Image image) throws IOException {
   384                     if (image instanceof BufferedImage) {
   385                         return (BufferedImage)image;
   387                     } else {
   388                         MediaTracker tracker = new MediaTracker(new Component(){}); // not sure if this is the right thing to do.
   389                         tracker.addImage(image, 0);
   390                         try {
   391                             tracker.waitForAll();
   392                         } catch (InterruptedException e) {
   393                             throw new IOException(e.getMessage());
   394                         }
   395                         BufferedImage bufImage = new BufferedImage(
   396                                 image.getWidth(null),
   397                                 image.getHeight(null),
   398                                 BufferedImage.TYPE_INT_ARGB);
   400                         Graphics g = bufImage.createGraphics();
   401                         g.drawImage(image, 0, 0, null);
   402                         return bufImage;
   403                     }
   404                 }
   406                 public Base64Data print(Image v) {
   407                     ByteArrayOutputStreamEx imageData = new ByteArrayOutputStreamEx();
   408                     XMLSerializer xs = XMLSerializer.getInstance();
   410                     String mimeType = xs.getXMIMEContentType();
   411                     if(mimeType==null || mimeType.startsWith("image/*"))
   412                         // because PNG is lossless, it's a good default
   413                         //
   414                         // mime type can be a range, in which case we can't just pass that
   415                         // to ImageIO.getImageWritersByMIMEType, so here I'm just assuming
   416                         // the default of PNG. Not sure if this is complete.
   417                         mimeType = "image/png";
   419                     try {
   420                         Iterator<ImageWriter> itr = ImageIO.getImageWritersByMIMEType(mimeType);
   421                         if(itr.hasNext()) {
   422                             ImageWriter w = itr.next();
   423                             ImageOutputStream os = ImageIO.createImageOutputStream(imageData);
   424                             w.setOutput(os);
   425                             w.write(convertToBufferedImage(v));
   426                             os.close();
   427                             w.dispose();
   428                         } else {
   429                             // no encoder
   430                             xs.handleEvent(new ValidationEventImpl(
   431                                 ValidationEvent.ERROR,
   432                                 Messages.NO_IMAGE_WRITER.format(mimeType),
   433                                 xs.getCurrentLocation(null) ));
   434                             // TODO: proper error reporting
   435                             throw new RuntimeException("no encoder for MIME type "+mimeType);
   436                         }
   437                     } catch (IOException e) {
   438                         xs.handleError(e);
   439                         // TODO: proper error reporting
   440                         throw new RuntimeException(e);
   441                     }
   442                     Base64Data bd = new Base64Data();
   443                     imageData.set(bd,mimeType);
   444                     return bd;
   445                 }
   446             });
   447         secondaryList.add(
   448             new PcdataImpl<DataHandler>(DataHandler.class, createXS("base64Binary")) {
   449                 public DataHandler parse(CharSequence text) {
   450                     if(text instanceof Base64Data)
   451                         return ((Base64Data)text).getDataHandler();
   452                     else
   453                         return new DataHandler(new ByteArrayDataSource(decodeBase64(text),
   454                             UnmarshallingContext.getInstance().getXMIMEContentType()));
   455                 }
   457                 public Base64Data print(DataHandler v) {
   458                     Base64Data bd = new Base64Data();
   459                     bd.set(v);
   460                     return bd;
   461                 }
   462             });
   463         secondaryList.add(
   464             new PcdataImpl<Source>(Source.class, createXS("base64Binary")) {
   465                 public Source parse(CharSequence text) throws SAXException  {
   466                     try {
   467                         if(text instanceof Base64Data)
   468                             return new DataSourceSource( ((Base64Data)text).getDataHandler() );
   469                         else
   470                             return new DataSourceSource(new ByteArrayDataSource(decodeBase64(text),
   471                                 UnmarshallingContext.getInstance().getXMIMEContentType()));
   472                     } catch (MimeTypeParseException e) {
   473                         UnmarshallingContext.getInstance().handleError(e);
   474                         return null;
   475                     }
   476                 }
   478                 public Base64Data print(Source v) {
   479                     XMLSerializer xs = XMLSerializer.getInstance();
   480                     Base64Data bd = new Base64Data();
   482                     String contentType = xs.getXMIMEContentType();
   483                     MimeType mt = null;
   484                     if(contentType!=null)
   485                         try {
   486                             mt = new MimeType(contentType);
   487                         } catch (MimeTypeParseException e) {
   488                             xs.handleError(e);
   489                             // recover by ignoring the content type specification
   490                         }
   492                     if( v instanceof DataSourceSource ) {
   493                         // if so, we already have immutable DataSource so
   494                         // this can be done efficiently
   495                         DataSource ds = ((DataSourceSource)v).getDataSource();
   497                         String dsct = ds.getContentType();
   498                         if(dsct!=null && (contentType==null || contentType.equals(dsct))) {
   499                             bd.set(new DataHandler(ds));
   500                             return bd;
   501                         }
   502                     }
   504                     // general case. slower.
   506                     // find out the encoding
   507                     String charset=null;
   508                     if(mt!=null)
   509                         charset = mt.getParameter("charset");
   510                     if(charset==null)
   511                         charset = "UTF-8";
   513                     try {
   514                         ByteArrayOutputStreamEx baos = new ByteArrayOutputStreamEx();
   515                         Transformer tr = xs.getIdentityTransformer();
   516                         String defaultEncoding = tr.getOutputProperty(OutputKeys.ENCODING);
   517                         tr.setOutputProperty(OutputKeys.ENCODING, charset);
   518                         tr.transform(v, new StreamResult(new OutputStreamWriter(baos,charset)));
   519                         tr.setOutputProperty(OutputKeys.ENCODING, defaultEncoding);
   520                         baos.set(bd,"application/xml; charset="+charset);
   521                         return bd;
   522                     } catch (TransformerException e) {
   523                         // TODO: marshaller error handling
   524                         xs.handleError(e);
   525                     } catch (UnsupportedEncodingException e) {
   526                         xs.handleError(e);
   527                     }
   529                     // error recoverly
   530                     bd.set(new byte[0],"application/xml");
   531                     return bd;
   532                 }
   533             });
   534         secondaryList.add(
   535             new StringImpl<XMLGregorianCalendar>(XMLGregorianCalendar.class,
   536                     createXS("anySimpleType"),
   537                     DatatypeConstants.DATE,
   538                     DatatypeConstants.DATETIME,
   539                     DatatypeConstants.TIME,
   540                     DatatypeConstants.GMONTH,
   541                     DatatypeConstants.GDAY,
   542                     DatatypeConstants.GYEAR,
   543                     DatatypeConstants.GYEARMONTH,
   544                     DatatypeConstants.GMONTHDAY
   545                 ) {
   546                 public String print(XMLGregorianCalendar cal) {
   547                     XMLSerializer xs = XMLSerializer.getInstance();
   549                     QName type = xs.getSchemaType();
   550                     if (type != null) {
   551                         try {
   552                             checkXmlGregorianCalendarFieldRef(type, cal);
   553                             String format = xmlGregorianCalendarFormatString.get(type);
   554                             if (format != null) {
   555                                 return format(format, cal);
   556                             }
   557                         } catch (javax.xml.bind.MarshalException e) {
   558                             // see issue 649
   559                             xs.handleEvent(new ValidationEventImpl(ValidationEvent.WARNING, e.getMessage(),
   560                                 xs.getCurrentLocation(null) ));
   561                             return "";
   562                         }
   563                     }
   564                     return cal.toXMLFormat();
   565                 }
   567                 public XMLGregorianCalendar parse(CharSequence lexical) throws SAXException {
   568                     try {
   569                         return DatatypeConverterImpl.getDatatypeFactory()
   570                                 .newXMLGregorianCalendar(lexical.toString().trim()); // (.trim() - issue 396)
   571                     } catch (Exception e) {
   572                         UnmarshallingContext.getInstance().handleError(e);
   573                         return null;
   574                     }
   575                 }
   577                 // code duplicated from JAXP RI 1.3. See 6277586
   578                 private String format( String format, XMLGregorianCalendar value ) {
   579                     StringBuilder buf = new StringBuilder();
   580                     int fidx=0,flen=format.length();
   582                     while(fidx<flen) {
   583                         char fch = format.charAt(fidx++);
   584                         if(fch!='%') {// not a meta char
   585                             buf.append(fch);
   586                             continue;
   587                         }
   589                         switch(format.charAt(fidx++)) {
   590                         case 'Y':
   591                             printNumber(buf,value.getEonAndYear(), 4);
   592                             break;
   593                         case 'M':
   594                             printNumber(buf,value.getMonth(),2);
   595                             break;
   596                         case 'D':
   597                             printNumber(buf,value.getDay(),2);
   598                             break;
   599                         case 'h':
   600                             printNumber(buf,value.getHour(),2);
   601                             break;
   602                         case 'm':
   603                             printNumber(buf,value.getMinute(),2);
   604                             break;
   605                         case 's':
   606                             printNumber(buf,value.getSecond(),2);
   607                     if (value.getFractionalSecond() != null) {
   608                         String frac = value.getFractionalSecond().toPlainString();
   609                         //skip leading zero.
   610                         buf.append(frac.substring(1, frac.length()));
   611                     }
   612                             break;
   613                         case 'z':
   614                     int offset = value.getTimezone();
   615                             if(offset == 0) {
   616                         buf.append('Z');
   617                     } else if (offset != DatatypeConstants.FIELD_UNDEFINED) {
   618                         if(offset<0) {
   619                         buf.append('-');
   620                         offset *= -1;
   621                         } else {
   622                         buf.append('+');
   623                         }
   624                         printNumber(buf,offset/60,2);
   625                                 buf.append(':');
   626                                 printNumber(buf,offset%60,2);
   627                             }
   628                             break;
   629                         default:
   630                             throw new InternalError();  // impossible
   631                         }
   632                     }
   634                     return buf.toString();
   635                 }
   636                 private void printNumber( StringBuilder out, BigInteger number, int nDigits) {
   637                     String s = number.toString();
   638                     for( int i=s.length(); i<nDigits; i++ )
   639                         out.append('0');
   640                     out.append(s);
   641                 }
   642                 private void printNumber( StringBuilder out, int number, int nDigits ) {
   643                     String s = String.valueOf(number);
   644                     for( int i=s.length(); i<nDigits; i++ )
   645                         out.append('0');
   646                     out.append(s);
   647                 }
   648                 @Override
   649                 public QName getTypeName(XMLGregorianCalendar cal) {
   650                     return cal.getXMLSchemaType();
   651                 }
   652             });
   654         ArrayList<RuntimeBuiltinLeafInfoImpl<?>> primaryList = new ArrayList<RuntimeBuiltinLeafInfoImpl<?>>();
   656         /*
   657             primary bindings
   658         */
   659         primaryList.add(STRING);
   660         primaryList.add(new StringImpl<Boolean>(Boolean.class,
   661                 createXS("boolean")
   662                 ) {
   663                 public Boolean parse(CharSequence text) {
   664                     return DatatypeConverterImpl._parseBoolean(text);
   665                 }
   667                 public String print(Boolean v) {
   668                     return v.toString();
   669                 }
   670             });
   671         primaryList.add(new PcdataImpl<byte[]>(byte[].class,
   672                 createXS("base64Binary"),
   673                 createXS("hexBinary")
   674                 ) {
   675                 public byte[] parse(CharSequence text) {
   676                     return decodeBase64(text);
   677                 }
   679                 public Base64Data print(byte[] v) {
   680                     XMLSerializer w = XMLSerializer.getInstance();
   681                     Base64Data bd = new Base64Data();
   682                     String mimeType = w.getXMIMEContentType();
   683                     bd.set(v,mimeType);
   684                     return bd;
   685                 }
   686             });
   687         primaryList.add(new StringImpl<Byte>(Byte.class,
   688                 createXS("byte")
   689                 ) {
   690                 public Byte parse(CharSequence text) {
   691                     return DatatypeConverterImpl._parseByte(text);
   692                 }
   694                 public String print(Byte v) {
   695                     return DatatypeConverterImpl._printByte(v);
   696                 }
   697             });
   698         primaryList.add(new StringImpl<Short>(Short.class,
   699                 createXS("short"),
   700                 createXS("unsignedByte")
   701                 ) {
   702                 public Short parse(CharSequence text) {
   703                     return DatatypeConverterImpl._parseShort(text);
   704                 }
   706                 public String print(Short v) {
   707                     return DatatypeConverterImpl._printShort(v);
   708                 }
   709             });
   710         primaryList.add(new StringImpl<Integer>(Integer.class,
   711                 createXS("int"),
   712                 createXS("unsignedShort")
   713                 ) {
   714                 public Integer parse(CharSequence text) {
   715                     return DatatypeConverterImpl._parseInt(text);
   716                 }
   718                 public String print(Integer v) {
   719                     return DatatypeConverterImpl._printInt(v);
   720                 }
   721             });
   722         primaryList.add(
   723             new StringImpl<Long>(Long.class,
   724                 createXS("long"),
   725                 createXS("unsignedInt")
   726                 ) {
   727                 public Long parse(CharSequence text) {
   728                     return DatatypeConverterImpl._parseLong(text);
   729                 }
   731                 public String print(Long v) {
   732                     return DatatypeConverterImpl._printLong(v);
   733                 }
   734             });
   735         primaryList.add(
   736             new StringImpl<Float>(Float.class,
   737                 createXS("float")
   738                 ) {
   739                 public Float parse(CharSequence text) {
   740                     return DatatypeConverterImpl._parseFloat(text.toString());
   741                 }
   743                 public String print(Float v) {
   744                     return DatatypeConverterImpl._printFloat(v);
   745                 }
   746             });
   747         primaryList.add(
   748             new StringImpl<Double>(Double.class,
   749                 createXS("double")
   750                 ) {
   751                 public Double parse(CharSequence text) {
   752                     return DatatypeConverterImpl._parseDouble(text);
   753                 }
   755                 public String print(Double v) {
   756                     return DatatypeConverterImpl._printDouble(v);
   757                 }
   758             });
   759         primaryList.add(
   760             new StringImpl<BigInteger>(BigInteger.class,
   761                 createXS("integer"),
   762                 createXS("positiveInteger"),
   763                 createXS("negativeInteger"),
   764                 createXS("nonPositiveInteger"),
   765                 createXS("nonNegativeInteger"),
   766                 createXS("unsignedLong")
   767                 ) {
   768                 public BigInteger parse(CharSequence text) {
   769                     return DatatypeConverterImpl._parseInteger(text);
   770                 }
   772                 public String print(BigInteger v) {
   773                     return DatatypeConverterImpl._printInteger(v);
   774                 }
   775             });
   776         primaryList.add(
   777             new StringImpl<BigDecimal>(BigDecimal.class,
   778                 createXS("decimal")
   779                 ) {
   780                 public BigDecimal parse(CharSequence text) {
   781                     return DatatypeConverterImpl._parseDecimal(text.toString());
   782                 }
   784                 public String print(BigDecimal v) {
   785                     return DatatypeConverterImpl._printDecimal(v);
   786                 }
   787             });
   788         primaryList.add(
   789             new StringImpl<QName>(QName.class,
   790                 createXS("QName")
   791                 ) {
   792                 public QName parse(CharSequence text) throws SAXException {
   793                     try {
   794                         return DatatypeConverterImpl._parseQName(text.toString(),UnmarshallingContext.getInstance());
   795                     } catch (IllegalArgumentException e) {
   796                         UnmarshallingContext.getInstance().handleError(e);
   797                         return null;
   798                     }
   799                 }
   801                 public String print(QName v) {
   802                     return DatatypeConverterImpl._printQName(v,XMLSerializer.getInstance().getNamespaceContext());
   803                 }
   805                 @Override
   806                 public boolean useNamespace() {
   807                     return true;
   808                 }
   810                 @Override
   811                 public void declareNamespace(QName v, XMLSerializer w) {
   812                     w.getNamespaceContext().declareNamespace(v.getNamespaceURI(),v.getPrefix(),false);
   813                 }
   814             });
   815         if (System.getProperty(MAP_ANYURI_TO_URI) != null) {
   816             primaryList.add(
   817                 new StringImpl<URI>(URI.class, createXS("anyURI")) {
   818                     public URI parse(CharSequence text) throws SAXException {
   819                         try {
   820                             return new URI(text.toString());
   821                         } catch (URISyntaxException e) {
   822                             UnmarshallingContext.getInstance().handleError(e);
   823                             return null;
   824                         }
   825                     }
   827                     public String print(URI v) {
   828                         return v.toString();
   829                     }
   830                 });
   831         }
   832         primaryList.add(
   833             new StringImpl<Duration>(Duration.class,  createXS("duration")) {
   834                 public String print(Duration duration) {
   835                     return duration.toString();
   836                 }
   838                 public Duration parse(CharSequence lexical) {
   839                     TODO.checkSpec("JSR222 Issue #42");
   840                     return DatatypeConverterImpl.getDatatypeFactory().newDuration(lexical.toString());
   841                 }
   842             });
   843         primaryList.add(
   844             new StringImpl<Void>(Void.class) {
   845                 // 'void' binding isn't defined by the spec, but when the JAX-RPC processes user-defined
   846                 // methods like "int actionFoo()", they need this pseudo-void property.
   848                 public String print(Void value) {
   849                     return "";
   850                 }
   852                 public Void parse(CharSequence lexical) {
   853                     return null;
   854                 }
   855             });
   857         List<RuntimeBuiltinLeafInfoImpl<?>> l = new ArrayList<RuntimeBuiltinLeafInfoImpl<?>>(secondaryList.size()+primaryList.size()+1);
   858         l.addAll(secondaryList);
   860         // UUID may fail to load if we are running on JDK 1.4. Handle gracefully
   861         try {
   862             l.add(new UUIDImpl());
   863         } catch (LinkageError e) {
   864             // ignore
   865         }
   867         l.addAll(primaryList);
   869         builtinBeanInfos = Collections.unmodifiableList(l);
   870     }
   872     private static byte[] decodeBase64(CharSequence text) {
   873         if (text instanceof Base64Data) {
   874             Base64Data base64Data = (Base64Data) text;
   875             return base64Data.getExact();
   876         } else {
   877             return DatatypeConverterImpl._parseBase64Binary(text.toString());
   878         }
   879     }
   881         private static void checkXmlGregorianCalendarFieldRef(QName type,
   882                 XMLGregorianCalendar cal)throws javax.xml.bind.MarshalException{
   883                 StringBuilder buf = new StringBuilder();
   884                 int bitField = xmlGregorianCalendarFieldRef.get(type);
   885                 final int l = 0x1;
   886                 int pos = 0;
   887                 while (bitField != 0x0){
   888                         int bit = bitField & l;
   889                         bitField >>>= 4;
   890                         pos++;
   892                         if (bit == 1) {
   893                                 switch(pos){
   894                                         case 1:
   895                                                 if (cal.getSecond() == DatatypeConstants.FIELD_UNDEFINED){
   896                                                         buf.append("  ").append(Messages.XMLGREGORIANCALENDAR_SEC);
   897                                                 }
   898                                                 break;
   899                                         case 2:
   900                                                 if (cal.getMinute() == DatatypeConstants.FIELD_UNDEFINED){
   901                                                         buf.append("  ").append(Messages.XMLGREGORIANCALENDAR_MIN);
   902                                                 }
   903                                                 break;
   904                                         case 3:
   905                                                 if (cal.getHour() == DatatypeConstants.FIELD_UNDEFINED){
   906                                                         buf.append("  ").append(Messages.XMLGREGORIANCALENDAR_HR);
   907                                                 }
   908                                                 break;
   909                                         case 4:
   910                                                 if (cal.getDay() == DatatypeConstants.FIELD_UNDEFINED){
   911                                                         buf.append("  ").append(Messages.XMLGREGORIANCALENDAR_DAY);
   912                                                 }
   913                                                 break;
   914                                         case 5:
   915                                                 if (cal.getMonth() == DatatypeConstants.FIELD_UNDEFINED){
   916                                                         buf.append("  ").append(Messages.XMLGREGORIANCALENDAR_MONTH);
   917                                                 }
   918                                                 break;
   919                                         case 6:
   920                                                 if (cal.getYear() == DatatypeConstants.FIELD_UNDEFINED){
   921                                                         buf.append("  ").append(Messages.XMLGREGORIANCALENDAR_YEAR);
   922                                                 }
   923                                                 break;
   924                                         case 7:  // ignore timezone setting
   925                                                 break;
   926                                 }
   927                         }
   928                 }
   929                 if (buf.length() > 0){
   930                         throw new javax.xml.bind.MarshalException(
   931                          Messages.XMLGREGORIANCALENDAR_INVALID.format(type.getLocalPart())
   932                          + buf.toString());
   933                 }
   934         }
   936     /**
   937      * Format string for the {@link XMLGregorianCalendar}.
   938      */
   939     private static final Map<QName,String> xmlGregorianCalendarFormatString = new HashMap<QName, String>();
   941     static {
   942         Map<QName,String> m = xmlGregorianCalendarFormatString;
   943         // See 4971612: be careful for SCCS substitution
   944         m.put(DatatypeConstants.DATETIME,   "%Y-%M-%DT%h:%m:%s"+ "%z");
   945         m.put(DatatypeConstants.DATE,       "%Y-%M-%D" +"%z");
   946         m.put(DatatypeConstants.TIME,       "%h:%m:%s"+ "%z");
   947         m.put(DatatypeConstants.GMONTH,     "--%M--%z");
   948         m.put(DatatypeConstants.GDAY,       "---%D" + "%z");
   949         m.put(DatatypeConstants.GYEAR,      "%Y" + "%z");
   950         m.put(DatatypeConstants.GYEARMONTH, "%Y-%M" + "%z");
   951         m.put(DatatypeConstants.GMONTHDAY,  "--%M-%D" +"%z");
   952     }
   954         /**
   955          * Field designations for XMLGregorianCalendar format string.
   956          * sec          0x0000001
   957          * min          0x0000010
   958          * hrs          0x0000100
   959          * day          0x0001000
   960          * month        0x0010000
   961          * year         0x0100000
   962          * timezone     0x1000000
   963          */
   964         private static final Map<QName, Integer> xmlGregorianCalendarFieldRef =
   965                 new HashMap<QName, Integer>();
   966         static {
   967                 Map<QName, Integer> f = xmlGregorianCalendarFieldRef;
   968                 f.put(DatatypeConstants.DATETIME,   0x1111111);
   969                 f.put(DatatypeConstants.DATE,       0x1111000);
   970                 f.put(DatatypeConstants.TIME,       0x1000111);
   971                 f.put(DatatypeConstants.GDAY,       0x1001000);
   972                 f.put(DatatypeConstants.GMONTH,     0x1010000);
   973                 f.put(DatatypeConstants.GYEAR,      0x1100000);
   974                 f.put(DatatypeConstants.GYEARMONTH, 0x1110000);
   975                 f.put(DatatypeConstants.GMONTHDAY,  0x1011000);
   976         }
   978     /**
   979      * {@link RuntimeBuiltinLeafInfoImpl} for {@link UUID}.
   980      *
   981      * This class is given a name so that failing to load this class won't cause a fatal problem.
   982      */
   983     private static class UUIDImpl extends StringImpl<UUID> {
   984         public UUIDImpl() {
   985             super(UUID.class, RuntimeBuiltinLeafInfoImpl.createXS("string"));
   986         }
   988         public UUID parse(CharSequence text) throws SAXException {
   989             TODO.checkSpec("JSR222 Issue #42");
   990             try {
   991                 return UUID.fromString(WhiteSpaceProcessor.trim(text).toString());
   992             } catch (IllegalArgumentException e) {
   993                 UnmarshallingContext.getInstance().handleError(e);
   994                 return null;
   995             }
   996         }
   998         public String print(UUID v) {
   999             return v.toString();
  1003     private static class StringImplImpl extends StringImpl<String> {
  1005         public StringImplImpl(Class type, QName[] typeNames) {
  1006             super(type, typeNames);
  1009         public String parse(CharSequence text) {
  1010             return text.toString();
  1013         public String print(String s) {
  1014             return s;
  1017         @Override
  1018         public final void writeText(XMLSerializer w, String o, String fieldName) throws IOException, SAXException, XMLStreamException {
  1019             w.text(o, fieldName);
  1022         @Override
  1023         public final void writeLeafElement(XMLSerializer w, Name tagName, String o, String fieldName) throws IOException, SAXException, XMLStreamException {
  1024             w.leafElement(tagName, o, fieldName);

mercurial