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:
668: try
669: {
670: if (!isName(dtd))
671: throw new IllegalArgumentException("illegal Name: " + dtd);
672:
673: writer.write("<!DOCTYPE ");
674: writer.write(dtd);
675: writer.write('>');
676: }
677: catch (IOException e)
678: {
679: XMLStreamException e2 = new XMLStreamException(e);
680: e2.initCause(e);
681: throw e2;
682: }
683: }
684:
685: public void writeEntityRef(String name)
686: throws XMLStreamException
687: {
688: try
689: {
690: if (!isName(name))
691: throw new IllegalArgumentException("illegal Name: " + name);
692:
693: endStartElement();
694:
695: writer.write('&');
696: writer.write(name);
697: writer.write(';');
698: }
699: catch (IOException e)
700: {
701: XMLStreamException e2 = new XMLStreamException(e);
702: e2.initCause(e);
703: throw e2;
704: }
705: }
706:
707: public void writeStartDocument()
708: throws XMLStreamException
709: {
710: writeStartDocument(null, null);
711: }
712:
713: public void writeStartDocument(String version)
714: throws XMLStreamException
715: {
716: writeStartDocument(null, version);
717: }
718:
719: public void writeStartDocument(String encoding, String version)
720: throws XMLStreamException
721: {
722: if (version == null)
723: version = "1.0";
724: else if ("1.1".equals(version))
725: xml11 = true;
726: encoding = this.encoding;
727: if (encoding == null)
728: encoding = "UTF-8";
729: if (!"1.0".equals(version) && !"1.1".equals(version))
730: throw new IllegalArgumentException(version);
731: try
732: {
733: writer.write("<?xml version=\"");
734: writer.write(version);
735: writer.write("\" encoding=\"");
736: writer.write(encoding);
737: writer.write("\"?>");
738: writer.write(System.getProperty("line.separator"));
739: }
740: catch (IOException e)
741: {
742: XMLStreamException e2 = new XMLStreamException(e);
743: e2.initCause(e);
744: throw e2;
745: }
746: }
747:
748: public void writeCharacters(String text)
749: throws XMLStreamException
750: {
751: if (text == null)
752: return;
753: try
754: {
755: if (!isChars(text))
756: throw new IllegalArgumentException("illegal XML character: " + text);
757:
758: endStartElement();
759:
760: if (hasXML11RestrictedChars)
761: writeEncodedWithRestrictedChars(text, false);
762: else
763: writeEncoded(text, false);
764: }
765: catch (IOException e)
766: {
767: XMLStreamException e2 = new XMLStreamException(e);
768: e2.initCause(e);
769: throw e2;
770: }
771: }
772:
773: public void writeCharacters(char[] text, int start, int len)
774: throws XMLStreamException
775: {
776: writeCharacters(new String(text, start, len));
777: }
778:
779: public String getPrefix(String uri)
780: throws XMLStreamException
781: {
782: String prefix = namespaces.getPrefix(uri);
783: if (prefix == null && namespaceContext != null)
784: prefix = namespaceContext.getPrefix(uri);
785: return prefix;
786: }
787:
788: public void setPrefix(String prefix, String uri)
789: throws XMLStreamException
790: {
791: try
792: {
793: if (!isURI(uri))
794: throw new IllegalArgumentException("illegal URI: " + uri);
795: if (!isNCName(prefix))
796: throw new IllegalArgumentException("illegal NCName: " + prefix);
797: }
798: catch (IOException e)
799: {
800: XMLStreamException e2 = new XMLStreamException(e);
801: e2.initCause(e);
802: throw e2;
803: }
804: if (!namespaces.declarePrefix(prefix, uri))
805: throw new XMLStreamException("illegal prefix " + prefix);
806: }
807:
808: public void setDefaultNamespace(String uri)
809: throws XMLStreamException
810: {
811: if (!isURI(uri))
812: throw new IllegalArgumentException("illegal URI: " + uri);
813: if (!namespaces.declarePrefix(XMLConstants.DEFAULT_NS_PREFIX, uri))
814: throw new XMLStreamException("illegal default namespace prefix");
815: }
816:
817: public void setNamespaceContext(NamespaceContext context)
818: throws XMLStreamException
819: {
820: namespaceContext = context;
821: }
822:
823: public NamespaceContext getNamespaceContext()
824: {
825: return namespaceContext;
826: }
827:
828: public Object getProperty(String name)
829: throws IllegalArgumentException
830: {
831: throw new IllegalArgumentException(name);
832: }
833:
834:
840: private void writeEncoded(String text, boolean inAttr)
841: throws IOException
842: {
843: char[] chars = text.toCharArray();
844: int start = 0;
845: int end = chars.length;
846: int len = 0;
847: for (int i = start; i < end; i++)
848: {
849: char c = chars[i];
850: if (c == '<' || c == '>' || c == '&')
851: {
852: writer.write(chars, start, len);
853: if (c == '<')
854: writer.write("<");
855: else if (c == '>')
856: writer.write(">");
857: else
858: writer.write("&");
859: start = i + 1;
860: len = 0;
861: }
862: else if (inAttr && (c == '"' || c == '\''))
863: {
864: writer.write(chars, start, len);
865: if (c == '"')
866: writer.write(""");
867: else
868: writer.write("'");
869: start = i + 1;
870: len = 0;
871: }
872: else
873: len++;
874: }
875: if (len > 0)
876: writer.write(chars, start, len);
877: }
878:
879:
883: private void writeEncodedWithRestrictedChars(String text, boolean inAttr)
884: throws IOException
885: {
886: int[] seq = UnicodeReader.toCodePointArray(text);
887: for (int i = 0; i < seq.length; i++)
888: {
889: int c = seq[i];
890: switch (c)
891: {
892: case 0x3c:
893: writer.write("<");
894: break;
895: case 0x3e:
896: writer.write(">");
897: break;
898: case 0x26:
899: writer.write("&");
900: break;
901: case 0x22:
902: if (inAttr)
903: writer.write(""");
904: else
905: writer.write(c);
906: break;
907: case 0x27:
908: if (inAttr)
909: writer.write("'");
910: else
911: writer.write(c);
912: break;
913: default:
914: if (XMLParser.isXML11RestrictedChar(c))
915: writer.write("&#x" + Integer.toHexString(c) + ";");
916: else
917: {
918: char[] chars = Character.toChars(c);
919: writer.write(chars, 0, chars.length);
920: }
921: }
922: }
923: }
924:
925: private boolean isName(String text)
926: throws IOException
927: {
928: if (text == null)
929: return false;
930: int[] seq = UnicodeReader.toCodePointArray(text);
931: if (seq.length < 1)
932: return false;
933: if (!XMLParser.isNameStartCharacter(seq[0], xml11))
934: return false;
935: for (int i = 1; i < seq.length; i++)
936: {
937: if (!XMLParser.isNameCharacter(seq[i], xml11))
938: return false;
939: }
940: return true;
941: }
942:
943: private boolean isNCName(String text)
944: throws IOException
945: {
946: if (text == null)
947: return false;
948: int[] seq = UnicodeReader.toCodePointArray(text);
949: if (seq.length < 1)
950: return false;
951: if (!XMLParser.isNameStartCharacter(seq[0], xml11) || seq[0] == 0x3a)
952: return false;
953: for (int i = 1; i < seq.length; i++)
954: {
955: if (!XMLParser.isNameCharacter(seq[i], xml11) || seq[i] == 0x3a)
956: return false;
957: }
958: return true;
959: }
960:
961: private boolean isChars(String text)
962: throws IOException
963: {
964: if (text == null)
965: return false;
966: int[] seq = UnicodeReader.toCodePointArray(text);
967: hasXML11RestrictedChars = false;
968: if (xml11)
969: {
970: for (int i = 0; i < seq.length; i++)
971: {
972: if (!XMLParser.isXML11Char(seq[i]))
973: return false;
974: if (XMLParser.isXML11RestrictedChar(seq[i]))
975: hasXML11RestrictedChars = true;
976: }
977: }
978: else
979: {
980: for (int i = 0; i < seq.length; i++)
981: {
982: if (!XMLParser.isChar(seq[i]))
983: return false;
984: }
985: }
986: return true;
987: }
988:
989: private boolean isURI(String text)
990: {
991: if (text == null)
992: return false;
993: char[] chars = text.toCharArray();
994: if (chars.length < 1)
995: return false;
996: for (int i = 0; i < chars.length; i++)
997: {
998: if (chars[i] < 0x20 || chars[i] >= 0x7f)
999: return false;
1000: }
1001: return true;
1002: }
1003:
1004: }