KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jibx > runtime > impl > StreamWriterBase


1 /*
2 Copyright (c) 2004, Dennis M. Sosnoski.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
7
8  * Redistributions of source code must retain the above copyright notice, this
9    list of conditions and the following disclaimer.
10  * Redistributions in binary form must reproduce the above copyright notice,
11    this list of conditions and the following disclaimer in the documentation
12    and/or other materials provided with the distribution.
13  * Neither the name of JiBX nor the names of its contributors may be used
14    to endorse or promote products derived from this software without specific
15    prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
21 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */

28
29 package org.jibx.runtime.impl;
30
31 import java.io.IOException JavaDoc;
32 import java.io.OutputStream JavaDoc;
33 import java.io.UnsupportedEncodingException JavaDoc;
34
35 /**
36  * Base handler for marshalling text document to an output stream. This is
37  * designed for use with character encodings that use standard one-byte values
38  * for Unicode characters in the 0x20-0x7F range, which includes the most
39  * widely used encodings for XML documents. It needs to be subclassed with
40  * implementation methods specific to the encoding used.
41  *
42  * @author Dennis M. Sosnoski
43  * @version 1.0
44  */

45
46 public abstract class StreamWriterBase extends XMLWriterBase
47 {
48     //
49
// Defined entities and special sequences as bytes
50

51     protected static final byte[] QUOT_ENTITY =
52     {
53         (byte)'&', (byte)'q', (byte)'u', (byte)'o', (byte)'t', (byte)';'
54     };
55     protected static final byte[] AMP_ENTITY =
56     {
57         (byte)'&', (byte)'a', (byte)'m', (byte)'p', (byte)';'
58     };
59     protected static final byte[] GT_ENTITY =
60     {
61         (byte)'&', (byte)'g', (byte)'t', (byte)';'
62     };
63     protected static final byte[] LT_ENTITY =
64     {
65         (byte)'&', (byte)'l', (byte)'t', (byte)';'
66     };
67     protected static final byte[] LT_CDATASTART =
68     {
69         (byte)'<', (byte)'!', (byte)'[', (byte)'C', (byte)'D', (byte)'A',
70         (byte)'T', (byte)'A', (byte)'['
71     };
72     protected static final byte[] LT_CDATAEND =
73     {
74         (byte)']', (byte)']', (byte)'>'
75     };
76     
77     /** Default output buffer size. */
78     private static final int INITIAL_BUFFER_SIZE = 2048;
79     
80     /** Name of encoding used for stream. */
81     private final String JavaDoc m_encodingName;
82     
83     /** Stream for text output. */
84     private OutputStream JavaDoc m_stream;
85     
86     /** Buffer for accumulating output bytes. */
87     protected byte[] m_buffer;
88     
89     /** Current offset in filling buffer. */
90     protected int m_fillOffset;
91     
92     /** Byte sequences for prefixes of namespaces in scope. */
93     protected byte[][] m_prefixBytes;
94     
95     /** Byte sequences for prefixes of extension namespaces in scope. */
96     protected byte[][][] m_extensionBytes;
97     
98     /** Indent tags for pretty-printed text. */
99     private boolean m_indent;
100     
101     /** Base number of characters in indent sequence (end of line only). */
102     private int m_indentBase;
103     
104     /** Number of extra characters in indent sequence per level of nesting. */
105     private int m_indentPerLevel;
106     
107     /** Raw text for indentation sequences. */
108     private byte[] m_indentSequence;
109     
110     /**
111      * Constructor.
112      *
113      * @param enc character encoding used for output to streams
114      * @param uris ordered array of URIs for namespaces used in document (must
115      * be constant; the value in position 0 must always be the empty string "",
116      * and the value in position 1 must always be the XML namespace
117      * "http://www.w3.org/XML/1998/namespace")
118      */

119     
120     public StreamWriterBase(String JavaDoc enc, String JavaDoc[] uris) {
121         super(uris);
122         m_encodingName = enc;
123         m_prefixBytes = new byte[uris.length][];
124         m_buffer = new byte[INITIAL_BUFFER_SIZE];
125     }
126     
127     /**
128      * Set output stream. If an output stream is currently open when this is
129      * called the existing stream is flushed and closed, with any errors
130      * ignored.
131      *
132      * @param outs stream for document data output
133      */

134     
135     public void setOutput(OutputStream JavaDoc outs) {
136         try {
137             close();
138         } catch (IOException JavaDoc e) { /* deliberately empty */ }
139         m_stream = outs;
140         reset();
141     }
142     
143     /**
144      * Set nesting indentation. This is advisory only, and implementations of
145      * this interface are free to ignore it. The intent is to indicate that the
146      * generated output should use indenting to illustrate element nesting.
147      *
148      * @param count number of character to indent per level, or disable
149      * indentation if negative (zero means new line only)
150      * @param newline sequence of characters used for a line ending
151      * (<code>null</code> means use the single character '\n')
152      * @param indent whitespace character used for indentation
153      */

154     
155     public void setIndentSpaces(int count, String JavaDoc newline, char indent) {
156         if (count >= 0) {
157             try {
158                 if (newline == null) {
159                     newline = "\n";
160                 }
161                 m_indent = true;
162                 byte[] base = newline.getBytes(m_encodingName);
163                 m_indentBase = base.length;
164                 byte[] per = new String JavaDoc(new char[]
165                     { indent }).getBytes(m_encodingName);
166                 m_indentPerLevel = count * per.length;
167                 int length = m_indentBase + m_indentPerLevel * 10;
168                 m_indentSequence = new byte[length];
169                 for (int i = 0; i < length; i++) {
170                     if (i < newline.length()) {
171                         m_indentSequence[i] = base[i];
172                     } else {
173                         int index = (i - m_indentBase) % per.length;
174                         m_indentSequence[i] = per[index];
175                     }
176                 }
177             } catch (UnsupportedEncodingException JavaDoc e) {
178                 throw new RuntimeException JavaDoc
179                     ("Encoding " + m_encodingName + " not recognized by JVM");
180             }
181         } else {
182             m_indent = false;
183         }
184     }
185     
186     /**
187      * Make at least the requested number of bytes available in the output
188      * buffer. If necessary, the output buffer will be replaced by a larger
189      * buffer.
190      *
191      * @param length number of bytes space to be made available
192      * @throws IOException if error writing to document
193      */

194     
195     protected void makeSpace(int length) throws IOException JavaDoc {
196         if (m_fillOffset + length > m_buffer.length) {
197             m_stream.write(m_buffer, 0, m_fillOffset);
198             m_fillOffset = 0;
199             if (length > m_buffer.length) {
200                 m_buffer = new byte[Math.max(length, m_buffer.length*2)];
201             }
202         }
203     }
204     
205     /**
206      * Report that namespace has been undefined.
207      *
208      * @param index namespace URI index number
209      */

210     
211     protected void undefineNamespace(int index) {
212         if (index < m_prefixBytes.length) {
213             m_prefixBytes[index] = null;
214         } else if (m_extensionBytes != null) {
215             index -= m_prefixes.length;
216             for (int i = 0; i < m_extensionBytes.length; i++) {
217                 int length = m_extensionBytes[i].length;
218                 if (index < length) {
219                     m_extensionBytes[i][index] = null;
220                     break;
221                 } else {
222                     index -= length;
223                 }
224             }
225         } else {
226             throw new IllegalArgumentException JavaDoc("Index out of range");
227         }
228     }
229     
230     /**
231      * Write namespace prefix to output. This internal method is used to throw
232      * an exception when an undeclared prefix is used.
233      *
234      * @param index namespace URI index number
235      * @throws IOException if error writing to document
236      */

237     
238     protected void writePrefix(int index) throws IOException JavaDoc {
239         try {
240             byte[] bytes = null;
241             if (index < m_prefixBytes.length) {
242                 bytes = m_prefixBytes[index];
243             } else if (m_extensionBytes != null) {
244                 index -= m_prefixes.length;
245                 for (int i = 0; i < m_extensionBytes.length; i++) {
246                     int length = m_extensionBytes[i].length;
247                     if (index < length) {
248                         bytes = m_extensionBytes[i][index];
249                         break;
250                     } else {
251                         index -= length;
252                     }
253                 }
254             }
255             makeSpace(bytes.length);
256             System.arraycopy(bytes, 0, m_buffer, m_fillOffset, bytes.length);
257             m_fillOffset += bytes.length;
258         } catch (NullPointerException JavaDoc ex) {
259             throw new IOException JavaDoc("Namespace URI has not been declared.");
260         }
261     }
262     
263     /**
264      * Write entity bytes to output.
265      *
266      * @param bytes actual bytes to be written
267      * @param offset starting offset in buffer
268      * @return offset for next data byte in buffer
269      */

270     
271     protected int writeEntity(byte[] bytes, int offset) {
272         System.arraycopy(bytes, 0, m_buffer, offset, bytes.length);
273         return offset + bytes.length;
274     }
275     
276     /**
277      * Append extension namespace URIs to those in mapping.
278      *
279      * @param uris namespace URIs to extend those in mapping
280      */

281     
282     public void pushExtensionNamespaces(String JavaDoc[] uris) {
283         super.pushExtensionNamespaces(uris);
284         byte[][] items = new byte[uris.length][];
285         if (m_extensionBytes == null) {
286             m_extensionBytes = new byte[][][] { items };
287         } else {
288             int length = m_extensionBytes.length;
289             byte[][][] grow = new byte[length+1][][];
290             System.arraycopy(m_extensionBytes, 0, grow, 0, length);
291             grow[length] = items;
292             m_extensionBytes = grow;
293         }
294     }
295     
296     /**
297      * Remove extension namespace URIs. This removes the last set of
298      * extension namespaces pushed using {@link #pushExtensionNamespaces}.
299      */

300     
301     public void popExtensionNamespaces() {
302         super.popExtensionNamespaces();
303         int length = m_extensionBytes.length;
304         if (length == 1) {
305             m_extensionBytes = null;
306         } else {
307             byte[][][] shrink = new byte[length-1][][];
308             System.arraycopy(m_extensionBytes, 0, shrink, 0, length-1);
309             m_extensionBytes = shrink;
310         }
311     }
312     
313     /**
314      * Request output indent. The writer implementation should normally indent
315      * output as appropriate. This method can be used to request indenting of
316      * output that might otherwise not be indented. The normal effect when used
317      * with a text-oriented writer should be to output the appropriate line end
318      * sequence followed by the appropriate number of indent characters for the
319      * current nesting level.
320      *
321      * @throws IOException on error writing to document
322      */

323
324     public void indent() throws IOException JavaDoc {
325         if (m_indent) {
326             int length = m_indentBase + m_nestingDepth * m_indentPerLevel;
327             if (length > m_indentSequence.length) {
328                 int use = Math.max(length,
329                     m_indentSequence.length*2 - m_indentBase);
330                 byte[] grow = new byte[use];
331                 System.arraycopy(m_indentSequence, 0, grow, 0,
332                     m_indentSequence.length);
333                 for (int i = m_indentSequence.length; i < use; i++) {
334                     grow[i] = grow[m_indentBase];
335                 }
336                 m_indentSequence = grow;
337             }
338             makeSpace(length);
339             System.arraycopy(m_indentSequence, 0, m_buffer, m_fillOffset,
340                 length);
341             m_fillOffset += length;
342         }
343     }
344     
345     /**
346      * Close document output. Completes writing of document output, including
347      * closing the output medium.
348      *
349      * @throws IOException on error writing to document
350      */

351
352     public void close() throws IOException JavaDoc {
353         if (m_stream != null) {
354             indent();
355             m_stream.write(m_buffer, 0, m_fillOffset);
356             m_fillOffset = 0;
357             m_stream.flush();
358             m_stream.close();
359             m_stream = null;
360         }
361     }
362     
363     /**
364      * Reset to initial state for reuse. This override of the base class
365      * method handles clearing the internal buffer when starting a new
366      * document.
367      */

368     
369     public void reset() {
370         super.reset();
371         m_fillOffset = 0;
372     }
373 }
Popular Tags