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