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)
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
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
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
;
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
;
55 * Simple XML stream writer.
57 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
59 public class XMLStreamWriterImpl
60 implements XMLStreamWriter
64 * The underlying character stream to write to.
66 protected final Writer writer
;
69 * The encoding being used.
70 * Note that this must match the encoding of the character stream.
72 protected final String encoding
;
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
80 protected final boolean prefixDefaulting
;
83 * The namespace context used to determine the namespace-prefix mappings
86 protected NamespaceContext namespaceContext
;
89 * The stack of elements in scope.
90 * Used to close the remaining elements.
92 private LinkedList elements
;
95 * Whether a start element has been opened but not yet closed.
97 private boolean inStartElement
;
100 * Whether we are in an empty element.
102 private boolean emptyElement
;
104 private NamespaceSupport namespaces
;
105 private int count
= 0;
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()
135 elements
.removeLast();
136 namespaces
.popContext();
137 emptyElement
= false;
140 inStartElement
= false;
143 public void writeStartElement(String localName
)
144 throws XMLStreamException
149 namespaces
.pushContext();
152 writer
.write(localName
);
154 elements
.addLast(new String
[] { null, localName
});
155 inStartElement
= true;
157 catch (IOException e
)
159 XMLStreamException e2
= new XMLStreamException(e
);
165 public void writeStartElement(String namespaceURI
, String localName
)
166 throws XMLStreamException
171 namespaces
.pushContext();
173 String prefix
= getPrefix(namespaceURI
);
174 boolean isDeclared
= (prefix
!= null);
177 if (prefixDefaulting
)
178 prefix
= createPrefix(namespaceURI
);
180 throw new XMLStreamException("namespace " + namespaceURI
+
181 " has not been declared");
184 if (!"".equals(prefix
))
186 writer
.write(prefix
);
189 writer
.write(localName
);
190 inStartElement
= true;
193 writeNamespace(prefix
, namespaceURI
);
196 elements
.addLast(new String
[] { prefix
, localName
});
198 catch (IOException e
)
200 XMLStreamException e2
= new XMLStreamException(e
);
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());
220 ret
= "ns" + (count
++);
222 while (prefixes
.contains(ret
));
226 public void writeStartElement(String prefix
, String localName
,
228 throws XMLStreamException
233 namespaces
.pushContext();
235 String currentPrefix
= getPrefix(namespaceURI
);
236 boolean isCurrent
= prefix
.equals(currentPrefix
);
238 if (!"".equals(prefix
))
240 writer
.write(prefix
);
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
);
260 public void writeEmptyElement(String namespaceURI
, String localName
)
261 throws XMLStreamException
263 writeStartElement(namespaceURI
, localName
);
267 public void writeEmptyElement(String prefix
, String localName
,
269 throws XMLStreamException
271 writeStartElement(prefix
, localName
, namespaceURI
);
275 public void writeEmptyElement(String localName
)
276 throws XMLStreamException
278 writeStartElement(localName
);
282 public void writeEndElement()
283 throws XMLStreamException
285 if (elements
.isEmpty())
286 throw new IllegalStateException("no matching start element");
290 String
[] element
= (String
[]) elements
.removeLast();
291 namespaces
.popContext();
295 if (element
[0] != null && !"".equals(element
[0]))
297 writer
.write(element
[0]);
300 writer
.write(element
[1]);
303 catch (IOException e
)
305 XMLStreamException e2
= new XMLStreamException(e
);
311 public void writeEndDocument()
312 throws XMLStreamException
314 while (!elements
.isEmpty())
319 throws XMLStreamException
325 throws XMLStreamException
331 catch (IOException e
)
333 XMLStreamException e2
= new XMLStreamException(e
);
339 public void writeAttribute(String localName
, String value
)
340 throws XMLStreamException
343 throw new IllegalStateException();
347 writer
.write(localName
);
350 writeEncoded(value
, true);
353 catch (IOException e
)
355 XMLStreamException e2
= new XMLStreamException(e
);
361 public void writeAttribute(String prefix
, String namespaceURI
,
362 String localName
, String value
)
363 throws XMLStreamException
366 throw new IllegalStateException();
369 String currentPrefix
= getPrefix(namespaceURI
);
370 if (currentPrefix
== null)
372 if (prefixDefaulting
)
373 writeNamespace(prefix
, namespaceURI
);
375 throw new XMLStreamException("namespace " + namespaceURI
+
378 else if (!currentPrefix
.equals(prefix
))
379 throw new XMLStreamException("namespace " + namespaceURI
+
380 " is bound to prefix " +
383 if (!"".equals(prefix
))
385 writer
.write(prefix
);
388 writer
.write(localName
);
391 writeEncoded(value
, true);
394 catch (IOException e
)
396 XMLStreamException e2
= new XMLStreamException(e
);
402 public void writeAttribute(String namespaceURI
, String localName
,
404 throws XMLStreamException
407 throw new IllegalStateException();
410 String prefix
= getPrefix(namespaceURI
);
413 if (prefixDefaulting
)
415 prefix
= XMLConstants
.DEFAULT_NS_PREFIX
;
416 writeNamespace(prefix
, namespaceURI
);
419 throw new XMLStreamException("namespace " + namespaceURI
+
423 if (!"".equals(prefix
))
425 writer
.write(prefix
);
428 writer
.write(localName
);
431 writeEncoded(value
, true);
434 catch (IOException e
)
436 XMLStreamException e2
= new XMLStreamException(e
);
442 public void writeNamespace(String prefix
, String namespaceURI
)
443 throws XMLStreamException
446 throw new IllegalStateException();
450 prefix
= XMLConstants
.DEFAULT_NS_PREFIX
;
452 setPrefix(prefix
, namespaceURI
);
455 writer
.write("xmlns");
456 if (!XMLConstants
.DEFAULT_NS_PREFIX
.equals(prefix
))
459 writer
.write(prefix
);
463 writer
.write(namespaceURI
);
466 catch (IOException e
)
468 XMLStreamException e2
= new XMLStreamException(e
);
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
487 if (data
!= null && data
.indexOf("--") != -1)
488 throw new IllegalArgumentException(data
);
490 writer
.write("<!--");
495 catch (IOException e
)
497 XMLStreamException e2
= new XMLStreamException(e
);
503 public void writeProcessingInstruction(String target
)
504 throws XMLStreamException
506 writeProcessingInstruction(target
, null);
509 public void writeProcessingInstruction(String target
, String data
)
510 throws XMLStreamException
518 writer
.write(target
);
527 catch (IOException e
)
529 XMLStreamException e2
= new XMLStreamException(e
);
535 public void writeCData(String data
)
536 throws XMLStreamException
542 if (data
.indexOf("]]") != -1)
543 throw new IllegalArgumentException(data
);
545 writer
.write("<![CDATA[");
549 catch (IOException e
)
551 XMLStreamException e2
= new XMLStreamException(e
);
557 public void writeDTD(String dtd
)
558 throws XMLStreamException
562 writer
.write("<!DOCTYPE ");
566 catch (IOException e
)
568 XMLStreamException e2
= new XMLStreamException(e
);
574 public void writeEntityRef(String name
)
575 throws XMLStreamException
585 catch (IOException e
)
587 XMLStreamException e2
= new XMLStreamException(e
);
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
610 encoding
= this.encoding
; // YES: the parameter must be ignored
611 if (encoding
== null)
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
);
632 public void writeCharacters(String text
)
633 throws XMLStreamException
640 writeEncoded(text
, false);
642 catch (IOException e
)
644 XMLStreamException e2
= new XMLStreamException(e
);
650 public void writeCharacters(char[] text
, int start
, int len
)
651 throws XMLStreamException
657 int end
= start
+ len
;
659 for (int i
= start
; i
< end
; i
++)
662 if (c
== '<' || c
== '>' || c
== '&')
664 writer
.write(text
, start
, len
);
666 writer
.write("<");
668 writer
.write(">");
670 writer
.write("&");
678 writer
.write(text
, start
, len
);
680 catch (IOException e
)
682 XMLStreamException e2
= new XMLStreamException(e
);
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
);
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
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
)
737 char[] chars
= text
.toCharArray();
739 int end
= chars
.length
;
741 for (int i
= start
; i
< end
; i
++)
744 if (c
== '<' || c
== '>' || c
== '&')
746 writer
.write(chars
, start
, len
);
748 writer
.write("<");
750 writer
.write(">");
752 writer
.write("&");
756 else if (inAttr
&& (c
== '"' || c
== '\''))
758 writer
.write(chars
, start
, len
);
760 writer
.write(""");
762 writer
.write("'");
770 writer
.write(chars
, start
, len
);