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:
033: import org.jibx.runtime.IXMLWriter;
034:
035: /**
036: * Base implementation of XML writer interface. This provides common handling of
037: * indentation and formatting that can be used for all forms of text output.
038: *
039: * @author Dennis M. Sosnoski
040: * @version 1.0
041: */
042: public abstract class XMLWriterBase extends XMLWriterNamespaceBase {
043: /** Flag for current element has text content. */
044: private boolean m_textSeen;
045:
046: /** Flag for current element has content. */
047: private boolean m_contentSeen;
048:
049: /**
050: * Constructor.
051: *
052: * @param uris ordered array of URIs for namespaces used in document (must
053: * be constant; the value in position 0 must always be the empty string "",
054: * and the value in position 1 must always be the XML namespace
055: * "http://www.w3.org/XML/1998/namespace")
056: */
057: public XMLWriterBase(String[] uris) {
058: super (uris);
059: m_contentSeen = true;
060: }
061:
062: /**
063: * Copy constructor. This initializes the extension namespace information
064: * from an existing instance.
065: *
066: * @param base existing instance
067: * @param uris ordered array of URIs for namespaces used in document
068: */
069: public XMLWriterBase(XMLWriterBase base, String[] uris) {
070: super (base, uris);
071: m_contentSeen = true;
072: }
073:
074: /**
075: * Write markup text to output. Markup text can be written directly to the
076: * output without the need for any escaping, but still needs to be properly
077: * encoded.
078: *
079: * @param text markup text to be written
080: * @throws IOException if error writing to document
081: */
082: protected abstract void writeMarkup(String text) throws IOException;
083:
084: /**
085: * Write markup character to output. Markup text can be written directly to
086: * the output without the need for any escaping, but still needs to be
087: * properly encoded.
088: *
089: * @param chr markup character to be written
090: * @throws IOException if error writing to document
091: */
092: protected abstract void writeMarkup(char chr) throws IOException;
093:
094: /**
095: * Write namespace prefix to output. This internal method is used to throw
096: * an exception when an undeclared prefix is used.
097: *
098: * @param index namespace URI index number
099: * @throws IOException if error writing to document
100: */
101: protected abstract void writePrefix(int index) throws IOException;
102:
103: /**
104: * Write attribute text to output. This needs to write the text with any
105: * appropriate escaping.
106: *
107: * @param text attribute value text to be written
108: * @throws IOException if error writing to document
109: */
110: protected abstract void writeAttributeText(String text)
111: throws IOException;
112:
113: /**
114: * Request output indent with bias from current element nesting level. This
115: * is used internally for proper indenting in special cases.
116: *
117: * @throws IOException on error writing to document
118: */
119: protected abstract void indent(int bias) throws IOException;
120:
121: /**
122: * Set up for writing any content to element. If the start tag for the
123: * element has not been closed, this will close it.
124: *
125: * @throws IOException on error writing to document
126: */
127: protected final void flagContent() throws IOException {
128: if (!m_contentSeen) {
129: writeMarkup('>');
130: incrementNesting();
131: m_contentSeen = true;
132: }
133: }
134:
135: /**
136: * Set up for writing text content to element. If the start tag for the
137: * element has not been closed, this will close it.
138: *
139: * @throws IOException on error writing to document
140: */
141: protected final void flagTextContent() throws IOException {
142: flagContent();
143: m_textSeen = true;
144: }
145:
146: /**
147: * Write XML declaration to document. This can only be called before any
148: * other methods in the interface are called.
149: *
150: * @param version XML version text
151: * @param encoding text for encoding attribute (unspecified if
152: * <code>null</code>)
153: * @param standalone text for standalone attribute (unspecified if
154: * <code>null</code>)
155: * @throws IOException on error writing to document
156: */
157: public void writeXMLDecl(String version, String encoding,
158: String standalone) throws IOException {
159: writeMarkup("<?xml version=\"");
160: writeAttributeText(version);
161: if (encoding != null) {
162: writeMarkup("\" encoding=\"");
163: writeAttributeText(encoding);
164: }
165: if (standalone != null) {
166: writeMarkup("\" standalone=\"");
167: writeAttributeText(standalone);
168: }
169: writeMarkup("\"?>");
170: }
171:
172: /**
173: * Generate open start tag. This allows attributes to be added to the start
174: * tag, but must be followed by a {@link #closeStartTag} call.
175: *
176: * @param index namespace URI index number
177: * @param name unqualified element name
178: * @throws IOException on error writing to document
179: */
180: public void startTagOpen(int index, String name) throws IOException {
181: flagContent();
182: indent();
183: writeMarkup('<');
184: writePrefix(index);
185: writeMarkup(name);
186: }
187:
188: /**
189: * Generate start tag for element with namespaces. This creates the actual
190: * start tag, along with any necessary namespace declarations. Previously
191: * active namespace declarations are not duplicated. The tag is
192: * left incomplete, allowing other attributes to be added.
193: *
194: * @param index namespace URI index number
195: * @param name element name
196: * @param nums array of namespace indexes defined by this element (must
197: * be constant, reference is kept until end of element)
198: * @param prefs array of namespace prefixes mapped by this element (no
199: * <code>null</code> values, use "" for default namespace declaration)
200: * @throws IOException on error writing to document
201: */
202: public void startTagNamespaces(int index, String name, int[] nums,
203: String[] prefs) throws IOException {
204:
205: // find the namespaces actually being declared
206: flagContent();
207: int[] deltas = openNamespaces(nums, prefs);
208:
209: // create the start tag for element
210: startTagOpen(index, name);
211:
212: // add namespace declarations to open element
213: for (int i = 0; i < deltas.length; i++) {
214: int slot = deltas[i];
215: String prefix = getNamespacePrefix(slot);
216: if (prefix.length() > 0) {
217: writeMarkup(" xmlns:");
218: writeMarkup(prefix);
219: writeMarkup("=\"");
220: } else {
221: writeMarkup(" xmlns=\"");
222: }
223: writeAttributeText(getNamespaceUri(slot));
224: writeMarkup('"');
225: }
226: }
227:
228: /**
229: * Add attribute to current open start tag. This is only valid after a call
230: * to {@link #startTagOpen} or {@link #startTagNamespaces} and before the
231: * corresponding call to {@link #closeStartTag}.
232: *
233: * @param index namespace URI index number
234: * @param name unqualified attribute name
235: * @param value text value for attribute
236: * @throws IOException on error writing to document
237: */
238: public void addAttribute(int index, String name, String value)
239: throws IOException {
240: writeMarkup(' ');
241: writePrefix(index);
242: writeMarkup(name);
243: writeMarkup("=\"");
244: writeAttributeText(value);
245: writeMarkup('"');
246: }
247:
248: /**
249: * Close the current open start tag. This is only valid after a call to
250: * {@link #startTagOpen}.
251: *
252: * @throws IOException on error writing to document
253: */
254: public void closeStartTag() throws IOException {
255: m_textSeen = m_contentSeen = false;
256: }
257:
258: /**
259: * Close the current open start tag as an empty element. This is only valid
260: * after a call to {@link #startTagOpen}.
261: *
262: * @throws IOException on error writing to document
263: */
264: public void closeEmptyTag() throws IOException {
265: writeMarkup("/>");
266: incrementNesting();
267: decrementNesting();
268: m_contentSeen = true;
269: }
270:
271: /**
272: * Generate closed start tag. No attributes or namespaces can be added to a
273: * start tag written using this call.
274: *
275: * @param index namespace URI index number
276: * @param name unqualified element name
277: * @throws IOException on error writing to document
278: */
279: public void startTagClosed(int index, String name)
280: throws IOException {
281: flagContent();
282: indent();
283: writeMarkup('<');
284: writePrefix(index);
285: writeMarkup(name);
286: m_textSeen = m_contentSeen = false;
287: }
288:
289: /**
290: * Generate end tag.
291: *
292: * @param index namespace URI index number
293: * @param name unqualified element name
294: * @throws IOException on error writing to document
295: */
296: public void endTag(int index, String name) throws IOException {
297:
298: // first adjust indentation
299: if (m_contentSeen && !m_textSeen) {
300: indent(-1);
301: }
302:
303: // check for content written to element
304: if (m_contentSeen) {
305:
306: // content was written, which means start tag closed and end needed
307: writeMarkup("</");
308: writePrefix(index);
309: writeMarkup(name);
310: writeMarkup('>');
311:
312: } else {
313:
314: // no content, just close start tag as empty tag
315: writeMarkup("/>");
316: incrementNesting();
317: }
318:
319: // adjust flags for containing element
320: decrementNesting();
321: m_textSeen = false;
322: m_contentSeen = true;
323: }
324:
325: /**
326: * Write comment to document.
327: *
328: * @param text comment text
329: * @throws IOException on error writing to document
330: */
331: public void writeComment(String text) throws IOException {
332: flagContent();
333: writeMarkup("<!--");
334: writeMarkup(text);
335: writeMarkup("-->");
336: }
337:
338: /**
339: * Write entity reference to document.
340: *
341: * @param name entity name
342: * @throws IOException on error writing to document
343: */
344: public void writeEntityRef(String name) throws IOException {
345: flagContent();
346: writeMarkup('&');
347: writeMarkup(name);
348: writeMarkup(';');
349: }
350:
351: /**
352: * Write DOCTYPE declaration to document.
353: *
354: * @param name root element name
355: * @param sys system ID (<code>null</code> if none, must be
356: * non-<code>null</code> for public ID to be used)
357: * @param pub public ID (<code>null</code> if none)
358: * @param subset internal subset (<code>null</code> if none)
359: * @throws IOException on error writing to document
360: */
361: public void writeDocType(String name, String sys, String pub,
362: String subset) throws IOException {
363: indent();
364: writeMarkup("<!DOCTYPE ");
365: writeMarkup(name);
366: writeMarkup(' ');
367: if (sys != null) {
368: if (pub == null) {
369: writeMarkup("SYSTEM \"");
370: writeMarkup(sys);
371: } else {
372: writeMarkup("PUBLIC \"");
373: writeMarkup(pub);
374: writeMarkup("\" \"");
375: writeMarkup(sys);
376: }
377: writeMarkup('"');
378: }
379: if (subset != null) {
380: writeMarkup('[');
381: writeMarkup(subset);
382: writeMarkup(']');
383: }
384: writeMarkup('>');
385: }
386:
387: /**
388: * Write processing instruction to document.
389: *
390: * @param target processing instruction target name
391: * @param data processing instruction data
392: * @throws IOException on error writing to document
393: */
394: public void writePI(String target, String data) throws IOException {
395: flagContent();
396: indent();
397: writeMarkup("<?");
398: writeMarkup(target);
399: writeMarkup(' ');
400: writeMarkup(data);
401: writeMarkup("?>");
402: }
403:
404: /**
405: * Flush document output. Forces out all output generated to this point.
406: *
407: * @throws IOException on error writing to document
408: */
409: public abstract void flush() throws IOException;
410:
411: /**
412: * Close document output. Completes writing of document output, including
413: * closing the output medium.
414: *
415: * @throws IOException on error writing to document
416: */
417: public abstract void close() throws IOException;
418:
419: /**
420: * Reset to initial state for reuse. The writer is serially reusable,
421: * as long as this method is called to clear any retained state information
422: * between uses. It is automatically called when output is set.
423: */
424: public void reset() {
425: m_textSeen = false;
426: m_contentSeen = true;
427: super .reset();
428: }
429:
430: /**
431: * Create a child writer instance to be used for a separate binding. The
432: * child writer inherits the stream and encoding from this writer, while
433: * using the supplied namespace URIs.
434: *
435: * @param uris ordered array of URIs for namespaces used in document
436: * (see {@link #XMLWriterBase(String[])})
437: * @return child writer
438: */
439: public abstract IXMLWriter createChildWriter(String[] uris);
440: }
|