001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: SerializerFactory.java 468654 2006-10-28 07:09:23Z minchau $
020     */
021    package org.apache.xml.serializer;
022    
023    import java.util.Hashtable;
024    import java.util.Properties;
025    
026    import javax.xml.transform.OutputKeys;
027    
028    import org.apache.xml.serializer.utils.MsgKey;
029    import org.apache.xml.serializer.utils.Utils;
030    import org.xml.sax.ContentHandler;
031    
032    /**
033     * This class is a public API, it is a factory for creating serializers.
034       * 
035       * The properties object passed to the getSerializer() method should be created by
036       * the OutputPropertiesFactory. Although the properties object
037       * used to create a serializer does not need to be obtained 
038       * from OutputPropertiesFactory,
039       * using this factory ensures that the default key/value properties
040       * are set for the given output "method".
041       * 
042       * <p>
043       * The standard property keys supported are: "method", "version", "encoding",
044       * "omit-xml-declaration", "standalone", doctype-public",
045       * "doctype-system", "cdata-section-elements", "indent", "media-type". 
046       * These property keys and their values are described in the XSLT recommendation,
047       * see {@link <a href="http://www.w3.org/TR/1999/REC-xslt-19991116"> XSLT 1.0 recommendation</a>}
048       * 
049       * <p>
050       * The value of the "cdata-section-elements" property key is a whitespace
051       * separated list of elements. If the element is in a namespace then 
052       * value is passed in this format: {uri}localName 
053       *
054       * <p>
055       * The non-standard property keys supported are defined in {@link OutputPropertiesFactory}.
056       *
057       * @see OutputPropertiesFactory
058       * @see Method
059       * @see Serializer
060       */
061    public final class SerializerFactory
062    {
063      /**
064       * This constructor is private just to prevent the creation of such an object.
065       */
066    
067      private SerializerFactory() {
068     
069      }
070      /**
071       * Associates output methods to default output formats.
072       */
073      private static Hashtable m_formats = new Hashtable();
074    
075      /**
076       * Returns a serializer for the specified output method. The output method
077       * is specified by the value of the property associated with the "method" key.
078       * If no implementation exists that supports the specified output method
079       * an exception of some type will be thrown.
080       * For a list of the output "method" key values see {@link Method}.
081       *
082       * @param format The output format, minimally the "method" property must be set.
083       * @return A suitable serializer.
084       * @throws IllegalArgumentException if method is
085       * null or an appropriate serializer can't be found
086       * @throws Exception if the class for the serializer is found but does not
087       * implement ContentHandler.
088       * @throws WrappedRuntimeException if an exception is thrown while trying to find serializer
089       */
090      public static Serializer getSerializer(Properties format)
091      {
092          Serializer ser;
093    
094          try
095          {
096            String method = format.getProperty(OutputKeys.METHOD);
097    
098            if (method == null) {
099                String msg = Utils.messages.createMessage(
100                    MsgKey.ER_FACTORY_PROPERTY_MISSING,
101                    new Object[] { OutputKeys.METHOD});
102                throw new IllegalArgumentException(msg);
103            }
104    
105            String className =
106                format.getProperty(OutputPropertiesFactory.S_KEY_CONTENT_HANDLER);
107    
108    
109            if (null == className)
110            {
111                // Missing Content Handler property, load default using OutputPropertiesFactory
112                Properties methodDefaults =
113                    OutputPropertiesFactory.getDefaultMethodProperties(method);
114                className = 
115                methodDefaults.getProperty(OutputPropertiesFactory.S_KEY_CONTENT_HANDLER);
116                if (null == className) {
117                    String msg = Utils.messages.createMessage(
118                        MsgKey.ER_FACTORY_PROPERTY_MISSING,
119                        new Object[] { OutputPropertiesFactory.S_KEY_CONTENT_HANDLER});
120                    throw new IllegalArgumentException(msg);
121                }
122    
123            }
124    
125    
126    
127            ClassLoader loader = ObjectFactory.findClassLoader();
128    
129            Class cls = ObjectFactory.findProviderClass(className, loader, true);
130    
131            // _serializers.put(method, cls);
132    
133            Object obj = cls.newInstance();
134    
135            if (obj instanceof SerializationHandler)
136            {
137                  // this is one of the supplied serializers
138                ser = (Serializer) cls.newInstance();
139                ser.setOutputFormat(format);
140            }
141            else
142            {
143                  /*
144                   *  This  must be a user defined Serializer.
145                   *  It had better implement ContentHandler.
146                   */
147                   if (obj instanceof ContentHandler)
148                   {
149    
150                      /*
151                       * The user defined serializer defines ContentHandler,
152                       * but we need to wrap it with ToXMLSAXHandler which
153                       * will collect SAX-like events and emit true
154                       * SAX ContentHandler events to the users handler.
155                       */
156                      className = SerializerConstants.DEFAULT_SAX_SERIALIZER;
157                      cls = ObjectFactory.findProviderClass(className, loader, true);
158                      SerializationHandler sh =
159                          (SerializationHandler) cls.newInstance();
160                      sh.setContentHandler( (ContentHandler) obj);
161                      sh.setOutputFormat(format);
162    
163                      ser = sh;
164                   }
165                   else
166                   {
167                      // user defined serializer does not implement
168                      // ContentHandler, ... very bad
169                       throw new Exception(
170                           Utils.messages.createMessage(
171                               MsgKey.ER_SERIALIZER_NOT_CONTENTHANDLER,
172                                   new Object[] { className}));
173                   }
174    
175            }
176          }
177          catch (Exception e)
178          {
179            throw new org.apache.xml.serializer.utils.WrappedRuntimeException(e);
180          }
181    
182          // If we make it to here ser is not null.
183          return ser;
184      }
185    }