1 /*
2  * Copyright (c) 2005, 2019, 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 com.sun.xml.internal.stream;
27 
28 import java.io.IOException;
29 import java.io.OutputStream;
30 import java.io.Writer;
31 import javax.xml.stream.XMLEventWriter;
32 import javax.xml.stream.XMLOutputFactory;
33 import javax.xml.stream.XMLStreamException;
34 import javax.xml.stream.XMLStreamWriter;
35 import javax.xml.transform.Result;
36 import javax.xml.transform.dom.DOMResult;
37 import javax.xml.transform.stream.StreamResult;
38 import javax.xml.transform.stax.StAXResult;
39 import com.sun.org.apache.xerces.internal.impl.Constants;
40 import com.sun.org.apache.xerces.internal.impl.PropertyManager;
41 import com.sun.xml.internal.stream.writers.XMLDOMWriterImpl;
42 import com.sun.xml.internal.stream.writers.XMLEventWriterImpl;
43 import com.sun.xml.internal.stream.writers.XMLStreamWriterImpl;
44 
45 /**
46  * This class provides the implementation of XMLOutputFactory.
47  *
48  * @author Neeraj Bajaj,
49  * @author k venugopal
50  */
51 public class XMLOutputFactoryImpl extends XMLOutputFactory {
52 
53     //List of supported properties and default values.
54     private PropertyManager fPropertyManager = new PropertyManager(PropertyManager.CONTEXT_WRITER);
55 
56     //cache the instance of XMLStreamWriterImpl
57     private XMLStreamWriterImpl fStreamWriter = null;
58 
59     /**
60      * TODO: at the current time, XMLStreamWriters are not Thread safe.
61      */
62     boolean fReuseInstance = false;
63 
64     /**
65      * Creates a new instance of XMLOutputFactory
66      */
XMLOutputFactoryImpl()67     public XMLOutputFactoryImpl() {
68     }
69 
createXMLEventWriter(OutputStream outputStream)70     public XMLEventWriter createXMLEventWriter(OutputStream outputStream)
71             throws XMLStreamException {
72         return createXMLEventWriter(outputStream, null);
73     }
74 
createXMLEventWriter(OutputStream outputStream, String encoding)75     public XMLEventWriter createXMLEventWriter(OutputStream outputStream, String encoding)
76             throws XMLStreamException {
77         return new XMLEventWriterImpl(createXMLStreamWriter(outputStream, encoding));
78     }
79 
createXMLEventWriter(Result result)80     public XMLEventWriter createXMLEventWriter(Result result)
81             throws XMLStreamException {
82 
83         if (result instanceof StAXResult && ((StAXResult) result).getXMLEventWriter() != null) {
84             return ((StAXResult) result).getXMLEventWriter();
85         }
86 
87         return new XMLEventWriterImpl(createXMLStreamWriter(result));
88     }
89 
createXMLEventWriter(java.io.Writer writer)90     public XMLEventWriter createXMLEventWriter(java.io.Writer writer)
91             throws XMLStreamException {
92         return new XMLEventWriterImpl(createXMLStreamWriter(writer));
93     }
94 
createXMLStreamWriter(Result result)95     public XMLStreamWriter createXMLStreamWriter(Result result)
96             throws XMLStreamException {
97 
98         if (result instanceof StreamResult) {
99             return createXMLStreamWriter((StreamResult) result, null);
100         } else if (result instanceof DOMResult) {
101             return new XMLDOMWriterImpl((DOMResult) result);
102         } else if (result instanceof StAXResult) {
103             if (((StAXResult) result).getXMLStreamWriter() != null) {
104                 return ((StAXResult) result).getXMLStreamWriter();
105             } else {
106                 throw new UnsupportedOperationException(
107                         "Result of type " + result + " is not supported");
108             }
109         } else if (result.getSystemId() != null) {
110             //this is not correct impl of SAXResult. Keep it for now for compatibility
111             return createXMLStreamWriter(new StreamResult(result.getSystemId()));
112         } else {
113             throw new UnsupportedOperationException(
114                     "Result of type " + result + " is not supported. Supported result "
115                             + "types are: DOMResult, StAXResult and StreamResult.");
116         }
117 
118     }
119 
createXMLStreamWriter(java.io.Writer writer)120     public XMLStreamWriter createXMLStreamWriter(java.io.Writer writer)
121             throws XMLStreamException {
122         return createXMLStreamWriter(toStreamResult(null, writer, null), null);
123     }
124 
createXMLStreamWriter(OutputStream outputStream)125     public XMLStreamWriter createXMLStreamWriter(OutputStream outputStream)
126             throws XMLStreamException {
127         return createXMLStreamWriter(outputStream, null);
128     }
129 
createXMLStreamWriter(OutputStream outputStream, String encoding)130     public XMLStreamWriter createXMLStreamWriter(OutputStream outputStream, String encoding)
131             throws XMLStreamException {
132         return createXMLStreamWriter(toStreamResult(outputStream, null, null), encoding);
133     }
134 
getProperty(String name)135     public Object getProperty(String name)
136             throws IllegalArgumentException {
137         if (name == null) {
138             throw new IllegalArgumentException("Property not supported");
139         }
140         if (fPropertyManager.containsProperty(name)) {
141             return fPropertyManager.getProperty(name);
142         }
143         throw new IllegalArgumentException("Property not supported");
144     }
145 
isPropertySupported(String name)146     public boolean isPropertySupported(String name) {
147         if (name == null) {
148             return false;
149         } else {
150             return fPropertyManager.containsProperty(name);
151         }
152     }
153 
setProperty(String name, Object value)154     public void setProperty(String name, Object value)
155             throws IllegalArgumentException {
156         if (name == null || value == null || !fPropertyManager.containsProperty(name)) {
157             throw new IllegalArgumentException("Property " + name + "is not supported");
158         }
159         if (name == Constants.REUSE_INSTANCE || name.equals(Constants.REUSE_INSTANCE)) {
160             fReuseInstance = (Boolean)value;
161             if (DEBUG) {
162                 System.out.println("fReuseInstance is set to " + fReuseInstance);
163             }
164 
165             // TODO: XMLStreamWriters are not Thread safe,
166             // don't let application think it is optimizing
167             if (fReuseInstance) {
168                 throw new IllegalArgumentException(
169                         "Property "
170                         + name
171                         + " is not supported: XMLStreamWriters are not Thread safe");
172             }
173         } else {//for any other property set the flag
174             //REVISIT: Even in this case instance can be reused, by passing PropertyManager
175             fPropertyChanged = true;
176         }
177         fPropertyManager.setProperty(name, value);
178     }
179 
180     /**
181      * StreamResult object is re-used and the values are set appropriately.
182      */
toStreamResult(OutputStream os, Writer writer, String systemId)183     StreamResult toStreamResult(OutputStream os, Writer writer, String systemId) {
184         StreamResult sr = new StreamResult();
185         sr.setOutputStream(os);
186         sr.setWriter(writer);
187         sr.setSystemId(systemId);
188         return sr;
189     }
190 
createXMLStreamWriter(StreamResult sr, String encoding)191     XMLStreamWriter createXMLStreamWriter(StreamResult sr, String encoding)
192             throws XMLStreamException {
193         //if factory is configured to reuse the instance & this instance can be reused
194         //& the setProperty() hasn't been called
195         try {
196             if (fReuseInstance && fStreamWriter != null && fStreamWriter.canReuse()
197                     && !fPropertyChanged) {
198                 fStreamWriter.reset();
199                 fStreamWriter.setOutput(sr, encoding);
200                 if (DEBUG) {
201                     System.out.println("reusing instance, object id : " + fStreamWriter);
202                 }
203                 return fStreamWriter;
204             }
205             return fStreamWriter = new XMLStreamWriterImpl(sr, encoding,
206                     new PropertyManager(fPropertyManager));
207         } catch (IOException io) {
208             throw new XMLStreamException(io);
209         }
210     }//createXMLStreamWriter(StreamResult,String)
211 
212     private static final boolean DEBUG = false;
213 
214     /**
215      * This flag indicates the change of property. If true,
216      * <code>PropertyManager</code> should be passed when creating
217      * <code>XMLStreamWriterImpl</code>
218      */
219     private boolean fPropertyChanged;
220 }//XMLOutputFactory
221