001: /*
002: Copyright (c) 2004-2005, Dennis M. Sosnoski.
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without modification,
006: are permitted provided that the following conditions are met:
007:
008: * Redistributions of source code must retain the above copyright notice, this
009: list of conditions and the following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice,
011: this list of conditions and the following disclaimer in the documentation
012: and/or other materials provided with the distribution.
013: * Neither the name of JiBX nor the names of its contributors may be used
014: to endorse or promote products derived from this software without specific
015: prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
018: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
019: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
021: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028:
029: package org.jibx.runtime.impl;
030:
031: import java.io.IOException;
032: import java.io.Writer;
033:
034: import org.jibx.runtime.ICharacterEscaper;
035: import org.jibx.runtime.IXMLWriter;
036:
037: /**
038: * Generic handler for marshalling text document to a writer. This is the
039: * most general output handler since it can be used with any character encoding
040: * and and output writer.
041: *
042: * @author Dennis M. Sosnoski
043: * @version 1.0
044: */
045: public class GenericXMLWriter extends XMLWriterBase {
046: /** Writer for text output. */
047: private Writer m_writer;
048:
049: /** Escaper for character data content output. */
050: private ICharacterEscaper m_escaper;
051:
052: /** Indent tags for pretty-printed text. */
053: private boolean m_indent;
054:
055: /** Base number of characters in indent sequence (end of line only). */
056: private int m_indentBase;
057:
058: /** Number of extra characters in indent sequence per level of nesting. */
059: private int m_indentPerLevel;
060:
061: /** Raw text for indentation sequences. */
062: private char[] m_indentSequence;
063:
064: /**
065: * Constructor.
066: *
067: * @param uris ordered array of URIs for namespaces used in document (must
068: * be constant; the value in position 0 must always be the empty string "",
069: * and the value in position 1 must always be the XML namespace
070: * "http://www.w3.org/XML/1998/namespace")
071: */
072: public GenericXMLWriter(String[] uris) {
073: super (uris);
074: }
075:
076: /**
077: * Copy constructor. This takes the writer from a supplied instance, while
078: * setting a new array of namespace URIs. It's intended for use when
079: * invoking one binding from within another binding.
080: *
081: * @param base instance to be used as base for writer
082: * @param uris ordered array of URIs for namespaces used in document
083: * (see {@link #GenericXMLWriter(String[])})
084: */
085: public GenericXMLWriter(GenericXMLWriter base, String[] uris) {
086: super (base, uris);
087: setOutput(base.m_writer, base.m_escaper);
088: m_indent = base.m_indent;
089: m_indentBase = base.m_indentBase;
090: m_indentPerLevel = base.m_indentPerLevel;
091: m_indentSequence = base.m_indentSequence;
092: }
093:
094: /**
095: * Set output writer and escaper. If an output writer is currently open when
096: * this is called the existing writer is flushed and closed, with any
097: * errors ignored.
098: *
099: * @param outw writer for document data output
100: * @param escaper character escaper for chosen encoding
101: */
102: public void setOutput(Writer outw, ICharacterEscaper escaper) {
103: try {
104: close();
105: } catch (IOException e) { /* deliberately empty */
106: }
107: m_writer = outw;
108: m_escaper = escaper;
109: reset();
110: }
111:
112: /**
113: * Set nesting indentation. This is advisory only, and implementations of
114: * this interface are free to ignore it. The intent is to indicate that the
115: * generated output should use indenting to illustrate element nesting.
116: *
117: * @param count number of character to indent per level, or disable
118: * indentation if negative (zero means new line only)
119: * @param newline sequence of characters used for a line ending
120: * (<code>null</code> means use the single character '\n')
121: * @param indent whitespace character used for indentation
122: */
123: public void setIndentSpaces(int count, String newline, char indent) {
124: if (count >= 0) {
125: if (newline == null) {
126: newline = "\n";
127: }
128: m_indent = true;
129: m_indentBase = newline.length();
130: m_indentPerLevel = count;
131: int length = newline.length() + count * 10;
132: m_indentSequence = new char[length];
133: for (int i = 0; i < length; i++) {
134: if (i < newline.length()) {
135: m_indentSequence[i] = newline.charAt(i);
136: } else {
137: m_indentSequence[i] = indent;
138: }
139: }
140: } else {
141: m_indent = false;
142: }
143: }
144:
145: /**
146: * Write markup text to output. Markup text can be written directly to the
147: * output without the need for any escaping.
148: *
149: * @param text markup text to be written
150: * @throws IOException if error writing to document
151: */
152: protected void writeMarkup(String text) throws IOException {
153: m_writer.write(text);
154: }
155:
156: /**
157: * Write markup character to output. Markup text can be written directly to
158: * the output without the need for any escaping.
159: *
160: * @param chr markup character to be written
161: * @throws IOException if error writing to document
162: */
163: protected void writeMarkup(char chr) throws IOException {
164: m_writer.write(chr);
165: }
166:
167: /**
168: * Report that namespace has been defined.
169: *
170: * @param index namespace URI index number
171: * @param prefix prefix used for namespace
172: */
173: protected void defineNamespace(int index, String prefix) {
174: }
175:
176: /**
177: * Report that namespace has been undefined.
178: *
179: * @param index namespace URI index number
180: */
181: protected void undefineNamespace(int index) {
182: }
183:
184: /**
185: * Write namespace prefix to output. This internal method is used to throw
186: * an exception when an undeclared prefix is used.
187: *
188: * @param index namespace URI index number
189: * @throws IOException if error writing to document
190: */
191: protected void writePrefix(int index) throws IOException {
192: try {
193: String text = getNamespacePrefix(index);
194: if (text.length() > 0) {
195: m_writer.write(text);
196: m_writer.write(':');
197: }
198: } catch (NullPointerException ex) {
199: throw new IOException(
200: "Namespace URI has not been declared.");
201: }
202: }
203:
204: /**
205: * Write attribute text to output. This needs to write the text with any
206: * appropriate escaping.
207: *
208: * @param text attribute value text to be written
209: * @throws IOException if error writing to document
210: */
211: protected void writeAttributeText(String text) throws IOException {
212: m_escaper.writeAttribute(text, m_writer);
213: }
214:
215: /**
216: * Write ordinary character data text content to document. This needs to
217: * write the text with any appropriate escaping.
218: *
219: * @param text content value text
220: * @throws IOException on error writing to document
221: */
222: public void writeTextContent(String text) throws IOException {
223: flagTextContent();
224: m_escaper.writeContent(text, m_writer);
225: }
226:
227: /**
228: * Write CDATA text to document. This needs to write the text with any
229: * appropriate escaping.
230: *
231: * @param text content value text
232: * @throws IOException on error writing to document
233: */
234: public void writeCData(String text) throws IOException {
235: flagTextContent();
236: m_escaper.writeCData(text, m_writer);
237: }
238:
239: /**
240: * Request output indent. Output the line end sequence followed by the
241: * appropriate number of indent characters.
242: *
243: * @param bias indent depth difference (positive or negative) from current
244: * element nesting depth
245: * @throws IOException on error writing to document
246: */
247: public void indent(int bias) throws IOException {
248: if (m_indent) {
249: int length = m_indentBase + (getNestingDepth() + bias)
250: * m_indentPerLevel;
251: if (length > m_indentSequence.length) {
252: int use = Math.max(length, m_indentSequence.length * 2
253: - m_indentBase);
254: char[] grow = new char[use];
255: System.arraycopy(m_indentSequence, 0, grow, 0,
256: m_indentSequence.length);
257: for (int i = m_indentSequence.length; i < use; i++) {
258: grow[i] = grow[m_indentBase];
259: }
260: m_indentSequence = grow;
261: }
262: m_writer.write(m_indentSequence, 0, length);
263: }
264: }
265:
266: /**
267: * Request output indent. Output the line end sequence followed by the
268: * appropriate number of indent characters for the current nesting level.
269: *
270: * @throws IOException on error writing to document
271: */
272: public void indent() throws IOException {
273: indent(0);
274: }
275:
276: /**
277: * Flush document output. Forces out all output generated to this point.
278: *
279: * @throws IOException on error writing to document
280: */
281: public void flush() throws IOException {
282: // internal flush only, do not pass through to writer
283: /* if (m_writer != null) {
284: indent();
285: m_writer.flush();
286: } */
287: }
288:
289: /**
290: * Close document output. Completes writing of document output, including
291: * closing the output medium.
292: *
293: * @throws IOException on error writing to document
294: */
295: public void close() throws IOException {
296: flush();
297: if (m_writer != null) {
298: m_writer.close();
299: m_writer = null;
300: }
301: }
302:
303: /**
304: * Create a child writer instance to be used for a separate binding. The
305: * child writer inherits the stream and encoding from this writer, while
306: * using the supplied namespace URIs.
307: *
308: * @param uris ordered array of URIs for namespaces used in document
309: * (see {@link #GenericXMLWriter(String[])})
310: * @return child writer
311: */
312: public IXMLWriter createChildWriter(String[] uris) {
313: return new GenericXMLWriter(this, uris);
314: }
315: }
|