KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > icl > saxon > output > XMLEmitter


1 package com.icl.saxon.output;
2 import com.icl.saxon.*;
3 import com.icl.saxon.charcode.*;
4 import com.icl.saxon.om.Namespace;
5 import com.icl.saxon.om.NamePool;
6 import org.xml.sax.Attributes JavaDoc;
7 import org.xml.sax.Locator JavaDoc;
8 import java.io.*;
9 import java.util.*;
10
11 import javax.xml.transform.Result JavaDoc;
12 import javax.xml.transform.OutputKeys JavaDoc;
13 import javax.xml.transform.TransformerException JavaDoc;
14 import javax.xml.transform.stream.StreamResult JavaDoc;
15
16 /**
17   * XMLEmitter is an Emitter that generates XML output
18   * to a specified destination.
19   */

20   
21 public class XMLEmitter extends Emitter
22 {
23     protected CharacterSet characterSet = null;
24
25     protected boolean empty = true;
26     protected boolean escaping = true;
27     protected boolean openStartTag = false;
28     protected boolean declarationIsWritten = false;
29     
30     protected boolean preferHex = false;
31
32     // a little cache...
33
protected int lastNameCode = -1;
34     protected String JavaDoc lastDisplayName;
35     protected String JavaDoc lastPrefix;
36     protected String JavaDoc lastURI;
37
38     static boolean[] specialInText; // lookup table for special characters in text
39
static boolean[] specialInAtt; // lookup table for special characters in attributes
40
// create look-up table for ASCII characters that need special treatment
41

42     static {
43         specialInText = new boolean[128];
44         for (int i=0; i<=127; i++) specialInText[i] = false;
45         specialInText['\r'] = true;
46         specialInText['<'] = true;
47         specialInText['>'] = true;
48         specialInText['&'] = true;
49
50         specialInAtt = new boolean[128];
51         for (int i=0; i<=127; i++) specialInAtt[i] = false;
52         specialInAtt['\r'] = true;
53         specialInAtt['\n'] = true;
54         specialInAtt['\t'] = true;
55         specialInAtt['<'] = true;
56         specialInAtt['>'] = true;
57         specialInAtt['&'] = true;
58         specialInAtt['\"'] = true;
59     }
60     
61     /**
62     * Set Document Locator. Provided merely to satisfy the interface.
63     */

64
65     public void setDocumentLocator(Locator JavaDoc locator) {}
66     
67
68     /**
69     * Start of the document. Make the writer and write the XML declaration.
70     */

71     
72     public void startDocument () throws TransformerException JavaDoc
73     {
74         if (characterSet==null) characterSet = new UnicodeCharacterSet();
75         writeDeclaration();
76         empty = true;
77         String JavaDoc rep = outputProperties.getProperty(SaxonOutputKeys.CHARACTER_REPRESENTATION);
78         if (rep!=null) {
79             preferHex = (rep.trim().equalsIgnoreCase("hex"));
80         }
81     }
82
83     /**
84     * Output the XML declaration
85     */

86
87     public void writeDeclaration() throws TransformerException JavaDoc {
88         if (declarationIsWritten) return;
89         declarationIsWritten = true;
90         try {
91
92             String JavaDoc omit = outputProperties.getProperty(OutputKeys.OMIT_XML_DECLARATION);
93             if (omit==null) omit = "no";
94
95             String JavaDoc version = outputProperties.getProperty(OutputKeys.VERSION);
96             if (version==null) version = "1.0";
97
98             String JavaDoc encoding = outputProperties.getProperty(OutputKeys.ENCODING);
99             if (encoding==null || encoding.equalsIgnoreCase("utf8")) {
100                 encoding = "utf-8";
101             }
102             
103             if (!(encoding.equalsIgnoreCase("utf-8"))) {
104                 omit = "no";
105             }
106
107             String JavaDoc standalone = outputProperties.getProperty(OutputKeys.STANDALONE);
108             
109             if (omit.equals("no")) {
110                 writer.write("<?xml version=\"" + version + "\" " +
111                               "encoding=\"" + encoding + "\"" +
112                               (standalone!=null ? (" standalone=\"" + standalone + "\"") : "") +
113                               "?>");
114                     // no longer write a newline character: it's wrong if the output is an
115
// external general parsed entity
116
}
117         } catch (java.io.IOException JavaDoc err) {
118             throw new TransformerException JavaDoc(err);
119         }
120     }
121
122
123     /**
124     * Output the document type declaration
125     */

126
127     boolean docTypeWritten = false;
128     protected void writeDocType(String JavaDoc type, String JavaDoc systemId, String JavaDoc publicId) throws TransformerException JavaDoc {
129         if (docTypeWritten) return;
130         docTypeWritten = true;
131         try {
132             writer.write("\n<!DOCTYPE " + type + "\n");
133             if (systemId!=null && publicId==null) {
134                 writer.write(" SYSTEM \"" + systemId + "\">\n");
135             } else if (systemId==null && publicId!=null) { // handles the HTML case
136
writer.write(" PUBLIC \"" + publicId + "\">\n");
137             } else {
138                 writer.write(" PUBLIC \"" + publicId + "\" \"" + systemId + "\">\n");
139             }
140         } catch (java.io.IOException JavaDoc err) {
141             throw new TransformerException JavaDoc(err);
142         }
143     }
144
145     /**
146     * End of the document. Close the output stream.
147     */

148     
149     public void endDocument () throws TransformerException JavaDoc
150     {
151         try {
152             writer.flush();
153         } catch (java.io.IOException JavaDoc err) {
154             throw new TransformerException JavaDoc(err);
155         }
156     }
157
158     /**
159     * Start of an element. Output the start tag, escaping special characters.
160     */

161     
162     public void startElement (int nameCode, Attributes JavaDoc attributes,
163                               int[] namespaces, int nscount) throws TransformerException JavaDoc
164     {
165         String JavaDoc prefix;
166         String JavaDoc uri;
167         String JavaDoc displayName;
168         
169         if (nameCode==lastNameCode) {
170             prefix = lastPrefix;
171             uri = lastURI;
172             displayName = lastDisplayName;
173         } else {
174             prefix = namePool.getPrefix(nameCode);
175             uri = namePool.getURI(nameCode);
176             displayName = namePool.getDisplayName(nameCode);
177             
178             lastNameCode = nameCode;
179             lastDisplayName = displayName;
180             lastPrefix = prefix;
181             lastURI = uri;
182         }
183                
184         try {
185             if (empty) {
186                 String JavaDoc systemId = outputProperties.getProperty(OutputKeys.DOCTYPE_SYSTEM);
187                 String JavaDoc publicId = outputProperties.getProperty(OutputKeys.DOCTYPE_PUBLIC);
188                 if (systemId!=null) {
189                     writeDocType(displayName, systemId, publicId);
190                 }
191                 empty = false;
192             }
193             if (openStartTag) {
194                 closeStartTag(nameCode, false);
195             }
196             writer.write('<');
197             testCharacters(displayName);
198             writer.write(displayName);
199
200             // output the namespaces
201

202             for (int n=0; n<nscount; n++) {
203                 writer.write(' ');
204                 String JavaDoc nsprefix = namePool.getPrefixFromNamespaceCode(namespaces[n]);
205                 String JavaDoc nsuri = namePool.getURIFromNamespaceCode(namespaces[n]);
206
207                 if (nsprefix.equals("")) {
208                     writeAttribute(nameCode, "xmlns", "CDATA", nsuri);
209                 } else {
210                     writeAttribute(nameCode, "xmlns:" + nsprefix, "CDATA", nsuri);
211                 }
212                 
213             }
214
215             // output the attributes
216

217             for (int i=0; i<attributes.getLength(); i++) {
218                 writer.write(' ');
219                 writeAttribute(
220                     nameCode,
221                     attributes.getQName(i),
222                     attributes.getType(i),
223                     attributes.getValue(i));
224             }
225             openStartTag = true;
226             
227         } catch (java.io.IOException JavaDoc err) {
228             throw new TransformerException JavaDoc(err);
229         }
230     }
231
232     protected void closeStartTag(int nameCode, boolean emptyTag) throws TransformerException JavaDoc {
233         try {
234             if (openStartTag) {
235                 if (emptyTag) {
236                     writer.write(emptyElementTagCloser(nameCode));
237                 } else {
238                     writer.write('>');
239                 }
240                 openStartTag = false;
241             }
242         } catch (java.io.IOException JavaDoc err) {
243             throw new TransformerException JavaDoc(err);
244         }
245     }
246
247     /**
248     * Close an empty element tag. (This is overridden in XHTMLEmitter).
249     */

250
251     protected String JavaDoc emptyElementTagCloser(int nameCode) {
252         return "/>";
253     }
254     
255     /**
256     * Write attribute name=value pair. The element name is not used in this version of the
257     * method, but is used in the HTML subclass.
258     */

259
260     char[] attbuff1 = new char[256];
261     protected void writeAttribute(int elCode, String JavaDoc attname, String JavaDoc type, String JavaDoc value) throws TransformerException JavaDoc {
262         try {
263             testCharacters(attname);
264             writer.write(attname);
265             if (type.equals("NO-ESC")) {
266                 // special attribute type to indicate that no escaping is needed
267
writer.write('=');
268                 char delimiter = (value.indexOf('"') >= 0 ? '\'' : '"');
269                 writer.write(delimiter);
270                 writer.write(value);
271                 writer.write(delimiter);
272             } else {
273                 writer.write("=\"");
274                 int len = value.length();
275                 if (len > attbuff1.length) {
276                     attbuff1 = new char[len];
277                 }
278                 value.getChars(0, len, attbuff1, 0);
279                 writeEscape(attbuff1, 0, len, true);
280                 writer.write('\"');
281             }
282         } catch (java.io.IOException JavaDoc err) {
283             throw new TransformerException JavaDoc(err);
284         }
285     }
286
287
288     /**
289     * Test that all characters in a name are supported in the target encoding
290     */

291
292     protected void testCharacters(String JavaDoc name) throws TransformerException JavaDoc {
293         for (int i=name.length()-1; i>=0; i--) {
294             if (!characterSet.inCharset(name.charAt(i))) {
295                 throw new TransformerException JavaDoc("Invalid character in output name (" + name + ")");
296             }
297         }
298     }
299
300     protected boolean testCharacters(char[] array, int start, int len)
301     //throws TransformerException
302
{
303         for (int i=start; i<len; i++) {
304             if (!characterSet.inCharset(array[i])) {
305                 //throw new TransformerException("Invalid character in output ( &#" + (int)array[i] + "; )");
306
return false;
307             }
308         }
309         return true;
310     }
311
312     /**
313     * End of an element.
314     */

315
316     public void endElement (int nameCode) throws TransformerException JavaDoc
317     {
318         try {
319             if (openStartTag) {
320                 closeStartTag(nameCode, true);
321             } else {
322                 String JavaDoc displayName;
323                 if (nameCode==lastNameCode) {
324                     displayName = lastDisplayName;
325                 } else {
326                     displayName = namePool.getDisplayName(nameCode);
327                 }
328                 writer.write("</");
329                 writer.write(displayName);
330                 writer.write('>');
331             }
332         } catch (java.io.IOException JavaDoc err) {
333             throw new TransformerException JavaDoc(err);
334         }
335     }
336
337     /**
338     * Character data.
339     */

340
341     public void characters (char[] ch, int start, int length) throws TransformerException JavaDoc
342     {
343         try {
344             if (openStartTag) {
345                 closeStartTag(-1, false);
346             }
347             if (!escaping) {
348                 if (testCharacters(ch, start, length)) {
349                     writer.write(ch, start, length);
350                 } else {
351                     // recoverable error - recover silently
352
writeEscape(ch, start, length, false);
353                 }
354             } else {
355                 writeEscape(ch, start, length, false);
356             }
357         } catch (java.io.IOException JavaDoc err) {
358             throw new TransformerException JavaDoc(err);
359         }
360     }
361
362
363     /**
364     * Handle a processing instruction.
365     */

366     
367     public void processingInstruction (String JavaDoc target, String JavaDoc data)
368         throws TransformerException JavaDoc
369     {
370         try {
371             if (openStartTag) {
372                 closeStartTag(-1, false);
373             }
374             writer.write("<?" + target + (data.length()>0 ? ' ' + data : "") + "?>");
375         } catch (java.io.IOException JavaDoc err) {
376             throw new TransformerException JavaDoc(err);
377         }
378     }
379
380     /**
381     * Write contents of array to current writer, after escaping special characters
382     * @param ch The character array containing the string
383     * @param start The start position of the input string within the character array
384     * @param length The length of the input string within the character array
385     * This method converts the XML special characters (such as < and &) into their
386     * predefined entities.
387     */

388
389     protected void writeEscape(char ch[], int start, int length, boolean inAttribute)
390     throws java.io.IOException JavaDoc {
391         int segstart = start;
392         boolean[] specialChars = (inAttribute ? specialInAtt : specialInText);
393         
394         while (segstart < start+length) {
395             int i = segstart;
396
397             // find a maximal sequence of "ordinary" characters
398
while (i < start+length &&
399                      (ch[i]<128 ? !specialChars[ch[i]] : characterSet.inCharset(ch[i]))) {
400                 i++;
401             }
402
403             // write out this sequence
404
writer.write(ch, segstart, i-segstart);
405
406             // exit if this was the whole string
407
if (i >= start+length) return;
408
409             if (ch[i]>127) {
410
411                 // process characters not available in the current encoding
412

413                 int charval;
414
415                 //test for surrogate pairs
416
//A surrogate pair is two consecutive Unicode characters. The first
417
//is in the range D800 to DBFF, the second is in the range DC00 to DFFF.
418
//To compute the numeric value of the character corresponding to a surrogate
419
//pair, use this formula (all numbers are hex):
420
//(FirstChar - D800) * 400 + (SecondChar - DC00) + 10000
421

422                 if (ch[i]>=55296 && ch[i]<=56319) {
423                     // we'll trust the data to be sound
424
charval = (((int)ch[i] - 55296) * 1024) + ((int)ch[i+1] - 56320) + 65536;
425                     i++;
426                 } else {
427                     charval = (int)ch[i];
428                 }
429
430                 outputCharacterReference(charval);
431
432             } else {
433
434                 // process special ASCII characters
435

436                 if (ch[i]=='<') {
437                     writer.write("&lt;");
438                 } else if (ch[i]=='>') {
439                     writer.write("&gt;");
440                 } else if (ch[i]=='&') {
441                     writer.write("&amp;");
442                 } else if (ch[i]=='\"') {
443                     writer.write("&#34;");
444                 } else if (ch[i]=='\n') {
445                     writer.write("&#xA;");
446                 } else if (ch[i]=='\r') {
447                     writer.write("&#xD;");
448                 } else if (ch[i]=='\t') {
449                     writer.write("&#x9;");
450                 }
451             }
452             segstart = ++i;
453         }
454     }
455
456     /**
457     * Output a decimal or hexadecimal character reference
458     */

459
460     private char[] charref = new char[10];
461     protected void outputCharacterReference(int charval) throws java.io.IOException JavaDoc {
462         if (preferHex) {
463             int o = 0;
464             charref[o++]='&';
465             charref[o++]='#';
466             charref[o++]='x';
467             String JavaDoc code = Integer.toHexString(charval);
468             int len = code.length();
469             for (int k=0; k<len; k++) {
470                 charref[o++]=code.charAt(k);
471             }
472             charref[o++]=';';
473             writer.write(charref, 0, o);
474         } else {
475             int o = 0;
476             charref[o++]='&';
477             charref[o++]='#';
478             String JavaDoc code = Integer.toString(charval);
479             int len = code.length();
480             for (int k=0; k<len; k++) {
481                 charref[o++]=code.charAt(k);
482             }
483             charref[o++]=';';
484             writer.write(charref, 0, o);
485         }
486     }
487
488     /**
489     * Set escaping on or off
490     */

491
492     public void setEscaping(boolean escaping) {
493         this.escaping = escaping;
494     }
495
496     /**
497     * Handle a comment.
498     */

499     
500     public void comment (char ch[], int start, int length) throws TransformerException JavaDoc
501     {
502         try {
503             if (openStartTag) {
504                 closeStartTag(-1, false);
505             }
506             writer.write("<!--");
507             writer.write(ch, start, length);
508             writer.write("-->");
509         } catch (java.io.IOException JavaDoc err) {
510             throw new TransformerException JavaDoc(err);
511         }
512     }
513
514     /**
515     * Set the result destination
516     */

517
518     public void setResult(Result JavaDoc result) {
519         if (!(result instanceof StreamResult JavaDoc)) {
520             throw new IllegalArgumentException JavaDoc("Destination for XMLEmitter must be a StreamResult");
521         }
522         writer = ((StreamResult JavaDoc)result).getWriter();
523         if (writer==null) {
524             throw new IllegalArgumentException JavaDoc("No writer supplied");
525         }
526         // TODO: must handle an OutputStream or a systemID
527
}
528
529     /**
530     * Set output properties
531     */

532     
533     public void setOutputProperties(Properties details) {
534         characterSet = CharacterSetFactory.getCharacterSet(details);
535         super.setOutputProperties(details);
536     }
537     
538     /**
539     * Set the URI for an unparsed entity in the document.
540     */

541
542     public void setUnparsedEntity(String JavaDoc name, String JavaDoc uri) throws TransformerException JavaDoc {
543     }
544     
545
546 }
547
548 //
549
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
550
// you may not use this file except in compliance with the License. You may obtain a copy of the
551
// License at http://www.mozilla.org/MPL/
552
//
553
// Software distributed under the License is distributed on an "AS IS" basis,
554
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
555
// See the License for the specific language governing rights and limitations under the License.
556
//
557
// The Original Code is: all this file.
558
//
559
// The Initial Developer of the Original Code is
560
// Michael Kay of International Computers Limited (mhkay@iclway.co.uk).
561
//
562
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
563
//
564
// Contributor(s): none.
565
//
566
Popular Tags