1 /*
2  * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Distribution License v. 1.0, which is available at
6  * http://www.eclipse.org/org/documents/edl-v10.php.
7  *
8  * SPDX-License-Identifier: BSD-3-Clause
9  */

10
11 package com.sun.xml.bind.v2.model.impl;
12
13 import java.awt.Component;
14 import java.awt.Graphics;
15 import java.awt.Image;
16 import java.awt.MediaTracker;
17 import java.awt.image.BufferedImage;
18 import java.io.ByteArrayInputStream;
19 import java.io.File;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStreamWriter;
23 import java.io.UnsupportedEncodingException;
24 import java.lang.reflect.Type;
25 import java.math.BigDecimal;
26 import java.math.BigInteger;
27 import java.net.MalformedURLException;
28 import java.net.URI;
29 import java.net.URISyntaxException;
30 import java.net.URL;
31 import java.security.AccessController;
32 import java.security.PrivilegedAction;
33 import java.util.ArrayList;
34 import java.util.Calendar;
35 import java.util.Collections;
36 import java.util.Date;
37 import java.util.GregorianCalendar;
38 import java.util.HashMap;
39 import java.util.Iterator;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.UUID;
43
44 import javax.activation.DataHandler;
45 import javax.activation.DataSource;
46 import javax.activation.MimeType;
47 import javax.activation.MimeTypeParseException;
48 import javax.imageio.ImageIO;
49 import javax.imageio.ImageWriter;
50 import javax.imageio.stream.ImageOutputStream;
51 import javax.xml.bind.ValidationEvent;
52 import javax.xml.bind.helpers.ValidationEventImpl;
53 import javax.xml.datatype.DatatypeConstants;
54 import javax.xml.datatype.Duration;
55 import javax.xml.datatype.XMLGregorianCalendar;
56 import javax.xml.namespace.QName;
57 import javax.xml.stream.XMLStreamException;
58 import javax.xml.transform.OutputKeys;
59 import javax.xml.transform.Source;
60 import javax.xml.transform.Transformer;
61 import javax.xml.transform.TransformerException;
62 import javax.xml.transform.stream.StreamResult;
63
64 import com.sun.istack.ByteArrayDataSource;
65 import com.sun.xml.bind.DatatypeConverterImpl;
66 import com.sun.xml.bind.WhiteSpaceProcessor;
67 import com.sun.xml.bind.api.AccessorException;
68 import com.sun.xml.bind.v2.TODO;
69 import com.sun.xml.bind.v2.WellKnownNamespace;
70 import com.sun.xml.bind.v2.model.runtime.RuntimeBuiltinLeafInfo;
71 import com.sun.xml.bind.v2.runtime.Name;
72 import com.sun.xml.bind.v2.runtime.Transducer;
73 import com.sun.xml.bind.v2.runtime.XMLSerializer;
74 import com.sun.xml.bind.v2.runtime.output.Pcdata;
75 import com.sun.xml.bind.v2.runtime.unmarshaller.Base64Data;
76 import com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext;
77 import com.sun.xml.bind.v2.util.ByteArrayOutputStreamEx;
78 import com.sun.xml.bind.v2.util.DataSourceSource;
79 import java.util.logging.Logger;
80 import com.sun.xml.bind.Utils;
81 import java.util.logging.Level;
82
83 import org.xml.sax.SAXException;
84
85 /**
86  * {@link BuiltinLeafInfoImpl} with a support for runtime.
87  *
88  * <p>
89  * In particular this class defines {@link Transducer}s for the built-in types.
90  *
91  * @author Kohsuke Kawaguchi
92  */

93 public abstract class RuntimeBuiltinLeafInfoImpl<T> extends BuiltinLeafInfoImpl<Type,Class>
94     implements RuntimeBuiltinLeafInfo, Transducer<T> {
95
96     private static final Logger logger = Utils.getClassLogger();
97     
98     private RuntimeBuiltinLeafInfoImpl(Class type, QName... typeNames) {
99         super(type, typeNames);
100         LEAVES.put(type,this);
101     }
102
103     public final Class getClazz() {
104         return (Class)getType();
105     }
106
107
108     public final Transducer getTransducer() {
109         return this;
110     }
111
112     public boolean useNamespace() {
113         return false;
114     }
115
116     public final boolean isDefault() {
117         return true;
118     }
119
120     public void declareNamespace(T o, XMLSerializer w) throws AccessorException {
121     }
122
123     public QName getTypeName(T instance) {
124         return null;
125     }
126
127     /**
128      * Those built-in types that print to {@link String}.
129      */

130     private static abstract class StringImpl<T> extends RuntimeBuiltinLeafInfoImpl<T> {
131         protected StringImpl(Class type, QName... typeNames) {
132             super(type,typeNames);
133         }
134
135         public abstract String print(T o) throws AccessorException;
136
137         public void writeText(XMLSerializer w, T o, String fieldName) throws IOException, SAXException, XMLStreamException, AccessorException {
138             w.text(print(o),fieldName);
139         }
140
141         public void writeLeafElement(XMLSerializer w, Name tagName, T o, String fieldName) throws IOException, SAXException, XMLStreamException, AccessorException {
142             w.leafElement(tagName,print(o),fieldName);
143         }
144     }
145
146     /**
147      * Those built-in types that print to {@link Pcdata}.
148      */

149     private static abstract class PcdataImpl<T> extends RuntimeBuiltinLeafInfoImpl<T> {
150         protected PcdataImpl(Class type, QName... typeNames) {
151             super(type,typeNames);
152         }
153
154         public abstract Pcdata print(T o) throws AccessorException;
155
156         public final void writeText(XMLSerializer w, T o, String fieldName) throws IOException, SAXException, XMLStreamException, AccessorException {
157             w.text(print(o),fieldName);
158         }
159
160         public final void writeLeafElement(XMLSerializer w, Name tagName, T o, String fieldName) throws IOException, SAXException, XMLStreamException, AccessorException {
161             w.leafElement(tagName,print(o),fieldName);
162         }
163
164     }
165
166     /**
167      * All instances of {@link RuntimeBuiltinLeafInfoImpl}s keyed by their type.
168      */

169     public static final Map<Type,RuntimeBuiltinLeafInfoImpl<?>> LEAVES = new HashMap<Type, RuntimeBuiltinLeafInfoImpl<?>>();
170
171     private static QName createXS(String typeName) {
172         return new QName(WellKnownNamespace.XML_SCHEMA,typeName);
173     }
174
175     public static final RuntimeBuiltinLeafInfoImpl<String> STRING;
176
177     private static final String DATE = "date";
178     
179     /**
180      * List of all {@link RuntimeBuiltinLeafInfoImpl}s.
181      *
182      * <p>
183      * This corresponds to the built-in Java classes that are specified to be
184      * handled differently than ordinary classes. See table 8-2 "Mapping of Standard Java classes".
185      */

186     public static final List<RuntimeBuiltinLeafInfoImpl<?>> builtinBeanInfos;
187
188     public static final String MAP_ANYURI_TO_URI = "mapAnyUriToUri";
189     public static final String USE_OLD_GMONTH_MAPPING = "jaxb.ri.useOldGmonthMapping";
190     
191     static {
192
193         String MAP_ANYURI_TO_URI_VALUE = AccessController.doPrivileged(
194                 new PrivilegedAction<String>() {
195                     @Override
196                     public String run() {
197                         return System.getProperty(MAP_ANYURI_TO_URI);
198                     }
199                 }
200         );
201         QName[] qnames = (MAP_ANYURI_TO_URI_VALUE == null) ? new QName[] {
202                                 createXS("string"),
203                                 createXS("anySimpleType"),
204                                 createXS("normalizedString"),
205                                 createXS("anyURI"),
206                                 createXS("token"),
207                                 createXS("language"),
208                                 createXS("Name"),
209                                 createXS("NCName"),
210                                 createXS("NMTOKEN"),
211                                 createXS("ENTITY")}
212                                     :
213                          new QName[] {
214                                 createXS("string"),
215                                 createXS("anySimpleType"),
216                                 createXS("normalizedString"),
217                                 createXS("token"),
218                                 createXS("language"),
219                                 createXS("Name"),
220                                 createXS("NCName"),
221                                 createXS("NMTOKEN"),
222                                 createXS("ENTITY")};
223
224         STRING = new StringImplImpl(String.class, qnames);
225
226         ArrayList<RuntimeBuiltinLeafInfoImpl<?>> secondaryList = new ArrayList<RuntimeBuiltinLeafInfoImpl<?>>();
227             /*
228                 There are cases where more than one Java classes map to the same XML type.
229                 But when we see the same XML type in an incoming document, we only pick
230                 one of those Java classes to unmarshal. This Java class is called 'primary'.
231                 The rest are called 'secondary'.
232
233                 Currently we lack the proper infrastructure to handle those nicely.
234                 For now, we rely on a hack.
235
236                 We define secondary mappings first, then primary ones later. GrammarInfo
237                 builds a map from type name to BeanInfo. By defining primary ones later,
238                 those primary bindings will overwrite the secondary ones.
239             */

240
241             /*
242                 secondary bindings
243             */

244         secondaryList.add(
245             new StringImpl<Character>(Character.class, createXS("unsignedShort")) {
246                 public Character parse(CharSequence text) {
247                     // TODO.checkSpec("default mapping for char is not defined yet");
248                     return (char)DatatypeConverterImpl._parseInt(text);
249                 }
250                 public String print(Character v) {
251                     return Integer.toString(v);
252                 }
253             });
254         secondaryList.add(
255             new StringImpl<Calendar>(Calendar.class, DatatypeConstants.DATETIME) {
256                 public Calendar parse(CharSequence text) {
257                     return DatatypeConverterImpl._parseDateTime(text.toString());
258                 }
259                 public String print(Calendar v) {
260                     return DatatypeConverterImpl._printDateTime(v);
261                 }
262             });
263         secondaryList.add(
264             new StringImpl<GregorianCalendar>(GregorianCalendar.class, DatatypeConstants.DATETIME) {
265                 public GregorianCalendar parse(CharSequence text) {
266                     return DatatypeConverterImpl._parseDateTime(text.toString());
267                 }
268                 public String print(GregorianCalendar v) {
269                     return DatatypeConverterImpl._printDateTime(v);
270                 }
271             });
272         secondaryList.add(
273             new StringImpl<Date>(Date.class, DatatypeConstants.DATETIME) {
274                 public Date parse(CharSequence text) {
275                     return DatatypeConverterImpl._parseDateTime(text.toString()).getTime();
276                 }
277                 public String print(Date v) {
278                     XMLSerializer xs = XMLSerializer.getInstance();
279                     QName type = xs.getSchemaType();
280                     GregorianCalendar cal = new GregorianCalendar(0,0,0);
281                     cal.setTime(v);
282                     if ((type != null) && (WellKnownNamespace.XML_SCHEMA.equals(type.getNamespaceURI())) &&
283                             DATE.equals(type.getLocalPart())) {
284                         return DatatypeConverterImpl._printDate(cal);
285                     } else {
286                         return DatatypeConverterImpl._printDateTime(cal);
287                     }
288                 }
289             });
290         secondaryList.add(
291             new StringImpl<File>(File.class, createXS("string")) {
292                 public File parse(CharSequence text) {
293                     return new File(WhiteSpaceProcessor.trim(text).toString());
294                 }
295                 public String print(File v) {
296                     return v.getPath();
297                 }
298             });
299         secondaryList.add(
300             new StringImpl<URL>(URL.class, createXS("anyURI")) {
301                 public URL parse(CharSequence text) throws SAXException {
302                     TODO.checkSpec("JSR222 Issue #42");
303                     try {
304                         return new URL(WhiteSpaceProcessor.trim(text).toString());
305                     } catch (MalformedURLException e) {
306                         UnmarshallingContext.getInstance().handleError(e);
307                         return null;
308                     }
309                 }
310                 public String print(URL v) {
311                     return v.toExternalForm();
312                 }
313             });
314         if (MAP_ANYURI_TO_URI_VALUE == null) {
315             secondaryList.add(
316                 new StringImpl<URI>(URI.class, createXS("string")) {
317                     public URI parse(CharSequence text) throws SAXException {
318                         try {
319                             return new URI(text.toString());
320                         } catch (URISyntaxException e) {
321                             UnmarshallingContext.getInstance().handleError(e);
322                             return null;
323                         }
324                     }
325
326                     public String print(URI v) {
327                         return v.toString();
328                     }
329                 });
330         }
331         secondaryList.add(
332             new StringImpl<Class>(Class.class, createXS("string")) {
333                 public Class parse(CharSequence text) throws SAXException {
334                     TODO.checkSpec("JSR222 Issue #42");
335                     try {
336                         String name = WhiteSpaceProcessor.trim(text).toString();
337                         ClassLoader cl = UnmarshallingContext.getInstance().classLoader;
338                         if(cl==null)
339                             cl = Thread.currentThread().getContextClassLoader();
340
341                         if(cl!=null)
342                             return cl.loadClass(name);
343                         else
344                             return Class.forName(name);
345                     } catch (ClassNotFoundException e) {
346                         UnmarshallingContext.getInstance().handleError(e);
347                         return null;
348                     }
349                 }
350                 public String print(Class v) {
351                     return v.getName();
352                 }
353             });
354
355             /*
356                 classes that map to base64Binary / MTOM related classes.
357                 a part of the secondary binding.
358             */

359         secondaryList.add(
360             new PcdataImpl<Image>(Image.class, createXS("base64Binary")) {
361                 public Image parse(CharSequence text) throws SAXException  {
362                     try {
363                         InputStream is;
364                         if(text instanceof Base64Data)
365                             is = ((Base64Data)text).getInputStream();
366                         else
367                             is = new ByteArrayInputStream(decodeBase64(text)); // TODO: buffering is inefficient
368
369                         // technically we should check the MIME type here, but
370                         // normally images can be content-sniffed.
371                         // so the MIME type check will only make us slower and draconian, both of which
372                         // JAXB 2.0 isn't interested.
373                         try {
374                             return ImageIO.read(is);
375                         } finally {
376                             is.close();
377                         }
378                     } catch (IOException e) {
379                         UnmarshallingContext.getInstance().handleError(e);
380                         return null;
381                     }
382                 }
383
384                 private BufferedImage convertToBufferedImage(Image image) throws IOException {
385                     if (image instanceof BufferedImage) {
386                         return (BufferedImage)image;
387
388                     } else {
389                         MediaTracker tracker = new MediaTracker(new Component(){}); // not sure if this is the right thing to do.
390                         tracker.addImage(image, 0);
391                         try {
392                             tracker.waitForAll();
393                         } catch (InterruptedException e) {
394                             throw new IOException(e.getMessage());
395                         }
396                         BufferedImage bufImage = new BufferedImage(
397                                 image.getWidth(null),
398                                 image.getHeight(null),
399                                 BufferedImage.TYPE_INT_ARGB);
400
401                         Graphics g = bufImage.createGraphics();
402                         g.drawImage(image, 0, 0, null);
403                         return bufImage;
404                     }
405                 }
406
407                 public Base64Data print(Image v) {
408                     ByteArrayOutputStreamEx imageData = new ByteArrayOutputStreamEx();
409                     XMLSerializer xs = XMLSerializer.getInstance();
410
411                     String mimeType = xs.getXMIMEContentType();
412                     if(mimeType==null || mimeType.startsWith("image/*"))
413                         // because PNG is lossless, it's a good default
414                         //
415                         // mime type can be a range, in which case we can't just pass that
416                         // to ImageIO.getImageWritersByMIMEType, so here I'm just assuming
417                         // the default of PNG. Not sure if this is complete.
418                         mimeType = "image/png";
419
420                     try {
421                         Iterator<ImageWriter> itr = ImageIO.getImageWritersByMIMEType(mimeType);
422                         if(itr.hasNext()) {
423                             ImageWriter w = itr.next();
424                             ImageOutputStream os = ImageIO.createImageOutputStream(imageData);
425                             w.setOutput(os);
426                             w.write(convertToBufferedImage(v));
427                             os.close();
428                             w.dispose();
429                         } else {
430                             // no encoder
431                             xs.handleEvent(new ValidationEventImpl(
432                                 ValidationEvent.ERROR,
433                                 Messages.NO_IMAGE_WRITER.format(mimeType),
434                                 xs.getCurrentLocation(null) ));
435                             // TODO: proper error reporting
436                             throw new RuntimeException("no encoder for MIME type "+mimeType);
437                         }
438                     } catch (IOException e) {
439                         xs.handleError(e);
440                         // TODO: proper error reporting
441                         throw new RuntimeException(e);
442                     }
443                     Base64Data bd = new Base64Data();
444                     imageData.set(bd,mimeType);
445                     return bd;
446                 }
447             });
448         secondaryList.add(
449             new PcdataImpl<DataHandler>(DataHandler.class, createXS("base64Binary")) {
450                 public DataHandler parse(CharSequence text) {
451                     if(text instanceof Base64Data)
452                         return ((Base64Data)text).getDataHandler();
453                     else
454                         return new DataHandler(new ByteArrayDataSource(decodeBase64(text),
455                             UnmarshallingContext.getInstance().getXMIMEContentType()));
456                 }
457
458                 public Base64Data print(DataHandler v) {
459                     Base64Data bd = new Base64Data();
460                     bd.set(v);
461                     return bd;
462                 }
463             });
464         secondaryList.add(
465             new PcdataImpl<Source>(Source.class, createXS("base64Binary")) {
466                 public Source parse(CharSequence text) throws SAXException  {
467                     try {
468                         if(text instanceof Base64Data)
469                             return new DataSourceSource( ((Base64Data)text).getDataHandler() );
470                         else
471                             return new DataSourceSource(new ByteArrayDataSource(decodeBase64(text),
472                                 UnmarshallingContext.getInstance().getXMIMEContentType()));
473                     } catch (MimeTypeParseException e) {
474                         UnmarshallingContext.getInstance().handleError(e);
475                         return null;
476                     }
477                 }
478
479                 public Base64Data print(Source v) {
480                     XMLSerializer xs = XMLSerializer.getInstance();
481                     Base64Data bd = new Base64Data();
482
483                     String contentType = xs.getXMIMEContentType();
484                     MimeType mt = null;
485                     if(contentType!=null)
486                         try {
487                             mt = new MimeType(contentType);
488                         } catch (MimeTypeParseException e) {
489                             xs.handleError(e);
490                             // recover by ignoring the content type specification
491                         }
492
493                     if( v instanceof DataSourceSource ) {
494                         // if so, we already have immutable DataSource so
495                         // this can be done efficiently
496                         DataSource ds = ((DataSourceSource)v).getDataSource();
497
498                         String dsct = ds.getContentType();
499                         if(dsct!=null && (contentType==null || contentType.equals(dsct))) {
500                             bd.set(new DataHandler(ds));
501                             return bd;
502                         }
503                     }
504
505                     // general case. slower.
506
507                     // find out the encoding
508                     String charset=null;
509                     if(mt!=null)
510                         charset = mt.getParameter("charset");
511                     if(charset==null)
512                         charset = "UTF-8";
513
514                     try {
515                         ByteArrayOutputStreamEx baos = new ByteArrayOutputStreamEx();
516                         Transformer tr = xs.getIdentityTransformer();
517                         String defaultEncoding = tr.getOutputProperty(OutputKeys.ENCODING);
518                         tr.setOutputProperty(OutputKeys.ENCODING, charset);                        
519                         tr.transform(v, new StreamResult(new OutputStreamWriter(baos,charset)));
520                         tr.setOutputProperty(OutputKeys.ENCODING, defaultEncoding);
521                         baos.set(bd,"application/xml; charset="+charset);
522                         return bd;
523                     } catch (TransformerException e) {
524                         // TODO: marshaller error handling
525                         xs.handleError(e);
526                     } catch (UnsupportedEncodingException e) {
527                         xs.handleError(e);
528                     }
529
530                     // error recoverly
531                     bd.set(new byte[0],"application/xml");
532                     return bd;
533                 }
534             });
535         secondaryList.add(
536             new StringImpl<XMLGregorianCalendar>(XMLGregorianCalendar.class,
537                     createXS("anySimpleType"),
538                     DatatypeConstants.DATE,
539                     DatatypeConstants.DATETIME,
540                     DatatypeConstants.TIME,
541                     DatatypeConstants.GMONTH,
542                     DatatypeConstants.GDAY,
543                     DatatypeConstants.GYEAR,
544                     DatatypeConstants.GYEARMONTH,
545                     DatatypeConstants.GMONTHDAY
546                 ) {
547                 public String print(XMLGregorianCalendar cal) {
548                     XMLSerializer xs = XMLSerializer.getInstance();
549
550                     QName type = xs.getSchemaType();
551                     if (type != null) {
552                         try {
553                             checkXmlGregorianCalendarFieldRef(type, cal);
554                             String format = xmlGregorianCalendarFormatString.get(type);
555                             if (format != null) {
556                                 return format(format, cal);
557                             }
558                         } catch (javax.xml.bind.MarshalException e) {
559                             // see issue 649
560                             xs.handleEvent(new ValidationEventImpl(ValidationEvent.WARNING, e.getMessage(),
561                                 xs.getCurrentLocation(null) ));
562                             return "";
563                         }
564                     }
565                     return cal.toXMLFormat();
566                 }
567
568                 public XMLGregorianCalendar parse(CharSequence lexical) throws SAXException {
569                     try {
570                         return DatatypeConverterImpl.getDatatypeFactory()
571                                 .newXMLGregorianCalendar(lexical.toString().trim()); // (.trim() - issue 396)
572                     } catch (Exception e) {
573                         UnmarshallingContext.getInstance().handleError(e);
574                         return null;
575                     }
576                 }
577
578                 // code duplicated from JAXP RI 1.3. See 6277586
579                 private String format( String format, XMLGregorianCalendar value ) {
580                     StringBuilder buf = new StringBuilder();
581                     int fidx=0,flen=format.length();
582
583                     while(fidx<flen) {
584                         char fch = format.charAt(fidx++);
585                         if(fch!='%') {// not a meta char
586                             buf.append(fch);
587                             continue;
588                         }
589
590                         switch(format.charAt(fidx++)) {
591                         case 'Y':
592                             printNumber(buf,value.getEonAndYear(), 4);
593                             break;
594                         case 'M':
595                             printNumber(buf,value.getMonth(),2);
596                             break;
597                         case 'D':
598                             printNumber(buf,value.getDay(),2);
599                             break;
600                         case 'h':
601                             printNumber(buf,value.getHour(),2);
602                             break;
603                         case 'm':
604                             printNumber(buf,value.getMinute(),2);
605                             break;
606                         case 's':
607                             printNumber(buf,value.getSecond(),2);
608                     if (value.getFractionalSecond() != null) {
609                         String frac = value.getFractionalSecond().toPlainString();
610                         //skip leading zero.
611                         buf.append(frac.substring(1, frac.length()));
612                     }
613                             break;
614                         case 'z':
615                     int offset = value.getTimezone();
616                             if(offset == 0) {
617                         buf.append('Z');
618                     } else if (offset != DatatypeConstants.FIELD_UNDEFINED) {
619                         if(offset<0) {
620                         buf.append('-');
621                         offset *= -1;
622                         } else {
623                         buf.append('+');
624                         }
625                         printNumber(buf,offset/60,2);
626                                 buf.append(':');
627                                 printNumber(buf,offset%60,2);
628                             }
629                             break;
630                         default:
631                             throw new InternalError();  // impossible
632                         }
633                     }
634
635                     return buf.toString();
636                 }
637                 private void printNumber( StringBuilder out, BigInteger number, int nDigits) {
638                     String s = number.toString();
639                     forint i=s.length(); i<nDigits; i++ )
640                         out.append('0');
641                     out.append(s);
642                 }
643                 private void printNumber( StringBuilder out, int number, int nDigits ) {
644                     String s = String.valueOf(number);
645                     forint i=s.length(); i<nDigits; i++ )
646                         out.append('0');
647                     out.append(s);
648                 }
649                 @Override
650                 public QName getTypeName(XMLGregorianCalendar cal) {
651                     return cal.getXMLSchemaType();
652                 }
653             });
654
655         ArrayList<RuntimeBuiltinLeafInfoImpl<?>> primaryList = new ArrayList<RuntimeBuiltinLeafInfoImpl<?>>();
656
657         /*
658             primary bindings
659         */

660         primaryList.add(STRING);
661         primaryList.add(new StringImpl<Boolean>(Boolean.class,
662                 createXS("boolean")
663                 ) {
664                 public Boolean parse(CharSequence text) {
665                     return DatatypeConverterImpl._parseBoolean(text);
666                 }
667
668                 public String print(Boolean v) {
669                     return v.toString();
670                 }
671             });
672         primaryList.add(new PcdataImpl<byte[]>(byte[].class,
673                 createXS("base64Binary"),
674                 createXS("hexBinary")
675                 ) {
676                 public byte[] parse(CharSequence text) {
677                     return decodeBase64(text);
678                 }
679
680                 public Base64Data print(byte[] v) {
681                     XMLSerializer w = XMLSerializer.getInstance();
682                     Base64Data bd = new Base64Data();
683                     String mimeType = w.getXMIMEContentType();
684                     bd.set(v,mimeType);
685                     return bd;
686                 }
687             });
688         primaryList.add(new StringImpl<Byte>(Byte.class,
689                 createXS("byte")
690                 ) {
691                 public Byte parse(CharSequence text) {
692                     return DatatypeConverterImpl._parseByte(text);
693                 }
694
695                 public String print(Byte v) {
696                     return DatatypeConverterImpl._printByte(v);
697                 }
698             });
699         primaryList.add(new StringImpl<Short>(Short.class,
700                 createXS("short"),
701                 createXS("unsignedByte")
702                 ) {
703                 public Short parse(CharSequence text) {
704                     return DatatypeConverterImpl._parseShort(text);
705                 }
706
707                 public String print(Short v) {
708                     return DatatypeConverterImpl._printShort(v);
709                 }
710             });
711         primaryList.add(new StringImpl<Integer>(Integer.class,
712                 createXS("int"),
713                 createXS("unsignedShort")
714                 ) {
715                 public Integer parse(CharSequence text) {
716                     return DatatypeConverterImpl._parseInt(text);
717                 }
718
719                 public String print(Integer v) {
720                     return DatatypeConverterImpl._printInt(v);
721                 }
722             });
723         primaryList.add(
724             new StringImpl<Long>(Long.class,
725                 createXS("long"),
726                 createXS("unsignedInt")
727                 ) {
728                 public Long parse(CharSequence text) {
729                     return DatatypeConverterImpl._parseLong(text);
730                 }
731
732                 public String print(Long v) {
733                     return DatatypeConverterImpl._printLong(v);
734                 }
735             });
736         primaryList.add(
737             new StringImpl<Float>(Float.class,
738                 createXS("float")
739                 ) {
740                 public Float parse(CharSequence text) {
741                     return DatatypeConverterImpl._parseFloat(text.toString());
742                 }
743
744                 public String print(Float v) {
745                     return DatatypeConverterImpl._printFloat(v);
746                 }
747             });
748         primaryList.add(
749             new StringImpl<Double>(Double.class,
750                 createXS("double")
751                 ) {
752                 public Double parse(CharSequence text) {
753                     return DatatypeConverterImpl._parseDouble(text);
754                 }
755
756                 public String print(Double v) {
757                     return DatatypeConverterImpl._printDouble(v);
758                 }
759             });
760         primaryList.add(
761             new StringImpl<BigInteger>(BigInteger.class,
762                 createXS("integer"),
763                 createXS("positiveInteger"),
764                 createXS("negativeInteger"),
765                 createXS("nonPositiveInteger"),
766                 createXS("nonNegativeInteger"),
767                 createXS("unsignedLong")
768                 ) {
769                 public BigInteger parse(CharSequence text) {
770                     return DatatypeConverterImpl._parseInteger(text);
771                 }
772
773                 public String print(BigInteger v) {
774                     return DatatypeConverterImpl._printInteger(v);
775                 }
776             });
777         primaryList.add(
778                 new StringImpl<BigDecimal>(BigDecimal.class,
779                         createXS("decimal")
780                 ) {
781                     public BigDecimal parse(CharSequence text) {
782                         return DatatypeConverterImpl._parseDecimal(text.toString());
783                     }
784
785                     public String print(BigDecimal v) {
786                         return DatatypeConverterImpl._printDecimal(v);
787                     }
788                 }
789         );
790         primaryList.add(
791             new StringImpl<QName>(QName.class,
792                 createXS("QName")
793                 ) {
794                 public QName parse(CharSequence text) throws SAXException {
795                     try {
796                         return DatatypeConverterImpl._parseQName(text.toString(),UnmarshallingContext.getInstance());
797                     } catch (IllegalArgumentException e) {
798                         UnmarshallingContext.getInstance().handleError(e);
799                         return null;
800                     }
801                 }
802
803                 public String print(QName v) {
804                     return DatatypeConverterImpl._printQName(v,XMLSerializer.getInstance().getNamespaceContext());
805                 }
806
807                 @Override
808                 public boolean useNamespace() {
809                     return true;
810                 }
811
812                 @Override
813                 public void declareNamespace(QName v, XMLSerializer w) {
814                     w.getNamespaceContext().declareNamespace(v.getNamespaceURI(),v.getPrefix(),false);
815                 }
816             });
817         if (MAP_ANYURI_TO_URI_VALUE != null) {
818             primaryList.add(
819                 new StringImpl<URI>(URI.class, createXS("anyURI")) {
820                     public URI parse(CharSequence text) throws SAXException {
821                         try {
822                             return new URI(text.toString());
823                         } catch (URISyntaxException e) {
824                             UnmarshallingContext.getInstance().handleError(e);
825                             return null;
826                         }
827                     }
828
829                     public String print(URI v) {
830                         return v.toString();
831                     }
832                 });
833         }
834         primaryList.add(
835                 new StringImpl<Duration>(Duration.class, createXS("duration")) {
836                     public String print(Duration duration) {
837                         return duration.toString();
838                     }
839
840                     public Duration parse(CharSequence lexical) {
841                         TODO.checkSpec("JSR222 Issue #42");
842                         return DatatypeConverterImpl.getDatatypeFactory().newDuration(lexical.toString());
843                     }
844                 }
845         );
846         primaryList.add(
847             new StringImpl<Void>(Void.class) {
848                 // 'void' binding isn't defined by the spec, but when the JAX-RPC processes user-defined
849                 // methods like "int actionFoo()", they need this pseudo-void property.
850
851                 public String print(Void value) {
852                     return "";
853                 }
854
855                 public Void parse(CharSequence lexical) {
856                     return null;
857                 }
858             });
859
860         List<RuntimeBuiltinLeafInfoImpl<?>> l = new ArrayList<RuntimeBuiltinLeafInfoImpl<?>>(secondaryList.size()+primaryList.size()+1);
861         l.addAll(secondaryList);
862
863         // UUID may fail to load if we are running on JDK 1.4. Handle gracefully
864         try {
865             l.add(new UUIDImpl());
866         } catch (LinkageError e) {
867             // ignore
868         }
869
870         l.addAll(primaryList);
871
872         builtinBeanInfos = Collections.unmodifiableList(l);
873     }
874
875     private static byte[] decodeBase64(CharSequence text) {
876         if (text instanceof Base64Data) {
877             Base64Data base64Data = (Base64Data) text;
878             return base64Data.getExact();
879         } else {
880             return DatatypeConverterImpl._parseBase64Binary(text.toString());
881         }
882     }
883
884     private static void checkXmlGregorianCalendarFieldRef(QName type, 
885         XMLGregorianCalendar cal)throws javax.xml.bind.MarshalException{
886         StringBuilder buf = new StringBuilder();
887         int bitField = xmlGregorianCalendarFieldRef.get(type);
888         final int l = 0x1;
889         int pos = 0;
890         while (bitField != 0x0){
891             int bit = bitField & l;
892             bitField >>>= 4;
893             pos++;
894             
895             if (bit == 1) {
896                 switch(pos){
897                     case 1:
898                         if (cal.getSecond() == DatatypeConstants.FIELD_UNDEFINED){
899                             buf.append("  ").append(Messages.XMLGREGORIANCALENDAR_SEC);
900                         }
901                         break;
902                     case 2:
903                         if (cal.getMinute() == DatatypeConstants.FIELD_UNDEFINED){
904                             buf.append("  ").append(Messages.XMLGREGORIANCALENDAR_MIN);
905                         }
906                         break;
907                     case 3:
908                         if (cal.getHour() == DatatypeConstants.FIELD_UNDEFINED){
909                             buf.append("  ").append(Messages.XMLGREGORIANCALENDAR_HR);
910                         }
911                         break;
912                     case 4:
913                         if (cal.getDay() == DatatypeConstants.FIELD_UNDEFINED){
914                             buf.append("  ").append(Messages.XMLGREGORIANCALENDAR_DAY);
915                         }
916                         break;
917                     case 5:
918                         if (cal.getMonth() == DatatypeConstants.FIELD_UNDEFINED){
919                             buf.append("  ").append(Messages.XMLGREGORIANCALENDAR_MONTH);
920                         }
921                         break;
922                     case 6:
923                         if (cal.getYear() == DatatypeConstants.FIELD_UNDEFINED){
924                             buf.append("  ").append(Messages.XMLGREGORIANCALENDAR_YEAR);
925                         }
926                         break;
927                     case 7:  // ignore timezone setting
928                         break;
929                 }
930             }
931         }
932         if (buf.length() > 0){
933             throw new javax.xml.bind.MarshalException(
934              Messages.XMLGREGORIANCALENDAR_INVALID.format(type.getLocalPart()) 
935              + buf.toString());
936         }
937     }
938     
939     /**
940      * Format string for the {@link XMLGregorianCalendar}.
941      */

942     private static final Map<QName,String> xmlGregorianCalendarFormatString = new HashMap<QName, String>();
943
944     static {
945         Map<QName,String> m = xmlGregorianCalendarFormatString;
946         // See 4971612: be careful for SCCS substitution
947         m.put(DatatypeConstants.DATETIME,   "%Y-%M-%DT%h:%m:%s""%z");
948         m.put(DatatypeConstants.DATE,       "%Y-%M-%D" +"%z");
949         m.put(DatatypeConstants.TIME,       "%h:%m:%s""%z");
950         final String oldGmonthMappingProperty = AccessController.doPrivileged(new PrivilegedAction<String>() {
951             @Override
952             public String run() {
953                 return System.getProperty(USE_OLD_GMONTH_MAPPING);
954             }
955         });
956         if (oldGmonthMappingProperty == null) {
957             m.put(DatatypeConstants.GMONTH, "--%M%z");      //  E2-12 Error. http://www.w3.org/2001/05/xmlschema-errata#e2-12
958         } else {                                            //  backw. compatibility
959             if (logger.isLoggable(Level.FINE)) {
960                 logger.log(Level.FINE, "Old GMonth mapping used.");
961             }
962             m.put(DatatypeConstants.GMONTH, "--%M--%z");    
963         }
964         m.put(DatatypeConstants.GDAY,       "---%D" + "%z");
965         m.put(DatatypeConstants.GYEAR,      "%Y" + "%z");
966         m.put(DatatypeConstants.GYEARMONTH, "%Y-%M" + "%z");
967         m.put(DatatypeConstants.GMONTHDAY,  "--%M-%D" +"%z");
968     }
969
970     /**
971      * Field designations for XMLGregorianCalendar format string.
972      * sec        0x0000001
973      * min        0x0000010
974      * hrs        0x0000100
975      * day        0x0001000
976      * month    0x0010000
977      * year        0x0100000
978      * timezone     0x1000000
979      */

980     private static final Map<QName, Integer> xmlGregorianCalendarFieldRef =
981         new HashMap<QName, Integer>();
982     static {
983         Map<QName, Integer> f = xmlGregorianCalendarFieldRef;
984         f.put(DatatypeConstants.DATETIME,   0x1111111);
985         f.put(DatatypeConstants.DATE,       0x1111000);
986         f.put(DatatypeConstants.TIME,       0x1000111);
987         f.put(DatatypeConstants.GDAY,       0x1001000);
988         f.put(DatatypeConstants.GMONTH,     0x1010000);
989         f.put(DatatypeConstants.GYEAR,      0x1100000);
990         f.put(DatatypeConstants.GYEARMONTH, 0x1110000);
991         f.put(DatatypeConstants.GMONTHDAY,  0x1011000);
992     }
993     
994     /**
995      * {@link RuntimeBuiltinLeafInfoImpl} for {@link UUID}.
996      *
997      * This class is given a name so that failing to load this class won't cause a fatal problem.
998      */

999     private static class UUIDImpl extends StringImpl<UUID> {
1000         public UUIDImpl() {
1001             super(UUID.class, RuntimeBuiltinLeafInfoImpl.createXS("string"));
1002         }
1003
1004         public UUID parse(CharSequence text) throws SAXException {
1005             TODO.checkSpec("JSR222 Issue #42");
1006             try {
1007                 return UUID.fromString(WhiteSpaceProcessor.trim(text).toString());
1008             } catch (IllegalArgumentException e) {
1009                 UnmarshallingContext.getInstance().handleError(e);
1010                 return null;
1011             }
1012         }
1013
1014         public String print(UUID v) {
1015             return v.toString();
1016         }
1017     }
1018
1019     private static class StringImplImpl extends StringImpl<String> {
1020
1021         public StringImplImpl(Class type, QName[] typeNames) {
1022             super(type, typeNames);
1023         }
1024
1025         public String parse(CharSequence text) {
1026             return text.toString();
1027         }
1028
1029         public String print(String s) {
1030             return s;
1031         }
1032
1033         @Override
1034         public final void writeText(XMLSerializer w, String o, String fieldName) throws IOException, SAXException, XMLStreamException {
1035             w.text(o, fieldName);
1036         }
1037
1038         @Override
1039         public final void writeLeafElement(XMLSerializer w, Name tagName, String o, String fieldName) throws IOException, SAXException, XMLStreamException {
1040             w.leafElement(tagName, o, fieldName);
1041         }
1042     }
1043 }
1044