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

Thu, 12 Oct 2017 19:44:07 +0800

author
aoqi
date
Thu, 12 Oct 2017 19:44:07 +0800
changeset 760
e530533619ec
parent 721
06807f9a6835
parent 637
9c07ef4934dd
permissions
-rw-r--r--

merge

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

mercurial