001: /*
002: Copyright (c) 2004, 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: * Handler for marshalling text document to a UTF-8 output stream.
037: *
038: * @author Dennis M. Sosnoski
039: * @version 1.0
040: */
041:
042: public class ISO88591StreamWriter extends StreamWriterBase {
043: /**
044: * Constructor.
045: *
046: * @param uris ordered array of URIs for namespaces used in document (must
047: * be constant; the value in position 0 must always be the empty string "",
048: * and the value in position 1 must always be the XML namespace
049: * "http://www.w3.org/XML/1998/namespace")
050: */
051: public ISO88591StreamWriter(String[] uris) {
052: super ("ISO-8859-1", uris);
053: m_prefixBytes = new byte[uris.length][];
054: try {
055: defineNamespace(0, "");
056: defineNamespace(1, "xml");
057: } catch (IOException e) {
058: throw new RuntimeException(e.getMessage());
059: }
060: }
061:
062: /**
063: * Copy constructor. This takes the stream from a supplied instance, while
064: * setting a new array of namespace URIs. It's intended for use when
065: * invoking one binding from within another binding.
066: *
067: * @param base instance to be used as base for writer
068: * @param uris ordered array of URIs for namespaces used in document
069: * (see {@link #ISO88591StreamWriter(String[])})
070: */
071: public ISO88591StreamWriter(ISO88591StreamWriter base, String[] uris) {
072: super (base, uris);
073: m_prefixBytes = new byte[uris.length][];
074: try {
075: defineNamespace(0, "");
076: defineNamespace(1, "xml");
077: } catch (IOException e) {
078: throw new RuntimeException(e.getMessage());
079: }
080: }
081:
082: /**
083: * Write markup text to output. Markup text can be written directly to the
084: * output without the need for any escaping, but still needs to be properly
085: * encoded.
086: *
087: * @param text markup text to be written
088: * @throws IOException if error writing to document
089: */
090: protected void writeMarkup(String text) throws IOException {
091: int length = text.length();
092: makeSpace(length);
093: int fill = m_fillOffset;
094: for (int i = 0; i < length; i++) {
095: char chr = text.charAt(i);
096: if (chr > 0xFF) {
097: throw new IOException(
098: "Unable to write character code 0x"
099: + Integer.toHexString(chr)
100: + " in encoding ISO-8859-1");
101: } else {
102: m_buffer[fill++] = (byte) chr;
103: }
104: }
105: m_fillOffset = fill;
106: }
107:
108: /**
109: * Write markup character to output. Markup text can be written directly to
110: * the output without the need for any escaping, but still needs to be
111: * properly encoded.
112: *
113: * @param chr markup character to be written
114: * @throws IOException if error writing to document
115: */
116: protected void writeMarkup(char chr) throws IOException {
117: makeSpace(1);
118: if (chr > 0xFF) {
119: throw new IOException("Unable to write character code 0x"
120: + Integer.toHexString(chr)
121: + " in encoding ISO-8859-1");
122: } else {
123: m_buffer[m_fillOffset++] = (byte) chr;
124: }
125: }
126:
127: /**
128: * Report that namespace has been defined.
129: *
130: * @param index namespace URI index number
131: * @param prefix prefix used for namespace
132: * @throws IOException if error writing to document
133: */
134: protected void defineNamespace(int index, String prefix)
135: throws IOException {
136: byte[] buff;
137: if (prefix.length() > 0) {
138: buff = new byte[prefix.length() + 1];
139: for (int i = 0; i < buff.length - 1; i++) {
140: char chr = prefix.charAt(i);
141: if (chr > 0xFF) {
142: throw new IOException(
143: "Unable to write character code 0x"
144: + Integer.toHexString(chr)
145: + " in encoding ISO-8859-1");
146: } else {
147: buff[i] = (byte) chr;
148: }
149: }
150: buff[buff.length - 1] = ':';
151: } else {
152: buff = new byte[0];
153: }
154: if (index < m_prefixBytes.length) {
155: m_prefixBytes[index] = buff;
156: } else if (m_extensionBytes != null) {
157: index -= m_prefixBytes.length;
158: for (int i = 0; i < m_extensionBytes.length; i++) {
159: int length = m_extensionBytes[i].length;
160: if (index < length) {
161: m_extensionBytes[i][index] = buff;
162: } else {
163: index -= length;
164: }
165: }
166: } else {
167: throw new IllegalArgumentException("Index out of range");
168: }
169: }
170:
171: /**
172: * Write attribute text to output. This needs to write the text with any
173: * appropriate escaping.
174: *
175: * @param text attribute value text to be written
176: * @throws IOException if error writing to document
177: */
178: protected void writeAttributeText(String text) throws IOException {
179: int length = text.length();
180: makeSpace(length);
181: int fill = m_fillOffset;
182: for (int i = 0; i < length; i++) {
183: char chr = text.charAt(i);
184: if (chr == '"') {
185: fill = writeEntity(QUOT_ENTITY, fill);
186: } else if (chr == '&') {
187: fill = writeEntity(AMP_ENTITY, fill);
188: } else if (chr == '<') {
189: fill = writeEntity(LT_ENTITY, fill);
190: } else if (chr == '>' && i > 2 && text.charAt(i - 1) == ']'
191: && text.charAt(i - 2) == ']') {
192: m_buffer[fill++] = (byte) ']';
193: m_buffer[fill++] = (byte) ']';
194: fill = writeEntity(GT_ENTITY, fill);
195: } else if (chr < 0x20) {
196: if (chr != 0x9 && chr != 0xA && chr != 0xD) {
197: throw new IOException("Illegal character code 0x"
198: + Integer.toHexString(chr)
199: + " in attribute value text");
200: } else {
201: m_buffer[fill++] = (byte) chr;
202: }
203: } else {
204: if (chr > 0xFF) {
205: if (chr > 0xD7FF
206: && (chr < 0xE000 || chr == 0xFFFE
207: || chr == 0xFFFF || chr > 0x10FFFF)) {
208: throw new IOException(
209: "Illegal character code 0x"
210: + Integer.toHexString(chr)
211: + " in attribute value text");
212: } else {
213: m_fillOffset = fill;
214: makeSpace(length - i + 8);
215: fill = m_fillOffset;
216: m_buffer[fill++] = (byte) '&';
217: m_buffer[fill++] = (byte) '#';
218: m_buffer[fill++] = (byte) 'x';
219: for (int j = 12; j >= 0; j -= 4) {
220: int nib = (chr >> j) & 0xF;
221: if (nib < 10) {
222: m_buffer[fill++] = (byte) ('0' + nib);
223: } else {
224: m_buffer[fill++] = (byte) ('A' + nib);
225: }
226: }
227: m_buffer[fill++] = (byte) ';';
228: }
229: } else {
230: m_buffer[fill++] = (byte) chr;
231: }
232: }
233: }
234: m_fillOffset = fill;
235: }
236:
237: /**
238: * Write ordinary character data text content to document.
239: *
240: * @param text content value text
241: * @throws IOException on error writing to document
242: */
243: public void writeTextContent(String text) throws IOException {
244: flagTextContent();
245: int length = text.length();
246: makeSpace(length);
247: int fill = m_fillOffset;
248: for (int i = 0; i < length; i++) {
249: char chr = text.charAt(i);
250: if (chr == '&') {
251: fill = writeEntity(AMP_ENTITY, fill);
252: } else if (chr == '<') {
253: fill = writeEntity(LT_ENTITY, fill);
254: } else if (chr == '>' && i > 2 && text.charAt(i - 1) == ']'
255: && text.charAt(i - 2) == ']') {
256: fill = writeEntity(GT_ENTITY, fill);
257: } else if (chr < 0x20) {
258: if (chr != 0x9 && chr != 0xA && chr != 0xD) {
259: throw new IOException("Illegal character code 0x"
260: + Integer.toHexString(chr)
261: + " in content text");
262: } else {
263: m_buffer[fill++] = (byte) chr;
264: }
265: } else {
266: if (chr > 0xFF) {
267: if (chr > 0xD7FF
268: && (chr < 0xE000 || chr == 0xFFFE
269: || chr == 0xFFFF || chr > 0x10FFFF)) {
270: throw new IOException(
271: "Illegal character code 0x"
272: + Integer.toHexString(chr)
273: + " in character data text");
274: } else {
275: m_fillOffset = fill;
276: makeSpace(length - i + 8);
277: fill = m_fillOffset;
278: m_buffer[fill++] = (byte) '&';
279: m_buffer[fill++] = (byte) '#';
280: m_buffer[fill++] = (byte) 'x';
281: for (int j = 12; j >= 0; j -= 4) {
282: int nib = (chr >> j) & 0xF;
283: if (nib < 10) {
284: m_buffer[fill++] = (byte) ('0' + nib);
285: } else {
286: m_buffer[fill++] = (byte) ('A' + nib);
287: }
288: }
289: m_buffer[fill++] = (byte) ';';
290: }
291: } else {
292: m_buffer[fill++] = (byte) chr;
293: }
294: }
295: }
296: m_fillOffset = fill;
297: }
298:
299: /**
300: * Write CDATA text to document.
301: *
302: * @param text content value text
303: * @throws IOException on error writing to document
304: */
305: public void writeCData(String text) throws IOException {
306: flagTextContent();
307: int length = text.length();
308: makeSpace(length + 12);
309: int fill = m_fillOffset;
310: fill = writeEntity(LT_CDATASTART, fill);
311: for (int i = 0; i < length; i++) {
312: char chr = text.charAt(i);
313: if (chr == '>' && i > 2 && text.charAt(i - 1) == ']'
314: && text.charAt(i - 2) == ']') {
315: throw new IOException(
316: "Sequence \"]]>\" is not allowed "
317: + "within CDATA section text");
318: } else if (chr < 0x20) {
319: if (chr != 0x9 && chr != 0xA && chr != 0xD) {
320: throw new IOException("Illegal character code 0x"
321: + Integer.toHexString(chr)
322: + " in content text");
323: } else {
324: m_buffer[fill++] = (byte) chr;
325: }
326: } else {
327: if (chr > 0xFF) {
328: throw new IOException(
329: "Character code 0x"
330: + Integer.toHexString(chr)
331: + " not allowed by encoding in CDATA section text");
332: } else {
333: m_buffer[fill++] = (byte) chr;
334: }
335: }
336: }
337: m_fillOffset = writeEntity(LT_CDATAEND, fill);
338: }
339:
340: /**
341: * Create a child writer instance to be used for a separate binding. The
342: * child writer inherits the stream and encoding from this writer, while
343: * using the supplied namespace URIs.
344: *
345: * @param uris ordered array of URIs for namespaces used in document
346: * (see {@link #ISO88591StreamWriter(String[])})
347: * @return child writer
348: */
349: public IXMLWriter createChildWriter(String[] uris) {
350: return new ISO88591StreamWriter(this, uris);
351: }
352: }
|