1:
37:
38: package ;
39:
40: import ;
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46:
47: import ;
48: import ;
49: import ;
50: import ;
51:
52: import ;
53:
54:
59: public class XMLStreamWriterImpl
60: implements XMLStreamWriter
61: {
62:
63:
66: protected final Writer writer;
67:
68:
72: protected final String encoding;
73:
74:
80: protected final boolean prefixDefaulting;
81:
82:
86: protected NamespaceContext namespaceContext;
87:
88:
92: private LinkedList elements;
93:
94:
97: private boolean inStartElement;
98:
99:
102: private boolean emptyElement;
103:
104: private NamespaceSupport namespaces;
105: private int count = 0;
106:
107: private boolean xml11;
108: private boolean hasXML11RestrictedChars;
109:
110:
116: protected XMLStreamWriterImpl(Writer writer, String encoding,
117: boolean prefixDefaulting)
118: {
119: this.writer = writer;
120: this.encoding = encoding;
121: this.prefixDefaulting = prefixDefaulting;
122: elements = new LinkedList();
123: namespaces = new NamespaceSupport();
124: }
125:
126:
130: private void endStartElement()
131: throws IOException
132: {
133: if (!inStartElement)
134: return;
135: if (emptyElement)
136: {
137: writer.write('/');
138: elements.removeLast();
139: namespaces.popContext();
140: emptyElement = false;
141: }
142: writer.write('>');
143: inStartElement = false;
144: }
145:
146: public void writeStartElement(String localName)
147: throws XMLStreamException
148: {
149: try
150: {
151: if (!isName(localName))
152: throw new IllegalArgumentException("illegal Name: " + localName);
153:
154: endStartElement();
155: namespaces.pushContext();
156:
157: writer.write('<');
158: writer.write(localName);
159:
160: elements.addLast(new String[] { null, localName });
161: inStartElement = true;
162: }
163: catch (IOException e)
164: {
165: XMLStreamException e2 = new XMLStreamException(e);
166: e2.initCause(e);
167: throw e2;
168: }
169: }
170:
171: public void writeStartElement(String namespaceURI, String localName)
172: throws XMLStreamException
173: {
174: try
175: {
176: if (namespaceURI != null && !isURI(namespaceURI))
177: throw new IllegalArgumentException("illegal URI: " + namespaceURI);
178: if (!isName(localName))
179: throw new IllegalArgumentException("illegal Name: " + localName);
180:
181: endStartElement();
182: namespaces.pushContext();
183:
184: String prefix = getPrefix(namespaceURI);
185: boolean isDeclared = (prefix != null);
186: if (!isDeclared)
187: {
188: if (prefixDefaulting)
189: prefix = createPrefix(namespaceURI);
190: else
191: throw new XMLStreamException("namespace " + namespaceURI +
192: " has not been declared");
193: }
194: writer.write('<');
195: if (!"".equals(prefix))
196: {
197: writer.write(prefix);
198: writer.write(':');
199: }
200: writer.write(localName);
201: inStartElement = true;
202: if (!isDeclared)
203: {
204: writeNamespaceImpl(prefix, namespaceURI);
205: }
206:
207: elements.addLast(new String[] { prefix, localName });
208: }
209: catch (IOException e)
210: {
211: XMLStreamException e2 = new XMLStreamException(e);
212: e2.initCause(e);
213: throw e2;
214: }
215: }
216:
217:
223: protected String createPrefix(String namespaceURI)
224: {
225: Set prefixes = new HashSet();
226: for (Enumeration e = namespaces.getPrefixes(); e.hasMoreElements(); )
227: prefixes.add(e.nextElement());
228: String ret;
229: do
230: {
231: ret = "ns" + (count++);
232: }
233: while (prefixes.contains(ret));
234: return ret;
235: }
236:
237: public void writeStartElement(String prefix, String localName,
238: String namespaceURI)
239: throws XMLStreamException
240: {
241: try
242: {
243: if (namespaceURI != null && !isURI(namespaceURI))
244: throw new IllegalArgumentException("illegal URI: " + namespaceURI);
245: if (prefix != null && !isNCName(prefix))
246: throw new IllegalArgumentException("illegal NCName: " + prefix);
247: if (!isNCName(localName))
248: throw new IllegalArgumentException("illegal NCName: " + localName);
249:
250: endStartElement();
251: namespaces.pushContext();
252:
253: String currentPrefix = getPrefix(namespaceURI);
254: boolean isCurrent = prefix.equals(currentPrefix);
255: writer.write('<');
256: if (!"".equals(prefix))
257: {
258: writer.write(prefix);
259: writer.write(':');
260: }
261: writer.write(localName);
262: if (prefixDefaulting && !isCurrent)
263: {
264: writeNamespaceImpl(prefix, namespaceURI);
265: }
266:
267: elements.addLast(new String[] { prefix, localName });
268: inStartElement = true;
269: }
270: catch (IOException e)
271: {
272: XMLStreamException e2 = new XMLStreamException(e);
273: e2.initCause(e);
274: throw e2;
275: }
276: }
277:
278: public void writeEmptyElement(String namespaceURI, String localName)
279: throws XMLStreamException
280: {
281: writeStartElement(namespaceURI, localName);
282: emptyElement = true;
283: }
284:
285: public void writeEmptyElement(String prefix, String localName,
286: String namespaceURI)
287: throws XMLStreamException
288: {
289: writeStartElement(prefix, localName, namespaceURI);
290: emptyElement = true;
291: }
292:
293: public void writeEmptyElement(String localName)
294: throws XMLStreamException
295: {
296: writeStartElement(localName);
297: emptyElement = true;
298: }
299:
300: public void writeEndElement()
301: throws XMLStreamException
302: {
303: if (elements.isEmpty())
304: throw new IllegalStateException("no matching start element");
305: try
306: {
307: endStartElement();
308: String[] element = (String[]) elements.removeLast();
309: namespaces.popContext();
310:
311: writer.write('<');
312: writer.write('/');
313: if (element[0] != null && !"".equals(element[0]))
314: {
315: writer.write(element[0]);
316: writer.write(':');
317: }
318: writer.write(element[1]);
319: writer.write('>');
320: }
321: catch (IOException e)
322: {
323: XMLStreamException e2 = new XMLStreamException(e);
324: e2.initCause(e);
325: throw e2;
326: }
327: }
328:
329: public void writeEndDocument()
330: throws XMLStreamException
331: {
332: while (!elements.isEmpty())
333: writeEndElement();
334: }
335:
336: public void close()
337: throws XMLStreamException
338: {
339: flush();
340: }
341:
342: public void flush()
343: throws XMLStreamException
344: {
345: try
346: {
347: writer.flush();
348: }
349: catch (IOException e)
350: {
351: XMLStreamException e2 = new XMLStreamException(e);
352: e2.initCause(e);
353: throw e2;
354: }
355: }
356:
357: public void writeAttribute(String localName, String value)
358: throws XMLStreamException
359: {
360: if (!inStartElement)
361: throw new IllegalStateException();
362: try
363: {
364: if (!isName(localName))
365: throw new IllegalArgumentException("illegal Name: " + localName);
366: if (!isChars(value))
367: throw new IllegalArgumentException("illegal character: " + value);
368:
369: writer.write(' ');
370: writer.write(localName);
371: writer.write('=');
372: writer.write('"');
373: if (hasXML11RestrictedChars)
374: writeEncodedWithRestrictedChars(value, true);
375: else
376: writeEncoded(value, true);
377: writer.write('"');
378: }
379: catch (IOException e)
380: {
381: XMLStreamException e2 = new XMLStreamException(e);
382: e2.initCause(e);
383: throw e2;
384: }
385: }
386:
387: public void writeAttribute(String prefix, String namespaceURI,
388: String localName, String value)
389: throws XMLStreamException
390: {
391: if (!inStartElement)
392: throw new IllegalStateException();
393: try
394: {
395: if (namespaceURI != null && !isURI(namespaceURI))
396: throw new IllegalArgumentException("illegal URI: " + namespaceURI);
397: if (prefix != null && !isNCName(prefix))
398: throw new IllegalArgumentException("illegal NCName: " + prefix);
399: if (!isNCName(localName))
400: throw new IllegalArgumentException("illegal NCName: " + localName);
401: if (!isChars(value))
402: throw new IllegalArgumentException("illegal character: " + value);
403:
404: String currentPrefix = getPrefix(namespaceURI);
405: if (currentPrefix == null)
406: {
407: if (prefixDefaulting)
408: writeNamespaceImpl(prefix, namespaceURI);
409: else
410: throw new XMLStreamException("namespace " + namespaceURI +
411: " is not bound");
412: }
413: else if (!currentPrefix.equals(prefix))
414: throw new XMLStreamException("namespace " + namespaceURI +
415: " is bound to prefix " +
416: currentPrefix);
417: writer.write(' ');
418: if (!"".equals(prefix))
419: {
420: writer.write(prefix);
421: writer.write(':');
422: }
423: writer.write(localName);
424: writer.write('=');
425: writer.write('"');
426: if (hasXML11RestrictedChars)
427: writeEncodedWithRestrictedChars(value, true);
428: else
429: writeEncoded(value, true);
430: writer.write('"');
431: }
432: catch (IOException e)
433: {
434: XMLStreamException e2 = new XMLStreamException(e);
435: e2.initCause(e);
436: throw e2;
437: }
438: }
439:
440: public void writeAttribute(String namespaceURI, String localName,
441: String value)
442: throws XMLStreamException
443: {
444: if (!inStartElement)
445: throw new IllegalStateException();
446: try
447: {
448: if (namespaceURI != null && !isURI(namespaceURI))
449: throw new IllegalArgumentException("illegal URI: " + namespaceURI);
450: if (!isName(localName))
451: throw new IllegalArgumentException("illegal Name: " + localName);
452: if (!isChars(value))
453: throw new IllegalArgumentException("illegal character: " + value);
454:
455: String prefix = getPrefix(namespaceURI);
456: if (prefix == null)
457: {
458: if (prefixDefaulting)
459: {
460: prefix = XMLConstants.DEFAULT_NS_PREFIX;
461: writeNamespaceImpl(prefix, namespaceURI);
462: }
463: else
464: throw new XMLStreamException("namespace " + namespaceURI +
465: " is not bound");
466: }
467: writer.write(' ');
468: if (!"".equals(prefix))
469: {
470: writer.write(prefix);
471: writer.write(':');
472: }
473: writer.write(localName);
474: writer.write('=');
475: writer.write('"');
476: if (hasXML11RestrictedChars)
477: writeEncodedWithRestrictedChars(value, true);
478: else
479: writeEncoded(value, true);
480: writer.write('"');
481: }
482: catch (IOException e)
483: {
484: XMLStreamException e2 = new XMLStreamException(e);
485: e2.initCause(e);
486: throw e2;
487: }
488: }
489:
490: public void writeNamespace(String prefix, String namespaceURI)
491: throws XMLStreamException
492: {
493: if (!inStartElement)
494: throw new IllegalStateException();
495: try
496: {
497: if (!isURI(namespaceURI))
498: throw new IllegalArgumentException("illegal URI: " + namespaceURI);
499: if (!isNCName(prefix))
500: throw new IllegalArgumentException("illegal NCName: " + prefix);
501: }
502: catch (IOException e)
503: {
504: XMLStreamException e2 = new XMLStreamException(e);
505: e2.initCause(e);
506: throw e2;
507: }
508: writeNamespaceImpl(prefix, namespaceURI);
509: }
510:
511: private void writeNamespaceImpl(String prefix, String namespaceURI)
512: throws XMLStreamException
513: {
514: try
515: {
516: if (prefix == null)
517: prefix = XMLConstants.DEFAULT_NS_PREFIX;
518:
519: setPrefix(prefix, namespaceURI);
520:
521: writer.write(' ');
522: writer.write("xmlns");
523: if (!XMLConstants.DEFAULT_NS_PREFIX.equals(prefix))
524: {
525: writer.write(':');
526: writer.write(prefix);
527: }
528: writer.write('=');
529: writer.write('"');
530: writer.write(namespaceURI);
531: writer.write('"');
532: }
533: catch (IOException e)
534: {
535: XMLStreamException e2 = new XMLStreamException(e);
536: e2.initCause(e);
537: throw e2;
538: }
539: }
540:
541: public void writeDefaultNamespace(String namespaceURI)
542: throws XMLStreamException
543: {
544: if (!inStartElement)
545: throw new IllegalStateException();
546: if (!isURI(namespaceURI))
547: throw new IllegalArgumentException("illegal URI: " + namespaceURI);
548: writeNamespaceImpl(XMLConstants.DEFAULT_NS_PREFIX, namespaceURI);
549: }
550:
551: public void writeComment(String data)
552: throws XMLStreamException
553: {
554: if (data == null)
555: return;
556: try
557: {
558: if (!isChars(data))
559: throw new IllegalArgumentException("illegal XML character: " + data);
560: if (data.indexOf("--") != -1)
561: throw new IllegalArgumentException("illegal comment: " + data);
562:
563: endStartElement();
564:
565: writer.write("<!--");
566: if (hasXML11RestrictedChars)
567: {
568: int[] seq = UnicodeReader.toCodePointArray(data);
569: for (int i = 0; i < seq.length; i++)
570: {
571: int c = seq[i];
572: if (XMLParser.isXML11RestrictedChar(c))
573: writer.write("&#x" + Integer.toHexString(c) + ";");
574: else
575: writer.write(Character.toChars(i));
576: }
577: }
578: else
579: writer.write(data);
580: writer.write("-->");
581: }
582: catch (IOException e)
583: {
584: XMLStreamException e2 = new XMLStreamException(e);
585: e2.initCause(e);
586: throw e2;
587: }
588: }
589:
590: public void writeProcessingInstruction(String target)
591: throws XMLStreamException
592: {
593: writeProcessingInstruction(target, null);
594: }
595:
596: public void writeProcessingInstruction(String target, String data)
597: throws XMLStreamException
598: {
599: try
600: {
601: if (!isName(target) || "xml".equalsIgnoreCase(target))
602: throw new IllegalArgumentException("illegal PITarget: " + target);
603: if (data != null && !isChars(data))
604: throw new IllegalArgumentException("illegal XML character: " + data);
605:
606: endStartElement();
607:
608: writer.write('<');
609: writer.write('?');
610: writer.write(target);
611: if (data != null)
612: {
613: writer.write(' ');
614: if (hasXML11RestrictedChars)
615: {
616: int[] seq = UnicodeReader.toCodePointArray(data);
617: for (int i = 0; i < seq.length; i++)
618: {
619: int c = seq[i];
620: if (XMLParser.isXML11RestrictedChar(c))
621: writer.write("&#x" + Integer.toHexString(c) + ";");
622: else
623: writer.write(Character.toChars(i));
624: }
625: }
626: else
627: writer.write(data);
628: }
629: writer.write('?');
630: writer.write('>');
631: }
632: catch (IOException e)
633: {
634: XMLStreamException e2 = new XMLStreamException(e);
635: e2.initCause(e);
636: throw e2;
637: }
638: }
639:
640: public void writeCData(String data)
641: throws XMLStreamException
642: {
643: try
644: {
645: if (!isChars(data) || hasXML11RestrictedChars)
646: throw new IllegalArgumentException("illegal XML character: " + data);
647: if (data.indexOf("]]") != -1)
648: throw new IllegalArgumentException("illegal CDATA section: " + data);
649:
650: endStartElement();
651:
652: writer.write("<![CDATA[");
653: writer.write(data);
654: writer.write("]]>");
655: }
656: catch (IOException e)
657: {
658: XMLStreamException e2 = new XMLStreamException(e);
659: e2.initCause(e);
660: throw e2;
661: }
662: }
663:
664: public void writeDTD(String dtd)
665: throws XMLStreamException
666: {
667: try
668: {
669:
670:
671: writer.write("<!DOCTYPE ");
672: writer.write(dtd);
673: writer.write('>');
674: }
675: catch (IOException e)
676: {
677: XMLStreamException e2 = new XMLStreamException(e);
678: e2.initCause(e);
679: throw e2;
680: }
681: }
682:
683: public void writeEntityRef(String name)
684: throws XMLStreamException
685: {
686: try
687: {
688: if (!isName(name))
689: throw new IllegalArgumentException("illegal Name: " + name);
690:
691: endStartElement();
692:
693: writer.write('&');
694: writer.write(name);
695: writer.write(';');
696: }
697: catch (IOException e)
698: {
699: XMLStreamException e2 = new XMLStreamException(e);
700: e2.initCause(e);
701: throw e2;
702: }
703: }
704:
705: public void writeStartDocument()
706: throws XMLStreamException
707: {
708: writeStartDocument(null, null);
709: }
710:
711: public void writeStartDocument(String version)
712: throws XMLStreamException
713: {
714: writeStartDocument(null, version);
715: }
716:
717: public void writeStartDocument(String encoding, String version)
718: throws XMLStreamException
719: {
720: if (version == null)
721: version = "1.0";
722: else if ("1.1".equals(version))
723: xml11 = true;
724: encoding = this.encoding;
725: if (encoding == null)
726: encoding = "UTF-8";
727: if (!"1.0".equals(version) && !"1.1".equals(version))
728: throw new IllegalArgumentException(version);
729: try
730: {
731: writer.write("<?xml version=\"");
732: writer.write(version);
733: writer.write("\" encoding=\"");
734: writer.write(encoding);
735: writer.write("\"?>");
736: writer.write(System.getProperty("line.separator"));
737: }
738: catch (IOException e)
739: {
740: XMLStreamException e2 = new XMLStreamException(e);
741: e2.initCause(e);
742: throw e2;
743: }
744: }
745:
746: public void writeCharacters(String text)
747: throws XMLStreamException
748: {
749: if (text == null)
750: return;
751: try
752: {
753: if (!isChars(text))
754: throw new IllegalArgumentException("illegal XML character: " + text);
755:
756: endStartElement();
757:
758: if (hasXML11RestrictedChars)
759: writeEncodedWithRestrictedChars(text, false);
760: else
761: writeEncoded(text, false);
762: }
763: catch (IOException e)
764: {
765: XMLStreamException e2 = new XMLStreamException(e);
766: e2.initCause(e);
767: throw e2;
768: }
769: }
770:
771: public void writeCharacters(char[] text, int start, int len)
772: throws XMLStreamException
773: {
774: writeCharacters(new String(text, start, len));
775: }
776:
777: public String getPrefix(String uri)
778: throws XMLStreamException
779: {
780: String prefix = namespaces.getPrefix(uri);
781: if (prefix == null && namespaceContext != null)
782: prefix = namespaceContext.getPrefix(uri);
783: return prefix;
784: }
785:
786: public void setPrefix(String prefix, String uri)
787: throws XMLStreamException
788: {
789: try
790: {
791: if (!isURI(uri))
792: throw new IllegalArgumentException("illegal URI: " + uri);
793: if (!isNCName(prefix))
794: throw new IllegalArgumentException("illegal NCName: " + prefix);
795: }
796: catch (IOException e)
797: {
798: XMLStreamException e2 = new XMLStreamException(e);
799: e2.initCause(e);
800: throw e2;
801: }
802: if (!namespaces.declarePrefix(prefix, uri))
803: throw new XMLStreamException("illegal prefix " + prefix);
804: }
805:
806: public void setDefaultNamespace(String uri)
807: throws XMLStreamException
808: {
809: if (!isURI(uri))
810: throw new IllegalArgumentException("illegal URI: " + uri);
811: if (!namespaces.declarePrefix(XMLConstants.DEFAULT_NS_PREFIX, uri))
812: throw new XMLStreamException("illegal default namespace prefix");
813: }
814:
815: public void setNamespaceContext(NamespaceContext context)
816: throws XMLStreamException
817: {
818: namespaceContext = context;
819: }
820:
821: public NamespaceContext getNamespaceContext()
822: {
823: return namespaceContext;
824: }
825:
826: public Object getProperty(String name)
827: throws IllegalArgumentException
828: {
829: throw new IllegalArgumentException(name);
830: }
831:
832:
838: private void writeEncoded(String text, boolean inAttr)
839: throws IOException
840: {
841: char[] chars = text.toCharArray();
842: int start = 0;
843: int end = chars.length;
844: int len = 0;
845: for (int i = start; i < end; i++)
846: {
847: char c = chars[i];
848: if (c == '<' || c == '>' || c == '&')
849: {
850: writer.write(chars, start, len);
851: if (c == '<')
852: writer.write("<");
853: else if (c == '>')
854: writer.write(">");
855: else
856: writer.write("&");
857: start = i + 1;
858: len = 0;
859: }
860: else if (inAttr && (c == '"' || c == '\''))
861: {
862: writer.write(chars, start, len);
863: if (c == '"')
864: writer.write(""");
865: else
866: writer.write("'");
867: start = i + 1;
868: len = 0;
869: }
870: else
871: len++;
872: }
873: if (len > 0)
874: writer.write(chars, start, len);
875: }
876:
877:
881: private void writeEncodedWithRestrictedChars(String text, boolean inAttr)
882: throws IOException
883: {
884: int[] seq = UnicodeReader.toCodePointArray(text);
885: for (int i = 0; i < seq.length; i++)
886: {
887: int c = seq[i];
888: switch (c)
889: {
890: case 0x3c:
891: writer.write("<");
892: break;
893: case 0x3e:
894: writer.write(">");
895: break;
896: case 0x26:
897: writer.write("&");
898: break;
899: case 0x22:
900: if (inAttr)
901: writer.write(""");
902: else
903: writer.write(c);
904: break;
905: case 0x27:
906: if (inAttr)
907: writer.write("'");
908: else
909: writer.write(c);
910: break;
911: default:
912: if (XMLParser.isXML11RestrictedChar(c))
913: writer.write("&#x" + Integer.toHexString(c) + ";");
914: else
915: {
916: char[] chars = Character.toChars(c);
917: writer.write(chars, 0, chars.length);
918: }
919: }
920: }
921: }
922:
923: private boolean isName(String text)
924: throws IOException
925: {
926: if (text == null)
927: return false;
928: int[] seq = UnicodeReader.toCodePointArray(text);
929: if (seq.length < 1)
930: return false;
931: if (!XMLParser.isNameStartCharacter(seq[0], xml11))
932: return false;
933: for (int i = 1; i < seq.length; i++)
934: {
935: if (!XMLParser.isNameCharacter(seq[i], xml11))
936: return false;
937: }
938: return true;
939: }
940:
941: private boolean isNCName(String text)
942: throws IOException
943: {
944: if (text == null)
945: return false;
946: int[] seq = UnicodeReader.toCodePointArray(text);
947: if (seq.length < 1)
948: return false;
949: if (!XMLParser.isNameStartCharacter(seq[0], xml11) || seq[0] == 0x3a)
950: return false;
951: for (int i = 1; i < seq.length; i++)
952: {
953: if (!XMLParser.isNameCharacter(seq[i], xml11) || seq[i] == 0x3a)
954: return false;
955: }
956: return true;
957: }
958:
959: private boolean isChars(String text)
960: throws IOException
961: {
962: if (text == null)
963: return false;
964: int[] seq = UnicodeReader.toCodePointArray(text);
965: hasXML11RestrictedChars = false;
966: if (xml11)
967: {
968: for (int i = 0; i < seq.length; i++)
969: {
970: if (!XMLParser.isXML11Char(seq[i]))
971: return false;
972: if (XMLParser.isXML11RestrictedChar(seq[i]))
973: hasXML11RestrictedChars = true;
974: }
975: }
976: else
977: {
978: for (int i = 0; i < seq.length; i++)
979: {
980: if (!XMLParser.isChar(seq[i]))
981: return false;
982: }
983: }
984: return true;
985: }
986:
987: private boolean isURI(String text)
988: {
989: if (text == null)
990: return false;
991: char[] chars = text.toCharArray();
992: if (chars.length < 1)
993: return false;
994: for (int i = 0; i < chars.length; i++)
995: {
996: if (chars[i] < 0x20 || chars[i] >= 0x7f)
997: return false;
998: }
999: return true;
1000: }
1001:
1002: }