Thu, 12 Oct 2017 19:44:07 +0800
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;
1007 }
1008 }
1010 public String print(UUID v) {
1011 return v.toString();
1012 }
1013 }
1015 private static class StringImplImpl extends StringImpl<String> {
1017 public StringImplImpl(Class type, QName[] typeNames) {
1018 super(type, typeNames);
1019 }
1021 public String parse(CharSequence text) {
1022 return text.toString();
1023 }
1025 public String print(String s) {
1026 return s;
1027 }
1029 @Override
1030 public final void writeText(XMLSerializer w, String o, String fieldName) throws IOException, SAXException, XMLStreamException {
1031 w.text(o, fieldName);
1032 }
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);
1037 }
1038 }
1039 }