diff --git a/src/main/java/org/jboss/staxmapper/FixedXMLStreamReader.java b/src/main/java/org/jboss/staxmapper/FixedXMLStreamReader.java index b1463ba..8fc4f60 100644 --- a/src/main/java/org/jboss/staxmapper/FixedXMLStreamReader.java +++ b/src/main/java/org/jboss/staxmapper/FixedXMLStreamReader.java @@ -1,221 +1,266 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2010, Red Hat, Inc., and individual contributors - * as indicated by the @author tags. See the copyright.txt file in the - * distribution for a full listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ - -package org.jboss.staxmapper; - -import javax.xml.namespace.NamespaceContext; -import javax.xml.namespace.QName; -import javax.xml.stream.Location; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; - -/** - * @author David M. Lloyd - */ -final class FixedXMLStreamReader implements XMLStreamReader { - - private final XMLStreamReader delegate; - - FixedXMLStreamReader(final XMLStreamReader delegate) { - this.delegate = delegate; - } - - public Object getProperty(final String name) throws IllegalArgumentException { - return delegate.getProperty(name); - } - - public int next() throws XMLStreamException { - throw new UnsupportedOperationException(); - } - - public void require(final int type, final String namespaceURI, final String localName) throws XMLStreamException { - delegate.require(type, namespaceURI, localName); - } - - public String getElementText() throws XMLStreamException { - return delegate.getElementText(); - } - - public int nextTag() throws XMLStreamException { - throw new UnsupportedOperationException(); - } - - public boolean hasNext() throws XMLStreamException { - return delegate.hasNext(); - } - - public void close() throws XMLStreamException { - throw new UnsupportedOperationException(); - } - - public String getNamespaceURI(final String prefix) { - return delegate.getNamespaceURI(prefix); - } - - public boolean isStartElement() { - return delegate.isStartElement(); - } - - public boolean isEndElement() { - return delegate.isEndElement(); - } - - public boolean isCharacters() { - return delegate.isCharacters(); - } - - public boolean isWhiteSpace() { - return delegate.isWhiteSpace(); - } - - public String getAttributeValue(final String namespaceURI, final String localName) { - return delegate.getAttributeValue(namespaceURI, localName); - } - - public int getAttributeCount() { - return delegate.getAttributeCount(); - } - - public QName getAttributeName(final int index) { - return delegate.getAttributeName(index); - } - - public String getAttributeNamespace(final int index) { - return delegate.getAttributeNamespace(index); - } - - public String getAttributeLocalName(final int index) { - return delegate.getAttributeLocalName(index); - } - - public String getAttributePrefix(final int index) { - return delegate.getAttributePrefix(index); - } - - public String getAttributeType(final int index) { - return delegate.getAttributeType(index); - } - - public String getAttributeValue(final int index) { - return delegate.getAttributeValue(index); - } - - public boolean isAttributeSpecified(final int index) { - return delegate.isAttributeSpecified(index); - } - - public int getNamespaceCount() { - return delegate.getNamespaceCount(); - } - - public String getNamespacePrefix(final int index) { - return delegate.getNamespacePrefix(index); - } - - public String getNamespaceURI(final int index) { - return delegate.getNamespaceURI(index); - } - - public NamespaceContext getNamespaceContext() { - return delegate.getNamespaceContext(); - } - - public int getEventType() { - return delegate.getEventType(); - } - - public String getText() { - return delegate.getText(); - } - - public char[] getTextCharacters() { - return delegate.getTextCharacters(); - } - - public int getTextCharacters(final int sourceStart, final char[] target, final int targetStart, final int length) throws XMLStreamException { - return delegate.getTextCharacters(sourceStart, target, targetStart, length); - } - - public int getTextStart() { - return delegate.getTextStart(); - } - - public int getTextLength() { - return delegate.getTextLength(); - } - - public String getEncoding() { - return delegate.getEncoding(); - } - - public boolean hasText() { - return delegate.hasText(); - } - - public Location getLocation() { - return delegate.getLocation(); - } - - public QName getName() { - return delegate.getName(); - } - - public String getLocalName() { - return delegate.getLocalName(); - } - - public boolean hasName() { - return delegate.hasName(); - } - - public String getNamespaceURI() { - return delegate.getNamespaceURI(); - } - - public String getPrefix() { - return delegate.getPrefix(); - } - - public String getVersion() { - return delegate.getVersion(); - } - - public boolean isStandalone() { - return delegate.isStandalone(); - } - - public boolean standaloneSet() { - return delegate.standaloneSet(); - } - - public String getCharacterEncodingScheme() { - return delegate.getCharacterEncodingScheme(); - } - - public String getPITarget() { - return delegate.getPITarget(); - } - - public String getPIData() { - return delegate.getPIData(); - } -} +/* + * JBoss, Home of Professional Open Source. + * Copyright 2010, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.staxmapper; + +import javax.xml.namespace.NamespaceContext; +import javax.xml.namespace.QName; +import javax.xml.stream.Location; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +/** + * @author David M. Lloyd + */ +final class FixedXMLStreamReader implements XMLStreamReader { + + private final XMLStreamReader delegate; + + FixedXMLStreamReader(final XMLStreamReader delegate) { + this.delegate = delegate; + } + + @Override + public Object getProperty(final String name) throws IllegalArgumentException { + return delegate.getProperty(name); + } + + @Override + public int next() throws XMLStreamException { + throw new UnsupportedOperationException(); + } + + @Override + public void require(final int type, final String namespaceURI, final String localName) throws XMLStreamException { + delegate.require(type, namespaceURI, localName); + } + + @Override + public String getElementText() throws XMLStreamException { + return delegate.getElementText(); + } + + @Override + public int nextTag() throws XMLStreamException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasNext() throws XMLStreamException { + return delegate.hasNext(); + } + + @Override + public void close() throws XMLStreamException { + throw new UnsupportedOperationException(); + } + + @Override + public String getNamespaceURI(final String prefix) { + return delegate.getNamespaceURI(prefix); + } + + @Override + public boolean isStartElement() { + return delegate.isStartElement(); + } + + @Override + public boolean isEndElement() { + return delegate.isEndElement(); + } + + @Override + public boolean isCharacters() { + return delegate.isCharacters(); + } + + @Override + public boolean isWhiteSpace() { + return delegate.isWhiteSpace(); + } + + @Override + public String getAttributeValue(final String namespaceURI, final String localName) { + return delegate.getAttributeValue(namespaceURI, localName); + } + + @Override + public int getAttributeCount() { + return delegate.getAttributeCount(); + } + + @Override + public QName getAttributeName(final int index) { + return delegate.getAttributeName(index); + } + + @Override + public String getAttributeNamespace(final int index) { + return delegate.getAttributeNamespace(index); + } + + @Override + public String getAttributeLocalName(final int index) { + return delegate.getAttributeLocalName(index); + } + + @Override + public String getAttributePrefix(final int index) { + return delegate.getAttributePrefix(index); + } + + @Override + public String getAttributeType(final int index) { + return delegate.getAttributeType(index); + } + + @Override + public String getAttributeValue(final int index) { + return delegate.getAttributeValue(index); + } + + @Override + public boolean isAttributeSpecified(final int index) { + return delegate.isAttributeSpecified(index); + } + + @Override + public int getNamespaceCount() { + return delegate.getNamespaceCount(); + } + + @Override + public String getNamespacePrefix(final int index) { + return delegate.getNamespacePrefix(index); + } + + @Override + public String getNamespaceURI(final int index) { + return delegate.getNamespaceURI(index); + } + + @Override + public NamespaceContext getNamespaceContext() { + return delegate.getNamespaceContext(); + } + + @Override + public int getEventType() { + return delegate.getEventType(); + } + + @Override + public String getText() { + return delegate.getText(); + } + + @Override + public char[] getTextCharacters() { + return delegate.getTextCharacters(); + } + + @Override + public int getTextCharacters(final int sourceStart, final char[] target, final int targetStart, final int length) throws XMLStreamException { + return delegate.getTextCharacters(sourceStart, target, targetStart, length); + } + + @Override + public int getTextStart() { + return delegate.getTextStart(); + } + + @Override + public int getTextLength() { + return delegate.getTextLength(); + } + + @Override + public String getEncoding() { + return delegate.getEncoding(); + } + + @Override + public boolean hasText() { + return delegate.hasText(); + } + + @Override + public Location getLocation() { + return delegate.getLocation(); + } + + @Override + public QName getName() { + return delegate.getName(); + } + + @Override + public String getLocalName() { + return delegate.getLocalName(); + } + + @Override + public boolean hasName() { + return delegate.hasName(); + } + + @Override + public String getNamespaceURI() { + return delegate.getNamespaceURI(); + } + + @Override + public String getPrefix() { + return delegate.getPrefix(); + } + + @Override + public String getVersion() { + return delegate.getVersion(); + } + + @Override + public boolean isStandalone() { + return delegate.isStandalone(); + } + + @Override + public boolean standaloneSet() { + return delegate.standaloneSet(); + } + + @Override + public String getCharacterEncodingScheme() { + return delegate.getCharacterEncodingScheme(); + } + + @Override + public String getPITarget() { + return delegate.getPITarget(); + } + + @Override + public String getPIData() { + return delegate.getPIData(); + } +} diff --git a/src/main/java/org/jboss/staxmapper/FormattingXMLStreamWriter.java b/src/main/java/org/jboss/staxmapper/FormattingXMLStreamWriter.java index 816a5d5..a731d75 100644 --- a/src/main/java/org/jboss/staxmapper/FormattingXMLStreamWriter.java +++ b/src/main/java/org/jboss/staxmapper/FormattingXMLStreamWriter.java @@ -1,528 +1,500 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2010, Red Hat, Inc., and individual contributors - * as indicated by the @author tags. See the copyright.txt file in the - * distribution for a full listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ - -package org.jboss.staxmapper; - -import java.lang.reflect.UndeclaredThrowableException; -import java.util.ArrayDeque; -import java.util.Iterator; - -import javax.xml.namespace.NamespaceContext; -import javax.xml.stream.XMLStreamConstants; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; - -/** - * An XML stream writer which nicely formats the XML for configuration files. - * - * @author David M. Lloyd - */ -public final class FormattingXMLStreamWriter implements XMLExtendedStreamWriter, XMLStreamConstants { - private static final String NO_NAMESPACE = new String(); - private final XMLStreamWriter delegate; - private final ArrayDeque attrQueue = new ArrayDeque(); - private int level; - private int state = START_DOCUMENT; - private boolean indentEndElement = false; - private ArrayDeque unspecifiedNamespaces = new ArrayDeque(); - - - public FormattingXMLStreamWriter(final XMLStreamWriter delegate) { - this.delegate = delegate; - unspecifiedNamespaces.push(NO_NAMESPACE); - } - - private void nl() throws XMLStreamException { - delegate.writeCharacters("\n"); - } - - private void indent() throws XMLStreamException { - int level = this.level; - final XMLStreamWriter delegate = this.delegate; - for (int i = 0; i < level; i ++) { - delegate.writeCharacters(" "); - } - } - - private interface ArgRunnable { - void run(int arg) throws XMLStreamException; - } - - @Override - public void setUnspecifiedElementNamespace(final String namespace) { - ArrayDeque namespaces = this.unspecifiedNamespaces; - namespaces.pop(); - namespaces.push(namespace == null ? NO_NAMESPACE : namespace); - } - - private String nestUnspecifiedNamespace() { - ArrayDeque namespaces = unspecifiedNamespaces; - String clone = namespaces.getFirst(); - namespaces.push(clone); - return clone; - } - - @Override - public void writeStartElement(final String localName) throws XMLStreamException { - ArrayDeque namespaces = unspecifiedNamespaces; - String namespace = namespaces.getFirst(); - if (namespace != NO_NAMESPACE) { - writeStartElement(namespace, localName); - return; - } - - unspecifiedNamespaces.push(namespace); - - // If this is a nested element flush the outer - runAttrQueue(); - nl(); - indent(); - attrQueue.add(new ArgRunnable() { - public void run(int arg) throws XMLStreamException { - if (arg == 0) { - delegate.writeStartElement(localName); - } else { - delegate.writeEmptyElement(localName); - } - } - }); - - level++; - state = START_ELEMENT; - indentEndElement = false; - } - - @Override - public void writeStartElement(final String namespaceURI, final String localName) throws XMLStreamException { - nestUnspecifiedNamespace(); - - // If this is a nested element flush the outer - runAttrQueue(); - nl(); - indent(); - attrQueue.add(new ArgRunnable() { - public void run(int arg) throws XMLStreamException { - if (arg == 0) { - delegate.writeStartElement(namespaceURI, localName); - } else { - delegate.writeEmptyElement(namespaceURI, localName); - } - } - }); - level++; - state = START_ELEMENT; - indentEndElement = false; - } - - @Override - public void writeStartElement(final String prefix, final String localName, final String namespaceURI) throws XMLStreamException { - nestUnspecifiedNamespace(); - - // If this is a nested element flush the outer - runAttrQueue(); - nl(); - indent(); - attrQueue.add(new ArgRunnable() { - public void run(int arg) throws XMLStreamException { - if (arg == 0) { - delegate.writeStartElement(prefix, namespaceURI, localName); - } else { - delegate.writeEmptyElement(prefix, namespaceURI, localName); - } - } - }); - level++; - state = START_ELEMENT; - indentEndElement = false; - } - - @Override - public void writeEmptyElement(final String namespaceURI, final String localName) throws XMLStreamException { - runAttrQueue(); - nl(); - indent(); - delegate.writeEmptyElement(namespaceURI, localName); - state = END_ELEMENT; - } - - @Override - public void writeEmptyElement(final String prefix, final String localName, final String namespaceURI) throws XMLStreamException { - runAttrQueue(); - nl(); - indent(); - delegate.writeEmptyElement(prefix, namespaceURI, localName); - state = END_ELEMENT; - } - - @Override - public void writeEmptyElement(final String localName) throws XMLStreamException { - String namespace = unspecifiedNamespaces.getFirst(); - if (namespace != NO_NAMESPACE) { - writeEmptyElement(namespace, localName); - return; - } - - runAttrQueue(); - nl(); - indent(); - delegate.writeEmptyElement(localName); - state = END_ELEMENT; - } - - @Override - public void writeEndElement() throws XMLStreamException { - level--; - if (state != START_ELEMENT) { - runAttrQueue(); - if (state != CHARACTERS || indentEndElement) { - nl(); - indent(); - indentEndElement = false; - } - delegate.writeEndElement(); - } else { - // Change the start element to an empty element - ArgRunnable start = attrQueue.poll(); - if (start == null) { - delegate.writeEndElement(); - } else { - start.run(1); - // Write everything else - runAttrQueue(); - } - } - - unspecifiedNamespaces.pop(); - state = END_ELEMENT; - } - - private void runAttrQueue() throws XMLStreamException { - ArgRunnable attr; - while ((attr = attrQueue.poll()) != null) { - attr.run(0); - } - } - - @Override - public void writeEndDocument() throws XMLStreamException { - delegate.writeEndDocument(); - state = END_DOCUMENT; - } - - @Override - public void close() throws XMLStreamException { - delegate.close(); - state = END_DOCUMENT; - } - - @Override - public void flush() throws XMLStreamException { - delegate.flush(); - } - - @Override - public void writeAttribute(final String localName, final String value) throws XMLStreamException { - attrQueue.add(new ArgRunnable() { - public void run(int arg) throws XMLStreamException { - try { - delegate.writeAttribute(localName, value); - } catch (XMLStreamException e) { - throw new UndeclaredThrowableException(e); - } - } - }); - } - - @Override - public void writeAttribute(final String prefix, final String namespaceURI, final String localName, final String value) throws XMLStreamException { - attrQueue.add(new ArgRunnable() { - public void run(int arg) throws XMLStreamException { - delegate.writeAttribute(prefix, namespaceURI, localName, value); - } - }); - } - - @Override - public void writeAttribute(final String namespaceURI, final String localName, final String value) throws XMLStreamException { - attrQueue.add(new ArgRunnable() { - public void run(int arg) throws XMLStreamException { - delegate.writeAttribute(namespaceURI, localName, value); - } - }); - } - - @Override - public void writeAttribute(final String localName, final String[] values) throws XMLStreamException { - attrQueue.add(new ArgRunnable() { - public void run(int arg) throws XMLStreamException { - delegate.writeAttribute(localName, join(values)); - } - }); - } - - @Override - public void writeAttribute(final String prefix, final String namespaceURI, final String localName, final String[] values) throws XMLStreamException { - attrQueue.add(new ArgRunnable() { - public void run(int arg) throws XMLStreamException { - delegate.writeAttribute(prefix, namespaceURI, localName, join(values)); - } - }); - } - - @Override - public void writeAttribute(final String namespaceURI, final String localName, final String[] values) throws XMLStreamException { - attrQueue.add(new ArgRunnable() { - public void run(int arg) throws XMLStreamException { - delegate.writeAttribute(namespaceURI, localName, join(values)); - } - }); - } - - @Override - public void writeAttribute(final String localName, final Iterable values) throws XMLStreamException { - attrQueue.add(new ArgRunnable() { - public void run(int arg) throws XMLStreamException { - delegate.writeAttribute(localName, join(values)); - } - }); - } - - @Override - public void writeAttribute(final String prefix, final String namespaceURI, final String localName, final Iterable values) throws XMLStreamException { - attrQueue.add(new ArgRunnable() { - public void run(int arg) throws XMLStreamException { - delegate.writeAttribute(prefix, namespaceURI, localName, join(values)); - } - }); - } - - @Override - public void writeAttribute(final String namespaceURI, final String localName, final Iterable values) throws XMLStreamException { - attrQueue.add(new ArgRunnable() { - public void run(int arg) throws XMLStreamException { - delegate.writeAttribute(namespaceURI, localName, join(values)); - } - }); - } - - @Override - public void writeNamespace(final String prefix, final String namespaceURI) throws XMLStreamException { - attrQueue.add(new ArgRunnable() { - public void run(int arg) throws XMLStreamException { - delegate.writeNamespace(prefix, namespaceURI); - } - }); - } - - @Override - public void writeDefaultNamespace(final String namespaceURI) throws XMLStreamException { - attrQueue.add(new ArgRunnable() { - public void run(int arg) throws XMLStreamException { - delegate.writeDefaultNamespace(namespaceURI); - } - }); - } - - @Override - public void writeComment(final String data) throws XMLStreamException { - runAttrQueue(); - nl(); - indent(); - final StringBuilder b = new StringBuilder(data.length()); - final Iterator i = Spliterator.over(data, '\n'); - if (! i.hasNext()) { - return; - } else { - final String first = i.next(); - if (! i.hasNext()) { - delegate.writeComment(" " + first + " "); - state = COMMENT; - return; - } else { - b.append('\n'); - for (int q = 0; q < level; q++) { - b.append(" "); - } - b.append(" ~ "); - b.append(first); - do { - b.append('\n'); - for (int q = 0; q < level; q++) { - b.append(" "); - } - b.append(" ~ "); - b.append(i.next()); - } while (i.hasNext()); - } - b.append('\n'); - for (int q = 0; q < level; q ++) { - b.append(" "); - } - b.append(" "); - delegate.writeComment(b.toString()); - state = COMMENT; - } - } - - @Override - public void writeProcessingInstruction(final String target) throws XMLStreamException { - runAttrQueue(); - nl(); - indent(); - delegate.writeProcessingInstruction(target); - state = PROCESSING_INSTRUCTION; - } - - @Override - public void writeProcessingInstruction(final String target, final String data) throws XMLStreamException { - runAttrQueue(); - nl(); - indent(); - delegate.writeProcessingInstruction(target, data); - state = PROCESSING_INSTRUCTION; - } - - @Override - public void writeCData(final String data) throws XMLStreamException { - runAttrQueue(); - delegate.writeCData(data); - state = CDATA; - } - - @Override - public void writeDTD(final String dtd) throws XMLStreamException { - nl(); - indent(); - delegate.writeDTD(dtd); - state = DTD; - } - - @Override - public void writeEntityRef(final String name) throws XMLStreamException { - runAttrQueue(); - delegate.writeEntityRef(name); - state = ENTITY_REFERENCE; - } - - @Override - public void writeStartDocument() throws XMLStreamException { - delegate.writeStartDocument(); - state = START_DOCUMENT; - } - - @Override - public void writeStartDocument(final String version) throws XMLStreamException { - delegate.writeStartDocument(version); - state = START_DOCUMENT; - } - - @Override - public void writeStartDocument(final String encoding, final String version) throws XMLStreamException { - delegate.writeStartDocument(encoding, version); - state = START_DOCUMENT; - } - - @Override - public void writeCharacters(final String text) throws XMLStreamException { - runAttrQueue(); - if (state != CHARACTERS) { - nl(); - indent(); - } - final Iterator iterator = Spliterator.over(text, '\n'); - while (iterator.hasNext()) { - final String t = iterator.next(); - delegate.writeCharacters(t); - if (iterator.hasNext()) { - nl(); - indent(); - } - } - state = CHARACTERS; - indentEndElement = true; - } - - @Override - public void writeCharacters(final char[] text, final int start, final int len) throws XMLStreamException { - runAttrQueue(); - delegate.writeCharacters(text, start, len); - state = CHARACTERS; - } - - @Override - public String getPrefix(final String uri) throws XMLStreamException { - return delegate.getPrefix(uri); - } - - @Override - public void setPrefix(final String prefix, final String uri) throws XMLStreamException { - delegate.setPrefix(prefix, uri); - } - - @Override - public void setDefaultNamespace(final String uri) throws XMLStreamException { - runAttrQueue(); - delegate.setDefaultNamespace(uri); - } - - @Override - public void setNamespaceContext(final NamespaceContext context) throws XMLStreamException { - delegate.setNamespaceContext(context); - } - - @Override - public NamespaceContext getNamespaceContext() { - return delegate.getNamespaceContext(); - } - - @Override - public Object getProperty(final String name) throws IllegalArgumentException { - return delegate.getProperty(name); - } - - private static String join(final String[] values) { - final StringBuilder b = new StringBuilder(); - for (int i = 0, valuesLength = values.length; i < valuesLength; i++) { - final String s = values[i]; - if (s != null) { - if (i > 0) { - b.append(' '); - } - b.append(s); - } - } - return b.toString(); - } - - private static String join(final Iterable values) { - final StringBuilder b = new StringBuilder(); - Iterator iterator = values.iterator(); - while (iterator.hasNext()) { - final String s = iterator.next(); - if (s != null) { - b.append(s); - if (iterator.hasNext()) b.append(' '); - } - } - return b.toString(); - } -} +/* + * JBoss, Home of Professional Open Source. + * Copyright 2010, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.staxmapper; + +import java.lang.reflect.UndeclaredThrowableException; +import java.util.ArrayDeque; +import java.util.Iterator; + +import javax.xml.namespace.NamespaceContext; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +/** + * An XML stream writer which nicely formats the XML for configuration files. + * + * @author David M. Lloyd + */ +public final class FormattingXMLStreamWriter implements XMLExtendedStreamWriter, XMLStreamConstants { + private static final String NO_NAMESPACE = new String(); + private final XMLStreamWriter delegate; + private final ArrayDeque attrQueue = new ArrayDeque<>(); + private int level; + private int state = START_DOCUMENT; + private boolean indentEndElement = false; + private ArrayDeque unspecifiedNamespaces = new ArrayDeque<>(); + + + public FormattingXMLStreamWriter(final XMLStreamWriter delegate) { + this.delegate = delegate; + unspecifiedNamespaces.push(NO_NAMESPACE); + } + + private void nl() throws XMLStreamException { + delegate.writeCharacters("\r\n"); + } + + private void indent() throws XMLStreamException { + int level = this.level; + final XMLStreamWriter delegate = this.delegate; + for (int i = 0; i < level; i ++) { + delegate.writeCharacters(" "); + } + } + + private interface ArgRunnable { + void run(int arg) throws XMLStreamException; + } + + @Override + public void setUnspecifiedElementNamespace(final String namespace) { + ArrayDeque namespaces = this.unspecifiedNamespaces; + namespaces.pop(); + namespaces.push(namespace == null ? NO_NAMESPACE : namespace); + } + + private String nestUnspecifiedNamespace() { + ArrayDeque namespaces = unspecifiedNamespaces; + String clone = namespaces.getFirst(); + namespaces.push(clone); + return clone; + } + + @Override + public void writeStartElement(final String localName) throws XMLStreamException { + ArrayDeque namespaces = unspecifiedNamespaces; + String namespace = namespaces.getFirst(); + if (namespace != NO_NAMESPACE) { + writeStartElement(namespace, localName); + return; + } + + unspecifiedNamespaces.push(namespace); + + // If this is a nested element flush the outer + runAttrQueue(); + nl(); + indent(); + attrQueue.add((ArgRunnable) (int arg) -> { + if (arg == 0) { + delegate.writeStartElement(localName); + } else { + delegate.writeEmptyElement(localName); + } + }); + + level++; + state = START_ELEMENT; + indentEndElement = false; + } + + @Override + public void writeStartElement(final String namespaceURI, final String localName) throws XMLStreamException { + nestUnspecifiedNamespace(); + + // If this is a nested element flush the outer + runAttrQueue(); + nl(); + indent(); + attrQueue.add((ArgRunnable) (int arg) -> { + if (arg == 0) { + delegate.writeStartElement(namespaceURI, localName); + } else { + delegate.writeEmptyElement(namespaceURI, localName); + } + }); + level++; + state = START_ELEMENT; + indentEndElement = false; + } + + @Override + public void writeStartElement(final String prefix, final String localName, final String namespaceURI) throws XMLStreamException { + nestUnspecifiedNamespace(); + + // If this is a nested element flush the outer + runAttrQueue(); + nl(); + indent(); + attrQueue.add((ArgRunnable) (int arg) -> { + if (arg == 0) { + delegate.writeStartElement(prefix, namespaceURI, localName); + } else { + delegate.writeEmptyElement(prefix, namespaceURI, localName); + } + }); + level++; + state = START_ELEMENT; + indentEndElement = false; + } + + @Override + public void writeEmptyElement(final String namespaceURI, final String localName) throws XMLStreamException { + runAttrQueue(); + nl(); + indent(); + delegate.writeEmptyElement(namespaceURI, localName); + state = END_ELEMENT; + } + + @Override + public void writeEmptyElement(final String prefix, final String localName, final String namespaceURI) throws XMLStreamException { + runAttrQueue(); + nl(); + indent(); + delegate.writeEmptyElement(prefix, namespaceURI, localName); + state = END_ELEMENT; + } + + @Override + public void writeEmptyElement(final String localName) throws XMLStreamException { + String namespace = unspecifiedNamespaces.getFirst(); + if (namespace != NO_NAMESPACE) { + writeEmptyElement(namespace, localName); + return; + } + + runAttrQueue(); + nl(); + indent(); + delegate.writeEmptyElement(localName); + state = END_ELEMENT; + } + + @Override + public void writeEndElement() throws XMLStreamException { + level--; + if (state != START_ELEMENT) { + runAttrQueue(); + if (state != CHARACTERS || indentEndElement) { + nl(); + indent(); + indentEndElement = false; + } + delegate.writeEndElement(); + } else { + // Change the start element to an empty element + ArgRunnable start = attrQueue.poll(); + if (start == null) { + delegate.writeEndElement(); + } else { + start.run(1); + // Write everything else + runAttrQueue(); + } + } + + unspecifiedNamespaces.pop(); + state = END_ELEMENT; + } + + private void runAttrQueue() throws XMLStreamException { + ArgRunnable attr; + while ((attr = attrQueue.poll()) != null) { + attr.run(0); + } + } + + @Override + public void writeEndDocument() throws XMLStreamException { + delegate.writeEndDocument(); + state = END_DOCUMENT; + } + + @Override + public void close() throws XMLStreamException { + delegate.close(); + state = END_DOCUMENT; + } + + @Override + public void flush() throws XMLStreamException { + delegate.flush(); + } + + @Override + public void writeAttribute(final String localName, final String value) throws XMLStreamException { + attrQueue.add((ArgRunnable) (int arg) -> { + try { + delegate.writeAttribute(localName, value); + } catch (XMLStreamException e) { + throw new UndeclaredThrowableException(e); + } + }); + } + + @Override + public void writeAttribute(final String prefix, final String namespaceURI, final String localName, final String value) throws XMLStreamException { + attrQueue.add((ArgRunnable) (int arg) -> { + delegate.writeAttribute(prefix, namespaceURI, localName, value); + }); + } + + @Override + public void writeAttribute(final String namespaceURI, final String localName, final String value) throws XMLStreamException { + attrQueue.add((ArgRunnable) (int arg) -> { + delegate.writeAttribute(namespaceURI, localName, value); + }); + } + + @Override + public void writeAttribute(final String localName, final String[] values) throws XMLStreamException { + attrQueue.add((ArgRunnable) (int arg) -> { + delegate.writeAttribute(localName, join(values)); + }); + } + + @Override + public void writeAttribute(final String prefix, final String namespaceURI, final String localName, final String[] values) throws XMLStreamException { + attrQueue.add((ArgRunnable) (int arg) -> { + delegate.writeAttribute(prefix, namespaceURI, localName, join(values)); + }); + } + + @Override + public void writeAttribute(final String namespaceURI, final String localName, final String[] values) throws XMLStreamException { + attrQueue.add((ArgRunnable) (int arg) -> { + delegate.writeAttribute(namespaceURI, localName, join(values)); + }); + } + + @Override + public void writeAttribute(final String localName, final Iterable values) throws XMLStreamException { + attrQueue.add((ArgRunnable) (int arg) -> { + delegate.writeAttribute(localName, join(values)); + }); + } + + @Override + public void writeAttribute(final String prefix, final String namespaceURI, final String localName, final Iterable values) throws XMLStreamException { + attrQueue.add((ArgRunnable) (int arg) -> { + delegate.writeAttribute(prefix, namespaceURI, localName, join(values)); + }); + } + + @Override + public void writeAttribute(final String namespaceURI, final String localName, final Iterable values) throws XMLStreamException { + attrQueue.add((ArgRunnable) (int arg) -> { + delegate.writeAttribute(namespaceURI, localName, join(values)); + }); + } + + @Override + public void writeNamespace(final String prefix, final String namespaceURI) throws XMLStreamException { + attrQueue.add((ArgRunnable) (int arg) -> { + delegate.writeNamespace(prefix, namespaceURI); + }); + } + + @Override + public void writeDefaultNamespace(final String namespaceURI) throws XMLStreamException { + attrQueue.add((ArgRunnable) (int arg) -> { + delegate.writeDefaultNamespace(namespaceURI); + }); + } + + @Override + public void writeComment(final String data) throws XMLStreamException { + runAttrQueue(); + nl(); + indent(); + final StringBuilder b = new StringBuilder(data.length()); + final Iterator i = Spliterator.over(data, '\n'); + if (! i.hasNext()) { + return; + } else { + final String first = i.next(); + if (! i.hasNext()) { + delegate.writeComment(" " + first + " "); + state = COMMENT; + return; + } else { + b.append("\r\n"); + for (int q = 0; q < level; q++) { + b.append(" "); + } + b.append(" ~ "); + b.append(first); + do { + b.append("\r\n"); + for (int q = 0; q < level; q++) { + b.append(" "); + } + b.append(" ~ "); + b.append(i.next()); + } while (i.hasNext()); + } + b.append("\r\n"); + for (int q = 0; q < level; q ++) { + b.append(" "); + } + b.append(" "); + delegate.writeComment(b.toString()); + state = COMMENT; + } + } + + @Override + public void writeProcessingInstruction(final String target) throws XMLStreamException { + runAttrQueue(); + nl(); + indent(); + delegate.writeProcessingInstruction(target); + state = PROCESSING_INSTRUCTION; + } + + @Override + public void writeProcessingInstruction(final String target, final String data) throws XMLStreamException { + runAttrQueue(); + nl(); + indent(); + delegate.writeProcessingInstruction(target, data); + state = PROCESSING_INSTRUCTION; + } + + @Override + public void writeCData(final String data) throws XMLStreamException { + runAttrQueue(); + delegate.writeCData(data); + state = CDATA; + } + + @Override + public void writeDTD(final String dtd) throws XMLStreamException { + nl(); + indent(); + delegate.writeDTD(dtd); + state = DTD; + } + + @Override + public void writeEntityRef(final String name) throws XMLStreamException { + runAttrQueue(); + delegate.writeEntityRef(name); + state = ENTITY_REFERENCE; + } + + @Override + public void writeStartDocument() throws XMLStreamException { + delegate.writeStartDocument(); + state = START_DOCUMENT; + } + + @Override + public void writeStartDocument(final String version) throws XMLStreamException { + delegate.writeStartDocument(version); + state = START_DOCUMENT; + } + + @Override + public void writeStartDocument(final String encoding, final String version) throws XMLStreamException { + delegate.writeStartDocument(encoding, version); + state = START_DOCUMENT; + } + + @Override + public void writeCharacters(final String text) throws XMLStreamException { + runAttrQueue(); + if (state != CHARACTERS) { + nl(); + indent(); + } + final Iterator iterator = Spliterator.over(text, '\n'); + while (iterator.hasNext()) { + final String t = iterator.next(); + delegate.writeCharacters(t); + if (iterator.hasNext()) { + nl(); + indent(); + } + } + state = CHARACTERS; + indentEndElement = true; + } + + @Override + public void writeCharacters(final char[] text, final int start, final int len) throws XMLStreamException { + runAttrQueue(); + delegate.writeCharacters(text, start, len); + state = CHARACTERS; + } + + @Override + public String getPrefix(final String uri) throws XMLStreamException { + return delegate.getPrefix(uri); + } + + @Override + public void setPrefix(final String prefix, final String uri) throws XMLStreamException { + delegate.setPrefix(prefix, uri); + } + + @Override + public void setDefaultNamespace(final String uri) throws XMLStreamException { + runAttrQueue(); + delegate.setDefaultNamespace(uri); + } + + @Override + public void setNamespaceContext(final NamespaceContext context) throws XMLStreamException { + delegate.setNamespaceContext(context); + } + + @Override + public NamespaceContext getNamespaceContext() { + return delegate.getNamespaceContext(); + } + + @Override + public Object getProperty(final String name) throws IllegalArgumentException { + return delegate.getProperty(name); + } + + private static String join(final String[] values) { + final StringBuilder b = new StringBuilder(); + for (int i = 0, valuesLength = values.length; i < valuesLength; i++) { + final String s = values[i]; + if (s != null) { + if (i > 0) { + b.append(' '); + } + b.append(s); + } + } + return b.toString(); + } + + private static String join(final Iterable values) { + final StringBuilder b = new StringBuilder(); + Iterator iterator = values.iterator(); + while (iterator.hasNext()) { + final String s = iterator.next(); + if (s != null) { + b.append(s); + if (iterator.hasNext()) b.append(' '); + } + } + return b.toString(); + } +} diff --git a/src/main/java/org/jboss/staxmapper/Spliterable.java b/src/main/java/org/jboss/staxmapper/Spliterable.java index 4be1bc5..7517113 100644 --- a/src/main/java/org/jboss/staxmapper/Spliterable.java +++ b/src/main/java/org/jboss/staxmapper/Spliterable.java @@ -1,46 +1,47 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2010, Red Hat, Inc., and individual contributors - * as indicated by the @author tags. See the copyright.txt file in the - * distribution for a full listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ - -package org.jboss.staxmapper; - -import java.util.Iterator; - -/** - * @author David M. Lloyd - */ -final class Spliterable implements Iterable { - private final String subject; - private final char delimiter; - - Spliterable(final String subject, final char delimiter) { - this.subject = subject; - this.delimiter = delimiter; - } - - static Spliterable over(String subject, char delimiter) { - return new Spliterable(subject, delimiter); - } - - public Iterator iterator() { - return new Spliterator(subject, delimiter); - } -} +/* + * JBoss, Home of Professional Open Source. + * Copyright 2010, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.staxmapper; + +import java.util.Iterator; + +/** + * @author David M. Lloyd + */ +final class Spliterable implements Iterable { + private final String subject; + private final char delimiter; + + Spliterable(final String subject, final char delimiter) { + this.subject = subject; + this.delimiter = delimiter; + } + + static Spliterable over(String subject, char delimiter) { + return new Spliterable(subject, delimiter); + } + + @Override + public Iterator iterator() { + return new Spliterator(subject, delimiter); + } +} diff --git a/src/main/java/org/jboss/staxmapper/Spliterator.java b/src/main/java/org/jboss/staxmapper/Spliterator.java index 0117406..f589600 100644 --- a/src/main/java/org/jboss/staxmapper/Spliterator.java +++ b/src/main/java/org/jboss/staxmapper/Spliterator.java @@ -1,66 +1,69 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2010, Red Hat, Inc., and individual contributors - * as indicated by the @author tags. See the copyright.txt file in the - * distribution for a full listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ - -package org.jboss.staxmapper; - -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** - * @author David M. Lloyd - */ -final class Spliterator implements Iterator { - private final String subject; - private final char delimiter; - private int i; - - Spliterator(final String subject, final char delimiter) { - this.subject = subject; - this.delimiter = delimiter; - i = 0; - } - - static Spliterator over(String subject, char delimiter) { - return new Spliterator(subject, delimiter); - } - - public boolean hasNext() { - return i != -1; - } - - public String next() { - final int i = this.i; - if (i == -1) { - throw new NoSuchElementException(); - } - int n = subject.indexOf(delimiter, i); - try { - return n == -1 ? subject.substring(i) : subject.substring(i, n); - } finally { - this.i = n == -1 ? -1 : n + 1; - } - } - - public void remove() { - throw new UnsupportedOperationException(); - } -} +/* + * JBoss, Home of Professional Open Source. + * Copyright 2010, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.staxmapper; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * @author David M. Lloyd + */ +final class Spliterator implements Iterator { + private final String subject; + private final char delimiter; + private int i; + + Spliterator(final String subject, final char delimiter) { + this.subject = subject; + this.delimiter = delimiter; + i = 0; + } + + static Spliterator over(String subject, char delimiter) { + return new Spliterator(subject, delimiter); + } + + @Override + public boolean hasNext() { + return i != -1; + } + + @Override + public String next() { + final int i = this.i; + if (i == -1) { + throw new NoSuchElementException(); + } + int n = subject.indexOf(delimiter, i); + try { + return n == -1 ? subject.substring(i) : subject.substring(i, n); + } finally { + this.i = n == -1 ? -1 : n + 1; + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } +} diff --git a/src/main/java/org/jboss/staxmapper/XMLExtendedStreamReader.java b/src/main/java/org/jboss/staxmapper/XMLExtendedStreamReader.java index d58cfa8..304c64b 100644 --- a/src/main/java/org/jboss/staxmapper/XMLExtendedStreamReader.java +++ b/src/main/java/org/jboss/staxmapper/XMLExtendedStreamReader.java @@ -1,160 +1,161 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2010, Red Hat, Inc., and individual contributors - * as indicated by the @author tags. See the copyright.txt file in the - * distribution for a full listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ - -package org.jboss.staxmapper; - -import java.util.List; - -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; - -/** - * An XML stream reader that can read nested {@code } content using a registered set of root elements. - * - * @author David M. Lloyd - */ -public interface XMLExtendedStreamReader extends XMLStreamReader { - /** - * Handle an {@code }-type nested element, passing in the given value, returning after the end of the element. - * Must be positioned on a {@code START_ELEMENT} or an exception will occur. On return the cursor will be positioned - * on the corresponding {@code END_ELEMENT}. - * - * @param value the value to pass in - * @throws XMLStreamException if an error occurs (e.g. the given value - * does not match the type of the handler for the element, or the element is - * unknown) - */ - void handleAny(Object value) throws XMLStreamException; - - /** - * Handle an extended attribute, passing in the given value. - * Must be positioned on a {@code START_ELEMENT} or an exception will occur. - * On return the cursor will be pointing at the same {@code START_ELEMENT}. - * - * @param value the value to pass in - * @param index the index of the attribute to process - * @throws XMLStreamException if an error occurs - */ - void handleAttribute(Object value, int index) throws XMLStreamException; - - /** - * Discard the remaining content of an element. Runs until a {@code END_ELEMENT} is - * encountered. If a {@code START_ELEMENT} is encountered, then recursively consume and ignore - * its content as well. - * - * @throws XMLStreamException if an error occurs. - */ - void discardRemainder() throws XMLStreamException; - - /** - * Get the value of an attribute as an integer. - * - * @param index the index of the attribute - * @return the integer value - * @throws XMLStreamException if an error occurs - */ - int getIntAttributeValue(int index) throws XMLStreamException; - - /** - * Get the value of an attribute as an integer list. - * - * @param index the index of the attribute - * @return the integer values - * @throws XMLStreamException if an error occurs - */ - int[] getIntListAttributeValue(int index) throws XMLStreamException; - - /** - * Get the value of an attribute as a space-delimited string list. - * - * @param index the index of the attribute - * @return the values - * @throws XMLStreamException if an error occurs - */ - List getListAttributeValue(int index) throws XMLStreamException; - - /** - * Get the value of an attribute as a long. - * - * @param index the index of the attribute - * @return the long value - * @throws XMLStreamException if an error occurs - */ - long getLongAttributeValue(int index) throws XMLStreamException; - - /** - * Get the value of an attribute as a long integer list. - * - * @param index the index of the attribute - * @return the long values - * @throws XMLStreamException if an error occurs - */ - long[] getLongListAttributeValue(int index) throws XMLStreamException; - - /** - * Get the attribute value using intelligent type conversion. Numeric types - * will be parsed; enum types will be mapped. - * - * @param index the index of the attribute - * @param kind the class of the expected object - * @param the type of the expected object - * @return the object equivalent - * @throws XMLStreamException if an error occurs - */ - T getAttributeValue(int index, Class kind) throws XMLStreamException; - - /** - * Get the attribute value as a list using intelligent type conversion. Numeric types - * will be parsed; enum types will be mapped. - * - * @param index the index of the attribute - * @param kind the class of the expected object - * @return the list of object equivalents - * @throws XMLStreamException if an error occurs - */ - List getListAttributeValue(int index, Class kind) throws XMLStreamException; - - /** - * Get the XML ID attribute, if any. - * - * @return the attribute value - * @throws XMLStreamException if an error occurs - */ - String getId() throws XMLStreamException; - - /** - * Gets the {@link XMLMapper} used to handle - * {@link #handleAttribute(Object, int) extended attributes} and - * {@link #handleAny(Object) xs:any-type nested elements}. - * - * @return the XMLMapper. Will not return {@code null} - */ - XMLMapper getXMLMapper(); - - /** - * Whether or not {@link #getElementText} should trim content. - * The default is true. - * - * @param trim trim if true, don't if false - */ - void setTrimElementText(boolean trim); -} +/* + * JBoss, Home of Professional Open Source. + * Copyright 2010, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.staxmapper; + +import java.util.List; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +/** + * An XML stream reader that can read nested {@code } content using a registered set of root elements. + * + * @author David M. Lloyd + */ +public interface XMLExtendedStreamReader extends XMLStreamReader { + /** + * Handle an {@code }-type nested element, passing in the given value, returning after the end of the element. + * Must be positioned on a {@code START_ELEMENT} or an exception will occur. On return the cursor will be positioned + * on the corresponding {@code END_ELEMENT}. + * + * @param value the value to pass in + * @throws XMLStreamException if an error occurs (e.g. the given value + * does not match the type of the handler for the element, or the element is + * unknown) + */ + void handleAny(Object value) throws XMLStreamException; + + /** + * Handle an extended attribute, passing in the given value. + * Must be positioned on a {@code START_ELEMENT} or an exception will occur. + * On return the cursor will be pointing at the same {@code START_ELEMENT}. + * + * @param value the value to pass in + * @param index the index of the attribute to process + * @throws XMLStreamException if an error occurs + */ + void handleAttribute(Object value, int index) throws XMLStreamException; + + /** + * Discard the remaining content of an element. Runs until a {@code END_ELEMENT} is + * encountered. If a {@code START_ELEMENT} is encountered, then recursively consume and ignore + * its content as well. + * + * @throws XMLStreamException if an error occurs. + */ + void discardRemainder() throws XMLStreamException; + + /** + * Get the value of an attribute as an integer. + * + * @param index the index of the attribute + * @return the integer value + * @throws XMLStreamException if an error occurs + */ + int getIntAttributeValue(int index) throws XMLStreamException; + + /** + * Get the value of an attribute as an integer list. + * + * @param index the index of the attribute + * @return the integer values + * @throws XMLStreamException if an error occurs + */ + int[] getIntListAttributeValue(int index) throws XMLStreamException; + + /** + * Get the value of an attribute as a space-delimited string list. + * + * @param index the index of the attribute + * @return the values + * @throws XMLStreamException if an error occurs + */ + List getListAttributeValue(int index) throws XMLStreamException; + + /** + * Get the value of an attribute as a long. + * + * @param index the index of the attribute + * @return the long value + * @throws XMLStreamException if an error occurs + */ + long getLongAttributeValue(int index) throws XMLStreamException; + + /** + * Get the value of an attribute as a long integer list. + * + * @param index the index of the attribute + * @return the long values + * @throws XMLStreamException if an error occurs + */ + long[] getLongListAttributeValue(int index) throws XMLStreamException; + + /** + * Get the attribute value using intelligent type conversion. Numeric types + * will be parsed; enum types will be mapped. + * + * @param index the index of the attribute + * @param kind the class of the expected object + * @param the type of the expected object + * @return the object equivalent + * @throws XMLStreamException if an error occurs + */ + T getAttributeValue(int index, Class kind) throws XMLStreamException; + + /** + * Get the attribute value as a list using intelligent type conversion. Numeric types + * will be parsed; enum types will be mapped. + * + * @param the type of the expected object + * @param index the index of the attribute + * @param kind the class of the expected object + * @return the list of object equivalents + * @throws XMLStreamException if an error occurs + */ + List getListAttributeValue(int index, Class kind) throws XMLStreamException; + + /** + * Get the XML ID attribute, if any. + * + * @return the attribute value + * @throws XMLStreamException if an error occurs + */ + String getId() throws XMLStreamException; + + /** + * Gets the {@link XMLMapper} used to handle + * {@link #handleAttribute(Object, int) extended attributes} and + * {@link #handleAny(Object) xs:any-type nested elements}. + * + * @return the XMLMapper. Will not return {@code null} + */ + XMLMapper getXMLMapper(); + + /** + * Whether or not {@link #getElementText} should trim content. + * The default is true. + * + * @param trim trim if true, don't if false + */ + void setTrimElementText(boolean trim); +} diff --git a/src/main/java/org/jboss/staxmapper/XMLExtendedStreamReaderImpl.java b/src/main/java/org/jboss/staxmapper/XMLExtendedStreamReaderImpl.java index 9b9eca6..825ec64 100644 --- a/src/main/java/org/jboss/staxmapper/XMLExtendedStreamReaderImpl.java +++ b/src/main/java/org/jboss/staxmapper/XMLExtendedStreamReaderImpl.java @@ -1,549 +1,549 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2010, Red Hat, Inc., and individual contributors - * as indicated by the @author tags. See the copyright.txt file in the - * distribution for a full listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ - -package org.jboss.staxmapper; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Deque; -import java.util.Iterator; -import java.util.List; - -import javax.xml.namespace.NamespaceContext; -import javax.xml.namespace.QName; -import javax.xml.stream.Location; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; - -/** - * @author David M. Lloyd - */ -final class XMLExtendedStreamReaderImpl implements XMLExtendedStreamReader { - - private final XMLMapperImpl xmlMapper; - private final XMLStreamReader streamReader; - private final XMLStreamReader fixedStreamReader; - private final Deque stack = new ArrayDeque<>(); - private boolean trimElementText = true; - - XMLExtendedStreamReaderImpl(final XMLMapperImpl xmlMapper, final XMLStreamReader streamReader) { - this.xmlMapper = xmlMapper; - this.streamReader = streamReader; - fixedStreamReader = new FixedXMLStreamReader(this.streamReader); - stack.push(new Context()); - } - - @Override - public void setTrimElementText(boolean trim) { - this.trimElementText = trim; - } - - @Override - public void handleAny(final Object value) throws XMLStreamException { - require(START_ELEMENT, null, null); - boolean ok = false; - try { - final Deque stack = this.stack; - stack.push(new Context()); - try { - xmlMapper.processNested(this, value); - } finally { - stack.pop(); - } - ok = true; - } finally { - if (! ok) { - safeClose(); - } - } - } - - @Override - public void handleAttribute(final Object value, final int index) throws XMLStreamException { - require(START_ELEMENT, null, null); - boolean ok = false; - try { - xmlMapper.processAttribute(fixedStreamReader, index, value); - } finally { - if (!ok) { - safeClose(); - } - } - } - - @Override - public void discardRemainder() throws XMLStreamException { - final Context context = stack.getFirst(); - if (context.depth > 0) { - try { - doDiscard(); - } finally { - context.depth--; - } - } else { - try { - throw readPastEnd(getLocation()); - } finally { - safeClose(); - } - } - } - - @Override - public Object getProperty(final String name) throws IllegalArgumentException { - return streamReader.getProperty(name); - } - - @Override - public int next() throws XMLStreamException { - final Context context = stack.getFirst(); - if (context.depth > 0) { - final int next = streamReader.next(); - if (next == END_ELEMENT) { - context.depth--; - } else if(next == START_ELEMENT) { - context.depth++; - } - return next; - } else { - try { - throw readPastEnd(getLocation()); - } finally { - safeClose(); - } - } - } - - @Override - public void require(final int type, final String namespaceURI, final String localName) throws XMLStreamException { - streamReader.require(type, namespaceURI, localName); - } - - @Override - public String getElementText() throws XMLStreamException { - String text = streamReader.getElementText(); - return trimElementText ? text.trim() : text; - } - - @Override - public int nextTag() throws XMLStreamException { - final Context context = stack.getFirst(); - if (context.depth > 0) { - final int next = streamReader.nextTag(); - if (next == END_ELEMENT) { - context.depth--; - } else if(next == START_ELEMENT) { - context.depth++; - } - return next; - } else { - try { - throw readPastEnd(getLocation()); - } finally { - safeClose(); - } - } - } - - @Override - public boolean hasNext() throws XMLStreamException { - return stack.getFirst().depth > 0 && streamReader.hasNext(); - } - - @Override - public void close() throws XMLStreamException { - throw new UnsupportedOperationException(); - } - - @Override - public String getNamespaceURI(final String prefix) { - return streamReader.getNamespaceURI(prefix); - } - - @Override - public boolean isStartElement() { - return streamReader.isStartElement(); - } - - @Override - public boolean isEndElement() { - return streamReader.isEndElement(); - } - - @Override - public boolean isCharacters() { - return streamReader.isCharacters(); - } - - @Override - public boolean isWhiteSpace() { - return streamReader.isWhiteSpace(); - } - - @Override - public String getAttributeValue(final String namespaceURI, final String localName) { - return streamReader.getAttributeValue(namespaceURI, localName); - } - - @Override - public int getAttributeCount() { - return streamReader.getAttributeCount(); - } - - @Override - public QName getAttributeName(final int index) { - return streamReader.getAttributeName(index); - } - - @Override - public String getAttributeNamespace(final int index) { - return streamReader.getAttributeNamespace(index); - } - - @Override - public String getAttributeLocalName(final int index) { - return streamReader.getAttributeLocalName(index); - } - - @Override - public String getAttributePrefix(final int index) { - return streamReader.getAttributePrefix(index); - } - - @Override - public String getAttributeType(final int index) { - return streamReader.getAttributeType(index); - } - - @Override - public String getAttributeValue(final int index) { - return streamReader.getAttributeValue(index); - } - - @Override - public boolean isAttributeSpecified(final int index) { - return streamReader.isAttributeSpecified(index); - } - - @Override - public int getNamespaceCount() { - return streamReader.getNamespaceCount(); - } - - @Override - public String getNamespacePrefix(final int index) { - return streamReader.getNamespacePrefix(index); - } - - @Override - public String getNamespaceURI(final int index) { - return streamReader.getNamespaceURI(index); - } - - @Override - public NamespaceContext getNamespaceContext() { - return streamReader.getNamespaceContext(); - } - - @Override - public int getEventType() { - return streamReader.getEventType(); - } - - @Override - public String getText() { - return streamReader.getText(); - } - - @Override - public char[] getTextCharacters() { - return streamReader.getTextCharacters(); - } - - @Override - public int getTextCharacters(final int sourceStart, final char[] target, final int targetStart, final int length) throws XMLStreamException { - return streamReader.getTextCharacters(sourceStart, target, targetStart, length); - } - - @Override - public int getTextStart() { - return streamReader.getTextStart(); - } - - @Override - public int getTextLength() { - return streamReader.getTextLength(); - } - - @Override - public String getEncoding() { - return streamReader.getEncoding(); - } - - @Override - public boolean hasText() { - return streamReader.hasText(); - } - - @Override - public Location getLocation() { - return streamReader.getLocation(); - } - - @Override - public QName getName() { - return streamReader.getName(); - } - - @Override - public String getLocalName() { - return streamReader.getLocalName(); - } - - @Override - public boolean hasName() { - return streamReader.hasName(); - } - - @Override - public String getNamespaceURI() { - return streamReader.getNamespaceURI(); - } - - @Override - public String getPrefix() { - return streamReader.getPrefix(); - } - - @Override - public String getVersion() { - return streamReader.getVersion(); - } - - @Override - public boolean isStandalone() { - return streamReader.isStandalone(); - } - - @Override - public boolean standaloneSet() { - return streamReader.standaloneSet(); - } - - @Override - public String getCharacterEncodingScheme() { - return streamReader.getCharacterEncodingScheme(); - } - - @Override - public String getPITarget() { - return streamReader.getPITarget(); - } - - @Override - public String getPIData() { - return streamReader.getPIData(); - } - - @Override - public int getIntAttributeValue(final int index) throws XMLStreamException { - try { - return Integer.parseInt(getAttributeValue(index)); - } catch (NumberFormatException e) { - throw intParseException(e, getLocation()); - } - } - - @Override - public int[] getIntListAttributeValue(final int index) throws XMLStreamException { - try { - return toInts(Spliterator.over(getAttributeValue(index), ' '), 0); - } catch (NumberFormatException e) { - throw intParseException(e, getLocation()); - } - } - - @Override - public List getListAttributeValue(final int index) throws XMLStreamException { - return Arrays.asList(toStrings(Spliterator.over(getAttributeValue(index), ' '), 0)); - } - - @Override - public long getLongAttributeValue(final int index) throws XMLStreamException { - try { - return Long.parseLong(getAttributeValue(index)); - } catch (NumberFormatException e) { - throw intParseException(e, getLocation()); - } - } - - @Override - public long[] getLongListAttributeValue(final int index) throws XMLStreamException { - try { - return toLongs(Spliterator.over(getAttributeValue(index), ' '), 0); - } catch (NumberFormatException e) { - throw intParseException(e, getLocation()); - } - } - - @Override - public T getAttributeValue(final int index, final Class kind) throws XMLStreamException { - if (kind == String.class || kind == Object.class) { - return kind.cast(getAttributeValue(index)); - } else if (kind == Integer.class || kind == Number.class) { - return kind.cast(Integer.valueOf(getIntAttributeValue(index))); - } else if (kind == Long.class) { - return kind.cast(Long.valueOf(getLongAttributeValue(index))); - } else if (kind.isEnum()) { - @SuppressWarnings({ "unchecked", "rawtypes" }) - T value = (T) Enum.valueOf((Class) kind, getAttributeValue(index)); - return value; - } else if (kind == char[].class) { - return kind.cast(getAttributeValue(index).toCharArray()); - } else { - throw new XMLStreamException("Unknown value type of '" + kind + "'", getLocation()); - } - } - - @Override - @SuppressWarnings({ "unchecked" }) - public List getListAttributeValue(final int index, final Class kind) throws XMLStreamException { - if (kind == String.class || kind == Object.class) { - return (List) getListAttributeValue(index); - } else if (kind == Integer.class || kind == Number.class) { - final List list = new ArrayList(); - try { - for (String s : Spliterable.over(getAttributeValue(index), ' ')) { - list.add(kind.cast(Integer.valueOf(s))); - } - return list; - } catch (NumberFormatException e) { - throw intParseException(e, getLocation()); - } - } else if (kind == Long.class) { - final List list = new ArrayList(); - try { - for (String s : Spliterable.over(getAttributeValue(index), ' ')) { - list.add(kind.cast(Long.valueOf(s))); - } - return list; - } catch (NumberFormatException e) { - throw intParseException(e, getLocation()); - } - } else if (kind.isEnum()) { - final List list = new ArrayList<>(); - for (String s : Spliterable.over(getAttributeValue(index), ' ')) { - Enum en = Enum.valueOf(kind.asSubclass(Enum.class), s); - list.add(kind.cast(en)); - } - return list; - } else if (kind == char[].class) { - final List list = new ArrayList(); - for (String s : Spliterable.over(getAttributeValue(index), ' ')) { - list.add(kind.cast(s.toCharArray())); - } - return list; - } else { - throw new XMLStreamException("Unknown value type of '" + kind + "'", getLocation()); - } - } - - @Override - public String getId() throws XMLStreamException { - return getAttributeValue(null, "id"); - } - - @Override - public XMLMapper getXMLMapper() { - return xmlMapper; - } - - // private members - - private static final class Context { - int depth = 1; - } - - private void doDiscard() throws XMLStreamException { - int i; - while ((i = streamReader.next()) != END_ELEMENT) { - if (i == START_ELEMENT) { - doDiscard(); - } - } - } - - private void safeClose() { - try { - streamReader.close(); - } catch (Exception e) { - // ignore - } - } - - private static final int[] NO_INTS = new int[0]; - private static final long[] NO_LONGS = new long[0]; - private static final String[] NO_STRINGS = new String[0]; - - private static int[] toInts(Iterator i, int count) { - if (i.hasNext()) { - final String n = i.next(); - final int[] ints = toInts(i, count + 1); - ints[count] = Integer.parseInt(n); - return ints; - } else { - return count == 0 ? NO_INTS : new int[count]; - } - } - - private static long[] toLongs(Iterator i, int count) { - if (i.hasNext()) { - final String n = i.next(); - final long[] longs = toLongs(i, count + 1); - longs[count] = Long.parseLong(n); - return longs; - } else { - return count == 0 ? NO_LONGS : new long[count]; - } - } - - private static String[] toStrings(Iterator i, int count) { - if (i.hasNext()) { - final String s = i.next(); - final String[] strings = toStrings(i, count + 1); - strings[count] = s; - return strings; - } else { - return count == 0 ? NO_STRINGS : new String[count]; - } - } - - private static XMLStreamException readPastEnd(final Location location) { - return new XMLStreamException("Attempt to read past end of element", location); - } - - private static XMLStreamException intParseException(final NumberFormatException e, final Location location) { - return new XMLStreamException("Failed to parse an integer attribute", location, e); - } - -} +/* + * JBoss, Home of Professional Open Source. + * Copyright 2010, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.staxmapper; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Deque; +import java.util.Iterator; +import java.util.List; + +import javax.xml.namespace.NamespaceContext; +import javax.xml.namespace.QName; +import javax.xml.stream.Location; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +/** + * @author David M. Lloyd + */ +final class XMLExtendedStreamReaderImpl implements XMLExtendedStreamReader { + + private final XMLMapperImpl xmlMapper; + private final XMLStreamReader streamReader; + private final XMLStreamReader fixedStreamReader; + private final Deque stack = new ArrayDeque<>(); + private boolean trimElementText = true; + + XMLExtendedStreamReaderImpl(final XMLMapperImpl xmlMapper, final XMLStreamReader streamReader) { + this.xmlMapper = xmlMapper; + this.streamReader = streamReader; + fixedStreamReader = new FixedXMLStreamReader(this.streamReader); + stack.push(new Context()); + } + + @Override + public void setTrimElementText(boolean trim) { + this.trimElementText = trim; + } + + @Override + public void handleAny(final Object value) throws XMLStreamException { + require(START_ELEMENT, null, null); + boolean ok = false; + try { + final Deque stack = this.stack; + stack.push(new Context()); + try { + xmlMapper.processNested(this, value); + } finally { + stack.pop(); + } + ok = true; + } finally { + if (! ok) { + safeClose(); + } + } + } + + @Override + public void handleAttribute(final Object value, final int index) throws XMLStreamException { + require(START_ELEMENT, null, null); + boolean ok = false; + try { + xmlMapper.processAttribute(fixedStreamReader, index, value); + } finally { + if (!ok) { + safeClose(); + } + } + } + + @Override + public void discardRemainder() throws XMLStreamException { + final Context context = stack.getFirst(); + if (context.depth > 0) { + try { + doDiscard(); + } finally { + context.depth--; + } + } else { + try { + throw readPastEnd(getLocation()); + } finally { + safeClose(); + } + } + } + + @Override + public Object getProperty(final String name) throws IllegalArgumentException { + return streamReader.getProperty(name); + } + + @Override + public int next() throws XMLStreamException { + final Context context = stack.getFirst(); + if (context.depth > 0) { + final int next = streamReader.next(); + if (next == END_ELEMENT) { + context.depth--; + } else if(next == START_ELEMENT) { + context.depth++; + } + return next; + } else { + try { + throw readPastEnd(getLocation()); + } finally { + safeClose(); + } + } + } + + @Override + public void require(final int type, final String namespaceURI, final String localName) throws XMLStreamException { + streamReader.require(type, namespaceURI, localName); + } + + @Override + public String getElementText() throws XMLStreamException { + String text = streamReader.getElementText(); + return trimElementText ? text.trim() : text; + } + + @Override + public int nextTag() throws XMLStreamException { + final Context context = stack.getFirst(); + if (context.depth > 0) { + final int next = streamReader.nextTag(); + if (next == END_ELEMENT) { + context.depth--; + } else if(next == START_ELEMENT) { + context.depth++; + } + return next; + } else { + try { + throw readPastEnd(getLocation()); + } finally { + safeClose(); + } + } + } + + @Override + public boolean hasNext() throws XMLStreamException { + return stack.getFirst().depth > 0 && streamReader.hasNext(); + } + + @Override + public void close() throws XMLStreamException { + throw new UnsupportedOperationException(); + } + + @Override + public String getNamespaceURI(final String prefix) { + return streamReader.getNamespaceURI(prefix); + } + + @Override + public boolean isStartElement() { + return streamReader.isStartElement(); + } + + @Override + public boolean isEndElement() { + return streamReader.isEndElement(); + } + + @Override + public boolean isCharacters() { + return streamReader.isCharacters(); + } + + @Override + public boolean isWhiteSpace() { + return streamReader.isWhiteSpace(); + } + + @Override + public String getAttributeValue(final String namespaceURI, final String localName) { + return streamReader.getAttributeValue(namespaceURI, localName); + } + + @Override + public int getAttributeCount() { + return streamReader.getAttributeCount(); + } + + @Override + public QName getAttributeName(final int index) { + return streamReader.getAttributeName(index); + } + + @Override + public String getAttributeNamespace(final int index) { + return streamReader.getAttributeNamespace(index); + } + + @Override + public String getAttributeLocalName(final int index) { + return streamReader.getAttributeLocalName(index); + } + + @Override + public String getAttributePrefix(final int index) { + return streamReader.getAttributePrefix(index); + } + + @Override + public String getAttributeType(final int index) { + return streamReader.getAttributeType(index); + } + + @Override + public String getAttributeValue(final int index) { + return streamReader.getAttributeValue(index); + } + + @Override + public boolean isAttributeSpecified(final int index) { + return streamReader.isAttributeSpecified(index); + } + + @Override + public int getNamespaceCount() { + return streamReader.getNamespaceCount(); + } + + @Override + public String getNamespacePrefix(final int index) { + return streamReader.getNamespacePrefix(index); + } + + @Override + public String getNamespaceURI(final int index) { + return streamReader.getNamespaceURI(index); + } + + @Override + public NamespaceContext getNamespaceContext() { + return streamReader.getNamespaceContext(); + } + + @Override + public int getEventType() { + return streamReader.getEventType(); + } + + @Override + public String getText() { + return streamReader.getText(); + } + + @Override + public char[] getTextCharacters() { + return streamReader.getTextCharacters(); + } + + @Override + public int getTextCharacters(final int sourceStart, final char[] target, final int targetStart, final int length) throws XMLStreamException { + return streamReader.getTextCharacters(sourceStart, target, targetStart, length); + } + + @Override + public int getTextStart() { + return streamReader.getTextStart(); + } + + @Override + public int getTextLength() { + return streamReader.getTextLength(); + } + + @Override + public String getEncoding() { + return streamReader.getEncoding(); + } + + @Override + public boolean hasText() { + return streamReader.hasText(); + } + + @Override + public Location getLocation() { + return streamReader.getLocation(); + } + + @Override + public QName getName() { + return streamReader.getName(); + } + + @Override + public String getLocalName() { + return streamReader.getLocalName(); + } + + @Override + public boolean hasName() { + return streamReader.hasName(); + } + + @Override + public String getNamespaceURI() { + return streamReader.getNamespaceURI(); + } + + @Override + public String getPrefix() { + return streamReader.getPrefix(); + } + + @Override + public String getVersion() { + return streamReader.getVersion(); + } + + @Override + public boolean isStandalone() { + return streamReader.isStandalone(); + } + + @Override + public boolean standaloneSet() { + return streamReader.standaloneSet(); + } + + @Override + public String getCharacterEncodingScheme() { + return streamReader.getCharacterEncodingScheme(); + } + + @Override + public String getPITarget() { + return streamReader.getPITarget(); + } + + @Override + public String getPIData() { + return streamReader.getPIData(); + } + + @Override + public int getIntAttributeValue(final int index) throws XMLStreamException { + try { + return Integer.parseInt(getAttributeValue(index)); + } catch (NumberFormatException e) { + throw intParseException(e, getLocation()); + } + } + + @Override + public int[] getIntListAttributeValue(final int index) throws XMLStreamException { + try { + return toInts(Spliterator.over(getAttributeValue(index), ' '), 0); + } catch (NumberFormatException e) { + throw intParseException(e, getLocation()); + } + } + + @Override + public List getListAttributeValue(final int index) throws XMLStreamException { + return Arrays.asList(toStrings(Spliterator.over(getAttributeValue(index), ' '), 0)); + } + + @Override + public long getLongAttributeValue(final int index) throws XMLStreamException { + try { + return Long.parseLong(getAttributeValue(index)); + } catch (NumberFormatException e) { + throw intParseException(e, getLocation()); + } + } + + @Override + public long[] getLongListAttributeValue(final int index) throws XMLStreamException { + try { + return toLongs(Spliterator.over(getAttributeValue(index), ' '), 0); + } catch (NumberFormatException e) { + throw intParseException(e, getLocation()); + } + } + + @Override + public T getAttributeValue(final int index, final Class kind) throws XMLStreamException { + if (kind == String.class || kind == Object.class) { + return kind.cast(getAttributeValue(index)); + } else if (kind == Integer.class || kind == Number.class) { + return kind.cast(Integer.valueOf(getIntAttributeValue(index))); + } else if (kind == Long.class) { + return kind.cast(Long.valueOf(getLongAttributeValue(index))); + } else if (kind.isEnum()) { + @SuppressWarnings({ "unchecked", "rawtypes" }) + T value = (T) Enum.valueOf((Class) kind, getAttributeValue(index)); + return value; + } else if (kind == char[].class) { + return kind.cast(getAttributeValue(index).toCharArray()); + } else { + throw new XMLStreamException("Unknown value type of '" + kind + "'", getLocation()); + } + } + + @Override + @SuppressWarnings({ "unchecked" }) + public List getListAttributeValue(final int index, final Class kind) throws XMLStreamException { + if (kind == String.class || kind == Object.class) { + return (List) getListAttributeValue(index); + } else if (kind == Integer.class || kind == Number.class) { + final List list = new ArrayList<>(); + try { + for (String s : Spliterable.over(getAttributeValue(index), ' ')) { + list.add(kind.cast(Integer.valueOf(s))); + } + return list; + } catch (NumberFormatException e) { + throw intParseException(e, getLocation()); + } + } else if (kind == Long.class) { + final List list = new ArrayList<>(); + try { + for (String s : Spliterable.over(getAttributeValue(index), ' ')) { + list.add(kind.cast(Long.valueOf(s))); + } + return list; + } catch (NumberFormatException e) { + throw intParseException(e, getLocation()); + } + } else if (kind.isEnum()) { + final List list = new ArrayList<>(); + for (String s : Spliterable.over(getAttributeValue(index), ' ')) { + Enum en = Enum.valueOf(kind.asSubclass(Enum.class), s); + list.add(kind.cast(en)); + } + return list; + } else if (kind == char[].class) { + final List list = new ArrayList<>(); + for (String s : Spliterable.over(getAttributeValue(index), ' ')) { + list.add(kind.cast(s.toCharArray())); + } + return list; + } else { + throw new XMLStreamException("Unknown value type of '" + kind + "'", getLocation()); + } + } + + @Override + public String getId() throws XMLStreamException { + return getAttributeValue(null, "id"); + } + + @Override + public XMLMapper getXMLMapper() { + return xmlMapper; + } + + // private members + + private static final class Context { + int depth = 1; + } + + private void doDiscard() throws XMLStreamException { + int i; + while ((i = streamReader.next()) != END_ELEMENT) { + if (i == START_ELEMENT) { + doDiscard(); + } + } + } + + private void safeClose() { + try { + streamReader.close(); + } catch (Exception e) { + // ignore + } + } + + private static final int[] NO_INTS = new int[0]; + private static final long[] NO_LONGS = new long[0]; + private static final String[] NO_STRINGS = new String[0]; + + private static int[] toInts(Iterator i, int count) { + if (i.hasNext()) { + final String n = i.next(); + final int[] ints = toInts(i, count + 1); + ints[count] = Integer.parseInt(n); + return ints; + } else { + return count == 0 ? NO_INTS : new int[count]; + } + } + + private static long[] toLongs(Iterator i, int count) { + if (i.hasNext()) { + final String n = i.next(); + final long[] longs = toLongs(i, count + 1); + longs[count] = Long.parseLong(n); + return longs; + } else { + return count == 0 ? NO_LONGS : new long[count]; + } + } + + private static String[] toStrings(Iterator i, int count) { + if (i.hasNext()) { + final String s = i.next(); + final String[] strings = toStrings(i, count + 1); + strings[count] = s; + return strings; + } else { + return count == 0 ? NO_STRINGS : new String[count]; + } + } + + private static XMLStreamException readPastEnd(final Location location) { + return new XMLStreamException("Attempt to read past end of element", location); + } + + private static XMLStreamException intParseException(final NumberFormatException e, final Location location) { + return new XMLStreamException("Failed to parse an integer attribute", location, e); + } + +} diff --git a/src/main/java/org/jboss/staxmapper/XMLMapper.java b/src/main/java/org/jboss/staxmapper/XMLMapper.java index 8e0dff2..b3b3b13 100644 --- a/src/main/java/org/jboss/staxmapper/XMLMapper.java +++ b/src/main/java/org/jboss/staxmapper/XMLMapper.java @@ -1,128 +1,130 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2010, Red Hat, Inc., and individual contributors - * as indicated by the @author tags. See the copyright.txt file in the - * distribution for a full listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ - -package org.jboss.staxmapper; - -import java.util.function.Supplier; -import javax.xml.namespace.QName; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; -import javax.xml.stream.XMLStreamWriter; - -/** - * An XML mapper. Allows the creation of extensible streaming XML parsers. - * - * @author David M. Lloyd - */ -public interface XMLMapper { - - /** - * Add a known root element which can be read by {@link XMLExtendedStreamReader#handleAny(Object)}. - * - * @param name the element name - * @param reader the reader which handles the element - */ - void registerRootElement(QName name, XMLElementReader reader); - - /** - * Add a known root element which can be read by {@link XMLExtendedStreamReader#handleAny(Object)}. - * - * @param name the element name - * @param supplier provider for the reader which handles the element - * - * It is recommended that supplier always creates new instance of the {@link XMLElementReader} - * instead of caching and returning always same instance. This way unused parsers can get GC-ed - * when not needed. - * - */ - void registerRootElement(QName name, Supplier> supplier); - - - /** - * Removes a {@link #registerRootElement(QName, XMLElementReader) previously registered root element}. - * - * @param name the element name - */ - void unregisterRootElement(QName name); - - /** - * Add a known root attribute which can be read by {@link XMLExtendedStreamReader#handleAttribute(Object, int)}. - * - * @param name the attribute name - * @param reader the reader which handles the attribute - */ - void registerRootAttribute(QName name, XMLAttributeReader reader); - - /** - * Removes a {@link #registerRootAttribute(QName, XMLAttributeReader) previously registered root attribute}. - * - * @param name the element name - */ - void unregisterRootAttribute(QName name); - - /** - * Parse a document. The document must have a known, registered root element which can accept the given root object. - * - * @param rootObject the root object to send in - * @param reader the reader from which the document should be read - * @throws XMLStreamException if an error occurs - */ - void parseDocument(Object rootObject, XMLStreamReader reader) throws XMLStreamException; - - /** - * Format the element writer's output on to an XML stream writer. - * - * @param writer the element writer - * @param rootObject the root object to send in - * @param streamWriter the stream writer - * @throws XMLStreamException if an exception occurs - */ - void deparseDocument(XMLElementWriter writer, Object rootObject, XMLStreamWriter streamWriter) throws XMLStreamException; - - /** - * Format the content writer's output on to an XML stream writer. - * - * @param contentWriter the content writer - * @param streamWriter the stream writer - * @throws XMLStreamException if an exception occurs - */ - @Deprecated - void deparseDocument(XMLContentWriter contentWriter, XMLStreamWriter streamWriter) throws XMLStreamException; - - /** - * A factory for creating an instance of {@link XMLMapper}. - */ - class Factory { - - private Factory() { - } - - /** - * Create a new mapper instance. - * - * @return the new instance - */ - public static XMLMapper create() { - return new XMLMapperImpl(); - } - } -} +/* + * JBoss, Home of Professional Open Source. + * Copyright 2010, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.staxmapper; + +import java.util.function.Supplier; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.XMLStreamWriter; + +/** + * An XML mapper. Allows the creation of extensible streaming XML parsers. + * + * @author David M. Lloyd + */ +public interface XMLMapper { + + /** + * Add a known root element which can be read by {@link XMLExtendedStreamReader#handleAny(Object)}. + * + * @param the type of the expected object + * @param name the element name + * @param reader the reader which handles the element + */ + void registerRootElement(QName name, XMLElementReader reader); + + /** + * Add a known root element which can be read by {@link XMLExtendedStreamReader#handleAny(Object)}. + * + * @param the type of the expected object + * @param name the element name + * @param supplier provider for the reader which handles the element + * + * It is recommended that supplier always creates new instance of the {@link XMLElementReader} + * instead of caching and returning always same instance. This way unused parsers can get GC-ed + * when not needed. + * + */ + void registerRootElement(QName name, Supplier> supplier); + + + /** + * Removes a {@link #registerRootElement(QName, XMLElementReader) previously registered root element}. + * + * @param name the element name + */ + void unregisterRootElement(QName name); + + /** + * Add a known root attribute which can be read by {@link XMLExtendedStreamReader#handleAttribute(Object, int)}. + * + * @param name the attribute name + * @param reader the reader which handles the attribute + */ + void registerRootAttribute(QName name, XMLAttributeReader reader); + + /** + * Removes a {@link #registerRootAttribute(QName, XMLAttributeReader) previously registered root attribute}. + * + * @param name the element name + */ + void unregisterRootAttribute(QName name); + + /** + * Parse a document. The document must have a known, registered root element which can accept the given root object. + * + * @param rootObject the root object to send in + * @param reader the reader from which the document should be read + * @throws XMLStreamException if an error occurs + */ + void parseDocument(Object rootObject, XMLStreamReader reader) throws XMLStreamException; + + /** + * Format the element writer's output on to an XML stream writer. + * + * @param writer the element writer + * @param rootObject the root object to send in + * @param streamWriter the stream writer + * @throws XMLStreamException if an exception occurs + */ + void deparseDocument(XMLElementWriter writer, Object rootObject, XMLStreamWriter streamWriter) throws XMLStreamException; + + /** + * Format the content writer's output on to an XML stream writer. + * + * @param contentWriter the content writer + * @param streamWriter the stream writer + * @throws XMLStreamException if an exception occurs + */ + @Deprecated + void deparseDocument(XMLContentWriter contentWriter, XMLStreamWriter streamWriter) throws XMLStreamException; + + /** + * A factory for creating an instance of {@link XMLMapper}. + */ + class Factory { + + private Factory() { + } + + /** + * Create a new mapper instance. + * + * @return the new instance + */ + public static XMLMapper create() { + return new XMLMapperImpl(); + } + } +} diff --git a/src/main/java/org/jboss/staxmapper/XMLMapperImpl.java b/src/main/java/org/jboss/staxmapper/XMLMapperImpl.java index 0ab438f..3fd0816 100644 --- a/src/main/java/org/jboss/staxmapper/XMLMapperImpl.java +++ b/src/main/java/org/jboss/staxmapper/XMLMapperImpl.java @@ -1,134 +1,141 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2010, Red Hat, Inc., and individual contributors - * as indicated by the @author tags. See the copyright.txt file in the - * distribution for a full listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ - -package org.jboss.staxmapper; - -import static javax.xml.stream.XMLStreamConstants.END_DOCUMENT; -import static javax.xml.stream.XMLStreamConstants.START_DOCUMENT; -import static javax.xml.stream.XMLStreamConstants.START_ELEMENT; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.function.Supplier; -import javax.xml.namespace.QName; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; -import javax.xml.stream.XMLStreamWriter; - -/** - * @author David M. Lloyd - */ -final class XMLMapperImpl implements XMLMapper { - private final ConcurrentMap> rootElements = new ConcurrentHashMap<>(); - private final ConcurrentMap>> rootElementsSuppliers = new ConcurrentHashMap<>(); - private final ConcurrentMap> rootAttributes = new ConcurrentHashMap<>(); - - public void registerRootElement(QName name, XMLElementReader reader) { - registerRootElement(name, () -> reader); - } - - public void registerRootElement(QName name, Supplier> supplier) { - if (rootElementsSuppliers.putIfAbsent(name, supplier) != null) { - throw new IllegalArgumentException("Root element supplier for " + name + " already registered"); - } - } - - @Override - public void unregisterRootElement(QName name) { - rootElements.remove(name); - } - - public void registerRootAttribute(QName name, XMLAttributeReader reader) { - if (rootAttributes.putIfAbsent(name, reader) != null) { - throw new IllegalArgumentException("Root attribute for " + name + " already registered"); - } - } - - @Override - public void unregisterRootAttribute(QName name) { - rootAttributes.remove(name); - } - - public void parseDocument(Object rootObject, XMLStreamReader reader) throws XMLStreamException { - try { - reader.require(START_DOCUMENT, null, null); - reader.nextTag(); - reader.require(START_ELEMENT, null, null); - processNested(new XMLExtendedStreamReaderImpl(this, reader), rootObject); - while (reader.next() != END_DOCUMENT) { - } - reader.close(); - rootElements.clear(); //clear the parsers cache - } finally { - try { - reader.close(); - } catch (Exception e) { - // log it? - } - } - } - - public void deparseDocument(final XMLElementWriter writer, final Object rootObject, final XMLStreamWriter streamWriter) throws XMLStreamException { - doDeparse(writer, rootObject, new FormattingXMLStreamWriter(streamWriter)); - } - - @SuppressWarnings( { "unchecked" }) - private void doDeparse(XMLElementWriter writer, final T value, final XMLExtendedStreamWriter streamWriter) throws XMLStreamException { - ((XMLElementWriter)writer).writeContent(streamWriter, value); - } - - /** - * Format the content writer's output on to an XML stream writer. - * - * @param contentWriter the content writer - * @param streamWriter the stream writer - * @throws XMLStreamException if an exception occurs - */ - public void deparseDocument(XMLContentWriter contentWriter, XMLStreamWriter streamWriter) throws XMLStreamException { - // todo: add validation based on the registered root elements? - contentWriter.writeContent(new FormattingXMLStreamWriter(streamWriter)); - } - - private XMLElementReader getParser(final QName name) { - return rootElements.computeIfAbsent(name, qName -> rootElementsSuppliers.getOrDefault(name, () -> null).get()); - } - - @SuppressWarnings({"unchecked"}) - void processNested(final XMLExtendedStreamReader streamReader, final T value) throws XMLStreamException { - final QName name = streamReader.getName(); - final XMLElementReader reader = (XMLElementReader) getParser(name); - if (reader == null) { - throw new XMLStreamException("Unexpected element '" + name + "'", streamReader.getLocation()); - } - reader.readElement(streamReader, value); - } - - @SuppressWarnings({ "unchecked" }) - void processAttribute(final XMLStreamReader streamReader, final int index, final T value) throws XMLStreamException { - final QName name = streamReader.getName(); - final XMLAttributeReader reader = (XMLAttributeReader) rootAttributes.get(name); - if (reader == null) { - throw new XMLStreamException("Unexpected attribute '" + name + "'", streamReader.getLocation()); - } - reader.readAttribute(streamReader, index, value); - } -} +/* + * JBoss, Home of Professional Open Source. + * Copyright 2010, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.staxmapper; + +import static javax.xml.stream.XMLStreamConstants.END_DOCUMENT; +import static javax.xml.stream.XMLStreamConstants.START_DOCUMENT; +import static javax.xml.stream.XMLStreamConstants.START_ELEMENT; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.Supplier; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.XMLStreamWriter; + +/** + * @author David M. Lloyd + */ +final class XMLMapperImpl implements XMLMapper { + private final ConcurrentMap> rootElements = new ConcurrentHashMap<>(); + private final ConcurrentMap>> rootElementsSuppliers = new ConcurrentHashMap<>(); + private final ConcurrentMap> rootAttributes = new ConcurrentHashMap<>(); + + @Override + public void registerRootElement(QName name, XMLElementReader reader) { + registerRootElement(name, () -> reader); + } + + @Override + public void registerRootElement(QName name, Supplier> supplier) { + if (rootElementsSuppliers.putIfAbsent(name, supplier) != null) { + throw new IllegalArgumentException("Root element supplier for " + name + " already registered"); + } + } + + @Override + public void unregisterRootElement(QName name) { + rootElements.remove(name); + } + + @Override + public void registerRootAttribute(QName name, XMLAttributeReader reader) { + if (rootAttributes.putIfAbsent(name, reader) != null) { + throw new IllegalArgumentException("Root attribute for " + name + " already registered"); + } + } + + @Override + public void unregisterRootAttribute(QName name) { + rootAttributes.remove(name); + } + + @Override + public void parseDocument(Object rootObject, XMLStreamReader reader) throws XMLStreamException { + try { + reader.require(START_DOCUMENT, null, null); + reader.nextTag(); + reader.require(START_ELEMENT, null, null); + processNested(new XMLExtendedStreamReaderImpl(this, reader), rootObject); + while (reader.next() != END_DOCUMENT) { + } + reader.close(); + rootElements.clear(); //clear the parsers cache + } finally { + try { + reader.close(); + } catch (Exception e) { + // log it? + } + } + } + + @Override + public void deparseDocument(final XMLElementWriter writer, final Object rootObject, final XMLStreamWriter streamWriter) throws XMLStreamException { + doDeparse(writer, rootObject, new FormattingXMLStreamWriter(streamWriter)); + } + + @SuppressWarnings( { "unchecked" }) + private void doDeparse(XMLElementWriter writer, final T value, final XMLExtendedStreamWriter streamWriter) throws XMLStreamException { + ((XMLElementWriter)writer).writeContent(streamWriter, value); + } + + /** + * Format the content writer's output on to an XML stream writer. + * + * @param contentWriter the content writer + * @param streamWriter the stream writer + * @throws XMLStreamException if an exception occurs + */ + @Override + @SuppressWarnings( "deprecation" ) + public void deparseDocument(XMLContentWriter contentWriter, XMLStreamWriter streamWriter) throws XMLStreamException { + // todo: add validation based on the registered root elements? + contentWriter.writeContent(new FormattingXMLStreamWriter(streamWriter)); + } + + private XMLElementReader getParser(final QName name) { + return rootElements.computeIfAbsent(name, qName -> rootElementsSuppliers.getOrDefault(name, () -> null).get()); + } + + @SuppressWarnings({"unchecked"}) + void processNested(final XMLExtendedStreamReader streamReader, final T value) throws XMLStreamException { + final QName name = streamReader.getName(); + final XMLElementReader reader = (XMLElementReader) getParser(name); + if (reader == null) { + throw new XMLStreamException("Unexpected element '" + name + "'", streamReader.getLocation()); + } + reader.readElement(streamReader, value); + } + + @SuppressWarnings({ "unchecked" }) + void processAttribute(final XMLStreamReader streamReader, final int index, final T value) throws XMLStreamException { + final QName name = streamReader.getName(); + final XMLAttributeReader reader = (XMLAttributeReader) rootAttributes.get(name); + if (reader == null) { + throw new XMLStreamException("Unexpected attribute '" + name + "'", streamReader.getLocation()); + } + reader.readAttribute(streamReader, index, value); + } +} diff --git a/src/test/java/org/jboss/staxmapper/SimpleReadTest1.java b/src/test/java/org/jboss/staxmapper/SimpleReadTest1.java index 61dbd8e..6b88118 100644 --- a/src/test/java/org/jboss/staxmapper/SimpleReadTest1.java +++ b/src/test/java/org/jboss/staxmapper/SimpleReadTest1.java @@ -1,81 +1,82 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2010, Red Hat, Inc., and individual contributors - * as indicated by the @author tags. See the copyright.txt file in the - * distribution for a full listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ - -package org.jboss.staxmapper; - -import static javax.xml.stream.XMLStreamConstants.COMMENT; -import static javax.xml.stream.XMLStreamConstants.END_ELEMENT; -import static javax.xml.stream.XMLStreamConstants.START_ELEMENT; - -import java.io.StringReader; - -import javax.xml.namespace.QName; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamException; -import org.junit.Test; - -/** - * @author David M. Lloyd - */ -public final class SimpleReadTest1 implements XMLElementReader { - - public static void main(String[] args) throws XMLStreamException { - new SimpleReadTest1().testReadContent(); - } - - @Test - public void testReadContent() throws XMLStreamException { - final XMLMapper mapper = XMLMapper.Factory.create(); - mapper.registerRootElement(new QName("urn:test:one", "root"), this); - mapper.parseDocument(Boolean.TRUE, XMLInputFactory.newInstance().createXMLStreamReader(new StringReader( - "\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " blah blah blah " + - " blah1 blah1 blah1 " + - "\n\n" - ))); - } - - public void readElement(final XMLExtendedStreamReader reader, final Object value) throws XMLStreamException { - System.out.println("Got my element at " + reader.getLocation()); - while (reader.hasNext()) { - switch (reader.next()) { - case COMMENT: - System.out.println("Got comment: " + reader.getText()); - break; - case END_ELEMENT: - return; - case START_ELEMENT: - if ("test".equals(reader.getLocalName())) { - System.out.println("Element text: [" + reader.getElementText() + "]"); - reader.setTrimElementText(false); - break; - } - reader.handleAny(value); - break; - } - } - } -} +/* + * JBoss, Home of Professional Open Source. + * Copyright 2010, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.staxmapper; + +import static javax.xml.stream.XMLStreamConstants.COMMENT; +import static javax.xml.stream.XMLStreamConstants.END_ELEMENT; +import static javax.xml.stream.XMLStreamConstants.START_ELEMENT; + +import java.io.StringReader; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import org.junit.Test; + +/** + * @author David M. Lloyd + */ +public final class SimpleReadTest1 implements XMLElementReader { + + public static void main(String[] args) throws XMLStreamException { + new SimpleReadTest1().testReadContent(); + } + + @Test + public void testReadContent() throws XMLStreamException { + final XMLMapper mapper = XMLMapper.Factory.create(); + mapper.registerRootElement(new QName("urn:test:one", "root"), this); + mapper.parseDocument(Boolean.TRUE, XMLInputFactory.newInstance().createXMLStreamReader(new StringReader( + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " blah blah blah " + + " blah1 blah1 blah1 " + + "\n\n" + ))); + } + + @Override + public void readElement(final XMLExtendedStreamReader reader, final Object value) throws XMLStreamException { + System.out.println("Got my element at " + reader.getLocation()); + while (reader.hasNext()) { + switch (reader.next()) { + case COMMENT: + System.out.println("Got comment: " + reader.getText()); + break; + case END_ELEMENT: + return; + case START_ELEMENT: + if ("test".equals(reader.getLocalName())) { + System.out.println("Element text: [" + reader.getElementText() + "]"); + reader.setTrimElementText(false); + break; + } + reader.handleAny(value); + break; + } + } + } +} diff --git a/src/test/java/org/jboss/staxmapper/SimpleWriteTest1.java b/src/test/java/org/jboss/staxmapper/SimpleWriteTest1.java index f0abad3..274482e 100644 --- a/src/test/java/org/jboss/staxmapper/SimpleWriteTest1.java +++ b/src/test/java/org/jboss/staxmapper/SimpleWriteTest1.java @@ -1,84 +1,86 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2010, Red Hat, Inc., and individual contributors - * as indicated by the @author tags. See the copyright.txt file in the - * distribution for a full listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ - -package org.jboss.staxmapper; - -import java.io.StringWriter; - -import javax.xml.stream.XMLOutputFactory; -import javax.xml.stream.XMLStreamException; -import org.junit.Test; - -/** - * @author David M. Lloyd - */ -public final class SimpleWriteTest1 implements XMLContentWriter { - - public static void main(String[] args) throws XMLStreamException { - new SimpleWriteTest1().testWriteContent(); - } - - @Test - public void testWriteContent() throws XMLStreamException { - final StringWriter writer = new StringWriter(512); - final XMLMapper mapper = XMLMapper.Factory.create(); - mapper.deparseDocument(this, XMLOutputFactory.newInstance().createXMLStreamWriter(writer)); - System.out.println("Output: " + writer.getBuffer().toString()); - } - - public void writeContent(final XMLExtendedStreamWriter streamWriter) throws XMLStreamException { - streamWriter.writeStartDocument("UTF-8", "1.0"); - streamWriter.writeStartElement("hello"); - streamWriter.writeNamespace("foo", "http://foo"); - streamWriter.writeNamespace("bar", "http://bar"); - streamWriter.writeAttribute("test", "this out"); - streamWriter.setUnspecifiedElementNamespace("http://foo"); - streamWriter.writeStartElement("hello-two"); - streamWriter.writeAttribute("test2", "this out2"); - streamWriter.writeStartElement("helloblah"); - streamWriter.setUnspecifiedElementNamespace("http://bar"); - streamWriter.writeAttribute("test3", "this out3"); - streamWriter.writeCharacters(" test "); - streamWriter.writeStartElement("helloblah2"); - streamWriter.writeAttribute("test4", "this out4"); - streamWriter.writeEndElement(); - streamWriter.writeEndElement(); - streamWriter.writeStartElement("inner"); - streamWriter.writeEndElement(); - streamWriter.writeEndElement(); - - streamWriter.writeStartElement("actually-empty"); - streamWriter.setDefaultNamespace("http://blah"); - streamWriter.writeEndElement(); - - streamWriter.setUnspecifiedElementNamespace(null); - - streamWriter.writeComment("this is a comment"); - streamWriter.writeComment("This is a comment\nthat spans multiple\nlines"); - streamWriter.writeEmptyElement("foo"); - streamWriter.writeCharacters("Some characters\n"); - streamWriter.writeCharacters("Some multi-\nline\ncharacters"); - streamWriter.writeEndElement(); - streamWriter.writeEndDocument(); - streamWriter.close(); - } -} +/* + * JBoss, Home of Professional Open Source. + * Copyright 2010, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.staxmapper; + +import java.io.StringWriter; + +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import org.junit.Test; + +/** + * @author David M. Lloyd + */ +@SuppressWarnings( "deprecation" ) +public final class SimpleWriteTest1 implements XMLContentWriter { + + public static void main(String[] args) throws XMLStreamException { + new SimpleWriteTest1().testWriteContent(); + } + + @Test + public void testWriteContent() throws XMLStreamException { + final StringWriter writer = new StringWriter(512); + final XMLMapper mapper = XMLMapper.Factory.create(); + mapper.deparseDocument(this, XMLOutputFactory.newInstance().createXMLStreamWriter(writer)); + System.out.println("Output: " + writer.getBuffer().toString()); + } + + @Override + public void writeContent(final XMLExtendedStreamWriter streamWriter) throws XMLStreamException { + streamWriter.writeStartDocument("UTF-8", "1.0"); + streamWriter.writeStartElement("hello"); + streamWriter.writeNamespace("foo", "http://foo"); + streamWriter.writeNamespace("bar", "http://bar"); + streamWriter.writeAttribute("test", "this out"); + streamWriter.setUnspecifiedElementNamespace("http://foo"); + streamWriter.writeStartElement("hello-two"); + streamWriter.writeAttribute("test2", "this out2"); + streamWriter.writeStartElement("helloblah"); + streamWriter.setUnspecifiedElementNamespace("http://bar"); + streamWriter.writeAttribute("test3", "this out3"); + streamWriter.writeCharacters(" test "); + streamWriter.writeStartElement("helloblah2"); + streamWriter.writeAttribute("test4", "this out4"); + streamWriter.writeEndElement(); + streamWriter.writeEndElement(); + streamWriter.writeStartElement("inner"); + streamWriter.writeEndElement(); + streamWriter.writeEndElement(); + + streamWriter.writeStartElement("actually-empty"); + streamWriter.setDefaultNamespace("http://blah"); + streamWriter.writeEndElement(); + + streamWriter.setUnspecifiedElementNamespace(null); + + streamWriter.writeComment("this is a comment"); + streamWriter.writeComment("This is a comment\nthat spans multiple\nlines"); + streamWriter.writeEmptyElement("foo"); + streamWriter.writeCharacters("Some characters\n"); + streamWriter.writeCharacters("Some multi-\nline\ncharacters"); + streamWriter.writeEndElement(); + streamWriter.writeEndDocument(); + streamWriter.close(); + } +} diff --git a/src/test/java/org/jboss/staxmapper/SimpleWriteTest2.java b/src/test/java/org/jboss/staxmapper/SimpleWriteTest2.java index 274d00d..b3d4510 100644 --- a/src/test/java/org/jboss/staxmapper/SimpleWriteTest2.java +++ b/src/test/java/org/jboss/staxmapper/SimpleWriteTest2.java @@ -1,84 +1,85 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2011, Red Hat, Inc., and individual contributors - * as indicated by the @author tags. See the copyright.txt file in the - * distribution for a full listing of individual contributors. - * - * This is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA, or see the FSF site: http://www.fsf.org. - */ - -package org.jboss.staxmapper; - -import java.io.StringWriter; - -import javax.xml.stream.XMLOutputFactory; -import javax.xml.stream.XMLStreamException; -import org.junit.Test; - -/** - * @author David M. Lloyd - */ -public final class SimpleWriteTest2 implements XMLElementWriter { - - public static void main(String[] args) throws XMLStreamException { - new SimpleWriteTest2().testWriteContent(); - } - - @Test - public void testWriteContent() throws XMLStreamException { - final StringWriter writer = new StringWriter(512); - final XMLMapper mapper = XMLMapper.Factory.create(); - mapper.deparseDocument(this, new Object(), XMLOutputFactory.newInstance().createXMLStreamWriter(writer)); - System.out.println("Output: " + writer.getBuffer().toString()); - } - - public void writeContent(final XMLExtendedStreamWriter streamWriter, final Object object) throws XMLStreamException { - streamWriter.writeStartDocument("UTF-8", "1.0"); - streamWriter.writeStartElement("hello"); - streamWriter.writeNamespace("foo", "http://foo"); - streamWriter.writeNamespace("bar", "http://bar"); - streamWriter.writeAttribute("test", "this out"); - streamWriter.setUnspecifiedElementNamespace("http://foo"); - streamWriter.writeStartElement("hello-two"); - streamWriter.writeAttribute("test2", "this out2"); - streamWriter.writeStartElement("helloblah"); - streamWriter.setUnspecifiedElementNamespace("http://bar"); - streamWriter.writeAttribute("test3", "this out3"); - streamWriter.writeCharacters(" test "); - streamWriter.writeStartElement("helloblah2"); - streamWriter.writeAttribute("test4", "this out4"); - streamWriter.writeEndElement(); - streamWriter.writeEndElement(); - streamWriter.writeStartElement("inner"); - streamWriter.writeEndElement(); - streamWriter.writeEndElement(); - - streamWriter.writeStartElement("actually-empty"); - streamWriter.setDefaultNamespace("http://blah"); - streamWriter.writeEndElement(); - - streamWriter.setUnspecifiedElementNamespace(null); - - streamWriter.writeComment("this is a comment"); - streamWriter.writeComment("This is a comment\nthat spans multiple\nlines"); - streamWriter.writeEmptyElement("foo"); - streamWriter.writeCharacters("Some characters\n"); - streamWriter.writeCharacters("Some multi-\nline\ncharacters"); - streamWriter.writeEndElement(); - streamWriter.writeEndDocument(); - streamWriter.close(); - } -} +/* + * JBoss, Home of Professional Open Source. + * Copyright 2011, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.jboss.staxmapper; + +import java.io.StringWriter; + +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import org.junit.Test; + +/** + * @author David M. Lloyd + */ +public final class SimpleWriteTest2 implements XMLElementWriter { + + public static void main(String[] args) throws XMLStreamException { + new SimpleWriteTest2().testWriteContent(); + } + + @Test + public void testWriteContent() throws XMLStreamException { + final StringWriter writer = new StringWriter(512); + final XMLMapper mapper = XMLMapper.Factory.create(); + mapper.deparseDocument(this, new Object(), XMLOutputFactory.newInstance().createXMLStreamWriter(writer)); + System.out.println("Output: " + writer.getBuffer().toString()); + } + + @Override + public void writeContent(final XMLExtendedStreamWriter streamWriter, final Object object) throws XMLStreamException { + streamWriter.writeStartDocument("UTF-8", "1.0"); + streamWriter.writeStartElement("hello"); + streamWriter.writeNamespace("foo", "http://foo"); + streamWriter.writeNamespace("bar", "http://bar"); + streamWriter.writeAttribute("test", "this out"); + streamWriter.setUnspecifiedElementNamespace("http://foo"); + streamWriter.writeStartElement("hello-two"); + streamWriter.writeAttribute("test2", "this out2"); + streamWriter.writeStartElement("helloblah"); + streamWriter.setUnspecifiedElementNamespace("http://bar"); + streamWriter.writeAttribute("test3", "this out3"); + streamWriter.writeCharacters(" test "); + streamWriter.writeStartElement("helloblah2"); + streamWriter.writeAttribute("test4", "this out4"); + streamWriter.writeEndElement(); + streamWriter.writeEndElement(); + streamWriter.writeStartElement("inner"); + streamWriter.writeEndElement(); + streamWriter.writeEndElement(); + + streamWriter.writeStartElement("actually-empty"); + streamWriter.setDefaultNamespace("http://blah"); + streamWriter.writeEndElement(); + + streamWriter.setUnspecifiedElementNamespace(null); + + streamWriter.writeComment("this is a comment"); + streamWriter.writeComment("This is a comment\nthat spans multiple\nlines"); + streamWriter.writeEmptyElement("foo"); + streamWriter.writeCharacters("Some characters\n"); + streamWriter.writeCharacters("Some multi-\nline\ncharacters"); + streamWriter.writeEndElement(); + streamWriter.writeEndDocument(); + streamWriter.close(); + } +}