001    /*
002     * @(#)$Id: AbstractMarshallerImpl.java,v 1.21 2003/02/13 23:41:06 kk122374 Exp $
003     *
004     * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved.
005     * 
006     * This software is the proprietary information of Sun Microsystems, Inc.  
007     * Use is subject to license terms.
008     * 
009     */
010    package javax.xml.bind.helpers;
011    
012    import javax.xml.bind.Marshaller;
013    import javax.xml.bind.JAXBException;
014    import javax.xml.bind.PropertyException;
015    import javax.xml.bind.ValidationEventHandler;
016    import javax.xml.transform.stream.StreamResult;
017    import javax.xml.transform.sax.SAXResult;
018    import javax.xml.transform.dom.DOMResult;
019    
020    import java.io.UnsupportedEncodingException;
021    // J2SE1.4 feature
022    // import java.nio.charset.Charset;
023    // import java.nio.charset.UnsupportedCharsetException;
024    
025    /**
026     * Partial default <tt>Marshaller</tt> implementation.
027     * 
028     * <p>
029     * This class provides a partial default implementation for the
030     * {@link javax.xml.bind.Marshaller} interface.
031     * 
032     * <p>
033     * The only method that a JAXB Provider has to implement is
034     * {@link Marshaller#marshal(Object, Result) marshal(Object, javax.xml.transform.Result)}.
035     *
036     * @author <ul><li>Kohsuke Kawaguchi, Sun Microsystems, Inc.</li></ul>
037     * @version $Revision: 1.21 $ $Date: 2003/02/13 23:41:06 $
038     * @see javax.xml.bind.Marshaller
039     * @since JAXB1.0
040     */
041    public abstract class AbstractMarshallerImpl implements Marshaller
042    {
043        /** handler that will be used to process errors and warnings during marshal */
044        private ValidationEventHandler eventHandler = 
045            new DefaultValidationEventHandler();
046        
047        //J2SE1.4 feature
048        //private Charset encoding = null;
049        
050        /** store the value of the encoding property. */
051        private String encoding = "UTF-8";
052        
053        /** store the value of the schemaLocation property. */
054        private String schemaLocation = null;
055    
056        /** store the value of the noNamespaceSchemaLocation property. */
057        private String noNSSchemaLocation = null;
058        
059        /** store the value of the formattedOutput property. */
060        private boolean formattedOutput = false;
061               
062        public final void marshal( Object obj, java.io.OutputStream os ) 
063            throws JAXBException {
064                
065            checkNotNull( obj, "obj", os, "os" );
066            marshal( obj, new StreamResult(os) );
067        }
068        
069        public final void marshal( Object obj, java.io.Writer w ) 
070            throws JAXBException {
071                
072            checkNotNull( obj, "obj", w, "writer" );
073            marshal( obj, new StreamResult(w) );
074        }
075        
076        public final void marshal( Object obj, org.xml.sax.ContentHandler handler ) 
077            throws JAXBException {
078                
079            checkNotNull( obj, "obj", handler, "handler" );
080            marshal( obj, new SAXResult(handler) );
081        }
082        
083        public final void marshal( Object obj, org.w3c.dom.Node node ) 
084            throws JAXBException {
085                
086            checkNotNull( obj, "obj", node, "node" );
087            marshal( obj, new DOMResult(node) );
088        }
089        
090        /**
091         * By default, the getNode method is unsupported and throw
092         * an {@link java.lang.UnsupportedOperationException}.
093         * 
094         * Implementations that choose to support this method must
095         * override this method.
096         */
097        public org.w3c.dom.Node getNode( Object obj ) throws JAXBException {
098            
099            checkNotNull( obj, "obj", Boolean.TRUE, "foo" );
100            
101            throw new UnsupportedOperationException();
102        }
103        
104        /**
105         * Convenience method for getting the current output encoding.
106         * 
107         * @return the current encoding or "UTF-8" if it hasn't been set.
108         */
109        protected String getEncoding() {
110            return encoding;
111        }
112        
113        /**
114         * Convenience method for setting the output encoding.
115         * 
116         * @param encoding a valid encoding as specified in the Marshaller class 
117         * documentation
118         */
119        protected void setEncoding( String encoding ) {
120            this.encoding = encoding;
121        }
122        
123        /**
124         * Convenience method for getting the current schemaLocation.
125         * 
126         * @return the current schemaLocation or null if it hasn't been set
127         */
128        protected String getSchemaLocation() {
129            return schemaLocation;
130        }
131        
132        /**
133         * Convenience method for setting the schemaLocation.
134         * 
135         * @param location the schemaLocation value
136         */
137        protected void setSchemaLocation( String location ) {
138            schemaLocation = location;
139        }
140        
141        /**
142         * Convenience method for getting the current noNamespaceSchemaLocation.
143         * 
144         * @return the current noNamespaceSchemaLocation or null if it hasn't
145         * been set
146         */
147        protected String getNoNSSchemaLocation() {
148            return noNSSchemaLocation;
149        }
150        
151        /**
152         * Convenience method for setting the noNamespaceSchemaLocation.
153         * 
154         * @param location the noNamespaceSchemaLocation value
155         */
156        protected void setNoNSSchemaLocation( String location ) {
157            noNSSchemaLocation = location;
158        }
159        
160        /**
161         * Convenience method for getting the formatted output flag.
162         * 
163         * @return the current value of the formatted output flag or false if
164         * it hasn't been set.
165         */
166        protected boolean isFormattedOutput() {
167            return formattedOutput;
168        }
169        
170        /**
171         * Convenience method for setting the formatted output flag.
172         * 
173         * @param v value of the formatted output flag.
174         */
175        protected void setFormattedOutput( boolean v ) {
176            formattedOutput = v;
177        }
178        
179        
180        static String[] aliases = {
181            "UTF-8", "UTF8",
182            "UTF-16", "Unicode",
183            "UTF-16BE", "UnicodeBigUnmarked",
184            "UTF-16LE", "UnicodeLittleUnmarked",
185            "US-ASCII", "ASCII",
186            "TIS-620", "TIS620",
187            
188            // taken from the project-X parser
189            "ISO-10646-UCS-2", "Unicode",
190        
191            "EBCDIC-CP-US", "cp037",
192            "EBCDIC-CP-CA", "cp037",
193            "EBCDIC-CP-NL", "cp037",
194            "EBCDIC-CP-WT", "cp037",
195        
196            "EBCDIC-CP-DK", "cp277",
197            "EBCDIC-CP-NO", "cp277",
198            "EBCDIC-CP-FI", "cp278",
199            "EBCDIC-CP-SE", "cp278",
200        
201            "EBCDIC-CP-IT", "cp280",
202            "EBCDIC-CP-ES", "cp284",
203            "EBCDIC-CP-GB", "cp285",
204            "EBCDIC-CP-FR", "cp297",
205        
206            "EBCDIC-CP-AR1", "cp420",
207            "EBCDIC-CP-HE", "cp424",
208            "EBCDIC-CP-BE", "cp500",
209            "EBCDIC-CP-CH", "cp500",
210        
211            "EBCDIC-CP-ROECE", "cp870",
212            "EBCDIC-CP-YU", "cp870",
213            "EBCDIC-CP-IS", "cp871",
214            "EBCDIC-CP-AR2", "cp918",
215            
216            // IANA also defines two that JDK 1.2 doesn't handle:
217            //  EBCDIC-CP-GR        --> CP423
218            //  EBCDIC-CP-TR        --> CP905
219        };
220        
221        /**
222         * Gets the corresponding Java encoding name from an IANA name.
223         * 
224         * This method is a helper method for the derived class to convert
225         * encoding names.
226         * 
227         * @exception UnsupportedEncodingException
228         *      If this implementation couldn't find the Java encoding name.
229         */
230        protected String getJavaEncoding( String encoding ) throws UnsupportedEncodingException {
231            try {
232                "1".getBytes(encoding);
233                return encoding;
234            } catch( UnsupportedEncodingException e ) {
235                // try known alias
236                for( int i=0; i<aliases.length; i+=2 ) {
237                    if(encoding.equals(aliases[i])) {
238                        "1".getBytes(aliases[i+1]);
239                        return aliases[i+1];
240                    }
241                }
242                
243                throw new UnsupportedEncodingException(encoding);
244            }
245            /* J2SE1.4 feature
246            try {
247                this.encoding = Charset.forName( _encoding );
248            } catch( UnsupportedCharsetException uce ) {
249                throw new JAXBException( uce );
250            }
251             */
252        }
253        
254        /**
255         * Default implementation of the setProperty method handles
256         * the four defined properties in Marshaller. If a provider 
257         * needs to handle additional properties, it should override 
258         * this method in a derived class.
259         */
260        public void setProperty( String name, Object value )
261            throws PropertyException {
262            
263            if( name == null ) {
264                throw new IllegalArgumentException( 
265                    Messages.format( Messages.MUST_NOT_BE_NULL, "name" ) );
266            }
267            
268            // recognize and handle four pre-defined properties.
269            if( JAXB_ENCODING.equals(name) ) {
270                checkString( name, value );
271                setEncoding( (String)value );
272                return;
273            }
274            if( JAXB_FORMATTED_OUTPUT.equals(name) ) {
275                checkBoolean( name, value );                    
276                setFormattedOutput( ((Boolean)value).booleanValue() );
277                return;
278            }
279            if( JAXB_NO_NAMESPACE_SCHEMA_LOCATION.equals(name) ) {
280                checkString( name, value );
281                setNoNSSchemaLocation( (String)value );
282                return;
283            }
284            if( JAXB_SCHEMA_LOCATION.equals(name) ) {
285                checkString( name, value );
286                setSchemaLocation( (String)value );
287                return;
288            }
289            
290            throw new PropertyException(name, value);
291        }
292        
293        /**
294         * Default implementation of the getProperty method handles
295         * the four defined properties in Marshaller.  If a provider 
296         * needs to support additional provider specific properties, 
297         * it should override this method in a derived class.
298         */
299        public Object getProperty( String name )
300            throws PropertyException {
301                
302            if( name == null ) {
303                throw new IllegalArgumentException( 
304                    Messages.format( Messages.MUST_NOT_BE_NULL, "name" ) );
305            }
306            
307            // recognize and handle four pre-defined properties.
308            if( JAXB_ENCODING.equals(name) )
309                return getEncoding();
310            if( JAXB_FORMATTED_OUTPUT.equals(name) )
311                return isFormattedOutput()?Boolean.TRUE:Boolean.FALSE;
312            if( JAXB_NO_NAMESPACE_SCHEMA_LOCATION.equals(name) )
313                return getNoNSSchemaLocation();
314            if( JAXB_SCHEMA_LOCATION.equals(name) )
315                return getSchemaLocation();
316                       
317            throw new PropertyException(name);
318        }
319        /**
320         * @see javax.xml.bind.Marshaller#getEventHandler()
321         */
322        public ValidationEventHandler getEventHandler() throws JAXBException {
323            return eventHandler;
324        }
325    
326        /**
327         * @see javax.xml.bind.Marshaller#setEventHandler(ValidationEventHandler)
328         */
329        public void setEventHandler(ValidationEventHandler handler)
330            throws JAXBException {
331            
332            if( handler == null ) {
333                eventHandler = new DefaultValidationEventHandler();
334            } else {
335                eventHandler = handler;
336            }
337        }
338    
339    
340    
341    
342        /*
343         * assert that the given object is a Boolean
344         */
345        private void checkBoolean( String name, Object value ) throws PropertyException {
346            if(!(value instanceof Boolean))
347                throw new PropertyException(
348                    Messages.format( Messages.MUST_BE_BOOLEAN, name ) );
349        }
350        
351        /*
352         * assert that the given object is a String
353         */
354        private void checkString( String name, Object value ) throws PropertyException {
355            if(!(value instanceof String))
356                throw new PropertyException(
357                    Messages.format( Messages.MUST_BE_STRING, name ) );
358        }
359        
360        /*
361         * assert that the parameters are not null
362         */
363        private void checkNotNull( Object o1, String o1Name,
364                                   Object o2, String o2Name ) {
365        
366            if( o1 == null ) {
367                throw new IllegalArgumentException( 
368                    Messages.format( Messages.MUST_NOT_BE_NULL, o1Name ) );
369            }
370            if( o2 == null ) {
371                throw new IllegalArgumentException( 
372                    Messages.format( Messages.MUST_NOT_BE_NULL, o2Name ) );
373            }
374        }
375    }