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 && !isPrefix(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 && !isPrefix(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 (prefix == null || "".equals(prefix) || "xmlns".equals(prefix))
494: {
495: writeDefaultNamespace(namespaceURI);
496: return;
497: }
498: if (!inStartElement)
499: throw new IllegalStateException();
500: try
501: {
502: if (!isURI(namespaceURI))
503: throw new IllegalArgumentException("illegal URI: " + namespaceURI);
504: if (!isPrefix(prefix))
505: throw new IllegalArgumentException("illegal NCName: " + prefix);
506: }
507: catch (IOException e)
508: {
509: XMLStreamException e2 = new XMLStreamException(e);
510: e2.initCause(e);
511: throw e2;
512: }
513: writeNamespaceImpl(prefix, namespaceURI);
514: }
515:
516: private void writeNamespaceImpl(String prefix, String namespaceURI)
517: throws XMLStreamException
518: {
519: try
520: {
521: if (prefix == null)
522: prefix = XMLConstants.DEFAULT_NS_PREFIX;
523:
524: setPrefix(prefix, namespaceURI);
525:
526: writer.write(' ');
527: writer.write("xmlns");
528: if (!XMLConstants.DEFAULT_NS_PREFIX.equals(prefix))
529: {
530: writer.write(':');
531: writer.write(prefix);
532: }
533: writer.write('=');
534: writer.write('"');
535: writer.write(namespaceURI);
536: writer.write('"');
537: }
538: catch (IOException e)
539: {
540: XMLStreamException e2 = new XMLStreamException(e);
541: e2.initCause(e);
542: throw e2;
543: }
544: }
545:
546: public void writeDefaultNamespace(String namespaceURI)
547: throws XMLStreamException
548: {
549: if (!inStartElement)
550: throw new IllegalStateException();
551: if (!isURI(namespaceURI))
552: throw new IllegalArgumentException("illegal URI: " + namespaceURI);
553: writeNamespaceImpl(XMLConstants.DEFAULT_NS_PREFIX, namespaceURI);
554: }
555:
556: public void writeComment(String data)
557: throws XMLStreamException
558: {
559: if (data == null)
560: return;
561: try
562: {
563: if (!isChars(data))
564: throw new IllegalArgumentException("illegal XML character: " + data);
565: if (data.indexOf("--") != -1)
566: throw new IllegalArgumentException("illegal comment: " + data);
567:
568: endStartElement();
569:
570: writer.write("<!--");
571: if (hasXML11RestrictedChars)
572: {
573: int[] seq = UnicodeReader.toCodePointArray(data);
574: for (int i = 0; i < seq.length; i++)
575: {
576: int c = seq[i];
577: if (XMLParser.isXML11RestrictedChar(c))
578: writer.write("&#x" + Integer.toHexString(c) + ";");
579: else
580: writer.write(Character.toChars(i));
581: }
582: }
583: else
584: writer.write(data);
585: writer.write("-->");
586: }
587: catch (IOException e)
588: {
589: XMLStreamException e2 = new XMLStreamException(e);
590: e2.initCause(e);
591: throw e2;
592: }
593: }
594:
595: public void writeProcessingInstruction(String target)
596: throws XMLStreamException
597: {
598: writeProcessingInstruction(target, null);
599: }
600:
601: public void writeProcessingInstruction(String target, String data)
602: throws XMLStreamException
603: {
604: try
605: {
606: if (!isName(target) || "xml".equalsIgnoreCase(target))
607: throw new IllegalArgumentException("illegal PITarget: " + target);
608: if (data != null && !isChars(data))
609: throw new IllegalArgumentException("illegal XML character: " + data);
610:
611: endStartElement();
612:
613: writer.write('<');
614: writer.write('?');
615: writer.write(target);
616: if (data != null)
617: {
618: writer.write(' ');
619: if (hasXML11RestrictedChars)
620: {
621: int[] seq = UnicodeReader.toCodePointArray(data);
622: for (int i = 0; i < seq.length; i++)
623: {
624: int c = seq[i];
625: if (XMLParser.isXML11RestrictedChar(c))
626: writer.write("&#x" + Integer.toHexString(c) + ";");
627: else
628: writer.write(Character.toChars(i));
629: }
630: }
631: else
632: writer.write(data);
633: }
634: writer.write('?');
635: writer.write('>');
636: }
637: catch (IOException e)
638: {
639: XMLStreamException e2 = new XMLStreamException(e);
640: e2.initCause(e);
641: throw e2;
642: }
643: }
644:
645: public void writeCData(String data)
646: throws XMLStreamException
647: {
648: try
649: {
650: if (!isChars(data) || hasXML11RestrictedChars)
651: throw new IllegalArgumentException("illegal XML character: " + data);
652: if (data.indexOf("]]") != -1)
653: throw new IllegalArgumentException("illegal CDATA section: " + data);
654:
655: endStartElement();
656:
657: writer.write("<![CDATA[");
658: writer.write(data);
659: writer.write("]]>");
660: }
661: catch (IOException e)
662: {
663: XMLStreamException e2 = new XMLStreamException(e);
664: e2.initCause(e);
665: throw e2;
666: }
667: }
668:
669: public void writeDTD(String dtd)
670: throws XMLStreamException
671: {
672: try
673: {
674:
675:
676: writer.write("<!DOCTYPE ");
677: writer.write(dtd);
678: writer.write('>');
679: }
680: catch (IOException e)
681: {
682: XMLStreamException e2 = new XMLStreamException(e);
683: e2.initCause(e);
684: throw e2;
685: }
686: }
687:
688: public void writeEntityRef(String name)
689: throws XMLStreamException
690: {
691: try
692: {
693: if (!isName(name))
694: throw new IllegalArgumentException("illegal Name: " + name);
695:
696: endStartElement();
697:
698: writer.write('&');
699: writer.write(name);
700: writer.write(';');
701: }
702: catch (IOException e)
703: {
704: XMLStreamException e2 = new XMLStreamException(e);
705: e2.initCause(e);
706: throw e2;
707: }
708: }
709:
710: public void writeStartDocument()
711: throws XMLStreamException
712: {
713: writeStartDocument(null, null);
714: }
715:
716: public void writeStartDocument(String version)
717: throws XMLStreamException
718: {
719: writeStartDocument(null, version);
720: }
721:
722: public void writeStartDocument(String encoding, String version)
723: throws XMLStreamException
724: {
725: if (version == null)
726: version = "1.0";
727: else if ("1.1".equals(version))
728: xml11 = true;
729: encoding = this.encoding;
730: if (encoding == null)
731: encoding = "UTF-8";
732: if (!"1.0".equals(version) && !"1.1".equals(version))
733: throw new IllegalArgumentException(version);
734: try
735: {
736: writer.write("<?xml version=\"");
737: writer.write(version);
738: writer.write("\" encoding=\"");
739: writer.write(encoding);
740: writer.write("\"?>");
741: writer.write(System.getProperty("line.separator"));
742: }
743: catch (IOException e)
744: {
745: XMLStreamException e2 = new XMLStreamException(e);
746: e2.initCause(e);
747: throw e2;
748: }
749: }
750:
751: public void writeCharacters(String text)
752: throws XMLStreamException
753: {
754: if (text == null)
755: return;
756: try
757: {
758: if (!isChars(text))
759: throw new IllegalArgumentException("illegal XML character: " + text);
760:
761: endStartElement();
762:
763: if (hasXML11RestrictedChars)
764: writeEncodedWithRestrictedChars(text, false);
765: else
766: writeEncoded(text, false);
767: }
768: catch (IOException e)
769: {
770: XMLStreamException e2 = new XMLStreamException(e);
771: e2.initCause(e);
772: throw e2;
773: }
774: }
775:
776: public void writeCharacters(char[] text, int start, int len)
777: throws XMLStreamException
778: {
779: writeCharacters(new String(text, start, len));
780: }
781:
782: public String getPrefix(String uri)
783: throws XMLStreamException
784: {
785: String prefix = namespaces.getPrefix(uri);
786: if (prefix == null && namespaceContext != null)
787: prefix = namespaceContext.getPrefix(uri);
788: return prefix;
789: }
790:
791: public void setPrefix(String prefix, String uri)
792: throws XMLStreamException
793: {
794: try
795: {
796: if (!isURI(uri))
797: throw new IllegalArgumentException("illegal URI: " + uri);
798: if (!isPrefix(prefix))
799: throw new IllegalArgumentException("illegal NCName: " + prefix);
800: }
801: catch (IOException e)
802: {
803: XMLStreamException e2 = new XMLStreamException(e);
804: e2.initCause(e);
805: throw e2;
806: }
807: if (!namespaces.declarePrefix(prefix, uri))
808: throw new XMLStreamException("illegal prefix " + prefix);
809: }
810:
811: public void setDefaultNamespace(String uri)
812: throws XMLStreamException
813: {
814: if (!isURI(uri))
815: throw new IllegalArgumentException("illegal URI: " + uri);
816: if (!namespaces.declarePrefix(XMLConstants.DEFAULT_NS_PREFIX, uri))
817: throw new XMLStreamException("illegal default namespace prefix");
818: }
819:
820: public void setNamespaceContext(NamespaceContext context)
821: throws XMLStreamException
822: {
823: namespaceContext = context;
824: }
825:
826: public NamespaceContext getNamespaceContext()
827: {
828: return namespaceContext;
829: }
830:
831: public Object getProperty(String name)
832: throws IllegalArgumentException
833: {
834: throw new IllegalArgumentException(name);
835: }
836:
837:
843: private void writeEncoded(String text, boolean inAttr)
844: throws IOException
845: {
846: char[] chars = text.toCharArray();
847: int start = 0;
848: int end = chars.length;
849: int len = 0;
850: for (int i = start; i < end; i++)
851: {
852: char c = chars[i];
853: if (c == '<' || c == '>' || c == '&')
854: {
855: writer.write(chars, start, len);
856: if (c == '<')
857: writer.write("<");
858: else if (c == '>')
859: writer.write(">");
860: else
861: writer.write("&");
862: start = i + 1;
863: len = 0;
864: }
865: else if (inAttr && (c == '"' || c == '\''))
866: {
867: writer.write(chars, start, len);
868: if (c == '"')
869: writer.write(""");
870: else
871: writer.write("'");
872: start = i + 1;
873: len = 0;
874: }
875: else
876: len++;
877: }
878: if (len > 0)
879: writer.write(chars, start, len);
880: }
881:
882:
886: private void writeEncodedWithRestrictedChars(String text, boolean inAttr)
887: throws IOException
888: {
889: int[] seq = UnicodeReader.toCodePointArray(text);
890: for (int i = 0; i < seq.length; i++)
891: {
892: int c = seq[i];
893: switch (c)
894: {
895: case 0x3c:
896: writer.write("<");
897: break;
898: case 0x3e:
899: writer.write(">");
900: break;
901: case 0x26:
902: writer.write("&");
903: break;
904: case 0x22:
905: if (inAttr)
906: writer.write(""");
907: else
908: writer.write(c);
909: break;
910: case 0x27:
911: if (inAttr)
912: writer.write("'");
913: else
914: writer.write(c);
915: break;
916: default:
917: if (XMLParser.isXML11RestrictedChar(c))
918: writer.write("&#x" + Integer.toHexString(c) + ";");
919: else
920: {
921: char[] chars = Character.toChars(c);
922: writer.write(chars, 0, chars.length);
923: }
924: }
925: }
926: }
927:
928: private boolean isName(String text)
929: throws IOException
930: {
931: if (text == null)
932: return false;
933: int[] seq = UnicodeReader.toCodePointArray(text);
934: if (seq.length < 1)
935: return false;
936: if (!XMLParser.isNameStartCharacter(seq[0], xml11))
937: return false;
938: for (int i = 1; i < seq.length; i++)
939: {
940: if (!XMLParser.isNameCharacter(seq[i], xml11))
941: return false;
942: }
943: return true;
944: }
945:
946: private boolean isPrefix(String text)
947: throws IOException
948: {
949: if (XMLConstants.DEFAULT_NS_PREFIX.equals(text)) {
950: return true;
951: }
952: return isNCName(text);
953: }
954:
955: private boolean isNCName(String text)
956: throws IOException
957: {
958: if (text == null)
959: return false;
960: int[] seq = UnicodeReader.toCodePointArray(text);
961: if (seq.length < 1)
962: return false;
963: if (!XMLParser.isNameStartCharacter(seq[0], xml11) || seq[0] == 0x3a)
964: return false;
965: for (int i = 1; i < seq.length; i++)
966: {
967: if (!XMLParser.isNameCharacter(seq[i], xml11) || seq[i] == 0x3a)
968: return false;
969: }
970: return true;
971: }
972:
973: private boolean isChars(String text)
974: throws IOException
975: {
976: if (text == null)
977: return false;
978: int[] seq = UnicodeReader.toCodePointArray(text);
979: hasXML11RestrictedChars = false;
980: if (xml11)
981: {
982: for (int i = 0; i < seq.length; i++)
983: {
984: if (!XMLParser.isXML11Char(seq[i]))
985: return false;
986: if (XMLParser.isXML11RestrictedChar(seq[i]))
987: hasXML11RestrictedChars = true;
988: }
989: }
990: else
991: {
992: for (int i = 0; i < seq.length; i++)
993: {
994: if (!XMLParser.isChar(seq[i]))
995: return false;
996: }
997: }
998: return true;
999: }
1000:
1001: private boolean isURI(String text)
1002: {
1003: if (text == null)
1004: return false;
1005: char[] chars = text.toCharArray();
1006: if (chars.length < 1)
1007: return false;
1008: for (int i = 0; i < chars.length; i++)
1009: {
1010: if (chars[i] < 0x20 || chars[i] >= 0x7f)
1011: return false;
1012: }
1013: return true;
1014: }
1015:
1016: }