1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.backsource.utils.io;
21
22 import java.io.FileInputStream;
23 import java.io.PipedInputStream;
24 import java.io.PipedOutputStream;
25 import java.io.IOException;
26 import java.util.Properties;
27 import java.util.Iterator;
28 import java.util.Map;
29 import java.util.Map.Entry;
30
31 import javax.xml.transform.TransformerFactory;
32 import javax.xml.transform.Transformer;
33 import javax.xml.transform.OutputKeys;
34 import javax.xml.transform.dom.DOMSource;
35 import javax.xml.transform.stream.StreamResult;
36
37 import org.w3c.dom.Document;
38 import org.w3c.dom.DocumentType;
39
40 import org.backsource.utils.io.IOHelper;
41 import org.backsource.utils.xml.DocumentUtil;
42 import org.backsource.utils.xml.Resolver;
43 import org.backsource.utils.xml.ResolverContext;
44 /***
45 * A stream that reads from a DOM Document (much like a FileInputStream reads from a file).
46 * <p>With DOMInputStream it is possible to setup an InputStream that reads directly from a DOM Document without having to write it out to an OutputStream first. This is however done with the use of two threads; so this class should probably not be used in EJB:s.</p>
47 * <p>Since the DOM tree is serializised it is important to notice that the same behaviour as when doing a default transformations i valid: i.e one has to set encoding and stuff like this if the default behaviour is not good enough.It is possible to use the generic setPropertyMethod to do this; or some of the convinience methods. Use the {@link javax.xml.transform.OutputKeys} properties. All tese properties must be set before any reads are done from the stream.</p>
48 <p>To solve resolve problems it is possible to use the {@link org.backsource.utils.xml.ResolverContext} to set a {@link se.tim.utils.xml.Resolver} that should be used during serialization.</p>
49 <p>Here are some example of properties to set: </p>
50 <pre>
51 // Make output indented
52 is.setProperty(OutputKeys.INDENT, "yes");
53
54 // Set another encoding that UTF-8
55 is.setProperty(OutputKeys.ENCODING, "iso-8859-1");
56
57 // Don't include XML prolog
58 is.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
59 </pre>
60 * @author <a href="mailto:pra@tim.se">Peter Antman</a>
61 * @version $Revision: 1.1.1.1 $
62 */
63
64 public class DOMInputStream extends PipedInputStream {
65 private Document doc;
66 private Thread domThread;
67 private Exception domEx;
68 private Transformer serializer;
69 private PipedOutputStream pos;
70 private boolean started = false;
71 private Properties props;
72
73 public DOMInputStream (Document doc){
74 super();
75 this.doc = doc;
76 props = new Properties();
77 }
78
79 /***
80 * Start the serializing thread if first read and delegates to parent;reports any exceptions also from the serializer thread
81 */
82 public synchronized int read() throws IOException {
83 if ( !started) {
84 startDOMWriter();
85 started = true;
86 }
87
88 if ( domEx != null) {
89 throw new IOException("Could not write DOM: " +domEx);
90 }
91
92 return super.read();
93 }
94
95 /***
96 * Read, and report any exceptions also from the serializer thread; delegates to parent.
97 */
98 public synchronized int read(byte b[], int off, int len) throws IOException {
99 if ( domEx != null) {
100 throw new IOException("Could not write DOM: " +domEx);
101 }
102 return super.read(b,off,len);
103 }
104
105 /***
106 * Close the serializer and this stream.
107 */
108 public void close() throws IOException{
109
110 if ( pos != null) {
111 pos.close();
112 }
113 super.close();
114 }
115
116 /***
117 * Set a transformation/serialization property, see {@link javax.xml.transform.OutputKeys} for possible types and values.
118 */
119 public void setProperty(String name, String value) {
120 props.setProperty(name,value);
121 }
122
123 /***
124 * Check if a specific property is set.
125 */
126 public String getProperty(String name) {
127 return props.getProperty(name);
128 }
129
130 /***
131 * Start the DOM serializer thread. Any exceptions throw during serializing is saved and reported i the read methods.
132 */
133 protected void startDOMWriter() throws IOException {
134 try {
135
136 TransformerFactory tfactory = TransformerFactory.newInstance();
137 serializer = tfactory.newTransformer();
138 serializer.setOutputProperty(OutputKeys.METHOD, "xml");
139
140
141 Resolver res = ResolverContext.get();
142 if ( res != null ) {
143 serializer.setURIResolver( res.getURIResolver());
144 }
145
146
147 Iterator entries = props.entrySet().iterator();
148 while ( entries.hasNext()) {
149 Map.Entry e = (Map.Entry)entries.next();
150 serializer.setOutputProperty(
151 (String)e.getKey(),
152 (String)e.getValue() );
153 }
154
155
156 DocumentType dt= doc.getDoctype();
157 if ( dt != null) {
158 if ( dt.getPublicId()!= null ) {
159 serializer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC , dt.getPublicId());
160 }
161 if ( dt.getSystemId() != null) {
162 serializer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM , dt.getSystemId());
163 }
164
165 }
166
167 } catch (Exception e) {
168 throw new IOException("Could not set up DOM writer end "+e);
169 }
170 pos = new PipedOutputStream();
171 this.connect(pos);
172 domThread = new Thread( new Runnable() {
173 public void run() {
174 try {
175
176
177 serializer.transform(new DOMSource(doc), new StreamResult(pos));
178 } catch (Exception e) {
179 domEx = e;
180 } finally {
181 try {
182 pos.close();
183 } catch (Exception e) {
184 domEx = e;
185 }
186
187 }
188
189 }
190 });
191 domThread.start();
192 }
193
194 /***
195 * Get the Document this stream reads from.
196 */
197 public Document getDocument() {
198 return doc;
199 }
200
201 }