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