1 /* 2 * Copyright (c) 1997, 2013, 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 */ 25 26 package com.sun.xml.internal.bind.v2.model.impl; 27 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; 56 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.DatatypeConfigurationException; 67 import javax.xml.datatype.DatatypeConstants; 68 import javax.xml.datatype.DatatypeFactory; 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; 78 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; 94 95 import org.xml.sax.SAXException; 96 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> { 107 108 private RuntimeBuiltinLeafInfoImpl(Class type, QName... typeNames) { 109 super(type, typeNames); 110 LEAVES.put(type,this); 111 } 112 113 public final Class getClazz() { 114 return (Class)getType(); 115 } 116 117 118 public final Transducer getTransducer() { 119 return this; 120 } 121 122 public boolean useNamespace() { 123 return false; 124 } 125 126 public final boolean isDefault() { 127 return true; 128 } 129 130 public void declareNamespace(T o, XMLSerializer w) throws AccessorException { 131 } 132 133 public QName getTypeName(T instance) { 134 return null; 135 } 136 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 } 144 145 public abstract String print(T o) throws AccessorException; 146 147 public void writeText(XMLSerializer w, T o, String fieldName) throws IOException, SAXException, XMLStreamException, AccessorException { 148 w.text(print(o),fieldName); 149 } 150 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 } 155 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 } 163 164 public abstract Pcdata print(T o) throws AccessorException; 165 166 public final void writeText(XMLSerializer w, T o, String fieldName) throws IOException, SAXException, XMLStreamException, AccessorException { 167 w.text(print(o),fieldName); 168 } 169 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 } 173 174 } 175 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<?>>(); 180 181 private static QName createXS(String typeName) { 182 return new QName(WellKnownNamespace.XML_SCHEMA,typeName); 183 } 184 185 public static final RuntimeBuiltinLeafInfoImpl<String> STRING; 186 187 private static final String DATE = "date"; 188 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; 197 198 public static final String MAP_ANYURI_TO_URI = "mapAnyUriToUri"; 199 200 static { 201 202 QName[] qnames = (System.getProperty(MAP_ANYURI_TO_URI) == null) ? new QName[] { 203 createXS("string"), 204 createXS("anySimpleType"), 205 createXS("normalizedString"), 206 createXS("anyURI"), 207 createXS("token"), 208 createXS("language"), 209 createXS("Name"), 210 createXS("NCName"), 211 createXS("NMTOKEN"), 212 createXS("ENTITY")} 213 : 214 new QName[] { 215 createXS("string"), 216 createXS("anySimpleType"), 217 createXS("normalizedString"), 218 createXS("token"), 219 createXS("language"), 220 createXS("Name"), 221 createXS("NCName"), 222 createXS("NMTOKEN"), 223 createXS("ENTITY")}; 224 225 STRING = new StringImplImpl(String.class, qnames); 226 227 ArrayList<RuntimeBuiltinLeafInfoImpl<?>> secondaryList = new ArrayList<RuntimeBuiltinLeafInfoImpl<?>>(); 228 /* 229 There are cases where more than one Java classes map to the same XML type. 230 But when we see the same XML type in an incoming document, we only pick 231 one of those Java classes to unmarshal. This Java class is called 'primary'. 232 The rest are called 'secondary'. 233 234 Currently we lack the proper infrastructure to handle those nicely. 235 For now, we rely on a hack. 236 237 We define secondary mappings first, then primary ones later. GrammarInfo 238 builds a map from type name to BeanInfo. By defining primary ones later, 239 those primary bindings will overwrite the secondary ones. 240 */ 241 242 /* 243 secondary bindings 244 */ 245 secondaryList.add( 246 new StringImpl<Character>(Character.class, createXS("unsignedShort")) { 247 public Character parse(CharSequence text) { 248 // TODO.checkSpec("default mapping for char is not defined yet"); 249 return (char)DatatypeConverterImpl._parseInt(text); 250 } 251 public String print(Character v) { 252 return Integer.toString(v); 253 } 254 }); 255 secondaryList.add( 256 new StringImpl<Calendar>(Calendar.class, DatatypeConstants.DATETIME) { 257 public Calendar parse(CharSequence text) { 258 return DatatypeConverterImpl._parseDateTime(text.toString()); 259 } 260 public String print(Calendar v) { 261 return DatatypeConverterImpl._printDateTime(v); 262 } 263 }); 264 secondaryList.add( 265 new StringImpl<GregorianCalendar>(GregorianCalendar.class, DatatypeConstants.DATETIME) { 266 public GregorianCalendar parse(CharSequence text) { 267 return DatatypeConverterImpl._parseDateTime(text.toString()); 268 } 269 public String print(GregorianCalendar v) { 270 return DatatypeConverterImpl._printDateTime(v); 271 } 272 }); 273 secondaryList.add( 274 new StringImpl<Date>(Date.class, DatatypeConstants.DATETIME) { 275 public Date parse(CharSequence text) { 276 return DatatypeConverterImpl._parseDateTime(text.toString()).getTime(); 277 } 278 public String print(Date v) { 279 XMLSerializer xs = XMLSerializer.getInstance(); 280 QName type = xs.getSchemaType(); 281 GregorianCalendar cal = new GregorianCalendar(0,0,0); 282 cal.setTime(v); 283 if ((type != null) && (WellKnownNamespace.XML_SCHEMA.equals(type.getNamespaceURI())) && 284 DATE.equals(type.getLocalPart())) { 285 return DatatypeConverterImpl._printDate(cal); 286 } else { 287 return DatatypeConverterImpl._printDateTime(cal); 288 } 289 } 290 }); 291 secondaryList.add( 292 new StringImpl<File>(File.class, createXS("string")) { 293 public File parse(CharSequence text) { 294 return new File(WhiteSpaceProcessor.trim(text).toString()); 295 } 296 public String print(File v) { 297 return v.getPath(); 298 } 299 }); 300 secondaryList.add( 301 new StringImpl<URL>(URL.class, createXS("anyURI")) { 302 public URL parse(CharSequence text) throws SAXException { 303 TODO.checkSpec("JSR222 Issue #42"); 304 try { 305 return new URL(WhiteSpaceProcessor.trim(text).toString()); 306 } catch (MalformedURLException e) { 307 UnmarshallingContext.getInstance().handleError(e); 308 return null; 309 } 310 } 311 public String print(URL v) { 312 return v.toExternalForm(); 313 } 314 }); 315 if (System.getProperty(MAP_ANYURI_TO_URI) == null) { 316 secondaryList.add( 317 new StringImpl<URI>(URI.class, createXS("string")) { 318 public URI parse(CharSequence text) throws SAXException { 319 try { 320 return new URI(text.toString()); 321 } catch (URISyntaxException e) { 322 UnmarshallingContext.getInstance().handleError(e); 323 return null; 324 } 325 } 326 327 public String print(URI v) { 328 return v.toString(); 329 } 330 }); 331 } 332 secondaryList.add( 333 new StringImpl<Class>(Class.class, createXS("string")) { 334 public Class parse(CharSequence text) throws SAXException { 335 TODO.checkSpec("JSR222 Issue #42"); 336 try { 337 String name = WhiteSpaceProcessor.trim(text).toString(); 338 ClassLoader cl = UnmarshallingContext.getInstance().classLoader; 339 if(cl==null) 340 cl = Thread.currentThread().getContextClassLoader(); 341 342 if(cl!=null) 343 return cl.loadClass(name); 344 else 345 return Class.forName(name); 346 } catch (ClassNotFoundException e) { 347 UnmarshallingContext.getInstance().handleError(e); 348 return null; 349 } 350 } 351 public String print(Class v) { 352 return v.getName(); 353 } 354 }); 355 356 /* 357 classes that map to base64Binary / MTOM related classes. 358 a part of the secondary binding. 359 */ 360 secondaryList.add( 361 new PcdataImpl<Image>(Image.class, createXS("base64Binary")) { 362 public Image parse(CharSequence text) throws SAXException { 363 try { 364 InputStream is; 365 if(text instanceof Base64Data) 366 is = ((Base64Data)text).getInputStream(); 367 else 368 is = new ByteArrayInputStream(decodeBase64(text)); // TODO: buffering is inefficient 369 370 // technically we should check the MIME type here, but 371 // normally images can be content-sniffed. 372 // so the MIME type check will only make us slower and draconian, both of which 373 // JAXB 2.0 isn't interested. 374 try { 375 return ImageIO.read(is); 376 } finally { 377 is.close(); 378 } 379 } catch (IOException e) { 380 UnmarshallingContext.getInstance().handleError(e); 381 return null; 382 } 383 } 384 385 private BufferedImage convertToBufferedImage(Image image) throws IOException { 386 if (image instanceof BufferedImage) { 387 return (BufferedImage)image; 388 389 } else { 390 MediaTracker tracker = new MediaTracker(new Component(){}); // not sure if this is the right thing to do. 391 tracker.addImage(image, 0); 392 try { 393 tracker.waitForAll(); 394 } catch (InterruptedException e) { 395 throw new IOException(e.getMessage()); 396 } 397 BufferedImage bufImage = new BufferedImage( 398 image.getWidth(null), 399 image.getHeight(null), 400 BufferedImage.TYPE_INT_ARGB); 401 402 Graphics g = bufImage.createGraphics(); 403 g.drawImage(image, 0, 0, null); 404 return bufImage; 405 } 406 } 407 408 public Base64Data print(Image v) { 409 ByteArrayOutputStreamEx imageData = new ByteArrayOutputStreamEx(); 410 XMLSerializer xs = XMLSerializer.getInstance(); 411 412 String mimeType = xs.getXMIMEContentType(); 413 if(mimeType==null || mimeType.startsWith("image/*")) 414 // because PNG is lossless, it's a good default 415 // 416 // mime type can be a range, in which case we can't just pass that 417 // to ImageIO.getImageWritersByMIMEType, so here I'm just assuming 418 // the default of PNG. Not sure if this is complete. 419 mimeType = "image/png"; 420 421 try { 422 Iterator<ImageWriter> itr = ImageIO.getImageWritersByMIMEType(mimeType); 423 if(itr.hasNext()) { 424 ImageWriter w = itr.next(); 425 ImageOutputStream os = ImageIO.createImageOutputStream(imageData); 426 w.setOutput(os); 427 w.write(convertToBufferedImage(v)); 428 os.close(); 429 w.dispose(); 430 } else { 431 // no encoder 432 xs.handleEvent(new ValidationEventImpl( 433 ValidationEvent.ERROR, 434 Messages.NO_IMAGE_WRITER.format(mimeType), 435 xs.getCurrentLocation(null) )); 436 // TODO: proper error reporting 437 throw new RuntimeException("no encoder for MIME type "+mimeType); 438 } 439 } catch (IOException e) { 440 xs.handleError(e); 441 // TODO: proper error reporting 442 throw new RuntimeException(e); 443 } 444 Base64Data bd = new Base64Data(); 445 imageData.set(bd,mimeType); 446 return bd; 447 } 448 }); 449 secondaryList.add( 450 new PcdataImpl<DataHandler>(DataHandler.class, createXS("base64Binary")) { 451 public DataHandler parse(CharSequence text) { 452 if(text instanceof Base64Data) 453 return ((Base64Data)text).getDataHandler(); 454 else 455 return new DataHandler(new ByteArrayDataSource(decodeBase64(text), 456 UnmarshallingContext.getInstance().getXMIMEContentType())); 457 } 458 459 public Base64Data print(DataHandler v) { 460 Base64Data bd = new Base64Data(); 461 bd.set(v); 462 return bd; 463 } 464 }); 465 secondaryList.add( 466 new PcdataImpl<Source>(Source.class, createXS("base64Binary")) { 467 public Source parse(CharSequence text) throws SAXException { 468 try { 469 if(text instanceof Base64Data) 470 return new DataSourceSource( ((Base64Data)text).getDataHandler() ); 471 else 472 return new DataSourceSource(new ByteArrayDataSource(decodeBase64(text), 473 UnmarshallingContext.getInstance().getXMIMEContentType())); 474 } catch (MimeTypeParseException e) { 475 UnmarshallingContext.getInstance().handleError(e); 476 return null; 477 } 478 } 479 480 public Base64Data print(Source v) { 481 XMLSerializer xs = XMLSerializer.getInstance(); 482 Base64Data bd = new Base64Data(); 483 484 String contentType = xs.getXMIMEContentType(); 485 MimeType mt = null; 486 if(contentType!=null) 487 try { 488 mt = new MimeType(contentType); 489 } catch (MimeTypeParseException e) { 490 xs.handleError(e); 491 // recover by ignoring the content type specification 492 } 493 494 if( v instanceof DataSourceSource ) { 495 // if so, we already have immutable DataSource so 496 // this can be done efficiently 497 DataSource ds = ((DataSourceSource)v).getDataSource(); 498 499 String dsct = ds.getContentType(); 500 if(dsct!=null && (contentType==null || contentType.equals(dsct))) { 501 bd.set(new DataHandler(ds)); 502 return bd; 503 } 504 } 505 506 // general case. slower. 507 508 // find out the encoding 509 String charset=null; 510 if(mt!=null) 511 charset = mt.getParameter("charset"); 512 if(charset==null) 513 charset = "UTF-8"; 514 515 try { 516 ByteArrayOutputStreamEx baos = new ByteArrayOutputStreamEx(); 517 Transformer tr = xs.getIdentityTransformer(); 518 String defaultEncoding = tr.getOutputProperty(OutputKeys.ENCODING); 519 tr.setOutputProperty(OutputKeys.ENCODING, charset); 520 tr.transform(v, new StreamResult(new OutputStreamWriter(baos,charset))); 521 tr.setOutputProperty(OutputKeys.ENCODING, defaultEncoding); 522 baos.set(bd,"application/xml; charset="+charset); 523 return bd; 524 } catch (TransformerException e) { 525 // TODO: marshaller error handling 526 xs.handleError(e); 527 } catch (UnsupportedEncodingException e) { 528 xs.handleError(e); 529 } 530 531 // error recoverly 532 bd.set(new byte[0],"application/xml"); 533 return bd; 534 } 535 }); 536 secondaryList.add( 537 new StringImpl<XMLGregorianCalendar>(XMLGregorianCalendar.class, 538 createXS("anySimpleType"), 539 DatatypeConstants.DATE, 540 DatatypeConstants.DATETIME, 541 DatatypeConstants.TIME, 542 DatatypeConstants.GMONTH, 543 DatatypeConstants.GDAY, 544 DatatypeConstants.GYEAR, 545 DatatypeConstants.GYEARMONTH, 546 DatatypeConstants.GMONTHDAY 547 ) { 548 public String print(XMLGregorianCalendar cal) { 549 XMLSerializer xs = XMLSerializer.getInstance(); 550 551 QName type = xs.getSchemaType(); 552 if (type != null) { 553 try { 554 checkXmlGregorianCalendarFieldRef(type, cal); 555 String format = xmlGregorianCalendarFormatString.get(type); 556 if (format != null) { 557 return format(format, cal); 558 } 559 } catch (javax.xml.bind.MarshalException e) { 560 // see issue 649 561 xs.handleEvent(new ValidationEventImpl(ValidationEvent.WARNING, e.getMessage(), 562 xs.getCurrentLocation(null) )); 563 return ""; 564 } 565 } 566 return cal.toXMLFormat(); 567 } 568 569 public XMLGregorianCalendar parse(CharSequence lexical) throws SAXException { 570 try { 571 return datatypeFactory.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 for( int 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 for( int 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 primaryList.add( 790 new StringImpl<QName>(QName.class, 791 createXS("QName") 792 ) { 793 public QName parse(CharSequence text) throws SAXException { 794 try { 795 return DatatypeConverterImpl._parseQName(text.toString(),UnmarshallingContext.getInstance()); 796 } catch (IllegalArgumentException e) { 797 UnmarshallingContext.getInstance().handleError(e); 798 return null; 799 } 800 } 801 802 public String print(QName v) { 803 return DatatypeConverterImpl._printQName(v,XMLSerializer.getInstance().getNamespaceContext()); 804 } 805 806 @Override 807 public boolean useNamespace() { 808 return true; 809 } 810 811 @Override 812 public void declareNamespace(QName v, XMLSerializer w) { 813 w.getNamespaceContext().declareNamespace(v.getNamespaceURI(),v.getPrefix(),false); 814 } 815 }); 816 if (System.getProperty(MAP_ANYURI_TO_URI) != null) { 817 primaryList.add( 818 new StringImpl<URI>(URI.class, createXS("anyURI")) { 819 public URI parse(CharSequence text) throws SAXException { 820 try { 821 return new URI(text.toString()); 822 } catch (URISyntaxException e) { 823 UnmarshallingContext.getInstance().handleError(e); 824 return null; 825 } 826 } 827 828 public String print(URI v) { 829 return v.toString(); 830 } 831 }); 832 } 833 primaryList.add( 834 new StringImpl<Duration>(Duration.class, createXS("duration")) { 835 public String print(Duration duration) { 836 return duration.toString(); 837 } 838 839 public Duration parse(CharSequence lexical) { 840 TODO.checkSpec("JSR222 Issue #42"); 841 return datatypeFactory.newDuration(lexical.toString()); 842 } 843 }); 844 primaryList.add( 845 new StringImpl<Void>(Void.class) { 846 // 'void' binding isn't defined by the spec, but when the JAX-RPC processes user-defined 847 // methods like "int actionFoo()", they need this pseudo-void property. 848 849 public String print(Void value) { 850 return ""; 851 } 852 853 public Void parse(CharSequence lexical) { 854 return null; 855 } 856 }); 857 858 List<RuntimeBuiltinLeafInfoImpl<?>> l = new ArrayList<RuntimeBuiltinLeafInfoImpl<?>>(secondaryList.size()+primaryList.size()+1); 859 l.addAll(secondaryList); 860 861 // UUID may fail to load if we are running on JDK 1.4. Handle gracefully 862 try { 863 l.add(new UUIDImpl()); 864 } catch (LinkageError e) { 865 // ignore 866 } 867 868 l.addAll(primaryList); 869 870 builtinBeanInfos = Collections.unmodifiableList(l); 871 } 872 873 private static byte[] decodeBase64(CharSequence text) { 874 if (text instanceof Base64Data) { 875 Base64Data base64Data = (Base64Data) text; 876 return base64Data.getExact(); 877 } else { 878 return DatatypeConverterImpl._parseBase64Binary(text.toString()); 879 } 880 } 881 882 883 /** 884 * Cached instance of {@link DatatypeFactory} to create 885 * {@link XMLGregorianCalendar} and {@link Duration}. 886 */ 887 private static final DatatypeFactory datatypeFactory = init(); 888 889 private static DatatypeFactory init() { 890 try { 891 return DatatypeFactory.newInstance(); 892 } catch (DatatypeConfigurationException e) { 893 throw new Error(Messages.FAILED_TO_INITIALE_DATATYPE_FACTORY.format(),e); 894 } 895 } 896 897 private static void checkXmlGregorianCalendarFieldRef(QName type, 898 XMLGregorianCalendar cal)throws javax.xml.bind.MarshalException{ 899 StringBuilder buf = new StringBuilder(); 900 int bitField = xmlGregorianCalendarFieldRef.get(type); 901 final int l = 0x1; 902 int pos = 0; 903 while (bitField != 0x0){ 904 int bit = bitField & l; 905 bitField >>>= 4; 906 pos++; 907 908 if (bit == 1) { 909 switch(pos){ 910 case 1: 911 if (cal.getSecond() == DatatypeConstants.FIELD_UNDEFINED){ 912 buf.append(" ").append(Messages.XMLGREGORIANCALENDAR_SEC); 913 } 914 break; 915 case 2: 916 if (cal.getMinute() == DatatypeConstants.FIELD_UNDEFINED){ 917 buf.append(" ").append(Messages.XMLGREGORIANCALENDAR_MIN); 918 } 919 break; 920 case 3: 921 if (cal.getHour() == DatatypeConstants.FIELD_UNDEFINED){ 922 buf.append(" ").append(Messages.XMLGREGORIANCALENDAR_HR); 923 } 924 break; 925 case 4: 926 if (cal.getDay() == DatatypeConstants.FIELD_UNDEFINED){ 927 buf.append(" ").append(Messages.XMLGREGORIANCALENDAR_DAY); 928 } 929 break; 930 case 5: 931 if (cal.getMonth() == DatatypeConstants.FIELD_UNDEFINED){ 932 buf.append(" ").append(Messages.XMLGREGORIANCALENDAR_MONTH); 933 } 934 break; 935 case 6: 936 if (cal.getYear() == DatatypeConstants.FIELD_UNDEFINED){ 937 buf.append(" ").append(Messages.XMLGREGORIANCALENDAR_YEAR); 938 } 939 break; 940 case 7: // ignore timezone setting 941 break; 942 } 943 } 944 } 945 if (buf.length() > 0){ 946 throw new javax.xml.bind.MarshalException( 947 Messages.XMLGREGORIANCALENDAR_INVALID.format(type.getLocalPart()) 948 + buf.toString()); 949 } 950 } 951 952 /** 953 * Format string for the {@link XMLGregorianCalendar}. 954 */ 955 private static final Map<QName,String> xmlGregorianCalendarFormatString = new HashMap<QName, String>(); 956 957 static { 958 Map<QName,String> m = xmlGregorianCalendarFormatString; 959 // See 4971612: be careful for SCCS substitution 960 m.put(DatatypeConstants.DATETIME, "%Y-%M-%DT%h:%m:%s"+ "%z"); 961 m.put(DatatypeConstants.DATE, "%Y-%M-%D" +"%z"); 962 m.put(DatatypeConstants.TIME, "%h:%m:%s"+ "%z"); 963 m.put(DatatypeConstants.GMONTH, "--%M--%z"); 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 }