1 /*
   2  * Copyright (c) 2003, 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 javax.xml.bind.helpers;
  27 
  28 import javax.xml.bind.JAXBException;
  29 import javax.xml.bind.Marshaller;
  30 import javax.xml.bind.PropertyException;
  31 import javax.xml.bind.ValidationEventHandler;
  32 import javax.xml.bind.annotation.adapters.XmlAdapter;
  33 import javax.xml.bind.attachment.AttachmentMarshaller;
  34 import javax.xml.stream.XMLEventWriter;
  35 import javax.xml.stream.XMLStreamWriter;
  36 import javax.xml.transform.dom.DOMResult;
  37 import javax.xml.transform.sax.SAXResult;
  38 import javax.xml.transform.stream.StreamResult;
  39 import javax.xml.validation.Schema;
  40 import java.io.UnsupportedEncodingException;
  41 import java.io.File;
  42 import java.io.OutputStream;
  43 import java.io.FileOutputStream;
  44 import java.io.BufferedOutputStream;
  45 import java.io.IOException;
  46 // J2SE1.4 feature
  47 // import java.nio.charset.Charset;
  48 // import java.nio.charset.UnsupportedCharsetException;
  49 
  50 /**
  51  * Partial default <tt>Marshaller</tt> implementation.
  52  *
  53  * <p>
  54  * This class provides a partial default implementation for the
  55  * {@link javax.xml.bind.Marshaller} interface.
  56  *
  57  * <p>
  58  * The only methods that a JAXB Provider has to implement are
  59  * {@link Marshaller#marshal(Object, javax.xml.transform.Result) marshal(Object, javax.xml.transform.Result)},
  60  * {@link Marshaller#marshal(Object, javax.xml.transform.Result) marshal(Object, javax.xml.stream.XMLStreamWriter)}, and
  61  * {@link Marshaller#marshal(Object, javax.xml.transform.Result) marshal(Object, javax.xml.stream.XMLEventWriter)}.
  62  *
  63  * @author <ul><li>Kohsuke Kawaguchi, Sun Microsystems, Inc.</li></ul>
  64  * @see javax.xml.bind.Marshaller
  65  * @since 1.6, JAXB 1.0
  66  */
  67 public abstract class AbstractMarshallerImpl implements Marshaller
  68 {
  69     /** handler that will be used to process errors and warnings during marshal */
  70     private ValidationEventHandler eventHandler =
  71         new DefaultValidationEventHandler();
  72 
  73     //J2SE1.4 feature
  74     //private Charset encoding = null;
  75 
  76     /** store the value of the encoding property. */
  77     private String encoding = "UTF-8";
  78 
  79     /** store the value of the schemaLocation property. */
  80     private String schemaLocation = null;
  81 
  82     /** store the value of the noNamespaceSchemaLocation property. */
  83     private String noNSSchemaLocation = null;
  84 
  85     /** store the value of the formattedOutput property. */
  86     private boolean formattedOutput = false;
  87 
  88     /** store the value of the fragment property. */
  89     private boolean fragment = false;
  90 
  91     public final void marshal( Object obj, java.io.OutputStream os )
  92         throws JAXBException {
  93 
  94         checkNotNull( obj, "obj", os, "os" );
  95         marshal( obj, new StreamResult(os) );
  96     }
  97 
  98     public void marshal(Object jaxbElement, File output) throws JAXBException {
  99         checkNotNull(jaxbElement, "jaxbElement", output, "output" );
 100         try {
 101             OutputStream os = new BufferedOutputStream(new FileOutputStream(output));
 102             try {
 103                 marshal( jaxbElement, new StreamResult(os) );
 104             } finally {
 105                 os.close();
 106             }
 107         } catch (IOException e) {
 108             throw new JAXBException(e);
 109         }
 110     }
 111 
 112     public final void marshal( Object obj, java.io.Writer w )
 113         throws JAXBException {
 114 
 115         checkNotNull( obj, "obj", w, "writer" );
 116         marshal( obj, new StreamResult(w) );
 117     }
 118 
 119     public final void marshal( Object obj, org.xml.sax.ContentHandler handler )
 120         throws JAXBException {
 121 
 122         checkNotNull( obj, "obj", handler, "handler" );
 123         marshal( obj, new SAXResult(handler) );
 124     }
 125 
 126     public final void marshal( Object obj, org.w3c.dom.Node node )
 127         throws JAXBException {
 128 
 129         checkNotNull( obj, "obj", node, "node" );
 130         marshal( obj, new DOMResult(node) );
 131     }
 132 
 133     /**
 134      * By default, the getNode method is unsupported and throw
 135      * an {@link java.lang.UnsupportedOperationException}.
 136      *
 137      * Implementations that choose to support this method must
 138      * override this method.
 139      */
 140     public org.w3c.dom.Node getNode( Object obj ) throws JAXBException {
 141 
 142         checkNotNull( obj, "obj", Boolean.TRUE, "foo" );
 143 
 144         throw new UnsupportedOperationException();
 145     }
 146 
 147     /**
 148      * Convenience method for getting the current output encoding.
 149      *
 150      * @return the current encoding or "UTF-8" if it hasn't been set.
 151      */
 152     protected String getEncoding() {
 153         return encoding;
 154     }
 155 
 156     /**
 157      * Convenience method for setting the output encoding.
 158      *
 159      * @param encoding a valid encoding as specified in the Marshaller class
 160      * documentation
 161      */
 162     protected void setEncoding( String encoding ) {
 163         this.encoding = encoding;
 164     }
 165 
 166     /**
 167      * Convenience method for getting the current schemaLocation.
 168      *
 169      * @return the current schemaLocation or null if it hasn't been set
 170      */
 171     protected String getSchemaLocation() {
 172         return schemaLocation;
 173     }
 174 
 175     /**
 176      * Convenience method for setting the schemaLocation.
 177      *
 178      * @param location the schemaLocation value
 179      */
 180     protected void setSchemaLocation( String location ) {
 181         schemaLocation = location;
 182     }
 183 
 184     /**
 185      * Convenience method for getting the current noNamespaceSchemaLocation.
 186      *
 187      * @return the current noNamespaceSchemaLocation or null if it hasn't
 188      * been set
 189      */
 190     protected String getNoNSSchemaLocation() {
 191         return noNSSchemaLocation;
 192     }
 193 
 194     /**
 195      * Convenience method for setting the noNamespaceSchemaLocation.
 196      *
 197      * @param location the noNamespaceSchemaLocation value
 198      */
 199     protected void setNoNSSchemaLocation( String location ) {
 200         noNSSchemaLocation = location;
 201     }
 202 
 203     /**
 204      * Convenience method for getting the formatted output flag.
 205      *
 206      * @return the current value of the formatted output flag or false if
 207      * it hasn't been set.
 208      */
 209     protected boolean isFormattedOutput() {
 210         return formattedOutput;
 211     }
 212 
 213     /**
 214      * Convenience method for setting the formatted output flag.
 215      *
 216      * @param v value of the formatted output flag.
 217      */
 218     protected void setFormattedOutput( boolean v ) {
 219         formattedOutput = v;
 220     }
 221 
 222 
 223     /**
 224      * Convenience method for getting the fragment flag.
 225      *
 226      * @return the current value of the fragment flag or false if
 227      * it hasn't been set.
 228      */
 229     protected boolean isFragment() {
 230         return fragment;
 231     }
 232 
 233     /**
 234      * Convenience method for setting the fragment flag.
 235      *
 236      * @param v value of the fragment flag.
 237      */
 238     protected void setFragment( boolean v ) {
 239         fragment = v;
 240     }
 241 
 242 
 243     static String[] aliases = {
 244         "UTF-8", "UTF8",
 245         "UTF-16", "Unicode",
 246         "UTF-16BE", "UnicodeBigUnmarked",
 247         "UTF-16LE", "UnicodeLittleUnmarked",
 248         "US-ASCII", "ASCII",
 249         "TIS-620", "TIS620",
 250 
 251         // taken from the project-X parser
 252         "ISO-10646-UCS-2", "Unicode",
 253 
 254         "EBCDIC-CP-US", "cp037",
 255         "EBCDIC-CP-CA", "cp037",
 256         "EBCDIC-CP-NL", "cp037",
 257         "EBCDIC-CP-WT", "cp037",
 258 
 259         "EBCDIC-CP-DK", "cp277",
 260         "EBCDIC-CP-NO", "cp277",
 261         "EBCDIC-CP-FI", "cp278",
 262         "EBCDIC-CP-SE", "cp278",
 263 
 264         "EBCDIC-CP-IT", "cp280",
 265         "EBCDIC-CP-ES", "cp284",
 266         "EBCDIC-CP-GB", "cp285",
 267         "EBCDIC-CP-FR", "cp297",
 268 
 269         "EBCDIC-CP-AR1", "cp420",
 270         "EBCDIC-CP-HE", "cp424",
 271         "EBCDIC-CP-BE", "cp500",
 272         "EBCDIC-CP-CH", "cp500",
 273 
 274         "EBCDIC-CP-ROECE", "cp870",
 275         "EBCDIC-CP-YU", "cp870",
 276         "EBCDIC-CP-IS", "cp871",
 277         "EBCDIC-CP-AR2", "cp918",
 278 
 279         // IANA also defines two that JDK 1.2 doesn't handle:
 280         //  EBCDIC-CP-GR        --> CP423
 281         //  EBCDIC-CP-TR        --> CP905
 282     };
 283 
 284     /**
 285      * Gets the corresponding Java encoding name from an IANA name.
 286      *
 287      * This method is a helper method for the derived class to convert
 288      * encoding names.
 289      *
 290      * @exception UnsupportedEncodingException
 291      *      If this implementation couldn't find the Java encoding name.
 292      */
 293     protected String getJavaEncoding( String encoding ) throws UnsupportedEncodingException {
 294         try {
 295             "1".getBytes(encoding);
 296             return encoding;
 297         } catch( UnsupportedEncodingException e ) {
 298             // try known alias
 299             for( int i=0; i<aliases.length; i+=2 ) {
 300                 if(encoding.equals(aliases[i])) {
 301                     "1".getBytes(aliases[i+1]);
 302                     return aliases[i+1];
 303                 }
 304             }
 305 
 306             throw new UnsupportedEncodingException(encoding);
 307         }
 308         /* J2SE1.4 feature
 309         try {
 310             this.encoding = Charset.forName( _encoding );
 311         } catch( UnsupportedCharsetException uce ) {
 312             throw new JAXBException( uce );
 313         }
 314          */
 315     }
 316 
 317     /**
 318      * Default implementation of the setProperty method handles
 319      * the four defined properties in Marshaller. If a provider
 320      * needs to handle additional properties, it should override
 321      * this method in a derived class.
 322      */
 323     public void setProperty( String name, Object value )
 324         throws PropertyException {
 325 
 326         if( name == null ) {
 327             throw new IllegalArgumentException(
 328                 Messages.format( Messages.MUST_NOT_BE_NULL, "name" ) );
 329         }
 330 
 331         // recognize and handle four pre-defined properties.
 332         if( JAXB_ENCODING.equals(name) ) {
 333             checkString( name, value );
 334             setEncoding( (String)value );
 335             return;
 336         }
 337         if( JAXB_FORMATTED_OUTPUT.equals(name) ) {
 338             checkBoolean( name, value );
 339             setFormattedOutput((Boolean) value );
 340             return;
 341         }
 342         if( JAXB_NO_NAMESPACE_SCHEMA_LOCATION.equals(name) ) {
 343             checkString( name, value );
 344             setNoNSSchemaLocation( (String)value );
 345             return;
 346         }
 347         if( JAXB_SCHEMA_LOCATION.equals(name) ) {
 348             checkString( name, value );
 349             setSchemaLocation( (String)value );
 350             return;
 351         }
 352         if( JAXB_FRAGMENT.equals(name) )  {
 353             checkBoolean(name, value);
 354             setFragment((Boolean) value );
 355             return;
 356         }
 357 
 358         throw new PropertyException(name, value);
 359     }
 360 
 361     /**
 362      * Default implementation of the getProperty method handles
 363      * the four defined properties in Marshaller.  If a provider
 364      * needs to support additional provider specific properties,
 365      * it should override this method in a derived class.
 366      */
 367     public Object getProperty( String name )
 368         throws PropertyException {
 369 
 370         if( name == null ) {
 371             throw new IllegalArgumentException(
 372                 Messages.format( Messages.MUST_NOT_BE_NULL, "name" ) );
 373         }
 374 
 375         // recognize and handle four pre-defined properties.
 376         if( JAXB_ENCODING.equals(name) )
 377             return getEncoding();
 378         if( JAXB_FORMATTED_OUTPUT.equals(name) )
 379             return isFormattedOutput()?Boolean.TRUE:Boolean.FALSE;
 380         if( JAXB_NO_NAMESPACE_SCHEMA_LOCATION.equals(name) )
 381             return getNoNSSchemaLocation();
 382         if( JAXB_SCHEMA_LOCATION.equals(name) )
 383             return getSchemaLocation();
 384         if( JAXB_FRAGMENT.equals(name) )
 385             return isFragment()?Boolean.TRUE:Boolean.FALSE;
 386 
 387         throw new PropertyException(name);
 388     }
 389     /**
 390      * @see javax.xml.bind.Marshaller#getEventHandler()
 391      */
 392     public ValidationEventHandler getEventHandler() throws JAXBException {
 393         return eventHandler;
 394     }
 395 
 396     /**
 397      * @see javax.xml.bind.Marshaller#setEventHandler(ValidationEventHandler)
 398      */
 399     public void setEventHandler(ValidationEventHandler handler)
 400         throws JAXBException {
 401 
 402         if( handler == null ) {
 403             eventHandler = new DefaultValidationEventHandler();
 404         } else {
 405             eventHandler = handler;
 406         }
 407     }
 408 
 409 
 410 
 411 
 412     /*
 413      * assert that the given object is a Boolean
 414      */
 415     private void checkBoolean( String name, Object value ) throws PropertyException {
 416         if(!(value instanceof Boolean))
 417             throw new PropertyException(
 418                 Messages.format( Messages.MUST_BE_BOOLEAN, name ) );
 419     }
 420 
 421     /*
 422      * assert that the given object is a String
 423      */
 424     private void checkString( String name, Object value ) throws PropertyException {
 425         if(!(value instanceof String))
 426             throw new PropertyException(
 427                 Messages.format( Messages.MUST_BE_STRING, name ) );
 428     }
 429 
 430     /*
 431      * assert that the parameters are not null
 432      */
 433     private void checkNotNull( Object o1, String o1Name,
 434                                Object o2, String o2Name ) {
 435 
 436         if( o1 == null ) {
 437             throw new IllegalArgumentException(
 438                 Messages.format( Messages.MUST_NOT_BE_NULL, o1Name ) );
 439         }
 440         if( o2 == null ) {
 441             throw new IllegalArgumentException(
 442                 Messages.format( Messages.MUST_NOT_BE_NULL, o2Name ) );
 443         }
 444     }
 445 
 446     public void marshal(Object obj, XMLEventWriter writer)
 447         throws JAXBException {
 448 
 449         throw new UnsupportedOperationException();
 450     }
 451 
 452     public void marshal(Object obj, XMLStreamWriter writer)
 453         throws JAXBException {
 454 
 455         throw new UnsupportedOperationException();
 456     }
 457 
 458     public void setSchema(Schema schema) {
 459         throw new UnsupportedOperationException();
 460     }
 461 
 462     public Schema getSchema() {
 463         throw new UnsupportedOperationException();
 464     }
 465 
 466     public void setAdapter(XmlAdapter adapter) {
 467         if(adapter==null)
 468             throw new IllegalArgumentException();
 469         setAdapter((Class)adapter.getClass(),adapter);
 470     }
 471 
 472     public <A extends XmlAdapter> void setAdapter(Class<A> type, A adapter) {
 473         throw new UnsupportedOperationException();
 474     }
 475 
 476     public <A extends XmlAdapter> A getAdapter(Class<A> type) {
 477         throw new UnsupportedOperationException();
 478     }
 479 
 480     public void setAttachmentMarshaller(AttachmentMarshaller am) {
 481         throw new UnsupportedOperationException();
 482     }
 483 
 484     public AttachmentMarshaller getAttachmentMarshaller() {
 485         throw new UnsupportedOperationException();
 486     }
 487 
 488     public void setListener(Listener listener) {
 489         throw new UnsupportedOperationException();
 490     }
 491 
 492     public Listener getListener() {
 493         throw new UnsupportedOperationException();
 494     }
 495 }