Imported GNU Classpath 0.20
[official-gcc.git] / libjava / classpath / gnu / xml / stream / XMLStreamWriterImpl.java
blob6157296c6b83dd8eafef883c93111f965dd4bfed
1 /* XMLStreamWriterImpl.java --
2 Copyright (C) 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package gnu.xml.stream;
40 import java.io.IOException;
41 import java.io.Writer;
42 import java.util.Enumeration;
43 import java.util.HashSet;
44 import java.util.LinkedList;
45 import java.util.Set;
47 import javax.xml.XMLConstants;
48 import javax.xml.namespace.NamespaceContext;
49 import javax.xml.stream.XMLStreamException;
50 import javax.xml.stream.XMLStreamWriter;
52 import org.xml.sax.helpers.NamespaceSupport;
54 /**
55 * Simple XML stream writer.
57 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
59 public class XMLStreamWriterImpl
60 implements XMLStreamWriter
63 /**
64 * The underlying character stream to write to.
66 protected final Writer writer;
68 /**
69 * The encoding being used.
70 * Note that this must match the encoding of the character stream.
72 protected final String encoding;
74 /**
75 * Whether prefix defaulting is being used.
76 * If true and a prefix has not been defined for a namespace specified on
77 * an element or an attribute, a new prefix and namespace declaration will
78 * be created.
80 protected final boolean prefixDefaulting;
82 /**
83 * The namespace context used to determine the namespace-prefix mappings
84 * in scope.
86 protected NamespaceContext namespaceContext;
88 /**
89 * The stack of elements in scope.
90 * Used to close the remaining elements.
92 private LinkedList elements;
94 /**
95 * Whether a start element has been opened but not yet closed.
97 private boolean inStartElement;
99 /**
100 * Whether we are in an empty element.
102 private boolean emptyElement;
104 private NamespaceSupport namespaces;
105 private int count = 0;
108 * Constructor.
109 * @see #writer
110 * @see #encoding
111 * @see #prefixDefaulting
113 protected XMLStreamWriterImpl(Writer writer, String encoding,
114 boolean prefixDefaulting)
116 this.writer = writer;
117 this.encoding = encoding;
118 this.prefixDefaulting = prefixDefaulting;
119 elements = new LinkedList();
120 namespaces = new NamespaceSupport();
124 * Write the end of a start-element event.
125 * This will close the element if it was defined to be an empty element.
127 private void endStartElement()
128 throws IOException
130 if (!inStartElement)
131 return;
132 if (emptyElement)
134 writer.write('/');
135 elements.removeLast();
136 namespaces.popContext();
137 emptyElement = false;
139 writer.write('>');
140 inStartElement = false;
143 public void writeStartElement(String localName)
144 throws XMLStreamException
148 endStartElement();
149 namespaces.pushContext();
151 writer.write('<');
152 writer.write(localName);
154 elements.addLast(new String[] { null, localName });
155 inStartElement = true;
157 catch (IOException e)
159 XMLStreamException e2 = new XMLStreamException(e);
160 e2.initCause(e);
161 throw e2;
165 public void writeStartElement(String namespaceURI, String localName)
166 throws XMLStreamException
170 endStartElement();
171 namespaces.pushContext();
173 String prefix = getPrefix(namespaceURI);
174 boolean isDeclared = (prefix != null);
175 if (!isDeclared)
177 if (prefixDefaulting)
178 prefix = createPrefix(namespaceURI);
179 else
180 throw new XMLStreamException("namespace " + namespaceURI +
181 " has not been declared");
183 writer.write('<');
184 if (!"".equals(prefix))
186 writer.write(prefix);
187 writer.write(':');
189 writer.write(localName);
190 inStartElement = true;
191 if (!isDeclared)
193 writeNamespace(prefix, namespaceURI);
196 elements.addLast(new String[] { prefix, localName });
198 catch (IOException e)
200 XMLStreamException e2 = new XMLStreamException(e);
201 e2.initCause(e);
202 throw e2;
207 * Creates a new unique prefix in the document.
208 * Subclasses may override this method to provide a suitably unique prefix
209 * for the given namespace.
210 * @param namespaceURI the namespace URI
212 protected String createPrefix(String namespaceURI)
214 Set prefixes = new HashSet();
215 for (Enumeration e = namespaces.getPrefixes(); e.hasMoreElements(); )
216 prefixes.add(e.nextElement());
217 String ret;
220 ret = "ns" + (count++);
222 while (prefixes.contains(ret));
223 return ret;
226 public void writeStartElement(String prefix, String localName,
227 String namespaceURI)
228 throws XMLStreamException
232 endStartElement();
233 namespaces.pushContext();
235 String currentPrefix = getPrefix(namespaceURI);
236 boolean isCurrent = prefix.equals(currentPrefix);
237 writer.write('<');
238 if (!"".equals(prefix))
240 writer.write(prefix);
241 writer.write(':');
243 writer.write(localName);
244 if (prefixDefaulting && !isCurrent)
246 writeNamespace(prefix, namespaceURI);
249 elements.addLast(new String[] { prefix, localName });
250 inStartElement = true;
252 catch (IOException e)
254 XMLStreamException e2 = new XMLStreamException(e);
255 e2.initCause(e);
256 throw e2;
260 public void writeEmptyElement(String namespaceURI, String localName)
261 throws XMLStreamException
263 writeStartElement(namespaceURI, localName);
264 emptyElement = true;
267 public void writeEmptyElement(String prefix, String localName,
268 String namespaceURI)
269 throws XMLStreamException
271 writeStartElement(prefix, localName, namespaceURI);
272 emptyElement = true;
275 public void writeEmptyElement(String localName)
276 throws XMLStreamException
278 writeStartElement(localName);
279 emptyElement = true;
282 public void writeEndElement()
283 throws XMLStreamException
285 if (elements.isEmpty())
286 throw new IllegalStateException("no matching start element");
289 endStartElement();
290 String[] element = (String[]) elements.removeLast();
291 namespaces.popContext();
293 writer.write('<');
294 writer.write('/');
295 if (element[0] != null && !"".equals(element[0]))
297 writer.write(element[0]);
298 writer.write(':');
300 writer.write(element[1]);
301 writer.write('>');
303 catch (IOException e)
305 XMLStreamException e2 = new XMLStreamException(e);
306 e2.initCause(e);
307 throw e2;
311 public void writeEndDocument()
312 throws XMLStreamException
314 while (!elements.isEmpty())
315 writeEndElement();
318 public void close()
319 throws XMLStreamException
321 flush();
324 public void flush()
325 throws XMLStreamException
329 writer.flush();
331 catch (IOException e)
333 XMLStreamException e2 = new XMLStreamException(e);
334 e2.initCause(e);
335 throw e2;
339 public void writeAttribute(String localName, String value)
340 throws XMLStreamException
342 if (!inStartElement)
343 throw new IllegalStateException();
346 writer.write(' ');
347 writer.write(localName);
348 writer.write('=');
349 writer.write('"');
350 writeEncoded(value, true);
351 writer.write('"');
353 catch (IOException e)
355 XMLStreamException e2 = new XMLStreamException(e);
356 e2.initCause(e);
357 throw e2;
361 public void writeAttribute(String prefix, String namespaceURI,
362 String localName, String value)
363 throws XMLStreamException
365 if (!inStartElement)
366 throw new IllegalStateException();
369 String currentPrefix = getPrefix(namespaceURI);
370 if (currentPrefix == null)
372 if (prefixDefaulting)
373 writeNamespace(prefix, namespaceURI);
374 else
375 throw new XMLStreamException("namespace " + namespaceURI +
376 " is not bound");
378 else if (!currentPrefix.equals(prefix))
379 throw new XMLStreamException("namespace " + namespaceURI +
380 " is bound to prefix " +
381 currentPrefix);
382 writer.write(' ');
383 if (!"".equals(prefix))
385 writer.write(prefix);
386 writer.write(':');
388 writer.write(localName);
389 writer.write('=');
390 writer.write('"');
391 writeEncoded(value, true);
392 writer.write('"');
394 catch (IOException e)
396 XMLStreamException e2 = new XMLStreamException(e);
397 e2.initCause(e);
398 throw e2;
402 public void writeAttribute(String namespaceURI, String localName,
403 String value)
404 throws XMLStreamException
406 if (!inStartElement)
407 throw new IllegalStateException();
410 String prefix = getPrefix(namespaceURI);
411 if (prefix == null)
413 if (prefixDefaulting)
415 prefix = XMLConstants.DEFAULT_NS_PREFIX;
416 writeNamespace(prefix, namespaceURI);
418 else
419 throw new XMLStreamException("namespace " + namespaceURI +
420 " is not bound");
422 writer.write(' ');
423 if (!"".equals(prefix))
425 writer.write(prefix);
426 writer.write(':');
428 writer.write(localName);
429 writer.write('=');
430 writer.write('"');
431 writeEncoded(value, true);
432 writer.write('"');
434 catch (IOException e)
436 XMLStreamException e2 = new XMLStreamException(e);
437 e2.initCause(e);
438 throw e2;
442 public void writeNamespace(String prefix, String namespaceURI)
443 throws XMLStreamException
445 if (!inStartElement)
446 throw new IllegalStateException();
449 if (prefix == null)
450 prefix = XMLConstants.DEFAULT_NS_PREFIX;
452 setPrefix(prefix, namespaceURI);
454 writer.write(' ');
455 writer.write("xmlns");
456 if (!XMLConstants.DEFAULT_NS_PREFIX.equals(prefix))
458 writer.write(':');
459 writer.write(prefix);
461 writer.write('=');
462 writer.write('"');
463 writer.write(namespaceURI);
464 writer.write('"');
466 catch (IOException e)
468 XMLStreamException e2 = new XMLStreamException(e);
469 e2.initCause(e);
470 throw e2;
474 public void writeDefaultNamespace(String namespaceURI)
475 throws XMLStreamException
477 writeNamespace(XMLConstants.DEFAULT_NS_PREFIX, namespaceURI);
480 public void writeComment(String data)
481 throws XMLStreamException
485 endStartElement();
487 if (data != null && data.indexOf("--") != -1)
488 throw new IllegalArgumentException(data);
490 writer.write("<!--");
491 if (data != null)
492 writer.write(data);
493 writer.write("-->");
495 catch (IOException e)
497 XMLStreamException e2 = new XMLStreamException(e);
498 e2.initCause(e);
499 throw e2;
503 public void writeProcessingInstruction(String target)
504 throws XMLStreamException
506 writeProcessingInstruction(target, null);
509 public void writeProcessingInstruction(String target, String data)
510 throws XMLStreamException
514 endStartElement();
516 writer.write('<');
517 writer.write('?');
518 writer.write(target);
519 if (data != null)
521 writer.write(' ');
522 writer.write(data);
524 writer.write('?');
525 writer.write('>');
527 catch (IOException e)
529 XMLStreamException e2 = new XMLStreamException(e);
530 e2.initCause(e);
531 throw e2;
535 public void writeCData(String data)
536 throws XMLStreamException
540 endStartElement();
542 if (data.indexOf("]]") != -1)
543 throw new IllegalArgumentException(data);
545 writer.write("<![CDATA[");
546 writer.write(data);
547 writer.write("]]>");
549 catch (IOException e)
551 XMLStreamException e2 = new XMLStreamException(e);
552 e2.initCause(e);
553 throw e2;
557 public void writeDTD(String dtd)
558 throws XMLStreamException
562 writer.write("<!DOCTYPE ");
563 writer.write(dtd);
564 writer.write('>');
566 catch (IOException e)
568 XMLStreamException e2 = new XMLStreamException(e);
569 e2.initCause(e);
570 throw e2;
574 public void writeEntityRef(String name)
575 throws XMLStreamException
579 endStartElement();
581 writer.write('&');
582 writer.write(name);
583 writer.write(';');
585 catch (IOException e)
587 XMLStreamException e2 = new XMLStreamException(e);
588 e2.initCause(e);
589 throw e2;
593 public void writeStartDocument()
594 throws XMLStreamException
596 writeStartDocument(null, null);
599 public void writeStartDocument(String version)
600 throws XMLStreamException
602 writeStartDocument(null, version);
605 public void writeStartDocument(String encoding, String version)
606 throws XMLStreamException
608 if (version == null)
609 version = "1.0";
610 encoding = this.encoding; // YES: the parameter must be ignored
611 if (encoding == null)
612 encoding = "UTF-8";
613 if (!"1.0".equals(version) && !"1.1".equals(version))
614 throw new IllegalArgumentException(version);
617 writer.write("<?xml version=\"");
618 writer.write(version);
619 writer.write("\" encoding=\"");
620 writer.write(encoding);
621 writer.write("\"?>");
622 writer.write(System.getProperty("line.separator"));
624 catch (IOException e)
626 XMLStreamException e2 = new XMLStreamException(e);
627 e2.initCause(e);
628 throw e2;
632 public void writeCharacters(String text)
633 throws XMLStreamException
637 endStartElement();
639 if (text != null)
640 writeEncoded(text, false);
642 catch (IOException e)
644 XMLStreamException e2 = new XMLStreamException(e);
645 e2.initCause(e);
646 throw e2;
650 public void writeCharacters(char[] text, int start, int len)
651 throws XMLStreamException
655 endStartElement();
657 int end = start + len;
658 len = 0;
659 for (int i = start; i < end; i++)
661 char c = text[i];
662 if (c == '<' || c == '>' || c == '&')
664 writer.write(text, start, len);
665 if (c == '<')
666 writer.write("&lt;");
667 else if (c == '>')
668 writer.write("&gt;");
669 else
670 writer.write("&amp;");
671 start = i + 1;
672 len = 0;
674 else
675 len++;
677 if (len > 0)
678 writer.write(text, start, len);
680 catch (IOException e)
682 XMLStreamException e2 = new XMLStreamException(e);
683 e2.initCause(e);
684 throw e2;
688 public String getPrefix(String uri)
689 throws XMLStreamException
691 String prefix = namespaces.getPrefix(uri);
692 if (prefix == null && namespaceContext != null)
693 prefix = namespaceContext.getPrefix(uri);
694 return prefix;
697 public void setPrefix(String prefix, String uri)
698 throws XMLStreamException
700 if (!namespaces.declarePrefix(prefix, uri))
701 throw new XMLStreamException("illegal prefix " + prefix);
704 public void setDefaultNamespace(String uri)
705 throws XMLStreamException
707 if (!namespaces.declarePrefix(XMLConstants.DEFAULT_NS_PREFIX, uri))
708 throw new XMLStreamException("illegal default namespace prefix");
711 public void setNamespaceContext(NamespaceContext context)
712 throws XMLStreamException
714 namespaceContext = context;
717 public NamespaceContext getNamespaceContext()
719 return namespaceContext;
722 public Object getProperty(String name)
723 throws IllegalArgumentException
725 throw new IllegalArgumentException(name);
729 * Write the specified text, ensuring that the content is suitably encoded
730 * for XML.
731 * @param text the text to write
732 * @param inAttr whether we are in an attribute value
734 private void writeEncoded(String text, boolean inAttr)
735 throws IOException
737 char[] chars = text.toCharArray();
738 int start = 0;
739 int end = chars.length;
740 int len = 0;
741 for (int i = start; i < end; i++)
743 char c = chars[i];
744 if (c == '<' || c == '>' || c == '&')
746 writer.write(chars, start, len);
747 if (c == '<')
748 writer.write("&lt;");
749 else if (c == '>')
750 writer.write("&gt;");
751 else
752 writer.write("&amp;");
753 start = i + 1;
754 len = 0;
756 else if (inAttr && (c == '"' || c == '\''))
758 writer.write(chars, start, len);
759 if (c == '"')
760 writer.write("&quot;");
761 else
762 writer.write("&apos;");
763 start = i + 1;
764 len = 0;
766 else
767 len++;
769 if (len > 0)
770 writer.write(chars, start, len);