1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package com.bea.xml.stream;
16
17 import com.bea.xml.stream.util.SymbolTable;
18 import com.bea.xml.stream.util.NamespaceContextImpl;
19 import com.bea.xml.stream.util.Stack;
20
21 import java.io.Writer;
22 import java.io.OutputStreamWriter;
23 import java.io.IOException;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import javax.xml.stream.XMLStreamWriter;
27 import javax.xml.stream.XMLStreamException;
28 import javax.xml.stream.XMLOutputFactory;
29 import javax.xml.namespace.NamespaceContext;
30 import java.nio.charset.Charset;
31 import java.nio.charset.CharsetEncoder;
32
33 /***
34 * <p> The base output class.</p>
35 */
36
37 public class XMLWriterBase
38 extends ReaderToWriter
39 implements XMLStreamWriter
40 {
41 protected static final String DEFAULTNS="";
42 private Writer writer;
43 private boolean startElementOpened=false;
44 private boolean isEmpty=false;
45 private ConfigurationContextBase config;
46 private CharsetEncoder encoder;
47
48
49
50 private Stack localNameStack = new Stack();
51 private Stack prefixStack = new Stack();
52 private Stack uriStack = new Stack();
53 protected NamespaceContextImpl context = new NamespaceContextImpl();
54
55 private HashSet needToWrite;
56 private boolean isPrefixDefaulting;
57 private int defaultPrefixCount=0;
58 public XMLWriterBase() {}
59 public XMLWriterBase(Writer writer) {
60 this.writer = writer;
61 setWriter(writer);
62 }
63
64 public void setWriter(Writer writer) {
65 this.writer = writer;
66 setStreamWriter(this);
67 if (writer instanceof OutputStreamWriter) {
68 String charsetName = ((OutputStreamWriter) writer).getEncoding();
69 this.encoder = Charset.forName(charsetName).newEncoder();
70 } else {
71 this.encoder = null;
72 }
73 }
74
75 public void setConfigurationContext(ConfigurationContextBase c) {
76 config = c;
77 isPrefixDefaulting = config.isPrefixDefaulting();
78 }
79
80 protected void write(String s)
81 throws XMLStreamException
82 {
83 try {
84 writer.write(s);
85 } catch (IOException e) {
86 throw new XMLStreamException(e);
87 }
88 }
89
90 protected void write (char c)
91 throws XMLStreamException
92 {
93 try {
94 writer.write(c);
95 } catch (IOException e) {
96 throw new XMLStreamException(e);
97 }
98 }
99
100 protected void write (char[] c)
101 throws XMLStreamException
102 {
103 try {
104 writer.write(c);
105 } catch (IOException e) {
106 throw new XMLStreamException(e);
107 }
108 }
109
110 protected void write (char[] c, int start, int len)
111 throws XMLStreamException
112 {
113 try {
114 writer.write(c,start,len);
115 } catch (IOException e) {
116 throw new XMLStreamException(e);
117 }
118 }
119
120 protected void writeCharactersInternal(char characters[],
121 int start,
122 int length,
123 boolean isAttributeValue)
124 throws XMLStreamException
125 {
126 if(length == 0) return;
127
128
129
130
131
132
133
134
135 boolean fastPath = true;
136
137 for(int i=0, len=length; i<len; i++) {
138 switch(characters[i+start]) {
139 case '&':
140 case '<':
141 case '>':
142 case '\"':
143 fastPath = false;
144 break;
145 }
146 if (encoder != null && !encoder.canEncode(characters[i+start])) {
147 fastPath = false;
148 break;
149 }
150 }
151
152 if(fastPath) {
153 write(characters,start,length);
154 } else {
155 slowWriteCharacters(characters,start,length, isAttributeValue);
156 }
157 }
158
159 private void slowWriteCharacters(char chars[],
160 int start,
161 int length,
162 boolean isAttributeValue)
163 throws XMLStreamException
164 {
165 for (int i=0,len=length; i < len; i++) {
166 final char c = chars[i+start];
167 switch (c) {
168 case '&':
169 write("&");
170 break;
171 case '<':
172 write("<");
173 break;
174 case '>':
175 write(">");
176 break;
177 case '\"':
178 if (isAttributeValue) {
179 write(""");
180 } else {
181 write('\"');
182 }
183 break;
184 default:
185 if (encoder != null && !encoder.canEncode(c)) {
186 write("&#");
187 write(Integer.toString(c));
188 write(';');
189 } else {
190 write(c);
191 }
192 }
193 }
194 }
195
196 protected void closeStartElement()
197 throws XMLStreamException
198 {
199 if (startElementOpened) {
200 closeStartTag();
201 startElementOpened = false;
202 }
203 }
204
205 protected boolean isOpen() {
206 return startElementOpened;
207 }
208
209 protected void closeStartTag()
210 throws XMLStreamException
211 {
212 flushNamespace();
213 if (isEmpty) {
214 write ("/>");
215 isEmpty = false;
216 }
217 else
218 write(">");
219 }
220
221 private void openStartElement()
222 throws XMLStreamException
223 {
224 if (startElementOpened)
225 closeStartTag();
226 else
227 startElementOpened = true;
228 }
229
230
231 protected String writeName(String prefix,String namespaceURI, String localName)
232 throws XMLStreamException
233 {
234 if (!("".equals(namespaceURI)))
235 prefix = getPrefixInternal(namespaceURI);
236 if (!("".equals(prefix))) {
237 write(prefix);
238 write(":");
239 }
240 write(localName);
241 return prefix;
242
243 }
244
245 private String getPrefixInternal(String namespaceURI) {
246 String prefix = context.getPrefix(namespaceURI);
247 if (prefix == null) {
248 return "";
249 }
250 return prefix;
251 }
252 protected String getURIInternal(String prefix) {
253 String uri = context.getNamespaceURI(prefix);
254 if (uri == null) {
255 return "";
256 }
257 return uri;
258 }
259
260
261 protected void openStartTag()
262 throws XMLStreamException
263 {
264 write("<");
265 }
266
267 private void needToWrite(String uri) {
268 if (needToWrite == null) {
269 needToWrite = new HashSet();
270 }
271 needToWrite.add(uri);
272 }
273
274 private void prepareNamespace(String uri)
275 throws XMLStreamException
276 {
277 if (!isPrefixDefaulting) return;
278 if ("".equals(uri)) return;
279 String prefix = getPrefix(uri);
280
281 if (prefix != null) return;
282
283 defaultPrefixCount++;
284 prefix = "ns"+defaultPrefixCount;
285 setPrefix(prefix,uri);
286 }
287
288 private void removeNamespace(String uri) {
289 if (!isPrefixDefaulting) return;
290 needToWrite.remove(uri);
291 }
292
293 private void flushNamespace()
294 throws XMLStreamException
295 {
296 if (!isPrefixDefaulting) return;
297 Iterator i = needToWrite.iterator();
298 while (i.hasNext()) {
299 String uri = (String) i.next();
300 String prefix = context.getPrefix(uri);
301 if (prefix == null) {
302 throw new XMLStreamException("Unable to default prefix with uri:"+
303 uri);
304 }
305 writeNamespace(prefix,uri);
306 }
307 needToWrite.clear();
308 }
309
310
311 protected void writeStartElementInternal(String namespaceURI, String localName)
312 throws XMLStreamException
313 {
314 if (namespaceURI == null)
315 throw new IllegalArgumentException("The namespace URI may not be null");
316 if (localName == null)
317 throw new IllegalArgumentException("The local name may not be null");
318
319 openStartElement();
320 openStartTag();
321 prepareNamespace(namespaceURI);
322 prefixStack.push(writeName("",namespaceURI, localName));
323 localNameStack.push(localName);
324 uriStack.push(namespaceURI);
325 }
326
327 public void writeStartElement(String namespaceURI, String localName)
328 throws XMLStreamException
329 {
330 context.openScope();
331 writeStartElementInternal(namespaceURI,localName);
332 }
333
334
335
336 public void writeStartElement(String prefix,
337 String localName,
338 String namespaceURI)
339 throws XMLStreamException
340 {
341 if (namespaceURI == null)
342 throw new IllegalArgumentException("The namespace URI may not be null");
343 if (localName == null)
344 throw new IllegalArgumentException("The local name may not be null");
345 if (prefix == null)
346 throw new IllegalArgumentException("The prefix may not be null");
347 context.openScope();
348 prepareNamespace(namespaceURI);
349 context.bindNamespace(prefix,namespaceURI);
350 writeStartElementInternal(namespaceURI,localName);
351 }
352
353 public void writeStartElement(String localName)
354 throws XMLStreamException
355 {
356 context.openScope();
357 writeStartElement("",localName);
358 }
359
360 public void writeEmptyElement(String namespaceURI, String localName)
361 throws XMLStreamException
362 {
363 openStartElement();
364 prepareNamespace(namespaceURI);
365 isEmpty = true;
366 write("<");
367 writeName("",namespaceURI,localName);
368 }
369
370 public void writeEmptyElement(String prefix,
371 String localName,
372 String namespaceURI)
373 throws XMLStreamException
374 {
375 openStartElement();
376 prepareNamespace(namespaceURI);
377 isEmpty = true;
378 write("<");
379 write(prefix);
380 write(":");
381 write(localName);
382 }
383
384 public void writeEmptyElement(String localName)
385 throws XMLStreamException
386 {
387 writeEmptyElement("",localName);
388 }
389
390 protected void openEndTag()
391 throws XMLStreamException
392 {
393 write("</");
394 }
395 protected void closeEndTag()
396 throws XMLStreamException
397 {
398 write(">");
399 }
400 public void writeEndElement()
401 throws XMLStreamException
402 {
403 closeStartElement();
404 String prefix = (String) prefixStack.pop();
405 String local = (String) localNameStack.pop();
406 uriStack.pop();
407 openEndTag();
408 writeName(prefix,"",local);
409 closeEndTag();
410 context.closeScope();
411 }
412
413 public void writeRaw(String data)
414 throws XMLStreamException
415 {
416 closeStartElement();
417 write(data);
418 }
419
420 public void close() throws XMLStreamException {
421 flush();
422 }
423 public void flush() throws XMLStreamException {
424 try {
425 writer.flush();
426 } catch (IOException e) {
427 throw new XMLStreamException(e);
428 }
429 }
430
431 public void writeEndDocument()
432 throws XMLStreamException
433 {
434 while(!localNameStack.isEmpty())
435 writeEndElement();
436 }
437
438 public void writeAttribute(String localName, String value)
439 throws XMLStreamException
440 {
441 writeAttribute("",localName,value);
442 }
443 public void writeAttribute(String namespaceURI,
444 String localName,
445 String value)
446 throws XMLStreamException
447 {
448 if (!isOpen())
449 throw new XMLStreamException("A start element must be written before an attribute");
450 prepareNamespace(namespaceURI);
451 write(" ");
452 writeName("",namespaceURI,localName);
453 write("=\"");
454 writeCharactersInternal(value.toCharArray(),0,value.length(),true);
455 write("\"");
456 }
457
458 public void writeAttribute(String prefix,
459 String namespaceURI,
460 String localName,
461 String value)
462 throws XMLStreamException
463 {
464 if (!isOpen())
465 throw new XMLStreamException("A start element must be written before an attribute");
466 prepareNamespace(namespaceURI);
467 context.bindNamespace(prefix,namespaceURI);
468 write(" ");
469 writeName(prefix,namespaceURI,localName);
470 write("=\"");
471 writeCharactersInternal(value.toCharArray(),0,value.length(),true);
472 write("\"");
473 }
474
475 public void writeNamespace(String prefix, String namespaceURI)
476 throws XMLStreamException
477 {
478 if(!isOpen())
479 throw new XMLStreamException("A start element must be written before a namespace");
480 if (prefix == null || "".equals(prefix) || "xmlns".equals(prefix)) {
481 writeDefaultNamespace(namespaceURI);
482 return;
483 }
484 write(" xmlns:");
485 write(prefix);
486 write("=\"");
487 write(namespaceURI);
488 write("\"");
489 setPrefix(prefix,namespaceURI);
490 }
491
492 public void writeDefaultNamespace(String namespaceURI)
493 throws XMLStreamException
494 {
495 if(!isOpen())
496 throw new XMLStreamException("A start element must be written before the default namespace");
497 write(" xmlns");
498 write("=\"");
499 write(namespaceURI);
500 write("\"");
501 setPrefix(DEFAULTNS,namespaceURI);
502 }
503
504 public void writeComment(String data)
505 throws XMLStreamException
506 {
507 closeStartElement();
508 write("<!--");
509 if (data != null)
510 write(data);
511 write("-->");
512 }
513
514 public void writeProcessingInstruction(String target)
515 throws XMLStreamException
516 {
517 closeStartElement();
518 writeProcessingInstruction(target,null);
519 }
520
521 public void writeProcessingInstruction(String target,
522 String text)
523 throws XMLStreamException
524 {
525 closeStartElement();
526 write("<?");
527 if (target != null)
528 write(target);
529 if (text != null) {
530 write(text);
531 }
532 write("?>");
533 }
534
535 public void writeDTD(String dtd)
536 throws XMLStreamException
537 {
538 write(dtd);
539 }
540 public void writeCData(String data)
541 throws XMLStreamException
542 {
543 closeStartElement();
544 write("<![CDATA[");
545 if (data != null)
546 write(data);
547 write("]]>");
548 }
549
550 public void writeEntityRef(String name)
551 throws XMLStreamException
552 {
553 closeStartElement();
554 write("&");
555 write(name);
556 write(";");
557 }
558
559 public void writeStartDocument()
560 throws XMLStreamException
561 {
562 write("<?xml version='1.0' encoding='utf-8'?>");
563 }
564
565 public void writeStartDocument(String version)
566 throws XMLStreamException
567 {
568 write("<?xml version='");
569 write(version);
570 write("'?>");
571 }
572
573 public void writeStartDocument(String encoding,
574 String version)
575 throws XMLStreamException
576 {
577 write("<?xml version='");
578 write(version);
579 write("' encoding='");
580 write(encoding);
581 write("'?>");
582 }
583
584 public void writeCharacters(String text)
585 throws XMLStreamException
586 {
587 closeStartElement();
588 writeCharactersInternal(text.toCharArray(),0,text.length(),false);
589 }
590
591 public void writeCharacters(char[] text, int start, int len)
592 throws XMLStreamException
593 {
594 closeStartElement();
595 writeCharactersInternal(text,start,len,false);
596 }
597
598 public String getPrefix(String uri)
599 throws XMLStreamException
600 {
601 return context.getPrefix(uri);
602 }
603
604 public void setPrefix(String prefix, String uri)
605 throws XMLStreamException
606 {
607 needToWrite(uri);
608 context.bindNamespace(prefix,uri);
609 }
610
611 public void setDefaultNamespace(String uri)
612 throws XMLStreamException
613 {
614 needToWrite(uri);
615 context.bindDefaultNameSpace(uri);
616 }
617
618 public void setNamespaceContext(NamespaceContext context)
619 throws XMLStreamException
620 {
621 if (context == null) throw new NullPointerException("The namespace "+
622 " context may"+
623 " not be null.");
624 this.context = new NamespaceContextImpl(context);
625 }
626
627 public NamespaceContext getNamespaceContext() {
628 return context;
629 }
630
631 public Object getProperty(String name)
632 throws IllegalArgumentException
633 {
634 return config.getProperty(name);
635 }
636
637 public static void main(String args[]) throws Exception {
638
639 /********
640 Writer w = new java.io.OutputStreamWriter(System.out);
641 XMLWriterBase writer =
642 new XMLWriterBase(w);
643 writer.writeStartDocument();
644 writer.setPrefix("c","http://c");
645 writer.setDefaultNamespace("http://c");
646 writer.writeStartElement("http://c","a");
647 writer.writeAttribute("b","blah");
648 writer.writeNamespace("c","http://c");
649 writer.writeDefaultNamespace("http://c");
650 writer.setPrefix("d","http://c");
651 writer.writeEmptyElement("http://c","d");
652 writer.writeAttribute("http://c","chris","fry");
653 writer.writeNamespace("d","http://c");
654 writer.writeCharacters("foo bar foo");
655 writer.writeEndElement();
656 writer.flush();
657 ********/
658 XMLOutputFactory output = XMLOutputFactoryBase.newInstance();
659 output.setProperty(javax.xml.stream.XMLOutputFactory.IS_REPAIRING_NAMESPACES,new Boolean(true));
660 Writer myWriter = new java.io.OutputStreamWriter(
661 new java.io.FileOutputStream("tmp"),"us-ascii");
662 XMLStreamWriter writer2 = output.createXMLStreamWriter(myWriter);
663 writer2.writeStartDocument();
664 writer2.setPrefix("c","http://c");
665 writer2.setDefaultNamespace("http://d");
666 writer2.writeStartElement("http://c","a");
667 writer2.writeAttribute("b","blah");
668 writer2.writeEmptyElement("http://c","d");
669 writer2.writeEmptyElement("http://d","e");
670 writer2.writeEmptyElement("http://e","f");
671 writer2.writeEmptyElement("http://f","g");
672 writer2.writeAttribute("http://c","chris","fry");
673 writer2.writeCharacters("foo bar foo");
674 writer2.writeCharacters("bad char coming[");
675 char c = 0x1024;
676 char[] array = new char[1];
677 array[0]=c;
678 writer2.writeCharacters(new String(array));
679 writer2.writeCharacters("]");
680 writer2.writeEndElement();
681 writer2.flush();
682
683
684 }
685 }