Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #105 from gburgett/stream_reader

Created StAX reader and writer to read/write directly from JDOM Documents...
  • Loading branch information...
commit 98bbbd5431cd7c566b487104e504403423c988a9 2 parents 54ed0cc + ba464a7
@rolfl rolfl authored
Showing with 3,087 additions and 17 deletions.
  1. +13 −0 core/src/java/org/jdom2/Element.java
  2. +164 −0 core/src/java/org/jdom2/jaxb/JDOMNamespaceContext.java
  3. +803 −0 core/src/java/org/jdom2/jaxb/JDOMStreamReader.java
  4. +713 −0 core/src/java/org/jdom2/jaxb/JDOMStreamWriter.java
  5. +368 −0 core/src/java/org/jdom2/output/StAXAsStreamReader.java
  6. +88 −0 core/src/java/org/jdom2/output/support/AbstractStAXAsStreamProcessor.java
  7. +18 −14 core/src/java/org/jdom2/output/support/AbstractStAXStreamProcessor.java
  8. +85 −0 core/src/java/org/jdom2/output/support/StAXAsStreamProcessor.java
  9. +102 −0 core/src/java/org/jdom2/util/NamespaceStack.java
  10. +173 −0 test/src/java/org/jdom2/test/cases/jaxb/TestJDOMNamespaceContext.java
  11. +221 −0 test/src/java/org/jdom2/test/cases/jaxb/TestJDOMStreamReader.java
  12. +210 −0 test/src/java/org/jdom2/test/cases/jaxb/TestJDOMStreamWriter.java
  13. +73 −0 test/src/java/org/jdom2/test/cases/output/AbstractTestRoundTrip.java
  14. +24 −0 test/src/java/org/jdom2/test/cases/output/TestStAXReaderWriter.java
  15. +22 −0 test/src/java/org/jdom2/test/cases/output/TestStAXWriterReader.java
  16. +7 −2 test/src/java/org/jdom2/test/util/UnitTestUtil.java
  17. +3 −1 test/src/resources/DOMBuilder/complex.xml
View
13 core/src/java/org/jdom2/Element.java
@@ -1095,9 +1095,13 @@ AttributeList getAttributeList() {
* This returns the complete set of attributes for this element, as a
* <code>List</code> of <code>Attribute</code> objects in no particular
* order, or an empty list if there are none.
+ * </p>
* The returned list is "live" and changes to it affect the
* element's actual attributes.
* </p>
+ * Use the methods {@link #hasAttributes()} or {@link #getAttributesSize()}
+ * if you just want to see whether there are attributes. Calling this method
+ * may be inefficient if there are no Attributes.
*
* @return attributes for the element
*/
@@ -1105,6 +1109,15 @@ AttributeList getAttributeList() {
return getAttributeList();
}
+
+ /**
+ * Get the number of Attributes currently attached to this Element.
+ * @return the number of Attributes attached.
+ */
+ public int getAttributesSize() {
+ return attributes == null ? 0 : attributes.size();
+ }
+
/**
* <p>
* This returns the attribute for this element with the given name
View
164 core/src/java/org/jdom2/jaxb/JDOMNamespaceContext.java
@@ -0,0 +1,164 @@
+/*--
+
+ Copyright (C) 2000-2012 Jason Hunter & Brett McLaughlin.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions, and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions, and the disclaimer that follows
+ these conditions in the documentation and/or other materials
+ provided with the distribution.
+
+ 3. The name "JDOM" must not be used to endorse or promote products
+ derived from this software without prior written permission. For
+ written permission, please contact <request_AT_jdom_DOT_org>.
+
+ 4. Products derived from this software may not be called "JDOM", nor
+ may "JDOM" appear in their name, without prior written permission
+ from the JDOM Project Management <request_AT_jdom_DOT_org>.
+
+ In addition, we request (but do not require) that you include in the
+ end-user documentation provided with the redistribution and/or in the
+ software itself an acknowledgement equivalent to the following:
+ "This product includes software developed by the
+ JDOM Project (http://www.jdom.org/)."
+ Alternatively, the acknowledgment may be graphical using the logos
+ available at http://www.jdom.org/images/logos.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+ This software consists of voluntary contributions made by many
+ individuals on behalf of the JDOM Project and was originally
+ created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
+ Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
+ on the JDOM Project, please see <http://www.jdom.org/>.
+
+ */
+
+package org.jdom2.jaxb;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.namespace.NamespaceContext;
+
+import org.jdom2.JDOMConstants;
+import org.jdom2.Namespace;
+import org.jdom2.internal.ArrayCopy;
+
+/**
+ * A Read-Only {@link NamespaceContext} that describes namespaces found
+ * in a JDOM node.
+ * @author gordon burgett https://github.com/gburgett
+ */
+public final class JDOMNamespaceContext implements NamespaceContext {
+
+ private final Namespace[] namespacearray;
+
+ /**
+ * Create a read-only representation of the input namespace list.
+ * @param namespaces the Namespace instances to represent.
+ */
+ public JDOMNamespaceContext(final Namespace[] namespaces){
+ if (namespaces == null) {
+ throw new IllegalArgumentException("Cannot process a null Namespace list");
+ }
+ this.namespacearray = ArrayCopy.copyOf(namespaces, namespaces.length);
+ for (int i = 1; i < namespacearray.length; i++) {
+ final Namespace n = namespacearray[i];
+ if (n == null) {
+ throw new IllegalArgumentException("Cannot process null namespace at position " + i);
+ }
+ final String p = n.getPrefix();
+ for (int j = 0; j < i; j++) {
+ if (p.equals(namespacearray[j].getPrefix())) {
+ throw new IllegalArgumentException("Cannot process multiple namespaces with the prefix '" + p + "'.");
+ }
+ }
+ }
+ }
+
+ @Override
+ public String getNamespaceURI(final String prefix) {
+ if (prefix == null) {
+ throw new IllegalArgumentException("NamespaceContext requires a non-null prefix");
+ }
+ if (JDOMConstants.NS_PREFIX_XML.equals(prefix)) {
+ return JDOMConstants.NS_URI_XML;
+ }
+ if (JDOMConstants.NS_PREFIX_XMLNS.equals(prefix)) {
+ return JDOMConstants.NS_URI_XMLNS;
+ }
+
+ for(final Namespace n : namespacearray){
+ if(n.getPrefix().equals(prefix)){
+ return n.getURI();
+ }
+ }
+
+ return "";
+ }
+
+ @Override
+ public String getPrefix(final String namespaceURI) {
+ if (namespaceURI == null) {
+ throw new IllegalArgumentException("NamespaceContext requires a non-null Namespace URI");
+ }
+ if (JDOMConstants.NS_URI_XML.equals(namespaceURI)) {
+ return JDOMConstants.NS_PREFIX_XML;
+ }
+ if (JDOMConstants.NS_URI_XMLNS.equals(namespaceURI)) {
+ return JDOMConstants.NS_PREFIX_XMLNS;
+ }
+
+ for(final Namespace n : namespacearray){
+ if(n.getURI().equals(namespaceURI)){
+ return n.getPrefix();
+ }
+ }
+
+ return null;
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Iterator getPrefixes(final String namespaceURI) {
+ if (namespaceURI == null) {
+ throw new IllegalArgumentException("NamespaceContext requires a non-null Namespace URI");
+ }
+ if (JDOMConstants.NS_URI_XML.equals(namespaceURI)) {
+ return Collections.singleton(JDOMConstants.NS_PREFIX_XML).iterator();
+ }
+ if (JDOMConstants.NS_URI_XMLNS.equals(namespaceURI)) {
+ return Collections.singleton(JDOMConstants.NS_PREFIX_XMLNS).iterator();
+ }
+
+ final List<String> ret = new ArrayList<String>();
+ for(final Namespace n : namespacearray){
+ if(n.getURI().equals(namespaceURI)){
+ ret.add(n.getPrefix());
+ }
+ }
+
+ return Collections.unmodifiableCollection(ret).iterator();
+ }
+
+}
View
803 core/src/java/org/jdom2/jaxb/JDOMStreamReader.java
@@ -0,0 +1,803 @@
+/*--
+
+ Copyright (C) 2000-2012 Jason Hunter & Brett McLaughlin.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions, and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions, and the disclaimer that follows
+ these conditions in the documentation and/or other materials
+ provided with the distribution.
+
+ 3. The name "JDOM" must not be used to endorse or promote products
+ derived from this software without prior written permission. For
+ written permission, please contact <request_AT_jdom_DOT_org>.
+
+ 4. Products derived from this software may not be called "JDOM", nor
+ may "JDOM" appear in their name, without prior written permission
+ from the JDOM Project Management <request_AT_jdom_DOT_org>.
+
+ In addition, we request (but do not require) that you include in the
+ end-user documentation provided with the redistribution and/or in the
+ software itself an acknowledgement equivalent to the following:
+ "This product includes software developed by the
+ JDOM Project (http://www.jdom.org/)."
+ Alternatively, the acknowledgment may be graphical using the logos
+ available at http://www.jdom.org/images/logos.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+ This software consists of voluntary contributions made by many
+ individuals on behalf of the JDOM Project and was originally
+ created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
+ Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
+ on the JDOM Project, please see <http://www.jdom.org/>.
+
+ */
+
+package org.jdom2.jaxb;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
+import javax.xml.stream.Location;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.jdom2.Attribute;
+import org.jdom2.Content;
+import org.jdom2.DocType;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.EntityRef;
+import org.jdom2.Namespace;
+import org.jdom2.ProcessingInstruction;
+import org.jdom2.Verifier;
+import org.jdom2.internal.ArrayCopy;
+import org.jdom2.output.Format;
+import org.jdom2.output.Format.TextMode;
+import org.jdom2.output.XMLOutputter;
+import org.jdom2.output.support.AbstractOutputProcessor;
+import org.jdom2.output.support.FormatStack;
+import org.jdom2.output.support.Walker;
+import org.jdom2.util.NamespaceStack;
+
+/**
+ * An {@link XMLStreamReader} implementation that reads the XML document
+ * out of a JDOM {@link Document}.
+ *
+ * The reader reads XML Events by walking the DOM tree, reporting all XML stream
+ * events as it encounters them in the DOM.
+ * @author gordon burgett https://github.com/gburgett
+ */
+public class JDOMStreamReader extends AbstractOutputProcessor implements XMLStreamReader {
+
+ private static final class DocumentWalker implements Walker {
+
+ private final Content[] data;
+ private int pos = 0;
+
+ public DocumentWalker(final Document doc) {
+ data = doc.getContent().toArray(new Content[doc.getContentSize()]);
+ }
+
+ @Override
+ public boolean isAllText() {
+ return false;
+ }
+
+ @Override
+ public boolean isAllWhitespace() {
+ return false;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return pos < data.length;
+ }
+
+ @Override
+ public Content next() {
+ return data[pos++];
+ }
+
+ @Override
+ public String text() {
+ return null;
+ }
+
+ @Override
+ public boolean isCDATA() {
+ return false;
+ }
+
+ }
+
+ private final FormatStack formatstack;
+ private final NamespaceStack nsstack = new NamespaceStack();
+
+ private Document document;
+
+ private String curi = null, clocalname = null, cprefix = null,
+ ctext = null, ctarget = null, cdata = null;
+ private Element [] emtstack = new Element[32];
+ private Walker[] stack = new Walker[32];
+ private int depth = 0;
+
+ private int currentEvt = START_DOCUMENT;
+
+ /**
+ * Create a new JDOMStreamReader that outputs a JDOM Document as an XMLStream.
+ * @param document the document to output.
+ * @param format The output format to use.
+ */
+ public JDOMStreamReader(Document document, Format format){
+ this.document = document;
+ this.formatstack = new FormatStack(format);
+ stack[0] = new DocumentWalker(document);
+ }
+
+ /**
+ * Create a new JDOMStreamReader that outputs a JDOM Document as an XMLStream using
+ * the Format.getRawFormat() format.
+ * @param document the document to output.
+ */
+ public JDOMStreamReader(Document document) {
+ this(document, Format.getRawFormat());
+ }
+
+ @Override
+ public boolean hasNext() throws XMLStreamException {
+ return depth >= 0;
+ }
+
+ @Override
+ public int next() throws XMLStreamException {
+ if (depth < 0) {
+ throw new NoSuchElementException("No more data available.");
+ }
+
+ curi = null;
+ clocalname = null;
+ cprefix = null;
+ ctext = null;
+ ctarget = null;
+ cdata = null;
+
+ if (currentEvt == END_ELEMENT) {
+ nsstack.pop();
+ formatstack.pop();
+ emtstack[depth + 1] = null;
+ }
+
+ // confirm next walker item.
+ if (!stack[depth].hasNext()) {
+ // no more items at this level.
+ stack[depth] = null;
+ // we kill the element stack at the end of the END_ELEMENT event.
+ // emtstack[depth] = null
+ depth--;
+ return currentEvt = (depth < 0 ? END_DOCUMENT : END_ELEMENT);
+ }
+
+ final Content c = stack[depth].next();
+ if (c == null) {
+ // formatted text or CDATA.
+ ctext = stack[depth].text();
+ return currentEvt = stack[depth].isCDATA() ? CDATA : CHARACTERS;
+ }
+
+ switch (c.getCType()) {
+ case CDATA:
+ ctext = c.getValue();
+ return currentEvt = CDATA;
+ case Text:
+ ctext = c.getValue();
+ return currentEvt = CHARACTERS;
+ case Comment:
+ ctext = c.getValue();
+ return currentEvt = COMMENT;
+ case DocType:
+ // format doctype appropriately.
+ XMLOutputter xout = new XMLOutputter();
+ ctext = xout.outputString((DocType)c);
+ return currentEvt = DTD;
+ case EntityRef:
+ clocalname = ((EntityRef)c).getName();
+ ctext = "";
+ return currentEvt = ENTITY_REFERENCE;
+ case ProcessingInstruction:
+ final ProcessingInstruction pi = (ProcessingInstruction)c;
+ ctarget = pi.getTarget();
+ cdata = pi.getData();
+ return currentEvt = PROCESSING_INSTRUCTION;
+ case Element:
+ // great
+ // we deal with Element outside the switch statement.
+ break;
+ default:
+ throw new IllegalStateException("Unexpected content " + c);
+ }
+ // OK, we break out here if we are an Element start.
+ final Element emt = (Element)c;
+ clocalname = emt.getName();
+ cprefix = emt.getNamespacePrefix();
+ curi = emt.getNamespaceURI();
+
+ nsstack.push(emt);
+ formatstack.push();
+ final String space = emt.getAttributeValue("space", Namespace.XML_NAMESPACE);
+ // Check for xml:space and adjust format settings
+ if ("default".equals(space)) {
+ formatstack.setTextMode(formatstack.getDefaultMode());
+ }
+ else if ("preserve".equals(space)) {
+ formatstack.setTextMode(TextMode.PRESERVE);
+ }
+
+ depth++;
+ if (depth >= stack.length) {
+ stack = ArrayCopy.copyOf(stack, depth + 32);
+ emtstack = ArrayCopy.copyOf(emtstack, depth+32);
+ }
+
+ emtstack[depth] = emt;
+ stack[depth] = buildWalker(formatstack, emt.getContent(), false);
+
+ return currentEvt = START_ELEMENT;
+
+ }
+
+ @Override
+ public int getEventType() {
+ return currentEvt;
+ }
+
+ @Override
+ public boolean isStartElement() {
+ return currentEvt == START_ELEMENT;
+ }
+
+ @Override
+ public boolean isEndElement() {
+ return currentEvt == END_ELEMENT;
+ }
+
+ @Override
+ public boolean isCharacters() {
+ return currentEvt == CHARACTERS;
+ }
+
+ @Override
+ public boolean isWhiteSpace() {
+ switch (currentEvt) {
+ case SPACE :
+ return true;
+ case CDATA:
+ case CHARACTERS:
+ return Verifier.isAllXMLWhitespace(ctext);
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public void require(int type, String namespaceURI, String localName) throws XMLStreamException {
+ if(type != getEventType()){
+ throw new XMLStreamException("required event " + type + " but got event " + getEventType());
+ }
+
+ if(localName != null){
+ if(!localName.equals(clocalname)){
+ throw new XMLStreamException("required name " + localName + " but got name " + clocalname);
+ }
+ }
+
+ if(namespaceURI != null){
+ if(!namespaceURI.equals(curi)){
+ throw new XMLStreamException("required namespace " + namespaceURI + " but got namespace " + curi);
+ }
+ }
+ }
+
+ @Override
+ public QName getName() {
+ switch (currentEvt) {
+ case START_ELEMENT:
+ case END_ELEMENT:
+ final Element emt = emtstack[depth];
+ return new QName(emt.getNamespaceURI(), emt.getName(), emt.getNamespacePrefix());
+ default:
+ throw new IllegalStateException("getName not supported for event " + currentEvt);
+ }
+ }
+
+ @Override
+ public String getLocalName() {
+ switch (currentEvt) {
+ case START_ELEMENT:
+ case END_ELEMENT:
+ case ENTITY_REFERENCE:
+ return clocalname;
+ default:
+ throw new IllegalStateException("getLocalName not supported for event " + currentEvt);
+ }
+ }
+
+ @Override
+ public boolean hasName() {
+ return currentEvt == START_ELEMENT || currentEvt == END_ELEMENT;
+ }
+
+ @Override
+ public String getNamespaceURI() {
+ switch (currentEvt) {
+ case START_ELEMENT:
+ case END_ELEMENT:
+ return curi;
+ default:
+ throw new IllegalStateException("getNamespaceURI not supported for event " + currentEvt);
+ }
+ }
+
+ @Override
+ public String getPrefix() {
+ switch (currentEvt) {
+ case START_ELEMENT:
+ case END_ELEMENT:
+ return cprefix;
+ default:
+ throw new IllegalStateException("getPrefix not supported for event " + currentEvt);
+ }
+ }
+
+ @Override
+ public String getPITarget() {
+ switch (currentEvt) {
+ case PROCESSING_INSTRUCTION:
+ return ctarget;
+ default:
+ throw new IllegalStateException("getPITarget not supported for event " + currentEvt);
+ }
+ }
+
+ @Override
+ public String getPIData() {
+ switch (currentEvt) {
+ case PROCESSING_INSTRUCTION:
+ return cdata;
+ default:
+ throw new IllegalStateException("getPIData not supported for event " + currentEvt);
+ }
+ }
+
+
+ @Override
+ public String getElementText() throws XMLStreamException {
+ // copied from documentation.
+ if(getEventType() != XMLStreamConstants.START_ELEMENT) {
+ throw new XMLStreamException("parser must be on START_ELEMENT to read next text");
+ }
+
+ int eventType = next();
+ StringBuilder buf = new StringBuilder();
+ while(eventType != XMLStreamConstants.END_ELEMENT ) {
+ if(eventType == XMLStreamConstants.CHARACTERS
+ || eventType == XMLStreamConstants.CDATA
+ || eventType == XMLStreamConstants.SPACE
+ || eventType == XMLStreamConstants.ENTITY_REFERENCE) {
+ buf.append(getText());
+ } else if(eventType == XMLStreamConstants.PROCESSING_INSTRUCTION
+ || eventType == XMLStreamConstants.COMMENT) {
+ // skipping
+ } else if(eventType == XMLStreamConstants.END_DOCUMENT) {
+ throw new XMLStreamException("unexpected end of document when reading element text content", getLocation());
+ } else if(eventType == XMLStreamConstants.START_ELEMENT) {
+ throw new XMLStreamException("element text content may not contain START_ELEMENT", getLocation());
+ } else {
+ throw new XMLStreamException("Unexpected event type "+eventType, getLocation());
+ }
+
+ eventType = next();
+ }
+ return buf.toString();
+ }
+
+ @Override
+ public int nextTag() throws XMLStreamException {
+ int eventType = next();
+ while((eventType == XMLStreamConstants.CHARACTERS && isWhiteSpace()) // skip whitespace
+ || (eventType == XMLStreamConstants.CDATA && isWhiteSpace())
+ // skip whitespace
+ || eventType == XMLStreamConstants.SPACE
+ || eventType == XMLStreamConstants.PROCESSING_INSTRUCTION
+ || eventType == XMLStreamConstants.COMMENT
+ )
+ {
+ eventType = next();
+ }
+
+ if (eventType != XMLStreamConstants.START_ELEMENT && eventType != XMLStreamConstants.END_ELEMENT) {
+ throw new XMLStreamException("expected start or end tag", getLocation());
+ }
+ return eventType;
+ }
+
+ @Override
+ public void close() throws XMLStreamException {
+ currentEvt = END_DOCUMENT;
+ while (depth >= 0) {
+ stack[depth] = null;
+ emtstack[depth] = null;
+ depth--;
+ }
+ cdata = null;
+ clocalname = null;
+ cprefix = null;
+ ctarget = null;
+ ctext = null;
+ curi = null;
+ this.document = null;
+ }
+
+ @Override
+ public String getNamespaceURI(String prefix) {
+ final Namespace ns = nsstack.getNamespaceForPrefix(prefix);
+ return ns == null ? null : ns.getURI();
+ }
+
+ @Override
+ public String getAttributeValue(String namespaceURI, String localName) {
+ if(currentEvt != START_ELEMENT){
+ throw new IllegalStateException("getAttributeCount not supported for event " + currentEvt);
+ }
+
+ final Element e = emtstack[depth];
+ if (!e.hasAttributes()) {
+ return null;
+ }
+
+ if(namespaceURI != null){
+ return e.getAttributeValue(localName, Namespace.getNamespace(namespaceURI));
+ }
+
+ //else search by local name only
+ for(Attribute a : e.getAttributes()){
+ if(a.getName().equalsIgnoreCase(localName)){
+ return a.getValue();
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public int getAttributeCount() {
+ if(currentEvt != START_ELEMENT){
+ throw new IllegalStateException("getAttributeCount not supported for event " + currentEvt);
+ }
+ return emtstack[depth].getAttributesSize();
+ }
+
+ @Override
+ public QName getAttributeName(int index) {
+ if(currentEvt != START_ELEMENT && currentEvt != ATTRIBUTE){
+ throw new IllegalStateException("getAttributeCount not supported for event " + currentEvt);
+ }
+
+ final Attribute a = emtstack[depth].getAttributes().get(index);
+
+ String ns = a.getNamespaceURI();
+ if("".equals(ns))
+ ns = null;
+ String prefix = a.getNamespacePrefix();
+ if(prefix == null || "".equals(prefix)){
+ prefix = XMLConstants.DEFAULT_NS_PREFIX;
+ }
+
+ return new QName(ns, a.getName(), prefix);
+ }
+
+ @Override
+ public String getAttributeNamespace(int index) {
+ if(currentEvt != START_ELEMENT && currentEvt != ATTRIBUTE){
+ throw new IllegalStateException("getAttributeCount not supported for event " + currentEvt);
+ }
+
+ final Attribute a = emtstack[depth].getAttributes().get(index);
+ return a.getNamespaceURI();
+ }
+
+ @Override
+ public String getAttributeLocalName(int index) {
+ if(currentEvt != START_ELEMENT && currentEvt != ATTRIBUTE){
+ throw new IllegalStateException("getAttributeCount not supported for event " + currentEvt);
+ }
+
+ final Attribute a = emtstack[depth].getAttributes().get(index);
+ return a.getName();
+ }
+
+ @Override
+ public String getAttributePrefix(int index) {
+ if(currentEvt != START_ELEMENT && currentEvt != ATTRIBUTE){
+ throw new IllegalStateException("getAttributeCount not supported for event " + currentEvt);
+ }
+
+ final Attribute a = emtstack[depth].getAttributes().get(index);
+ return a.getNamespacePrefix();
+ }
+
+ @Override
+ public String getAttributeType(int index) {
+ if(currentEvt != START_ELEMENT && currentEvt != ATTRIBUTE){
+ throw new IllegalStateException("getAttributeCount not supported for event " + currentEvt);
+ }
+
+ final Attribute a = emtstack[depth].getAttributes().get(index);
+ return a.getAttributeType().name();
+ }
+
+ @Override
+ public String getAttributeValue(int index) {
+ if(currentEvt != START_ELEMENT && currentEvt != ATTRIBUTE){
+ throw new IllegalStateException("getAttributeCount not supported for event " + currentEvt);
+ }
+
+ final Attribute a = emtstack[depth].getAttributes().get(index);
+ return a.getValue();
+ }
+
+ @Override
+ public boolean isAttributeSpecified(int index) {
+ if(currentEvt != START_ELEMENT && currentEvt != ATTRIBUTE){
+ throw new IllegalStateException("getAttributeCount not supported for event " + currentEvt);
+ }
+
+ final Attribute a = emtstack[depth].getAttributes().get(index);
+ return a.isSpecified();
+ }
+
+ @Override
+ public int getNamespaceCount() {
+ switch(currentEvt){
+ case START_ELEMENT:
+ case END_ELEMENT:
+ final Iterator<?> it = nsstack.addedForward().iterator();
+ int cnt = 0;
+ while (it.hasNext()) {
+ cnt++;
+ it.next();
+ }
+ return cnt;
+ }
+
+ throw new IllegalStateException("getNamespaceCount not supported for event " + currentEvt);
+ }
+
+ private final Namespace getNamespaceByIndex(int index) {
+ final Iterator<Namespace> it = nsstack.addedForward().iterator();
+ int cnt = 0;
+ while (it.hasNext()) {
+ if (cnt == index) {
+ return it.next();
+ }
+ it.next();
+ cnt++;
+ }
+ throw new NoSuchElementException("No Namespace with index " + index +
+ " (there are only " + cnt + ").");
+ }
+
+ @Override
+ public String getNamespacePrefix(int index) {
+ switch(currentEvt){
+ case START_ELEMENT:
+ case END_ELEMENT:
+ return getNamespaceByIndex(index).getPrefix();
+ }
+
+ throw new IllegalStateException("getNamespacePrefix not supported for event " + currentEvt);
+ }
+
+ @Override
+ public String getNamespaceURI(int index) {
+
+ switch(currentEvt){
+ case START_ELEMENT:
+ case NAMESPACE:
+ case END_ELEMENT:
+ return getNamespaceByIndex(index).getURI();
+ }
+
+ throw new IllegalStateException("getNamespaceURI not supported for event " + currentEvt);
+ }
+
+ @Override
+ public NamespaceContext getNamespaceContext() {
+ return new JDOMNamespaceContext(nsstack.getScope());
+ }
+
+ @Override
+ public boolean hasText() {
+ switch(currentEvt){
+ case CDATA:
+ case CHARACTERS:
+ case COMMENT:
+ case DTD:
+ case ENTITY_REFERENCE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public String getText() {
+ switch(currentEvt){
+ case CDATA:
+ case CHARACTERS:
+ case COMMENT:
+ case DTD:
+ case ENTITY_REFERENCE:
+ return ctext;
+ default:
+ throw new IllegalStateException("getText not valid for event type " + currentEvt);
+ }
+
+ }
+
+ @Override
+ public char[] getTextCharacters() {
+ return getText().toCharArray();
+ }
+
+ @Override
+ public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length) throws XMLStreamException {
+ char[] chars = getText().toCharArray();
+ int i = 0;
+ for(; i < length; i++){
+ if(sourceStart > chars.length){
+ return i;
+ }
+ if(targetStart > target.length){
+ return i;
+ }
+
+ target[targetStart++] = chars[sourceStart++];
+ }
+
+ return i;
+ }
+
+ @Override
+ public int getTextStart() {
+ return 0;
+ }
+
+ @Override
+ public int getTextLength() {
+ return getText().length();
+ }
+
+ @Override
+ public String getEncoding() {
+ Object ret = document.getProperty("ENCODING");
+ if(ret == null)
+ return null;
+
+ return ret.toString();
+ }
+
+ @Override
+ public Location getLocation() {
+ return new Location(){
+ @Override
+ public int getLineNumber() {
+ return -1;
+ }
+
+ @Override
+ public int getColumnNumber() {
+ return -1;
+ }
+
+ @Override
+ public int getCharacterOffset() {
+ return -1;
+ }
+
+ @Override
+ public String getPublicId() {
+ return null;
+ }
+
+ @Override
+ public String getSystemId() {
+ return null;
+ }
+
+ };
+ }
+
+
+ @Override
+ public String getVersion() {
+ return null;
+ }
+
+ @Override
+ public boolean isStandalone() {
+ Object ret = document.getProperty("STANDALONE");
+ return Boolean.TRUE.equals(ret);
+ }
+
+ @Override
+ public boolean standaloneSet() {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public String getCharacterEncodingScheme() {
+ Object ret = document.getProperty("ENCODING_SCHEME");
+ if(ret == null)
+ return null;
+
+ return ret.toString();
+ }
+
+ @Override
+ public Object getProperty(final String name) throws IllegalArgumentException {
+ if (name == null) {
+ throw new IllegalArgumentException("Property name is not allowed to be null");
+ }
+ if (XMLInputFactory.ALLOCATOR.equals(name)) {
+ return null;
+ }
+ if (XMLInputFactory.IS_COALESCING.equals(name)) {
+ return formatstack.getDefaultMode() != TextMode.PRESERVE;
+ }
+ if (XMLInputFactory.IS_NAMESPACE_AWARE.equals(name)) {
+ return Boolean.TRUE;
+ }
+ if (XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES.equals(name)) {
+ return Boolean.FALSE;
+ }
+ if (XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES.equals(name)) {
+ return Boolean.FALSE;
+ }
+ if (XMLInputFactory.IS_VALIDATING.equals(name)) {
+ return Boolean.TRUE;
+ }
+ if (XMLInputFactory.REPORTER.equals(name)) {
+ return null;
+ }
+ if (XMLInputFactory.RESOLVER.equals(name)) {
+ return null;
+ }
+ return null;
+ }
+
+}
View
713 core/src/java/org/jdom2/jaxb/JDOMStreamWriter.java
@@ -0,0 +1,713 @@
+/*--
+
+ Copyright (C) 2000-2012 Jason Hunter & Brett McLaughlin.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions, and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions, and the disclaimer that follows
+ these conditions in the documentation and/or other materials
+ provided with the distribution.
+
+ 3. The name "JDOM" must not be used to endorse or promote products
+ derived from this software without prior written permission. For
+ written permission, please contact <request_AT_jdom_DOT_org>.
+
+ 4. Products derived from this software may not be called "JDOM", nor
+ may "JDOM" appear in their name, without prior written permission
+ from the JDOM Project Management <request_AT_jdom_DOT_org>.
+
+ In addition, we request (but do not require) that you include in the
+ end-user documentation provided with the redistribution and/or in the
+ software itself an acknowledgement equivalent to the following:
+ "This product includes software developed by the
+ JDOM Project (http://www.jdom.org/)."
+ Alternatively, the acknowledgment may be graphical using the logos
+ available at http://www.jdom.org/images/logos.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+ This software consists of voluntary contributions made by many
+ individuals on behalf of the JDOM Project and was originally
+ created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
+ Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
+ on the JDOM Project, please see <http://www.jdom.org/>.
+
+ */
+package org.jdom2.jaxb;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+import org.jdom2.DefaultJDOMFactory;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.JDOMConstants;
+import org.jdom2.JDOMFactory;
+import org.jdom2.Namespace;
+import org.jdom2.Parent;
+import org.jdom2.Text;
+import org.jdom2.util.NamespaceStack;
+
+/**
+ * An {@link XMLStreamWriter} implementation that writes XML data to a new JDOM
+ * {@link Document}. The document can be retrieved using {@link #getDocument() }.
+ *
+ * @author gordon burgett https://github.com/gburgett
+ */
+public class JDOMStreamWriter implements XMLStreamWriter {
+
+ private static final DefaultJDOMFactory DEFFAC = new DefaultJDOMFactory();
+
+ // The optional global namespace context for namespace bindings.
+ private NamespaceContext globalcontext = null;
+
+ // The active Element NamespaceStack of actual used Namespaces.
+ private NamespaceStack usednsstack = new NamespaceStack();
+ // The stack of namespace bindings which may not necessarily be used in an element
+ private NamespaceStack boundstack = new NamespaceStack();
+
+ // The namespaces pending in the current Element that need to be actively bound.
+ private LinkedList<Namespace> pendingns = new LinkedList<Namespace>();
+
+ // Are we throwing exceptions for namespace problems, or fixing them.
+ private final boolean repairnamespace;
+
+ private Document document = null;
+ private boolean done = false;
+ private Parent parent = null;
+ private Element activeelement = null;
+ private boolean isempty = false;
+ private Text activetext = null;
+
+ private int genprefix = 0;
+
+ private final JDOMFactory factory;
+
+ /**
+ * Create a JDOMStreamWriter with the default JDOMFactory for creating JDOM
+ * Content.
+ */
+ public JDOMStreamWriter() {
+ this(DEFFAC, true);
+ }
+
+ /**
+ * Create a JDOMStreamWriter with the specified JDOMFactory for creating
+ * JDOM Content.
+ *
+ * @param fac
+ * The JDOMFactory to use.
+ * @param repairnamespace
+ * If true, then repair namespace errors.
+ */
+ public JDOMStreamWriter(final JDOMFactory fac, final boolean repairnamespace) {
+ this.factory = fac;
+ this.repairnamespace = repairnamespace;
+ // create an empty level in the namespace stack.
+ boundstack.push(new Namespace[0]);
+ }
+
+ /**
+ * Gets the {@link Document} that was created by this writer. Only available
+ * after {@link #writeEndDocument() } has been called, and before the Writer
+ * is closed.
+ *
+ * @return The created {@link Document}
+ */
+ public Document getDocument() {
+ if (done && document != null) {
+ return document;
+ }
+
+ if (done) {
+ throw new IllegalStateException("Writer is closed");
+ }
+
+ throw new IllegalStateException(
+ "Cannot get Document until writer has ended the document");
+
+ }
+
+ @Override
+ public void writeStartDocument() throws XMLStreamException {
+ this.writeStartDocument(null, null);
+ }
+
+ @Override
+ public void writeStartDocument(final String version)
+ throws XMLStreamException {
+ this.writeStartDocument(null, version);
+ }
+
+ @Override
+ public void writeStartDocument(final String encoding, final String version)
+ throws XMLStreamException {
+
+ // JDOM has no support for XML Version specification/handling
+ // ignore version.
+
+ if (done || document != null) {
+ throw new IllegalStateException(
+ "Cannot write start document twice.");
+ }
+
+ document = factory.document(null);
+ parent = document;
+ if (encoding != null && !"".equals(encoding))
+ this.document.setProperty("ENCODING", encoding);
+
+ activeelement = null;
+ }
+
+ @Override
+ public void setNamespaceContext(final NamespaceContext context)
+ throws XMLStreamException {
+ if (document == null || document.hasRootElement()) {
+ throw new XMLStreamException("Can only set the NamespaceContext at the Document start");
+ }
+ globalcontext = context;
+ }
+
+ @Override
+ public String getPrefix(final String uri) throws XMLStreamException {
+ if (document == null) {
+ return null;
+ }
+ final Namespace n = boundstack.getFirstNamespaceForURI(uri);
+ if (n != null) {
+ return n.getPrefix();
+ }
+ if (globalcontext != null) {
+ return globalcontext.getPrefix(uri);
+ }
+ return null;
+ }
+
+ @Override
+ public void setPrefix(final String prefix, final String uri)
+ throws XMLStreamException {
+ if (prefix == null) {
+ throw new IllegalArgumentException("prefix may not be null");
+ }
+ if (document == null || done) {
+ throw new IllegalStateException(
+ "Attempt to set prefix at an illegal stream state.");
+ }
+
+ /*
+ * Setting a prefix (or a default namespace) is not the same as writing the namespace.
+ * It is only a 'binding' of the URI to a prefix. This binding is used for two purposes:
+ * - any prefixes bound in here are used if a prefix is not specified in the startelement
+ * - if repairnamespace is false, then all used namespaces must first be pre-bound
+ * This method is specified to allow a null URI. This makes no sense unless the prefix is ""
+ * and will cause a Namespace exception if the prefix is not "".
+ */
+
+ final Namespace ns = Namespace.getNamespace(prefix, uri);
+ if (!boundstack.isInScope(ns)) {
+ final ArrayList<Namespace> al = new ArrayList<Namespace>();
+ for (Namespace n : boundstack) {
+ if (n.getPrefix().equals(prefix)) {
+ // do not rebind the same prefix with the old URI.
+ continue;
+ }
+ al.add(n);
+ }
+ // bind the new URI to the prefix.
+ al.add(ns);
+ // kill the previous bindings.
+ boundstack.pop();
+ // reset the binding.
+ boundstack.push(al);
+ }
+ }
+
+ @Override
+ public void setDefaultNamespace(final String uri) throws XMLStreamException {
+ setPrefix(JDOMConstants.NS_PREFIX_DEFAULT, uri);
+ }
+
+ @Override
+ public void writeDTD(final String dtd) throws XMLStreamException {
+ // FIXME to do ... ?
+ throw new UnsupportedOperationException("not supported yet");
+ }
+
+ @Override
+ public void writeStartElement(final String localName) throws XMLStreamException {
+ writeStartElement("", localName);
+ }
+
+ @Override
+ public void writeStartElement(final String namespaceURI, final String localName)
+ throws XMLStreamException {
+ if (namespaceURI == null) {
+ throw new XMLStreamException("Cannot have a null namespaceURI");
+ }
+ if (localName == null) {
+ throw new XMLStreamException("Cannot have a null localname");
+ }
+ this.buildElement("", localName, namespaceURI, false, false);
+ }
+
+ @Override
+ public void writeStartElement(final String prefix, final String localName,
+ final String namespaceURI) throws XMLStreamException {
+ if (prefix == null) {
+ throw new XMLStreamException("Cannot have a null prefix");
+ }
+ if (localName == null) {
+ throw new XMLStreamException("Cannot have a null localName");
+ }
+ if (namespaceURI == null) {
+ throw new XMLStreamException("Cannot have a null namespaceURI");
+ }
+ buildElement(prefix, localName, namespaceURI, true, false);
+ }
+
+ @Override
+ public void writeEmptyElement(final String localName)
+ throws XMLStreamException {
+ if (localName == null) {
+ throw new XMLStreamException("Cannot have a null localname");
+ }
+ this.buildElement("", localName, "", false, true);
+ }
+
+ @Override
+ public void writeEmptyElement(final String namespaceURI,
+ final String localName) throws XMLStreamException {
+ if (namespaceURI == null) {
+ throw new XMLStreamException("Cannot have a null namespaceURI");
+ }
+ if (localName == null) {
+ throw new XMLStreamException("Cannot have a null localname");
+ }
+ this.buildElement("", localName, namespaceURI, false, true);
+ }
+
+ @Override
+ public void writeEmptyElement(final String prefix, final String localName,
+ final String namespaceURI) throws XMLStreamException {
+ if (prefix == null) {
+ throw new XMLStreamException("Cannot have a null prefix");
+ }
+ if (localName == null) {
+ throw new XMLStreamException("Cannot have a null localname");
+ }
+ if (namespaceURI == null) {
+ throw new XMLStreamException("Cannot have a null namespaceURI");
+ }
+ buildElement(prefix, localName, namespaceURI, true, true);
+ }
+
+ @Override
+ public void writeDefaultNamespace(final String namespaceURI)
+ throws XMLStreamException {
+ this.writeNamespace("", namespaceURI);
+ }
+
+ @Override
+ public void writeNamespace(final String prefix, final String namespaceURI)
+ throws XMLStreamException {
+ if (activeelement == null) {
+ throw new IllegalStateException("Can only write a Namespace after starting an Element" +
+ " and before adding content to that Element.");
+ }
+ if (prefix == null || JDOMConstants.NS_PREFIX_XMLNS.equals(prefix)) {
+ // recurse with the "" prefix.
+ writeNamespace("", namespaceURI);
+ } else {
+ pendingns.add(Namespace.getNamespace(prefix, namespaceURI));
+ }
+ }
+
+ @Override
+ public void writeAttribute(final String localName, final String value)
+ throws XMLStreamException {
+ buildAttribute("", "", localName, value, false);
+ }
+
+ @Override
+ public void writeAttribute(final String namespaceURI,
+ final String localName, final String value)
+ throws XMLStreamException {
+ buildAttribute("", namespaceURI == null ? "" : namespaceURI, localName, value, false);
+ }
+
+ @Override
+ public void writeAttribute(final String prefix, final String namespaceURI,
+ final String localName, final String value)
+ throws XMLStreamException {
+ buildAttribute(prefix == null ? "" : prefix, namespaceURI == null ? "" : namespaceURI, localName, value, true);
+ }
+
+ @Override
+ public void writeComment(final String data) throws XMLStreamException {
+ if (document == null || done) {
+ throw new XMLStreamException("Can only add a Comment to the Document or an Element.");
+ }
+ flushActiveElement();
+ flushActiveText();
+ factory.addContent(parent, factory.comment(data));
+ }
+
+ @Override
+ public void writeProcessingInstruction(final String target)
+ throws XMLStreamException {
+ if (document == null || done) {
+ throw new XMLStreamException("Can only add a ProcessingInstruction to the Document or an Element.");
+ }
+ flushActiveElement();
+ flushActiveText();
+ factory.addContent(parent, factory.processingInstruction(target));
+ }
+
+ @Override
+ public void writeProcessingInstruction(final String target,
+ final String data) throws XMLStreamException {
+ if (document == null || done) {
+ throw new XMLStreamException("Can only add a ProcessingInstruction to the Document or an Element.");
+ }
+ flushActiveElement();
+ flushActiveText();
+ factory.addContent(parent, factory.processingInstruction(target, data));
+ }
+
+ @Override
+ public void writeCData(final String data) throws XMLStreamException {
+ if (!(parent instanceof Element)) {
+ throw new XMLStreamException("Can only writeCDATA() inside an Element.");
+ }
+ flushActiveElement();
+ flushActiveText();
+ factory.addContent(parent, factory.cdata(data));
+ }
+
+ @Override
+ public void writeEntityRef(final String name) throws XMLStreamException {
+ if (!(parent instanceof Element)) {
+ throw new XMLStreamException("Can only writeEntityRef() inside an Element.");
+ }
+ flushActiveElement();
+ flushActiveText();
+ factory.addContent(parent, factory.entityRef(name));
+ }
+
+ @Override
+ public void writeCharacters(final String chars) throws XMLStreamException {
+ if (document == null || done) {
+ throw new XMLStreamException("Unable to add Characters at this point in the stream.");
+ }
+ flushActiveElement();
+ if (chars == null) {
+ return;
+ }
+ if (parent instanceof Element) {
+ if (activetext != null) {
+ activetext.append(chars);
+ } else {
+ activetext = factory.text(chars);
+ factory.addContent(parent, activetext);
+ }
+ }
+ // ignore case where parent is Document.
+ }
+
+ @Override
+ public void writeCharacters(final char[] chars, final int start,
+ final int len) throws XMLStreamException {
+ writeCharacters(new String(chars, start, len));
+ }
+
+ @Override
+ public void writeEndElement() throws XMLStreamException {
+ if (!(parent instanceof Element)) {
+ throw new XMLStreamException("Cannot end an Element unless you are in an Element.");
+ }
+ flushActiveElement();
+ flushActiveText();
+ usednsstack.pop();
+ boundstack.pop();
+ boundstack.pop();
+ boundstack.pop();
+ parent = parent.getParent();
+ }
+
+ @Override
+ public void writeEndDocument() throws XMLStreamException {
+ // flush may change state if isempty root element
+ if (document == null || done || parent instanceof Element) {
+ throw new IllegalStateException(
+ "Cannot write end document before writing the end of root element");
+ }
+ flushActiveElement();
+ done = true;
+ }
+
+ @Override
+ public void close() throws XMLStreamException {
+ this.document = null;
+ this.parent = null;
+ activeelement = null;
+ activetext = null;
+ boundstack = null;
+ usednsstack = null;
+ done = true;
+ }
+
+ @Override
+ public void flush() throws XMLStreamException {
+ // flush does nothing.
+ }
+
+ @Override
+ public NamespaceContext getNamespaceContext() {
+ if (document == null) {
+ return new JDOMNamespaceContext(new Namespace[0]);
+ }
+ return new JDOMNamespaceContext(boundstack.getScope());
+ }
+
+ @Override
+ public Object getProperty(String name) throws IllegalArgumentException {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ // *****************************
+ // Private methods.
+ // *****************************
+
+ /**
+ * Simple method that implements the empty-element logic without the spec-required
+ * null-check logic
+ * @param prefix The namespace prefix (may be null).
+ * @param localName The Element tag
+ * @param namespaceURI The namespace URI (may be null).
+ * @param empty Is this an Empty element (expecting children?)
+ * @throws XMLStreamException If the stream is not in an appropriate state for a new Element.
+ */
+ private final void buildElement(final String prefix, final String localName,
+ final String namespaceURI, final boolean withpfx, boolean empty) throws XMLStreamException {
+
+ if (document == null || done) {
+ throw new XMLStreamException(
+ "Cannot write new element when in current state.");
+ }
+
+ if (parent == document && document.hasRootElement()) {
+ throw new XMLStreamException(
+ "Document can have only one root Element.");
+ }
+
+
+ flushActiveElement();
+ flushActiveText();
+
+ // create an element-specific namespace binding layer.
+ boundstack.push();
+
+ final Namespace ns = resolveElementNamespace(prefix, namespaceURI, withpfx);
+
+ final Element e = factory.element(localName, ns);
+
+ factory.addContent(parent, e);
+ activeelement = e;
+ if (empty) {
+ isempty = true;
+ } else {
+ isempty = false;
+ parent = e;
+ }
+ }
+
+ private Namespace resolveElementNamespace(String prefix, String namespaceURI,
+ boolean withpfx) throws XMLStreamException {
+
+ if ("".equals(namespaceURI)) {
+ final Namespace defns = boundstack.getNamespaceForPrefix("");
+ if(Namespace.NO_NAMESPACE != defns) {
+ // inconsistency in XMLStreamWriter specification....
+ // In theory the repairing code should create a generated prefic for unbound
+ // namespace URI, but you can't create a prefixed ""-URI namespace.
+ throw new XMLStreamException("This attempt to use the empty URI \"\" as an " +
+ "Element Namespace is illegal because the default Namespace is already " +
+ "bound to the URI '" + defns.getURI() + "'. You must call " +
+ "setPrefix(\"\", \"\") prior to this call.");
+ }
+ }
+
+ final Namespace ns = Namespace.getNamespace(prefix, namespaceURI);
+
+ if (withpfx) {
+ final Namespace bnd = boundstack.getNamespaceForPrefix(prefix);
+ if (bnd == null || bnd == ns) {
+ // no existing binding for prefix, or same binding.
+ return ns;
+ }
+ // prefix is bound to a different URI
+ if (repairnamespace) {
+ return Namespace.getNamespace(generatePrefix(), namespaceURI);
+ }
+ throw new XMLStreamException("Namespace prefix " + prefix +
+ " in this scope is bound to a different URI '" + bnd.getURI() +
+ "' (repairing not set for this XMLStreamWriter).");
+ }
+ // see if the default namespace is bound correctly...
+ if (boundstack.getNamespaceForPrefix("") == ns) {
+ return ns;
+ }
+ // nope, so see if there's any other bound namespace with the same URI.
+ final Namespace bound = boundstack.getFirstNamespaceForURI(namespaceURI);
+ if (bound != null) {
+ return bound;
+ }
+ // there are no existing bindings with the specified URI.
+ if (repairnamespace) {
+ return Namespace.getNamespace(generatePrefix(), namespaceURI);
+ }
+ throw new XMLStreamException("Namespace URI " + namespaceURI +
+ " is not bound in this scope (repairing not set for this XMLStreamWriter).");
+ }
+
+ private Namespace resolveAttributeNamespace(String prefix, String namespaceURI,
+ boolean withpfx) throws XMLStreamException {
+ final Namespace ns = Namespace.getNamespace(prefix, namespaceURI);
+ if (ns == Namespace.NO_NAMESPACE) {
+ return ns;
+ }
+ if (withpfx && !"".equals(prefix)) {
+
+ Namespace bnd = boundstack.getNamespaceForPrefix(prefix);
+
+ if (bnd == null || bnd == ns) {
+ // no existing binding for prefix, or same binding.
+ return ns;
+ }
+ // prefix is bound to a different URI
+ if (repairnamespace) {
+ final Namespace gen = Namespace.getNamespace(generatePrefix(), namespaceURI);
+ setPrefix(gen.getPrefix(), gen.getURI());
+ return gen;
+ }
+ throw new XMLStreamException("Namespace prefix " + prefix +
+ " in this scope is bound to a different URI '" + bnd.getURI() +
+ "' (repairing not set for this XMLStreamWriter).");
+ }
+
+ // see if there's any other bound namespace with the same URI.
+ final Namespace[] bound = boundstack.getAllNamespacesForURI(namespaceURI);
+ for (Namespace b : bound) {
+ if (!"".equals(b.getPrefix())) {
+ // use an existing prefixed Namespace binding.
+ // good for both repairing and non-repairing
+ return b;
+ }
+ }
+ // there are no existing prefixed bindings with the specified URI.
+ if (repairnamespace || bound.length > 0) {
+ return Namespace.getNamespace(generatePrefix(), namespaceURI);
+ }
+ throw new XMLStreamException("Namespace URI " + namespaceURI +
+ " is not bound in this attribute scope (repairing not set for this XMLStreamWriter).");
+ }
+
+ private String generatePrefix() {
+ String pfx = String.format("ns%03d", ++genprefix);
+ while (boundstack.getNamespaceForPrefix(pfx) != null) {
+ pfx = String.format("ns%03d", ++genprefix);
+ }
+ return pfx;
+ }
+
+ private final void buildAttribute(final String prefix,
+ final String namespaceURI, final String localName,
+ final String value, final boolean withpfx) throws XMLStreamException {
+ if (!(parent instanceof Element)) {
+ throw new IllegalStateException(
+ "Cannot write attribute unless inside an Element.");
+ }
+ if (localName == null) {
+ throw new XMLStreamException("localName is not allowed to be null");
+ }
+ if (value == null) {
+ throw new XMLStreamException("value is not allowed to be null");
+ }
+ if (activeelement == null) {
+ throw new IllegalStateException("Cannot add Attributes to an Element after other content was added.");
+ }
+
+ Namespace ns = resolveAttributeNamespace(prefix, namespaceURI, withpfx);
+
+ factory.setAttribute(activeelement, factory.attribute(localName, value, ns));
+ }
+
+ private final void flushActiveElement() {
+ if (activeelement != null) {
+
+ /*
+ * Add any written namespaces to the element as declared namespaces
+ * unless they are implied namespaces used by attributes, or something.
+ * If this Element is expecting content (it's not empty) then we also
+ * push the modified Element on to the Namespace stack.
+ */
+
+ boolean mod = false;
+ usednsstack.push(activeelement);
+ for (Namespace ns : pendingns) {
+ if (!usednsstack.isInScope(ns)) {
+ activeelement.addNamespaceDeclaration(ns);
+ mod = true;
+ }
+ }
+ pendingns.clear();
+
+ if (mod) {
+ usednsstack.pop();
+ if (isempty) {
+ boundstack.pop();
+ activeelement = null;
+ return;
+ }
+ // reload the stack with the additional namespaces.
+ usednsstack.push(activeelement);
+ }
+ if (isempty) {
+ boundstack.pop();
+ activeelement = null;
+ return;
+ }
+ boundstack.push(activeelement);
+ boundstack.push();
+
+ activeelement = null;
+ }
+ }
+
+ private final void flushActiveText() {
+ activetext = null;
+ }
+
+}
View
368 core/src/java/org/jdom2/output/StAXAsStreamReader.java
@@ -0,0 +1,368 @@
+/*--
+
+ Copyright (C) 2000-2012 Jason Hunter & Brett McLaughlin.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions, and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions, and the disclaimer that follows
+ these conditions in the documentation and/or other materials
+ provided with the distribution.
+
+ 3. The name "JDOM" must not be used to endorse or promote products
+ derived from this software without prior written permission. For
+ written permission, please contact <request_AT_jdom_DOT_org>.
+
+ 4. Products derived from this software may not be called "JDOM", nor
+ may "JDOM" appear in their name, without prior written permission
+ from the JDOM Project Management <request_AT_jdom_DOT_org>.
+
+ In addition, we request (but do not require) that you include in the
+ end-user documentation provided with the redistribution and/or in the
+ software itself an acknowledgement equivalent to the following:
+ "This product includes software developed by the
+ JDOM Project (http://www.jdom.org/)."
+ Alternatively, the acknowledgment may be graphical using the logos
+ available at http://www.jdom.org/images/logos.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+ This software consists of voluntary contributions made by many
+ individuals on behalf of the JDOM Project and was originally
+ created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
+ Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
+ on the JDOM Project, please see <http://www.jdom.org/>.
+
+ */
+
+package org.jdom2.output;
+
+import javax.xml.stream.XMLStreamReader;
+
+import org.jdom2.Document;
+import org.jdom2.jaxb.JDOMStreamReader;
+import org.jdom2.output.support.AbstractStAXAsStreamProcessor;
+import org.jdom2.output.support.StAXAsStreamProcessor;
+
+/**
+ * Represents a JDOM document as a StAX StreamReader that can be read from.
+ * <p>
+ * The StAXAsStreamReader can manage many styles of document formatting, from
+ * untouched to 'pretty' printed. The default is to output the document content
+ * exactly as created, but this can be changed by setting a new Format object:
+ * <ul>
+ * <li>For pretty-print output, use
+ * <code>{@link Format#getPrettyFormat()}</code>.
+ * <li>For whitespace-normalised output, use
+ * <code>{@link Format#getCompactFormat()}</code>.
+ * <li>For unmodified-format output, use
+ * <code>{@link Format#getRawFormat()}</code>.
+ * </ul>
+ * <p>
+ * There is only one <code>{@link #output output(Document)}</code> method that exposes
+ * a JDOM Document as a StAX Stream.
+ * <p>
+ * If changing the {@link Format} settings are insufficient for your output
+ * needs you can customise this StAXAsStreamReader further by setting a different
+ * {@link StAXAsStreamProcessor} with the
+ * {@link #setStAXAsStreamProcessor(StAXAsStreamProcessor)} method or an appropriate
+ * constructor. A fully-enabled Abstract class
+ * {@link AbstractStAXAsStreamProcessor} is available to be further extended to
+ * your needs if all you want to do is tweak some details.
+ *
+ * @author Rolf Lear
+ */
+
+public final class StAXAsStreamReader implements Cloneable {
+
+ /*
+ * =====================================================================
+ * Static content.
+ * =====================================================================
+ */
+
+ /**
+ * Create a final and static instance of the StAXAsStreamProcessor The
+ * final part is important because it improves performance.
+ * <p>
+ * The JDOM user can change the actual XMLOutputProcessor with the
+ * {@link StAXAsStreamReader#setStAXAsStreamProcessor(StAXAsStreamProcessor)} method.
+ *
+ * @author rolf
+ */
+ private static final class DefaultStAXAsStreamProcessor implements StAXAsStreamProcessor {
+
+ private static final class MyXMLStreamReader extends JDOMStreamReader {
+ public MyXMLStreamReader(Document doc, Format format) {
+ super(doc, format);
+ }
+ }
+
+ @Override
+ public XMLStreamReader buildReader(Document doc, Format format) {
+ return new MyXMLStreamReader(doc, format);
+ }
+
+ }
+
+ /**
+ * This constant XMLOutputProcessor is used for all non-customised
+ * XMLOutputters
+ */
+ private static final DefaultStAXAsStreamProcessor DEFAULTPROCESSOR =
+ new DefaultStAXAsStreamProcessor();
+
+ /*
+ * =====================================================================
+ * Instance content.
+ * =====================================================================
+ */
+
+ // For normal output
+ private Format myFormat = null;
+
+ // The actual XMLOutputProcessor to delegate to.
+ private StAXAsStreamProcessor myProcessor = null;
+
+ /*
+ * =====================================================================
+ * Constructors
+ * =====================================================================
+ */
+
+ /**
+ * This will create an <code>XMLOutputter</code> with the specified format
+ * characteristics.
+ * <p>
+ * <b>Note:</b> the format object is cloned internally before use. If you
+ * want to modify the Format after constructing the XMLOutputter you can
+ * modify the Format instance {@link #getFormat()} returns.
+ *
+ * @param format
+ * The Format instance to use. This instance will be cloned() and as
+ * a consequence, changes made to the specified format instance
+ * <b>will not</b> be reflected in this XMLOutputter. A null input
+ * format indicates that XMLOutputter should use the default
+ * {@link Format#getRawFormat()}
+ * @param processor
+ * The XMLOutputProcessor to delegate output to. If null the
+ * XMLOutputter will use the default XMLOutputProcessor.
+ */
+ public StAXAsStreamReader(Format format, StAXAsStreamProcessor processor) {
+ myFormat = format == null ? Format.getRawFormat() : format.clone();
+ myProcessor = processor == null ? DEFAULTPROCESSOR : processor;
+ }
+
+ /**
+ * This will create an <code>XMLOutputter</code> with a default
+ * {@link Format} and {@link StAXAsStreamProcessor}.
+ */
+ public StAXAsStreamReader() {
+ this(null, null);
+ }
+
+ /**
+ * This will create an <code>XMLOutputter</code> with the same
+ * customisations set in the given <code>XMLOutputter</code> instance. Note
+ * that <code>XMLOutputter two = one.clone();</code> would work equally
+ * well.
+ *
+ * @param that
+ * the XMLOutputter to clone
+ */
+ public StAXAsStreamReader(StAXAsStreamReader that) {
+ this(that.myFormat, null);
+ }
+
+ /**
+ * This will create an <code>XMLOutputter</code> with the specified format
+ * characteristics.
+ * <p>
+ * <b>Note:</b> the format object is cloned internally before use.
+ *
+ * @param format
+ * The Format instance to use. This instance will be cloned() and as
+ * a consequence, changes made to the specified format instance
+ * <b>will not</b> be reflected in this XMLOutputter. A null input
+ * format indicates that XMLOutputter should use the default
+ * {@link Format#getRawFormat()}
+ */
+ public StAXAsStreamReader(Format format) {
+ this(format, null);
+ }
+
+ /**
+ * This will create an <code>XMLOutputter</code> with the specified
+ * XMLOutputProcessor.
+ *
+ * @param processor
+ * The XMLOutputProcessor to delegate output to. If null the
+ * XMLOutputter will use the default XMLOutputProcessor.
+ */
+ public StAXAsStreamReader(StAXAsStreamProcessor processor) {
+ this(null, processor);
+ }
+
+ /*
+ * =======================================================================
+ * API - Settings...
+ * =======================================================================
+ */
+
+ /**
+ * Sets the new format logic for the XMLOutputter. Note the Format object is
+ * cloned internally before use.
+ *
+ * @see #getFormat()
+ * @param newFormat
+ * the format to use for subsequent output
+ */
+ public void setFormat(Format newFormat) {
+ this.myFormat = newFormat.clone();
+ }
+
+ /**
+ * Returns the current format in use by the XMLOutputter. Note the Format
+ * object returned is <b>not</b> a clone of the one used internally, thus,
+ * an XMLOutputter instance is able to have it's Format changed by changing
+ * the settings on the Format instance returned by this method.
+ *
+ * @return the current Format instance used by this XMLOutputter.
+ */
+ public Format getFormat() {
+ return myFormat;
+ }
+
+ /**
+ * Returns the current XMLOutputProcessor instance in use by the
+ * StAXAsStreamReader.
+ *
+ * @return the current XMLOutputProcessor instance.
+ */
+ public StAXAsStreamProcessor getStAXAsStreamProcessor() {
+ return myProcessor;
+ }
+
+ /**
+ * Sets a new StAXAsStreamProcessor instance for this StAXAsStreamReader. Note the
+ * processor object is expected to be thread-safe.
+ *
+ * @param processor
+ * the new XMLOutputProcesor to use for output
+ */
+ public void setStAXAsStreamProcessor(StAXAsStreamProcessor processor) {
+ this.myProcessor = processor;
+ }
+
+ /*
+ * =======================================================================
+ * API - Output to STREAM Methods ... All methods defer to the WRITER
+ * equivalents
+ * =======================================================================
+ */
+
+ /**
+ * This will expose the <code>{@link Document}</code> as a StAX XMLStreamReader.
+ *
+ * @param doc
+ * <code>Document</code> to format.
+ * @return The XMLStreamReader representing the input Document.
+ * @throws NullPointerException
+ * if the specified content is null.
+ */
+ public final XMLStreamReader output(Document doc) {
+ return myProcessor.buildReader(doc, myFormat.clone());
+ }
+
+ /*
+ * ========================================================================
+ * Basic Support methods.
+ * ========================================================================
+ */
+
+ /**
+ * Returns a cloned copy of this XMLOutputter.
+ */
+ @Override
+ public StAXAsStreamReader clone() {
+ // Implementation notes: Since all state of an XMLOutputter is
+ // embodied in simple private instance variables, Object.clone
+ // can be used. Note that since Object.clone is totally
+ // broken, we must catch an exception that will never be
+ // thrown.
+ try {
+ return (StAXAsStreamReader) super.clone();
+ } catch (java.lang.CloneNotSupportedException e) {
+ // even though this should never ever happen, it's still
+ // possible to fool Java into throwing a
+ // CloneNotSupportedException. If that happens, we
+ // shouldn't swallow it.
+ throw new RuntimeException(e.toString());
+ }
+ }
+
+ /**
+ * Return a string listing of the settings for this XMLOutputter instance.
+ *
+ * @return a string listing the settings for this XMLOutputter instance
+ */
+ @Override
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append("StAXAsStreamReader[omitDeclaration = ");
+ buffer.append(myFormat.omitDeclaration);
+ buffer.append(", ");
+ buffer.append("encoding = ");
+ buffer.append(myFormat.encoding);
+ buffer.append(", ");
+ buffer.append("omitEncoding = ");
+ buffer.append(myFormat.omitEncoding);
+ buffer.append(", ");
+ buffer.append("indent = '");
+ buffer.append(myFormat.indent);
+ buffer.append("'");
+ buffer.append(", ");
+ buffer.append("expandEmptyElements = ");
+ buffer.append(myFormat.expandEmptyElements);
+ buffer.append(", ");
+ buffer.append("lineSeparator = '");
+ for (char ch : myFormat.lineSeparator.toCharArray()) {
+ switch (ch) {
+ case '\r':
+ buffer.append("\\r");
+ break;
+ case '\n':
+ buffer.append("\\n");
+ break;
+ case '\t':
+ buffer.append("\\t");
+ break;
+ default:
+ buffer.append("[" + ((int) ch) + "]");
+ break;
+ }
+ }
+ buffer.append("', ");
+ buffer.append("textMode = ");
+ buffer.append(myFormat.mode + "]");
+ return buffer.toString();
+ }
+
+}
View
88 core/src/java/org/jdom2/output/support/AbstractStAXAsStreamProcessor.java
@@ -0,0 +1,88 @@
+/*--
+
+ Copyright (C) 2000-2012 Jason Hunter & Brett McLaughlin.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions, and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions, and the disclaimer that follows
+ these conditions in the documentation and/or other materials
+ provided with the distribution.
+
+ 3. The name "JDOM" must not be used to endorse or promote products
+ derived from this software without prior written permission. For
+ written permission, please contact <request_AT_jdom_DOT_org>.
+
+ 4. Products derived from this software may not be called "JDOM", nor
+ may "JDOM" appear in their name, without prior written permission
+ from the JDOM Project Management <request_AT_jdom_DOT_org>.
+
+ In addition, we request (but do not require) that you include in the
+ end-user documentation provided with the redistribution and/or in the
+ software itself an acknowledgement equivalent to the following:
+ "This product includes software developed by the
+ JDOM Project (http://www.jdom.org/)."
+ Alternatively, the acknowledgment may be graphical using the logos
+ available at http://www.jdom.org/images/logos.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+ This software consists of voluntary contributions made by many
+ individuals on behalf of the JDOM Project and was originally
+ created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
+ Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
+ on the JDOM Project, please see <http://www.jdom.org/>.
+
+ */
+
+package org.jdom2.output.support;
+
+import javax.xml.stream.XMLStreamReader;
+
+import org.jdom2.Document;
+import org.jdom2.jaxb.JDOMStreamReader;
+import org.jdom2.output.Format;
+
+/**
+ * A complete (but still abstract) implementation of a class that produces
+ * XMLStreamReaders based on the class AbstractXMLStreamReader. Users who need
+ * to make changes to the output format that are not possible with just the
+ * Format class, may need to extend this class to create instances of their own
+ * XMLStreamReader. Their own implementation could in turn extend the AbstractXMLReader
+ * class which contains the bulk of the StAX Logic.
+ *
+ * @author Rolf Lear
+ *
+ */
+public class AbstractStAXAsStreamProcessor implements StAXAsStreamProcessor {
+
+ private static final class MyXMLStreamReader extends JDOMStreamReader {
+
+ public MyXMLStreamReader(Document doc, Format format) {
+ super(doc, format);
+ }
+ }
+
+ @Override
+ public XMLStreamReader buildReader(Document doc, Format format) {
+ return new MyXMLStreamReader(doc, format);
+ }
+
+}
View
32 core/src/java/org/jdom2/output/support/AbstractStAXStreamProcessor.java
@@ -603,13 +603,15 @@ else if ("preserve".equals(space)) {
if (expandit) {
Namespace ns = element.getNamespace();
- if (ns == Namespace.NO_NAMESPACE) {
- out.writeStartElement(element.getName());
- } else if ("".equals(ns.getPrefix())) {
- out.writeStartElement(ns.getURI(), element.getName());
- } else {
- out.writeStartElement(ns.getPrefix(), element.getName(), ns.getURI());
- }
+ out.setPrefix(ns.getPrefix(), ns.getURI());
+ out.writeStartElement(ns.getPrefix(), element.getName(), ns.getURI());
+// if (ns == Namespace.NO_NAMESPACE) {
+// out.writeStartElement(element.getName());
+// } else if ("".equals(ns.getPrefix())) {
+// out.writeStartElement(ns.getURI(), element.getName());
+// } else {
+// out.writeStartElement(ns.getPrefix(), element.getName(), ns.getURI());
+// }
// Print the element's namespace, if appropriate
for (final Namespace nsd : nstack.addedForward()) {
@@ -659,13 +661,15 @@ else if ("preserve".equals(space)) {
// and whiteonly == true
Namespace ns = element.getNamespace();
- if (ns == Namespace.NO_NAMESPACE) {
- out.writeEmptyElement(element.getName());
- } else if ("".equals(ns.getPrefix())) {
- out.writeEmptyElement("", element.getName(), ns.getURI());
- } else {
- out.writeEmptyElement(ns.getPrefix(), element.getName(), ns.getURI());
- }
+ out.setPrefix(ns.getPrefix(), ns.getURI());
+ out.writeEmptyElement(ns.getPrefix(), element.getName(), ns.getURI());
+// if (ns == Namespace.NO_NAMESPACE) {
+// out.writeEmptyElement(element.getName());
+// } else if ("".equals(ns.getPrefix())) {
+// out.writeEmptyElement("", element.getName(), ns.getURI());
+// } else {
+// out.writeEmptyElement(ns.getPrefix(), element.getName(), ns.getURI());
+// }
// Print the element's namespace, if appropriate
for (final Namespace nsd : nstack.addedForward()) {
View
85 core/src/java/org/jdom2/output/support/StAXAsStreamProcessor.java
@@ -0,0 +1,85 @@
+/*--
+
+ Copyright (C) 2000-2012 Jason Hunter & Brett McLaughlin.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions, and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions, and the disclaimer that follows
+ these conditions in the documentation and/or other materials
+ provided with the distribution.
+
+ 3. The name "JDOM" must not be used to endorse or promote products
+ derived from this software without prior written permission. For
+ written permission, please contact <request_AT_jdom_DOT_org>.
+
+ 4. Products derived from this software may not be called "JDOM", nor
+ may "JDOM" appear in their name, without prior written permission
+ from the JDOM Project Management <request_AT_jdom_DOT_org>.
+
+ In addition, we request (but do not require) that you include in the
+ end-user documentation provided with the redistribution and/or in the
+ software itself an acknowledgement equivalent to the following:
+ "This product includes software developed by the
+ JDOM Project (http://www.jdom.org/)."
+ Alternatively, the acknowledgment may be graphical using the logos
+ available at http://www.jdom.org/images/logos.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+ This software consists of voluntary contributions made by many
+ individuals on behalf of the JDOM Project and was originally
+ created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
+ Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
+ on the JDOM Project, please see <http://www.jdom.org/>.
+
+ */
+
+package org.jdom2.output.support;
+
+import javax.xml.stream.XMLStreamReader;
+
+import org.jdom2.Document;
+import org.jdom2.jaxb.JDOMStreamReader;
+import org.jdom2.output.Format;
+
+/**
+ * A simple interface that allows the implementation of a StAX XMLStreamReader
+ * instance for representing a JDOM Document. If a user needs to create a custom
+ * way to output as an XMLStreamReader they can implement their own XMLStreamReader
+ * class (perhaps by extending {@link JDOMStreamReader}) and then creating an
+ * implementation of this class that creates their own custom XMLStreamReader
+ * version. The implementation of this class could then be given to the
+ * StAXAsStreamReader.
+ *
+ * @author Rolf Lear
+ *
+ */
+public interface StAXAsStreamProcessor {
+ /**
+ * Return an implementation of an XMLStreamReader that represents
+ * a JDOM Document.
+ *
+ * @param doc The Document to represent.
+ * @param format The Format to apply to the document.
+ * @return The XMLStreamReader that expresses the JDOM Document.
+ */
+ public XMLStreamReader buildReader(Document doc, Format format);
+}
View
102 core/src/java/org/jdom2/util/NamespaceStack.java
@@ -56,6 +56,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
@@ -227,6 +228,9 @@ public void remove() {
/** A simple empty Namespace Array to avoid redundant empty instances */
private static final Namespace[] EMPTY = new Namespace[0];
+
+ private static final List<Namespace> EMPTYLIST = Collections.emptyList();
+
/** A simple Iterable instance that is always empty. Saves some memory */
private static final Iterable<Namespace> EMPTYITER = new EmptyIterable();
@@ -474,6 +478,51 @@ public void push(Attribute att) {
pushStack(mns, newscope, toadd);
}
+ /**
+ * Create a new in-scope level for the Stack based on an arbitrary set of Namespaces.
+ * @param namespaces The Iterable format for the Namespaces.
+ */
+ public void push(Iterable<Namespace> namespaces) {
+
+ // how many times do you add more than 8 namespaces in one go...
+ // we can add more if we need to...
+ final List<Namespace> toadd = new ArrayList<Namespace>(8);
+ Namespace[] newscope = scope[depth];
+ for (final Namespace ns : namespaces) {
+ // check to see whether the Namespace is new-to-scope.
+ newscope = checkNamespace(toadd, ns, newscope);
+ }
+
+ pushStack(Namespace.XML_NAMESPACE, newscope, toadd);
+ }
+
+ /**
+ * Create a new in-scope level for the Stack based on an arbitrary set of Namespaces.
+ * The first Namespace in the list will be considered the 'primary' namespace for this scope
+ * and will be sorted to the front. If no namespaces are supplied then the 'current' scope will
+ * be duplicated (including sort order) as the new scope.
+ * @param namespaces The array of Namespaces.
+ */
+ public void push(Namespace ... namespaces) {
+
+ if (namespaces == null || namespaces.length == 0) {
+ // duplicate the current level to the new one.
+ pushStack(scope[depth][0], scope[depth], EMPTYLIST);
+ return;
+ }
+
+ // how many times do you add more than 8 namespaces in one go...
+ // we can add more if we need to...
+ final List<Namespace> toadd = new ArrayList<Namespace>(8);
+ Namespace[] newscope = scope[depth];
+ for (final Namespace ns : namespaces) {
+ // check to see whether the Namespace is new-to-scope.
+ newscope = checkNamespace(toadd, ns, newscope);
+ }
+
+ pushStack(namespaces[0], newscope, toadd);
+ }
+
private final void pushStack(final Namespace mns, Namespace[] newscope,
final List<Namespace> toadd) {
// OK, we've checked the namespaces in the Element, and 'toadd' contains
@@ -602,5 +651,58 @@ public boolean isInScope(Namespace ns) {
}
return false;
}
+
+ /**
+ * Get the Namespace in the current scope with the specified prefix.
+ * @param prefix The prefix to get the namespace for (null is treated the same as "").
+ * @return The Namespace with the specified prefix, or null if the prefix is not in scope.
+ */
+ public Namespace getNamespaceForPrefix(final String prefix) {
+ if (prefix == null) {
+ return getNamespaceForPrefix("");
+ }
+ final Namespace[] nsa = scope[depth];
+ for (final Namespace ns : nsa) {
+ if (prefix.equals(ns.getPrefix())) {
+ return ns;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the <strong>first</strong> Namespace in the current scope that is bound to the specified URI.
+ * @param uri The URI to get the first prefix for (null is treated the same as "").
+ * @return The first bound Namespace for the specified URI, or null if the URI is not bound.
+ */
+ public Namespace getFirstNamespaceForURI(final String uri) {
+ if (uri == null) {