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