View Javadoc

1   /*
2    * Copyright (c) 2003 Peter Antman, Teknik i Media  <peter.antman@tim.se>
3    *
4    * $Id: DOMInputStream.java,v 1.1.1.1 2004/05/19 12:07:30 pra Exp $
5    *
6    * This library is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License as published by the Free Software Foundation; either
9    * version 2 of the License, or (at your option) any later version
10   * 
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Lesser General Public License for more details.
15   * 
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this library; if not, write to the Free Software
18   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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();//This works because the fact that also the byte version calles read() first
85           started = true;
86        } // end of if ()
87  
88        if ( domEx != null) {
89           throw new IOException("Could not write DOM: " +domEx);
90        } // end of if ()
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       } // end of if ()
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       // Wonder if this may lock?
110       if ( pos != null) {
111          pos.close();
112       } // end of if ()
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           // Set any resolver
141           Resolver res = ResolverContext.get();
142           if ( res != null ) {
143              serializer.setURIResolver( res.getURIResolver());
144           } // end of if ()
145 
146           // Set any properties
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           } // end of while ()
154 
155           // Include any publicId or systemId
156           DocumentType dt= doc.getDoctype();
157           if ( dt != null) {
158              if ( dt.getPublicId()!= null ) {
159                 serializer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC , dt.getPublicId());
160              } // end of if ()
161              if ( dt.getSystemId() != null) {
162                 serializer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM , dt.getSystemId());
163              } // end of if ()
164              
165           } // end of if ()
166           
167       } catch (Exception e) {
168          throw new IOException("Could not set up DOM writer end "+e);
169       } // end of try-catch
170       pos = new PipedOutputStream();
171       this.connect(pos);
172       domThread = new Thread( new Runnable() {
173             public void run() {
174                try {
175                   // Do an identity transform and pipe the output into
176                   // this stream from the thread.
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                   } // end of try-catch
186                   
187                } // end of finally
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 } // DOMInputStream