001/* XMLOutputFactory.java --
002   Copyright (C) 2005,2006,2009  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038package javax.xml.stream;
039
040import java.io.BufferedReader;
041import java.io.File;
042import java.io.FileInputStream;
043import java.io.InputStream;
044import java.io.InputStreamReader;
045import java.io.IOException;
046import java.io.OutputStream;
047import java.io.Writer;
048import java.util.Properties;
049import javax.xml.transform.Result;
050
051/**
052 * Factory for obtaining XML stream and event writers for various kinds of
053 * output sink.
054 * <h3>Configuration</h3>
055 * <table>
056 * <tr>
057 * <th>Name</th>
058 * <th>Description</th>
059 * <th>Type</th>
060 * <th>Default</th>
061 * <th>Required</th>
062 * </tr>
063 * <tr>
064 * <td>javax.xml.stream.isRepairingNamespaces</td>
065 * <td>default namespace prefixes</td>
066 * <td>Boolean</td>
067 * <td>Boolean.FALSE</td>
068 * <td>yes</td>
069 * </tr>
070 * </table>
071 */
072public abstract class XMLOutputFactory
073{
074
075  /**
076   * Property used to control whether to default namespace prefixes.
077   * If true, the writer will create a namespace declaration for any
078   * attribute that doesn't have a namespace declaration in scope.
079   */
080  public static final java.lang.String IS_REPAIRING_NAMESPACES =
081    "javax.xml.stream.isRepairingNamespaces";
082
083  protected XMLOutputFactory()
084  {
085  }
086
087  /**
088   * Creates a new <b>output</b> factory.
089   * @see #newInstance(String,ClassLoader)
090   */
091  public static XMLOutputFactory newInstance()
092    throws FactoryConfigurationError
093  {
094    return newInstance(null, null);
095  }
096
097  /**
098   * Creates a new <b>output</b> factory.
099   * The implementation class to load is the first found in the following
100   * locations:
101   * <ol>
102   * <li>the <code>javax.xml.stream.XMLOutputFactory</code> system
103   * property</li>
104   * <li>the above named property value in the
105   * <code><i>$JAVA_HOME</i>/lib/stax.properties</code> file</li>
106   * <li>the class name specified in the
107   * <code>META-INF/services/javax.xml.stream.XMLOutputFactory</code>
108   * system resource</li>
109   * <li>the default factory class</li>
110   * </ol>
111   * @param factoryId the name of the factory, same as the property
112   * @param classLoader the class loader to use
113   * @return a new factory instance
114   * @exception FactoryConfigurationError if an instance of this factory
115   * could not be loaded
116   */
117  public static XMLOutputFactory newInstance(String factoryId,
118                                             ClassLoader classLoader)
119    throws FactoryConfigurationError
120  {
121    if (classLoader == null)
122      {
123        classLoader = Thread.currentThread().getContextClassLoader();
124      }
125    if (classLoader == null)
126      {
127        classLoader = XMLOutputFactory.class.getClassLoader();
128      }
129    String className = null;
130    int count = 0;
131    do
132      {
133        className = getFactoryClassName(classLoader, count++);
134        if (className != null)
135          {
136            try
137              {
138                Class<?> t = (classLoader != null) ?
139                  classLoader.loadClass(className) :
140                  Class.forName(className);
141                return (XMLOutputFactory) t.newInstance();
142              }
143            catch (ClassNotFoundException e)
144              {
145                className = null;
146              }
147            catch (Exception e)
148              {
149                throw new FactoryConfigurationError(e,
150                     "error instantiating class " + className);
151              }
152          }
153      }
154    while (className == null && count < 3);
155    return new gnu.xml.stream.XMLOutputFactoryImpl();
156  }
157
158  private static String getFactoryClassName(ClassLoader loader, int attempt)
159  {
160    final String propertyName = "javax.xml.stream.XMLOutputFactory";
161    switch (attempt)
162      {
163        case 0:
164          return System.getProperty(propertyName);
165        case 1:
166          try
167            {
168              File file = new File(System.getProperty("java.home"));
169              file = new File(file, "lib");
170              file = new File(file, "stax.properties");
171              InputStream in = new FileInputStream(file);
172              Properties props = new Properties();
173              props.load(in);
174              in.close();
175              return props.getProperty(propertyName);
176            }
177          catch (IOException e)
178            {
179              return null;
180            }
181        case 2:
182          try
183            {
184              String serviceKey = "/META-INF/services/" + propertyName;
185              InputStream in = (loader != null) ?
186                 loader.getResourceAsStream(serviceKey) :
187                XMLOutputFactory.class.getResourceAsStream(serviceKey);
188              if (in != null)
189                {
190                  BufferedReader r =
191                     new BufferedReader(new InputStreamReader(in));
192                  String ret = r.readLine();
193                  r.close();
194                  return ret;
195                }
196            }
197          catch (IOException e)
198            {
199            }
200          return null;
201        default:
202          return null;
203      }
204  }
205
206  /**
207   * Creates a new stream writer.
208   */
209  public abstract XMLStreamWriter createXMLStreamWriter(Writer stream)
210    throws XMLStreamException;
211
212  /**
213   * Creates a new stream writer.
214   */
215  public abstract XMLStreamWriter createXMLStreamWriter(OutputStream stream)
216    throws XMLStreamException;
217
218  /**
219   * Creates a new stream writer.
220   */
221  public abstract XMLStreamWriter createXMLStreamWriter(OutputStream stream,
222                                                        String encoding)
223    throws XMLStreamException;
224
225  /**
226   * Creates a new stream writer.
227   * @exception UnsupportedOperationException if this method is not
228   * supported
229   */
230  public abstract XMLStreamWriter createXMLStreamWriter(Result result)
231    throws XMLStreamException;
232
233  /**
234   * Creates a new event writer.
235   * @exception UnsupportedOperationException if this method is not
236   * supported
237   */
238  public abstract XMLEventWriter createXMLEventWriter(Result result)
239    throws XMLStreamException;
240
241  /**
242   * Creates a new event writer.
243   */
244  public abstract XMLEventWriter createXMLEventWriter(OutputStream stream)
245    throws XMLStreamException;
246
247  /**
248   * Creates a new event writer.
249   */
250  public abstract XMLEventWriter createXMLEventWriter(OutputStream stream,
251                                                      String encoding)
252    throws XMLStreamException;
253
254  /**
255   * Creates a new event writer.
256   */
257  public abstract XMLEventWriter createXMLEventWriter(Writer stream)
258    throws XMLStreamException;
259
260  /**
261   * Sets the implementation-specific property of the given name.
262   * @exception IllegalArgumentException if the property is not supported
263   */
264  public abstract void setProperty(String name, Object value)
265    throws IllegalArgumentException;
266
267  /**
268   * Returns the implementation-specific property of the given name.
269   * @exception IllegalArgumentException if the property is not supported
270   */
271  public abstract Object getProperty(String name)
272    throws IllegalArgumentException;
273
274  /**
275   * Indicates whether the specified property is supported.
276   */
277  public abstract boolean isPropertySupported(String name);
278
279}