Moved Spring-WS to separate dir.

This commit is contained in:
Arjen Poutsma
2006-09-24 19:22:56 +00:00
commit 66d3aef26c
623 changed files with 44122 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml;
/**
* Helper class used to find the current version of JAXP. We cannot depend on the Java version, since JAXP can be
* upgraded idenpendantly of the Java version.
* <p/>
* Only distinguishes between JAXP 1.0, 1.1, 1.3, and 1.4, since JAXP 1.2 was a maintenance release with no new
* classes.
*
* @author Arjen Poutsma
*/
public abstract class JaxpVersion {
public static final int JAXP_10 = 0;
public static final int JAXP_11 = 1;
public static final int JAXP_13 = 3;
public static final int JAXP_14 = 4;
private static final String JAXP_11_CLASS_NAME = "javax.xml.transform.Transformer";
private static final String JAXP_13_CLASS_NAME = "javax.xml.xpath.XPath";
private static final String JAXP_14_CLASS_NAME = "javax.xml.transform.stax.StAXSource";
private static int jaxpVersion = JAXP_10;
static {
try {
Class.forName(JAXP_14_CLASS_NAME);
jaxpVersion = JAXP_14;
}
catch (ClassNotFoundException ex1) {
try {
Class.forName(JAXP_13_CLASS_NAME);
jaxpVersion = JAXP_13;
}
catch (ClassNotFoundException ex2) {
try {
Class.forName(JAXP_11_CLASS_NAME);
jaxpVersion = JAXP_11;
}
catch (ClassNotFoundException ex3) {
// default to JAXP 1.0
}
}
}
}
/**
* Gets the major JAXP version. This means we can do things like if <code>(getJaxpVersion() < JAXP_13)</code>.
*
* @return a code comparable to the JAXP_XX codes in this class
* @see #JAXP_10
* @see #JAXP_11
* @see #JAXP_13
* @see #JAXP_14
*/
public static int getJaxpVersion() {
return jaxpVersion;
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml;
import org.springframework.core.NestedRuntimeException;
/**
* Root of the hierarchy of XML exception.
*
* @author Arjen Poutsma
*/
public abstract class XmlException extends NestedRuntimeException {
/**
* Constructs a new instance of the <code>XmlException</code> with the specific detail message.
*
* @param message the detail message
*/
protected XmlException(String message) {
super(message);
}
/**
* Constructs a new instance of the <code>XmlException</code> with the specific detail message and exception.
*
* @param message the detail message
* @param throwable the wrapped exception
*/
protected XmlException(String message, Throwable throwable) {
super(message, throwable);
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.namespace;
import java.beans.PropertyEditorSupport;
import javax.xml.namespace.QName;
import org.springframework.util.StringUtils;
/**
* PropertyEditor for <code>javax.xml.namespace.QName</code>, to populate a property of type QName from a String value.
* <p/>
* Expects the syntax
* <pre>
* localPart
* </pre>
* or
* <pre>
* {namespace}localPart
* </pre>
* or
* <pre>
* {namespace}prefix:localPart
* </pre>
* This resembles the <code>toString()</code> representation of <code>QName</code> itself, but allows for prefixes to be
* specified as well.
*
* @author Arjen Poutsma
* @see javax.xml.namespace.QName
* @see javax.xml.namespace.QName#toString()
* @see javax.xml.namespace.QName#valueOf(String)
*/
public class QNameEditor extends PropertyEditorSupport {
public void setAsText(String text) throws IllegalArgumentException {
setValue(QNameUtils.parseQNameString(text));
}
public String getAsText() {
Object value = getValue();
if (value == null || !(value instanceof QName)) {
return "";
}
else {
QName qName = (QName) value;
String prefix = QNameUtils.getPrefix(qName);
if (StringUtils.hasLength(qName.getNamespaceURI()) && StringUtils.hasLength(prefix)) {
return "{" + qName.getNamespaceURI() + "}" + prefix + ":" + qName.getLocalPart();
}
else if (StringUtils.hasLength(qName.getNamespaceURI())) {
return "{" + qName.getNamespaceURI() + "}" + qName.getLocalPart();
}
else {
return qName.getLocalPart();
}
}
}
}

View File

@@ -0,0 +1,189 @@
/*
* Copyright 2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.namespace;
import javax.xml.namespace.QName;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.w3c.dom.Node;
/**
* Helper class for using <code>javax.xml.namespace.QName</code>.
*
* @author Arjen Poutsma
* @see javax.xml.namespace.QName
*/
public abstract class QNameUtils {
private static boolean qNameHasPrefix;
static {
try {
QName.class.getDeclaredConstructor(new Class[]{String.class, String.class, String.class});
qNameHasPrefix = true;
}
catch (NoSuchMethodException e) {
qNameHasPrefix = false;
}
}
/**
* Creates a new <code>QName</code> with the given parameters. Sets the prefix if possible, i.e. if the
* <code>QName(String, String, String)</code> constructor can be found. If this constructor is not available (as is
* the case on older implementations of JAX-RPC), the prefix is ignored.
*
* @param namespaceUri namespace URI of the <code>QName</code>
* @param localPart local part of the <code>QName</code>
* @param prefix prefix of the <code>QName</code>. May be ignored.
* @return the created <code>QName</code>
* @see QName#QName(String, String, String)
*/
public static QName createQName(String namespaceUri, String localPart, String prefix) {
if (qNameHasPrefix) {
return new QName(namespaceUri, localPart, prefix);
}
else {
return new QName(namespaceUri, localPart);
}
}
/**
* Returns the prefix of the given <code>QName</code>. Returns the prefix if available, i.e. if the
* <code>QName.getPrefix()</code> method can be found. If this method is not available (as is the case on older
* implementations of JAX-RPC), an empty string is returned.
*
* @param qName the <code>QName</code> to return the prefix from
* @return the prefix, if available, or an empty string
* @see javax.xml.namespace.QName#getPrefix()
*/
public static String getPrefix(QName qName) {
return qNameHasPrefix ? qName.getPrefix() : "";
}
/**
* Validates the given String as a QName
*
* @param text the qualified name
* @return <code>true</code> if valid, <code>false</code> otherwise
*/
public static boolean validateQName(String text) {
if (!StringUtils.hasLength(text)) {
return false;
}
if (text.charAt(0) == '{') {
int i = text.indexOf('}');
if (i == -1 || i == text.length() - 1) {
return false;
}
}
return true;
}
/**
* Returns the qualified name of the given DOM Node.
*
* @param node the node
* @return the qualified name of the node
*/
public static QName getQNameForNode(Node node) {
if (node.getNamespaceURI() != null && node.getPrefix() != null && node.getLocalName() != null) {
return createQName(node.getNamespaceURI(), node.getLocalName(), node.getPrefix());
}
else if (node.getNamespaceURI() != null && node.getLocalName() != null) {
return new QName(node.getNamespaceURI(), node.getLocalName());
}
else if (node.getLocalName() != null) {
return new QName(node.getLocalName());
}
else {
// as a last resort, use the node name
return new QName(node.getNodeName());
}
}
/**
* Convert a <code>QName</code> to a qualified name, as used by DOM and SAX. The returned string has a format of
* <code>prefix:localName</code> if the prefix is set, or just <code>localName</code> if not.
*
* @param qName the <code>QName</code>
* @return the qualified name
*/
public static String toQualifiedName(QName qName) {
String prefix = getPrefix(qName);
if (!StringUtils.hasLength(prefix)) {
return qName.getLocalPart();
}
else {
return prefix + ":" + qName.getLocalPart();
}
}
/**
* Convert a namespace URI and DOM or SAX qualified name to a <code>QName</code>. The qualified name can have the
* form <code>prefix:localname</code> or <code>localName</code>.
*
* @param namespaceUri the namespace URI
* @param qualifiedName the qualified name
* @return a QName
*/
public static QName toQName(String namespaceUri, String qualifiedName) {
int idx = qualifiedName.indexOf(':');
if (idx == -1) {
return new QName(namespaceUri, qualifiedName);
}
else {
return createQName(namespaceUri, qualifiedName.substring(idx + 1), qualifiedName.substring(0, idx));
}
}
/**
* Parse the given qualified name string into a <code>QName</code>. Expects the syntax <code>localPart</code>,
* <code>{namespace}localPart</code>, or <code>{namespace}prefix:localPart</code>. This format resembles the
* <code>toString()</code> representation of <code>QName</code> itself, but allows for prefixes to be specified as
* well.
*
* @return a corresponding QName instance
* @throws IllegalArgumentException when the given string is <code>null</code> or empty.
*/
public static QName parseQNameString(String qNameString) {
Assert.hasLength(qNameString, "QName text may not be null or empty");
if (qNameString.charAt(0) != '{') {
return new QName(qNameString);
}
else {
int endOfNamespaceURI = qNameString.indexOf('}');
if (endOfNamespaceURI == -1) {
throw new IllegalArgumentException(
"Cannot create QName from \"" + qNameString + "\", missing closing \"}\"");
}
int prefixSeperator = qNameString.indexOf(':', endOfNamespaceURI + 1);
String namespaceURI = qNameString.substring(1, endOfNamespaceURI);
if (prefixSeperator == -1) {
return new QName(namespaceURI, qNameString.substring(endOfNamespaceURI + 1));
}
else {
return createQName(namespaceURI, qNameString.substring(prefixSeperator + 1),
qNameString.substring(endOfNamespaceURI + 1, prefixSeperator));
}
}
}
}

View File

@@ -0,0 +1,165 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.namespace;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import org.springframework.util.Assert;
/**
* Simple <code>javax.xml.namespace.NamespaceContext</code> implementation. Follows the standard
* <code>NamespaceContext</code> contract, and is loadable via a <code>java.util.Map</code> or
* <code>java.util.Properties</code> object
*
* @author Arjen Poutsma
*/
public class SimpleNamespaceContext implements NamespaceContext {
private Map prefixToNamespaceUri = new HashMap();
/**
* Maps a <code>String</code> namespaceUri to a <code>List</code> of prefixes
*/
private Map namespaceUriToPrefixes = new HashMap();
private String defaultNamespaceUri = "";
public String getNamespaceURI(String prefix) {
Assert.notNull(prefix, "prefix is null");
if (XMLConstants.XML_NS_PREFIX.equals(prefix)) {
return XMLConstants.XML_NS_URI;
}
else if (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix)) {
return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
}
else if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) {
return defaultNamespaceUri;
}
else if (prefixToNamespaceUri.containsKey(prefix)) {
return (String) prefixToNamespaceUri.get(prefix);
}
return "";
}
public String getPrefix(String namespaceUri) {
List prefixes = getPrefixesInternal(namespaceUri);
return prefixes.isEmpty() ? null : (String) prefixes.get(0);
}
public Iterator getPrefixes(String namespaceUri) {
return getPrefixesInternal(namespaceUri).iterator();
}
/**
* Sets the bindings for this namespace context. The supplied map must consist of string key value pairs.
*
* @param bindings the bindings
*/
public void setBindings(Map bindings) {
for (Iterator iterator = bindings.keySet().iterator(); iterator.hasNext();) {
String prefix = (String) iterator.next();
bindNamespaceUri(prefix, (String) bindings.get(prefix));
}
}
/**
* Binds the given namespace as default namespace.
*
* @param namespaceUri the namespace uri
*/
public void bindDefaultNamespaceUri(String namespaceUri) {
bindNamespaceUri(XMLConstants.DEFAULT_NS_PREFIX, namespaceUri);
}
/**
* Binds the given prefix to the given namespace.
*
* @param prefix the namespace prefix
* @param namespaceUri the namespace uri
*/
public void bindNamespaceUri(String prefix, String namespaceUri) {
Assert.notNull(prefix, "No prefix given");
Assert.notNull(namespaceUri, "No namespaceUri given");
if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) {
defaultNamespaceUri = namespaceUri;
}
else {
prefixToNamespaceUri.put(prefix, namespaceUri);
getPrefixesInternal(namespaceUri).add(prefix);
}
}
/**
* Removes all declared prefixes.
*/
public void clear() {
prefixToNamespaceUri.clear();
}
/**
* Returns all declared prefixes.
*
* @return the declared prefixes
*/
public Iterator getBoundPrefixes() {
return prefixToNamespaceUri.keySet().iterator();
}
private List getPrefixesInternal(String namespaceUri) {
if (defaultNamespaceUri.equals(namespaceUri)) {
return Collections.singletonList(XMLConstants.DEFAULT_NS_PREFIX);
}
else if (XMLConstants.XML_NS_URI.equals(namespaceUri)) {
return Collections.singletonList(XMLConstants.XML_NS_PREFIX);
}
else if (XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(namespaceUri)) {
return Collections.singletonList(XMLConstants.XMLNS_ATTRIBUTE);
}
else {
List list = (List) namespaceUriToPrefixes.get(namespaceUri);
if (list == null) {
list = new ArrayList();
namespaceUriToPrefixes.put(namespaceUri, list);
}
return list;
}
}
/**
* Removes the given prefix from this context.
*
* @param prefix the prefix to be removed
*/
public void removeBinding(String prefix) {
if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) {
defaultNamespaceUri = "";
}
else {
String namespaceUri = (String) namespaceUriToPrefixes.get(prefix);
List prefixes = getPrefixesInternal(namespaceUri);
prefixes.remove(prefix);
namespaceUriToPrefixes.remove(prefixes);
}
}
}

View File

@@ -0,0 +1,5 @@
<html>
<body>
Provides classes that help with XML Namespace processing. Mostly for internal use by the framework.
</body>
</html>

View File

@@ -0,0 +1,6 @@
<html>
<body>
Provides classes for XML handling: version detection and a base XML exception class. Mostly for internal use by the
framework.
</body>
</html>

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.sax;
import java.io.IOException;
import org.springframework.core.io.Resource;
import org.xml.sax.InputSource;
/**
* @author Arjen Poutsma
*/
public abstract class SaxUtils {
/**
* Creates a SAX <code>InputSource</code> from the given resource. Sets the system identifier to the resource's
* <code>URL</code>, if available.
*
* @param resource the resource
* @return the input source created from the resource
* @throws IOException if an I/O exception occurs
* @see InputSource#setSystemId(String)
* @see #getSystemId(org.springframework.core.io.Resource)
*/
public static InputSource createInputSource(Resource resource) throws IOException {
InputSource inputSource = new InputSource(resource.getInputStream());
inputSource.setSystemId(getSystemId(resource));
return inputSource;
}
/**
* Retrieves the URL from the given resource as System ID. Returns <code>null</code> if it cannot be openened.
*/
public static String getSystemId(Resource resource) {
try {
return resource.getURL().toString();
}
catch (IOException e) {
return null;
}
}
}

View File

@@ -0,0 +1,5 @@
<html>
<body>
Provides classes that help with SAX: the Simple API for XML. Mostly for internal use by the framework.
</body>
</html>

View File

@@ -0,0 +1,181 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.stream;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.springframework.util.Assert;
/**
* Abstract base class for <code>XMLStreamReader</code>s.
*
* @author Arjen Poutsma
*/
public abstract class AbstractXmlStreamReader implements XMLStreamReader {
public String getElementText() throws XMLStreamException {
if (getEventType() != XMLStreamConstants.START_ELEMENT) {
throw new XMLStreamException("parser must be on START_ELEMENT to read next text", getLocation());
}
int eventType = next();
StringBuffer buffer = new StringBuffer();
while (eventType != XMLStreamConstants.END_ELEMENT) {
if (eventType == XMLStreamConstants.CHARACTERS || eventType == XMLStreamConstants.CDATA ||
eventType == XMLStreamConstants.SPACE || eventType == XMLStreamConstants.ENTITY_REFERENCE) {
buffer.append(getText());
}
else
if (eventType == XMLStreamConstants.PROCESSING_INSTRUCTION || eventType == XMLStreamConstants.COMMENT) {
// skipping
}
else if (eventType == XMLStreamConstants.END_DOCUMENT) {
throw new XMLStreamException("unexpected end of document when reading element text content",
getLocation());
}
else if (eventType == XMLStreamConstants.START_ELEMENT) {
throw new XMLStreamException("element text content may not contain START_ELEMENT", getLocation());
}
else {
throw new XMLStreamException("Unexpected event type " + eventType, getLocation());
}
eventType = next();
}
return buffer.toString();
}
public String getAttributeLocalName(int index) {
return getAttributeName(index).getLocalPart();
}
public String getAttributeNamespace(int index) {
return getAttributeName(index).getNamespaceURI();
}
public String getAttributePrefix(int index) {
return getAttributeName(index).getPrefix();
}
public String getNamespaceURI() {
int eventType = getEventType();
if (eventType == XMLStreamConstants.START_ELEMENT || eventType == XMLStreamConstants.END_ELEMENT) {
return getName().getNamespaceURI();
}
else {
throw new IllegalStateException("parser must be on START_ELEMENT or END_ELEMENT state");
}
}
public String getNamespaceURI(String prefix) {
Assert.notNull(prefix, "No prefix given");
return getNamespaceContext().getNamespaceURI(prefix);
}
public boolean hasText() {
int eventType = getEventType();
return eventType == XMLStreamConstants.SPACE || eventType == XMLStreamConstants.CHARACTERS ||
eventType == XMLStreamConstants.COMMENT || eventType == XMLStreamConstants.CDATA ||
eventType == XMLStreamConstants.ENTITY_REFERENCE;
}
public String getPrefix() {
int eventType = getEventType();
if (eventType == XMLStreamConstants.START_ELEMENT || eventType == XMLStreamConstants.END_ELEMENT) {
return getName().getPrefix();
}
else {
throw new IllegalStateException("parser must be on START_ELEMENT or END_ELEMENT state");
}
}
public boolean hasName() {
int eventType = getEventType();
return eventType == XMLStreamConstants.START_ELEMENT || eventType == XMLStreamConstants.END_ELEMENT;
}
public boolean isWhiteSpace() {
return getEventType() == XMLStreamConstants.SPACE;
}
public boolean isStartElement() {
return getEventType() == XMLStreamConstants.START_ELEMENT;
}
public boolean isEndElement() {
return getEventType() == XMLStreamConstants.END_ELEMENT;
}
public boolean isCharacters() {
return getEventType() == XMLStreamConstants.CHARACTERS;
}
public int nextTag() throws XMLStreamException {
int eventType = next();
while (eventType == XMLStreamConstants.CHARACTERS && isWhiteSpace() ||
eventType == XMLStreamConstants.CDATA && isWhiteSpace() || eventType == XMLStreamConstants.SPACE ||
eventType == XMLStreamConstants.PROCESSING_INSTRUCTION || eventType == XMLStreamConstants.COMMENT) {
eventType = next();
}
if (eventType != XMLStreamConstants.START_ELEMENT && eventType != XMLStreamConstants.END_ELEMENT) {
throw new XMLStreamException("expected start or end tag", getLocation());
}
return eventType;
}
public void require(int expectedType, String namespaceURI, String localName) throws XMLStreamException {
int eventType = getEventType();
if (eventType != expectedType) {
throw new XMLStreamException("Expected [" + expectedType + "] but read [" + eventType + "]");
}
}
public String getAttributeValue(String namespaceURI, String localName) {
for (int i = 0; i < getAttributeCount(); i++) {
QName name = getAttributeName(i);
if (name.getNamespaceURI().equals(namespaceURI) && name.getLocalPart().equals(localName)) {
return getAttributeValue(i);
}
}
return null;
}
public boolean hasNext() throws XMLStreamException {
return getEventType() != END_DOCUMENT;
}
public String getLocalName() {
return getName().getLocalPart();
}
public char[] getTextCharacters() {
return getText().toCharArray();
}
public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length)
throws XMLStreamException {
char[] source = getTextCharacters();
length = Math.min(length, source.length);
System.arraycopy(source, sourceStart, target, targetStart, length);
return length;
}
public int getTextLength() {
return getText().length();
}
}

View File

@@ -0,0 +1,152 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.stream;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.springframework.xml.namespace.QNameUtils;
import org.springframework.xml.namespace.SimpleNamespaceContext;
/**
* Abstract base class for SAX <code>ContentHandler</code> implementations that use StAX as a basis. All methods
* delegate to internal template methods, capable of throwing a <code>XMLStreamException</code>. Additionally, an
* namespace context is used to keep track of declared namespaces.
*
* @author Arjen Poutsma
*/
public abstract class StaxContentHandler implements ContentHandler {
private SimpleNamespaceContext namespaceContext = new SimpleNamespaceContext();
/**
* @throws SAXException
*/
public final void startDocument() throws SAXException {
namespaceContext.clear();
try {
startDocumentInternal();
}
catch (XMLStreamException ex) {
throw new SAXException("Could not handle startDocument: " + ex.getMessage(), ex);
}
}
protected abstract void startDocumentInternal() throws XMLStreamException;
public final void endDocument() throws SAXException {
namespaceContext.clear();
try {
endDocumentInternal();
}
catch (XMLStreamException ex) {
throw new SAXException("Could not handle startDocument: " + ex.getMessage(), ex);
}
}
protected abstract void endDocumentInternal() throws XMLStreamException;
/**
* Binds the given prefix to the given namespaces.
*
* @see SimpleNamespaceContext#bindNamespaceUri(String, String)
*/
public final void startPrefixMapping(String prefix, String uri) {
namespaceContext.bindNamespaceUri(prefix, uri);
}
/**
* Removes the binding for the given prefix.
*
* @see SimpleNamespaceContext#removeBinding(String)
*/
public final void endPrefixMapping(String prefix) {
namespaceContext.removeBinding(prefix);
}
public final void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
try {
startElementInternal(QNameUtils.toQName(uri, qName), atts, namespaceContext);
}
catch (XMLStreamException ex) {
throw new SAXException("Could not handle startElement: " + ex.getMessage(), ex);
}
}
protected abstract void startElementInternal(QName name, Attributes atts, SimpleNamespaceContext namespaceContext)
throws XMLStreamException;
public final void endElement(String uri, String localName, String qName) throws SAXException {
try {
endElementInternal(QNameUtils.toQName(uri, qName), namespaceContext);
}
catch (XMLStreamException ex) {
throw new SAXException("Could not handle endElement: " + ex.getMessage(), ex);
}
}
protected abstract void endElementInternal(QName name, SimpleNamespaceContext namespaceContext)
throws XMLStreamException;
public final void characters(char ch[], int start, int length) throws SAXException {
try {
charactersInternal(ch, start, length);
}
catch (XMLStreamException ex) {
throw new SAXException("Could not handle characters: " + ex.getMessage(), ex);
}
}
protected abstract void charactersInternal(char[] ch, int start, int length) throws XMLStreamException;
public final void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
try {
ignorableWhitespaceInternal(ch, start, length);
}
catch (XMLStreamException ex) {
throw new SAXException("Could not handle ignorableWhitespace:" + ex.getMessage(), ex);
}
}
protected abstract void ignorableWhitespaceInternal(char[] ch, int start, int length) throws XMLStreamException;
public final void processingInstruction(String target, String data) throws SAXException {
try {
processingInstructionInternal(target, data);
}
catch (XMLStreamException ex) {
throw new SAXException("Could not handle processingInstruction: " + ex.getMessage(), ex);
}
}
protected abstract void processingInstructionInternal(String target, String data) throws XMLStreamException;
public final void skippedEntity(String name) throws SAXException {
try {
skippedEntityInternal(name);
}
catch (XMLStreamException ex) {
throw new SAXException("Could not handle skippedEntity: " + ex.getMessage(), ex);
}
}
protected abstract void skippedEntityInternal(String name) throws XMLStreamException;
}

View File

@@ -0,0 +1,181 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.stream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.stream.Location;
import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;
import javax.xml.stream.util.XMLEventConsumer;
import org.springframework.util.StringUtils;
import org.springframework.xml.namespace.QNameUtils;
import org.springframework.xml.namespace.SimpleNamespaceContext;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
/**
* SAX <code>ContentHandler</code> that transforms callback calls to <code>XMLEvent</code>s and writes them to a
* <code>XMLEventConsumer</code>.
*
* @author Arjen Poutsma
* @see XMLEvent
* @see XMLEventConsumer
*/
public class StaxEventContentHandler extends StaxContentHandler {
private final XMLEventFactory eventFactory;
private final XMLEventConsumer eventConsumer;
private Locator locator;
/**
* Constructs a new instance of the <code>StaxEventContentHandler</code> that writes to the given
* <code>XMLEventConsumer</code>. A default <code>XMLEventFactory</code> will be created.
*
* @param consumer the consumer to write events to
*/
public StaxEventContentHandler(XMLEventConsumer consumer) {
eventFactory = XMLEventFactory.newInstance();
eventConsumer = consumer;
}
/**
* Constructs a new instance of the <code>StaxEventContentHandler</code> that uses the given event factory to create
* events and writes to the given <code>XMLEventConsumer</code>.
*
* @param consumer the consumer to write events to
* @param factory the factory used to create events
*/
public StaxEventContentHandler(XMLEventConsumer consumer, XMLEventFactory factory) {
eventFactory = factory;
eventConsumer = consumer;
}
public void setDocumentLocator(Locator locator) {
this.locator = locator;
}
protected void startDocumentInternal() throws XMLStreamException {
consumeEvent(eventFactory.createStartDocument());
}
protected void endDocumentInternal() throws XMLStreamException {
consumeEvent(eventFactory.createEndDocument());
}
protected void startElementInternal(QName name, Attributes atts, SimpleNamespaceContext namespaceContext)
throws XMLStreamException {
List attributes = getAttributes(atts);
List namespaces = createNamespaces(namespaceContext);
consumeEvent(eventFactory.createStartElement(name, attributes.iterator(), namespaces.iterator()));
}
protected void endElementInternal(QName name, SimpleNamespaceContext namespaceContext) throws XMLStreamException {
List namespaces = createNamespaces(namespaceContext);
consumeEvent(eventFactory.createEndElement(name, namespaces.iterator()));
}
protected void charactersInternal(char[] ch, int start, int length) throws XMLStreamException {
consumeEvent(eventFactory.createCharacters(new String(ch, start, length)));
}
protected void ignorableWhitespaceInternal(char[] ch, int start, int length) throws XMLStreamException {
consumeEvent(eventFactory.createIgnorableSpace(new String(ch, start, length)));
}
protected void processingInstructionInternal(String target, String data) throws XMLStreamException {
consumeEvent(eventFactory.createProcessingInstruction(target, data));
}
private void consumeEvent(XMLEvent event) throws XMLStreamException {
if (locator != null) {
eventFactory.setLocation(new SaxLocation(locator));
}
eventConsumer.add(event);
}
/**
* Creates and returns a list of <code>NameSpace</code> objects from the <code>NamespaceContext</code>.
*/
private List createNamespaces(SimpleNamespaceContext namespaceContext) {
List namespaces = new ArrayList();
String defaultNamespaceUri = namespaceContext.getNamespaceURI(XMLConstants.DEFAULT_NS_PREFIX);
if (StringUtils.hasLength(defaultNamespaceUri)) {
namespaces.add(eventFactory.createNamespace(defaultNamespaceUri));
}
for (Iterator iterator = namespaceContext.getBoundPrefixes(); iterator.hasNext();) {
String prefix = (String) iterator.next();
String namespaceUri = namespaceContext.getNamespaceURI(prefix);
namespaces.add(eventFactory.createNamespace(prefix, namespaceUri));
}
return namespaces;
}
private List getAttributes(Attributes attributes) {
List list = new ArrayList();
for (int i = 0; i < attributes.getLength(); i++) {
QName name = QNameUtils.toQName(attributes.getURI(i), attributes.getQName(i));
if (!("xmlns".equals(name.getLocalPart()) || "xmlns".equals(QNameUtils.getPrefix(name)))) {
list.add(eventFactory.createAttribute(name, attributes.getValue(i)));
}
}
return list;
}
//
// No operation
//
protected void skippedEntityInternal(String name) throws XMLStreamException {
}
private static class SaxLocation implements Location {
private Locator locator;
public SaxLocation(Locator locator) {
this.locator = locator;
}
public int getLineNumber() {
return locator.getLineNumber();
}
public int getColumnNumber() {
return locator.getColumnNumber();
}
public int getCharacterOffset() {
return -1;
}
public String getPublicId() {
return locator.getPublicId();
}
public String getSystemId() {
return locator.getSystemId();
}
}
}

View File

@@ -0,0 +1,187 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.stream;
import java.util.Iterator;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.Namespace;
import javax.xml.stream.events.NotationDeclaration;
import javax.xml.stream.events.ProcessingInstruction;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.springframework.xml.namespace.QNameUtils;
/**
* SAX <code>XMLReader</code> that reads from a StAX <code>XMLEventReader</code>. Consumes <code>XMLEvents</code> from
* an <code>XMLEventReader</code>, and calls the corresponding methods on the SAX callback interfaces.
*
* @author Arjen Poutsma
* @see XMLEventReader
* @see #setContentHandler(org.xml.sax.ContentHandler)
* @see #setDTDHandler(org.xml.sax.DTDHandler)
* @see #setEntityResolver(org.xml.sax.EntityResolver)
* @see #setErrorHandler(org.xml.sax.ErrorHandler)
*/
public class StaxEventXmlReader extends StaxXmlReader {
private final XMLEventReader reader;
/**
* Constructs a new instance of the <code>StaxEventXmlReader</code> that reads from the given
* <code>XMLEventReader</code>. The supplied event reader must be in <code>XMLStreamConstants.START_DOCUMENT</code>
* or <code>XMLStreamConstants.START_ELEMENT</code> state.
*
* @param reader the <code>XMLEventReader</code> to read from
* @throws IllegalStateException if the reader is not at the start of a document or element
*/
public StaxEventXmlReader(XMLEventReader reader) {
try {
XMLEvent event = reader.peek();
if (event == null || !(event.isStartDocument() || event.isStartElement())) {
throw new IllegalStateException("XMLEventReader not at start of document or element");
}
}
catch (XMLStreamException ex) {
throw new IllegalStateException("Could not read first element: " + ex.getMessage(), ex);
}
this.reader = reader;
}
protected void parseInternal() throws SAXException, XMLStreamException {
boolean documentEnded = false;
while (reader.hasNext()) {
XMLEvent event = reader.nextEvent();
switch (event.getEventType()) {
case XMLStreamConstants.START_ELEMENT:
handleStartElement(event.asStartElement());
break;
case XMLStreamConstants.END_ELEMENT:
handleEndElement(event.asEndElement());
break;
case XMLStreamConstants.PROCESSING_INSTRUCTION:
handleProcessingInstruction((ProcessingInstruction) event);
break;
case XMLStreamConstants.CHARACTERS:
case XMLStreamConstants.SPACE:
case XMLStreamConstants.CDATA:
handleCharacters(event.asCharacters());
break;
case XMLStreamConstants.START_DOCUMENT:
setLocator(event.getLocation());
handleStartDocument();
break;
case XMLStreamConstants.END_DOCUMENT:
handleEndDocument();
documentEnded = true;
break;
case XMLStreamConstants.NOTATION_DECLARATION:
handleNotationDeclaration((NotationDeclaration) event);
break;
}
}
if (!documentEnded) {
handleEndDocument();
}
}
private void handleCharacters(Characters characters) throws SAXException {
if (getContentHandler() != null) {
if (characters.isIgnorableWhiteSpace()) {
getContentHandler()
.ignorableWhitespace(characters.getData().toCharArray(), 0, characters.getData().length());
}
else {
getContentHandler().characters(characters.getData().toCharArray(), 0, characters.getData().length());
}
}
}
private void handleEndDocument() throws SAXException {
if (getContentHandler() != null) {
getContentHandler().endDocument();
}
}
private void handleEndElement(EndElement endElement) throws SAXException {
if (getContentHandler() != null) {
getContentHandler().endElement(endElement.getName().getNamespaceURI(), endElement.getName().getLocalPart(),
QNameUtils.toQualifiedName(endElement.getName()));
for (Iterator i = endElement.getNamespaces(); i.hasNext();) {
Namespace namespace = (Namespace) i.next();
getContentHandler().endPrefixMapping(namespace.getPrefix());
}
}
}
private void handleNotationDeclaration(NotationDeclaration declaration) throws SAXException {
if (getDtdHandler() != null) {
getDtdHandler().notationDecl(declaration.getName(), declaration.getPublicId(), declaration.getSystemId());
}
}
private void handleProcessingInstruction(ProcessingInstruction pi) throws SAXException {
if (getContentHandler() != null) {
getContentHandler().processingInstruction(pi.getTarget(), pi.getData());
}
}
private void handleStartDocument() throws SAXException {
if (getContentHandler() != null) {
getContentHandler().startDocument();
}
}
private void handleStartElement(StartElement startElement) throws SAXException {
if (getContentHandler() != null) {
for (Iterator i = startElement.getNamespaces(); i.hasNext();) {
Namespace namespace = (Namespace) i.next();
getContentHandler().startPrefixMapping(namespace.getPrefix(), namespace.getNamespaceURI());
}
getContentHandler().startElement(startElement.getName().getNamespaceURI(),
startElement.getName().getLocalPart(), QNameUtils.toQualifiedName(startElement.getName()),
getAttributes(startElement));
}
}
private Attributes getAttributes(StartElement event) {
AttributesImpl attributes = new AttributesImpl();
for (Iterator i = event.getAttributes(); i.hasNext();) {
Attribute attribute = (Attribute) i.next();
attributes.addAttribute(attribute.getName().getNamespaceURI(), attribute.getName().getLocalPart(),
QNameUtils.toQualifiedName(attribute.getName()), attribute.getDTDType(), attribute.getValue());
}
return attributes;
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.stream;
import java.util.Iterator;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.springframework.util.StringUtils;
import org.springframework.xml.namespace.QNameUtils;
import org.springframework.xml.namespace.SimpleNamespaceContext;
import org.xml.sax.Attributes;
import org.xml.sax.Locator;
/**
* SAX <code>ContentHandler</code> that writes to a <code>XMLStreamWriter</code>.
*
* @author Arjen Poutsma
* @see XMLStreamWriter
*/
public class StaxStreamContentHandler extends StaxContentHandler {
private final XMLStreamWriter streamWriter;
/**
* Constructs a new instance of the <code>StaxStreamContentHandler</code> that writes to the given
* <code>XMLStreamWriter</code>.
*
* @param streamWriter the stream writer to write to
*/
public StaxStreamContentHandler(XMLStreamWriter streamWriter) {
this.streamWriter = streamWriter;
}
public void setDocumentLocator(Locator locator) {
}
protected void charactersInternal(char[] ch, int start, int length) throws XMLStreamException {
streamWriter.writeCharacters(ch, start, length);
}
protected void endDocumentInternal() throws XMLStreamException {
streamWriter.writeEndDocument();
}
protected void endElementInternal(QName name, SimpleNamespaceContext namespaceContext) throws XMLStreamException {
streamWriter.writeEndElement();
}
protected void ignorableWhitespaceInternal(char[] ch, int start, int length) throws XMLStreamException {
streamWriter.writeCharacters(ch, start, length);
}
protected void processingInstructionInternal(String target, String data) throws XMLStreamException {
streamWriter.writeProcessingInstruction(target, data);
}
protected void skippedEntityInternal(String name) {
}
protected void startDocumentInternal() throws XMLStreamException {
streamWriter.writeStartDocument();
}
protected void startElementInternal(QName name, Attributes attributes, SimpleNamespaceContext namespaceContext)
throws XMLStreamException {
streamWriter.writeStartElement(QNameUtils.getPrefix(name), name.getLocalPart(), name.getNamespaceURI());
String defaultNamespaceUri = namespaceContext.getNamespaceURI("");
if (StringUtils.hasLength(defaultNamespaceUri)) {
streamWriter.writeNamespace("", defaultNamespaceUri);
streamWriter.setDefaultNamespace(defaultNamespaceUri);
}
for (Iterator iterator = namespaceContext.getBoundPrefixes(); iterator.hasNext();) {
String prefix = (String) iterator.next();
streamWriter.writeNamespace(prefix, namespaceContext.getNamespaceURI(prefix));
streamWriter.setPrefix(prefix, namespaceContext.getNamespaceURI(prefix));
}
for (int i = 0; i < attributes.getLength(); i++) {
QName attrName = QNameUtils.toQName(attributes.getURI(i), attributes.getQName(i));
String attrPrefix = QNameUtils.getPrefix(attrName);
if (!("xmlns".equals(attrName.getLocalPart()) || "xmlns".equals(attrPrefix))) {
streamWriter.writeAttribute(attrPrefix, attrName.getNamespaceURI(), attrName.getLocalPart(),
attributes.getValue(i));
}
}
}
}

View File

@@ -0,0 +1,187 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.stream;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.springframework.xml.namespace.QNameUtils;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
/**
* SAX <code>XMLReader</code> that reads from a StAX <code>XMLStreamReader</code>. Reads from an
* <code>XMLStreamReader</code>, and calls the corresponding methods on the SAX callback interfaces.
*
* @author Arjen Poutsma
* @see XMLStreamReader
* @see #setContentHandler(org.xml.sax.ContentHandler)
* @see #setDTDHandler(org.xml.sax.DTDHandler)
* @see #setEntityResolver(org.xml.sax.EntityResolver)
* @see #setErrorHandler(org.xml.sax.ErrorHandler)
*/
public class StaxStreamXmlReader extends StaxXmlReader {
private final XMLStreamReader reader;
/**
* Constructs a new instance of the <code>StaxStreamXmlReader</code> that reads from the given
* <code>XMLStreamReader</code>. The supplied stream reader must be in <code>XMLStreamConstants.START_DOCUMENT</code>
* or <code>XMLStreamConstants.START_ELEMENT</code> state.
*
* @param reader the <code>XMLEventReader</code> to read from
* @throws IllegalStateException if the reader is not at the start of a document or element
*/
public StaxStreamXmlReader(XMLStreamReader reader) {
int event = reader.getEventType();
if (!(event == XMLStreamConstants.START_DOCUMENT || event == XMLStreamConstants.START_ELEMENT)) {
throw new IllegalStateException("XMLEventReader not at start of document or element");
}
this.reader = reader;
}
protected void parseInternal() throws SAXException, XMLStreamException {
boolean documentEnded = false;
while (true) {
switch (reader.getEventType()) {
case XMLStreamConstants.START_ELEMENT:
handleStartElement();
break;
case XMLStreamConstants.END_ELEMENT:
handleEndElement();
break;
case XMLStreamConstants.PROCESSING_INSTRUCTION:
handleProcessingInstruction();
break;
case XMLStreamConstants.CHARACTERS:
case XMLStreamConstants.SPACE:
case XMLStreamConstants.CDATA:
handleCharacters();
break;
case XMLStreamConstants.START_DOCUMENT:
handleStartDocument();
break;
case XMLStreamConstants.END_DOCUMENT:
handleEndDocument();
documentEnded = true;
break;
}
if (reader.hasNext()) {
reader.next();
}
else {
break;
}
}
if (!documentEnded) {
handleEndDocument();
}
}
private void handleCharacters() throws SAXException {
if (getContentHandler() != null) {
if (reader.isWhiteSpace()) {
getContentHandler()
.ignorableWhitespace(reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
}
else {
getContentHandler()
.characters(reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
}
}
}
private void handleEndDocument() throws SAXException {
if (getContentHandler() != null) {
getContentHandler().endDocument();
}
}
private void handleEndElement() throws SAXException {
if (getContentHandler() != null) {
QName qName = reader.getName();
getContentHandler()
.endElement(qName.getNamespaceURI(), qName.getLocalPart(), QNameUtils.toQualifiedName(qName));
for (int i = 0; i < reader.getNamespaceCount(); i++) {
String prefix = reader.getNamespacePrefix(i);
if (prefix == null) {
prefix = "";
}
getContentHandler().endPrefixMapping(prefix);
}
}
}
private void handleProcessingInstruction() throws SAXException {
if (getContentHandler() != null) {
getContentHandler().processingInstruction(reader.getPITarget(), reader.getPIData());
}
}
private void handleStartDocument() throws SAXException {
setLocator(reader.getLocation());
if (getContentHandler() != null) {
getContentHandler().startDocument();
}
}
private void handleStartElement() throws SAXException {
if (getContentHandler() != null) {
for (int i = 0; i < reader.getNamespaceCount(); i++) {
String prefix = reader.getNamespacePrefix(i);
if (prefix == null) {
prefix = "";
}
getContentHandler().startPrefixMapping(prefix, reader.getNamespaceURI(i));
}
QName qName = reader.getName();
getContentHandler().startElement(qName.getNamespaceURI(),
qName.getLocalPart(),
QNameUtils.toQualifiedName(qName),
getAttributes());
}
}
private Attributes getAttributes() {
AttributesImpl attributes = new AttributesImpl();
for (int i = 0; i < reader.getAttributeCount(); i++) {
String namespace = reader.getAttributeNamespace(i);
if (namespace == null) {
namespace = "";
}
String type = reader.getAttributeType(i);
if (type == null) {
type = "";
}
attributes.addAttribute(namespace,
reader.getAttributeLocalName(i),
QNameUtils.toQualifiedName(reader.getAttributeName(i)),
type,
reader.getAttributeValue(i));
}
return attributes;
}
}

View File

@@ -0,0 +1,215 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.stream;
import javax.xml.stream.Location;
import javax.xml.stream.XMLStreamException;
import org.xml.sax.ContentHandler;
import org.xml.sax.DTDHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
/**
* Abstract base class for SAX <code>XMLReader</code> implementations that use StAX as a basis.
*
* @author Arjen Poutsma
* @see #setContentHandler(org.xml.sax.ContentHandler)
* @see #setDTDHandler(org.xml.sax.DTDHandler)
* @see #setEntityResolver(org.xml.sax.EntityResolver)
* @see #setErrorHandler(org.xml.sax.ErrorHandler)
*/
public abstract class StaxXmlReader implements XMLReader {
private DTDHandler dtdHandler;
private ContentHandler contentHandler;
private EntityResolver entityResolver;
private ErrorHandler errorHandler;
public ContentHandler getContentHandler() {
return contentHandler;
}
public void setContentHandler(ContentHandler contentHandler) {
this.contentHandler = contentHandler;
}
public DTDHandler getDtdHandler() {
return dtdHandler;
}
public void setDtdHandler(DTDHandler dtdHandler) {
this.dtdHandler = dtdHandler;
}
public EntityResolver getEntityResolver() {
return entityResolver;
}
public void setEntityResolver(EntityResolver entityResolver) {
this.entityResolver = entityResolver;
}
public ErrorHandler getErrorHandler() {
return errorHandler;
}
public void setErrorHandler(ErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}
/**
* Throws a <code>SAXNotRecognizedException</code> exception.
*
* @throws SAXNotRecognizedException always
*/
public boolean getFeature(String name) throws SAXNotRecognizedException {
throw new SAXNotRecognizedException(name);
}
/**
* Throws a <code>SAXNotRecognizedException</code> exception.
*
* @throws SAXNotRecognizedException always
*/
public void setFeature(String name, boolean value) throws SAXNotRecognizedException {
throw new SAXNotRecognizedException(name);
}
/**
* Throws a <code>SAXNotRecognizedException</code> exception.
*
* @throws SAXNotRecognizedException always
*/
public Object getProperty(String name) throws SAXNotRecognizedException {
throw new SAXNotRecognizedException(name);
}
/**
* Throws a <code>SAXNotRecognizedException</code> exception.
*
* @throws SAXNotRecognizedException always
*/
public void setProperty(String name, Object value) throws SAXNotRecognizedException {
throw new SAXNotRecognizedException(name);
}
public void setDTDHandler(DTDHandler dtdHandler) {
this.setDtdHandler(dtdHandler);
}
public DTDHandler getDTDHandler() {
return getDtdHandler();
}
/**
* Parses the StAX XML reader passed at construction-time.
* <p/>
* <strong>Note</strong> that the given <code>InputSource</code> is not read, but ignored.
*
* @param ignored is ignored
* @throws SAXException A SAX exception, possibly wrapping a <code>XMLStreamException</code>
*/
public final void parse(InputSource ignored) throws SAXException {
parse();
}
/**
* Parses the StAX XML reader passed at construction-time.
* <p/>
* <strong>Note</strong> that the given system identifier is not read, but ignored.
*
* @param ignored is ignored
* @throws SAXException A SAX exception, possibly wrapping a <code>XMLStreamException</code>
*/
public final void parse(String ignored) throws SAXException {
parse();
}
private void parse() throws SAXException {
try {
parseInternal();
}
catch (XMLStreamException ex) {
SAXParseException saxException = new SAXParseException(ex.getMessage(), null, null,
ex.getLocation().getLineNumber(), ex.getLocation().getColumnNumber(), ex);
if (errorHandler != null) {
errorHandler.fatalError(saxException);
}
else {
throw saxException;
}
}
}
/**
* Sets the SAX <code>Locator</code> based on the given StAX <code>Location</code>.
*
* @param location the location
* @see ContentHandler#setDocumentLocator(org.xml.sax.Locator)
*/
protected void setLocator(Location location) {
if (contentHandler != null) {
contentHandler.setDocumentLocator(new StaxLocator(location));
}
}
/**
* Template-method that parses the StAX reader passed at construction-time.
*/
protected abstract void parseInternal() throws SAXException, XMLStreamException;
/**
* Implementation of the <code>Locator</code> interface that is based on a StAX <code>Location</code>.
*
* @see Locator
* @see Location
*/
private static class StaxLocator implements Locator {
private Location location;
protected StaxLocator(Location location) {
this.location = location;
}
public String getPublicId() {
return location.getPublicId();
}
public String getSystemId() {
return location.getSystemId();
}
public int getLineNumber() {
return location.getLineNumber();
}
public int getColumnNumber() {
return location.getColumnNumber();
}
}
}

View File

@@ -0,0 +1,253 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.stream;
import java.util.Iterator;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.Location;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.Namespace;
import javax.xml.stream.events.ProcessingInstruction;
import javax.xml.stream.events.StartDocument;
import javax.xml.stream.events.XMLEvent;
/**
* Implementation of the <code>XMLStreamReader</code> interface that wraps a <code>XMLEventReader</code>. Useful,
* because the StAX <code>XMLInputFactory</code> allows one to create a event reader from a stream reader, but not
* vice-versa.
*
* @author Arjen Poutsma
*/
public class XmlEventStreamReader extends AbstractXmlStreamReader {
private XMLEvent event;
private final XMLEventReader eventReader;
public XmlEventStreamReader(XMLEventReader eventReader) throws XMLStreamException {
this.eventReader = eventReader;
event = eventReader.nextEvent();
}
public boolean isStandalone() {
if (event.isStartDocument()) {
return ((StartDocument) event).isStandalone();
}
else {
throw new IllegalStateException();
}
}
public String getVersion() {
if (event.isStartDocument()) {
return ((StartDocument) event).getVersion();
}
else {
throw new IllegalStateException();
}
}
public int getTextStart() {
return 0;
}
public String getText() {
if (event.isCharacters()) {
return event.asCharacters().getData();
}
else {
throw new IllegalStateException();
}
}
public String getPITarget() {
if (event.isProcessingInstruction()) {
return ((ProcessingInstruction) event).getTarget();
}
else {
throw new IllegalStateException();
}
}
public String getPIData() {
if (event.isProcessingInstruction()) {
return ((ProcessingInstruction) event).getData();
}
else {
throw new IllegalStateException();
}
}
public int getNamespaceCount() {
Iterator namespaces;
if (event.isStartElement()) {
namespaces = event.asStartElement().getNamespaces();
}
else if (event.isEndElement()) {
namespaces = event.asEndElement().getNamespaces();
}
else {
throw new IllegalStateException();
}
return countIterator(namespaces);
}
public NamespaceContext getNamespaceContext() {
if (event.isStartElement()) {
return event.asStartElement().getNamespaceContext();
}
else {
throw new IllegalStateException();
}
}
public QName getName() {
if (event.isStartElement()) {
return event.asStartElement().getName();
}
else if (event.isEndElement()) {
return event.asEndElement().getName();
}
else {
throw new IllegalStateException();
}
}
public Location getLocation() {
return event.getLocation();
}
public int getEventType() {
return event.getEventType();
}
public String getEncoding() {
return null;
}
public String getCharacterEncodingScheme() {
return null;
}
public int getAttributeCount() {
if (!event.isStartElement()) {
throw new IllegalStateException();
}
Iterator attributes = event.asStartElement().getAttributes();
return countIterator(attributes);
}
public void close() throws XMLStreamException {
eventReader.close();
}
public QName getAttributeName(int index) {
return getAttribute(index).getName();
}
public String getAttributeType(int index) {
return getAttribute(index).getDTDType();
}
public String getAttributeValue(int index) {
return getAttribute(index).getValue();
}
public String getNamespacePrefix(int index) {
return getNamespace(index).getPrefix();
}
public String getNamespaceURI(int index) {
return getNamespace(index).getNamespaceURI();
}
public Object getProperty(String name) throws IllegalArgumentException {
return eventReader.getProperty(name);
}
public boolean isAttributeSpecified(int index) {
return getAttribute(index).isSpecified();
}
public int next() throws XMLStreamException {
event = eventReader.nextEvent();
return event.getEventType();
}
public boolean standaloneSet() {
if (event.isStartDocument()) {
return ((StartDocument) event).standaloneSet();
}
else {
throw new IllegalStateException();
}
}
private int countIterator(Iterator iterator) {
int count = 0;
while (iterator.hasNext()) {
iterator.next();
count++;
}
return count;
}
private Attribute getAttribute(int index) {
if (!event.isStartElement()) {
throw new IllegalStateException();
}
int count = 0;
Iterator attributes = event.asStartElement().getAttributes();
while (attributes.hasNext()) {
Attribute attribute = (Attribute) attributes.next();
if (count == index) {
return attribute;
}
else {
count++;
}
}
throw new IllegalArgumentException();
}
private Namespace getNamespace(int index) {
Iterator namespaces;
if (event.isStartElement()) {
namespaces = event.asStartElement().getNamespaces();
}
else if (event.isEndElement()) {
namespaces = event.asEndElement().getNamespaces();
}
else {
throw new IllegalStateException();
}
int count = 0;
while (namespaces.hasNext()) {
Namespace namespace = (Namespace) namespaces.next();
if (count == index) {
return namespace;
}
else {
count++;
}
}
throw new IllegalArgumentException();
}
}

View File

@@ -0,0 +1,5 @@
<html>
<body>
Provides classes that help with StAX: the Streaming API for XML. Mostly for internal use by the framework.
</body>
</html>

View File

@@ -0,0 +1,115 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.transform;
import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.sax.SAXResult;
import org.springframework.xml.stream.StaxEventContentHandler;
import org.springframework.xml.stream.StaxStreamContentHandler;
import org.xml.sax.ContentHandler;
/**
* Implementation of the <code>Result</code> tagging interface for StAX writers. Can be constructed with a
* <code>XMLEventConsumer</code> or a <code>XMLStreamWriter</code>.
* <p/>
* This class is necessary because there is no implementation of <code>Source</code> for StaxReaders in JAXP 1.3. There
* will be a <code>StaxResult</code> in JAXP 1.4 (JDK 1.6), and by the time that version is available, this class will
* probably be deprecated.
* <p/>
* Even though <code>StaxResult</code> extends from <code>SAXResult</code>, calling the methods of
* <code>SAXResult</code> is <strong>not supported</strong>. In general, the only supported operation on this class is
* to use the <code>ContentHandler</code> obtained via {@link #getHandler()} to parse an input source using an
* <code>XMLReader</code>. Calling {@link #setHandler(org.xml.sax.ContentHandler)} will result in
* <code>UnsupportedOperationException</code>s.
*
* @author Arjen Poutsma
* @see XMLEventWriter
* @see XMLStreamWriter
* @see javax.xml.transform.Transformer
*/
public class StaxResult extends SAXResult {
private XMLEventWriter eventWriter;
private XMLStreamWriter streamWriter;
/**
* Constructs a new instance of the <code>StaxResult</code> with the specified <code>XMLStreamWriter</code>.
*
* @param streamWriter the <code>XMLStreamWriter</code> to write to
*/
public StaxResult(XMLStreamWriter streamWriter) {
super.setHandler(new StaxStreamContentHandler(streamWriter));
this.streamWriter = streamWriter;
}
/**
* Constructs a new instance of the <code>StaxResult</code> with the specified <code>XMLEventWriter</code>.
*
* @param eventWriter the <code>XMLEventWriter</code> to write to
*/
public StaxResult(XMLEventWriter eventWriter) {
super.setHandler(new StaxEventContentHandler(eventWriter));
this.eventWriter = eventWriter;
}
/**
* Constructs a new instance of the <code>StaxResult</code> with the specified <code>XMLEventWriter</code> and
* <code>XMLEventFactory</code>.
*
* @param eventWriter the <code>XMLEventWriter</code> to write to
* @param eventFactory the <code>XMLEventFactory</code> to use for creating events
*/
public StaxResult(XMLEventWriter eventWriter, XMLEventFactory eventFactory) {
super.setHandler(new StaxEventContentHandler(eventWriter, eventFactory));
this.eventWriter = eventWriter;
}
/**
* Returns the <code>XMLEventWriter</code> used by this <code>StaxResult</code>. If this <code>StaxResult</code> was
* created with an <code>XMLStreamWriter</code>, the result will be <code>null</code>.
*
* @return the StAX event writer used by this result
* @see #StaxResult(javax.xml.stream.XMLEventWriter)
*/
public XMLEventWriter getXMLEventWriter() {
return eventWriter;
}
/**
* Returns the <code>XMLStreamWriter</code> used by this <code>StaxResult</code>. If this <code>StaxResult</code>
* was created with an <code>XMLEventConsumer</code>, the result will be <code>null</code>.
*
* @return the StAX stream writer used by this result
* @see #StaxResult(javax.xml.stream.XMLStreamWriter)
*/
public XMLStreamWriter getXMLStreamWriter() {
return streamWriter;
}
/**
* Throws a <code>UnsupportedOperationException</code>.
*
* @throws UnsupportedOperationException always
*/
public void setHandler(ContentHandler handler) {
throw new UnsupportedOperationException("setHandler is not supported");
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.transform;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.sax.SAXSource;
import org.springframework.xml.stream.StaxEventXmlReader;
import org.springframework.xml.stream.StaxStreamXmlReader;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
/**
* Implementation of the <code>Source</code> tagging interface for StAX readers. Can be constructed with a
* <code>XMLEventReader</code> or a <code>XMLStreamReader</code>.
* <p/>
* This class is necessary because there is no implementation of <code>Source</code> for StaxReaders in JAXP 1.3. There
* will be a <code>StaxSource</code> in JAXP 1.4 (JDK 1.6), and by the time that version is available, this class will
* probably be deprecated.
* <p/>
* Even though <code>StaxSource</code> extends from <code>SAXSource</code>, calling the methods of
* <code>SAXSource</code> is <strong>not supported</strong>. In general, the only supported operation on this class is
* to use the <code>XMLReader</code> obtained via {@link #getXMLReader()} to parse the input source obtained via {@link
* #getInputSource()}. Calling {@link #setXMLReader(org.xml.sax.XMLReader)} or {@link
* #setInputSource(org.xml.sax.InputSource)} will result in <code>UnsupportedOperationException</code>s.
*
* @author Arjen Poutsma
* @see XMLEventReader
* @see XMLStreamReader
* @see javax.xml.transform.Transformer
*/
public class StaxSource extends SAXSource {
private XMLEventReader eventReader;
private XMLStreamReader streamReader;
/**
* Constructs a new instance of the <code>StaxSource</code> with the specified <code>XMLStreamReader</code>. The
* supplied stream reader must be in <code>XMLStreamConstants.START_DOCUMENT</code> or
* <code>XMLStreamConstants.START_ELEMENT</code> state.
*
* @param streamReader the <code>XMLStreamReader</code> to read from
* @throws IllegalStateException if the reader is not at the start of a document or element
*/
public StaxSource(XMLStreamReader streamReader) {
super(new StaxStreamXmlReader(streamReader), new InputSource());
this.streamReader = streamReader;
}
/**
* Constructs a new instance of the <code>StaxSource</code> with the specified <code>XMLEventReader</code>. The
* supplied event reader must be in <code>XMLStreamConstants.START_DOCUMENT</code> or
* <code>XMLStreamConstants.START_ELEMENT</code> state.
*
* @param eventReader the <code>XMLEventReader</code> to read from
* @throws IllegalStateException if the reader is not at the start of a document or element
*/
public StaxSource(XMLEventReader eventReader) {
super(new StaxEventXmlReader(eventReader), new InputSource());
this.eventReader = eventReader;
}
/**
* Returns the <code>XMLEventReader</code> used by this <code>StaxSource</code>. If this <code>StaxSource</code> was
* created with an <code>XMLStreamReader</code>, the result will be <code>null</code>.
*
* @return the StAX event reader used by this source
* @see StaxSource#StaxSource(javax.xml.stream.XMLEventReader)
*/
public XMLEventReader getXMLEventReader() {
return eventReader;
}
/**
* Returns the <code>XMLStreamReader</code> used by this <code>StaxSource</code>. If this <code>StaxSource</code>
* was created with an <code>XMLEventReader</code>, the result will be <code>null</code>.
*
* @return the StAX event reader used by this source
* @see StaxSource#StaxSource(javax.xml.stream.XMLEventReader)
*/
public XMLStreamReader getXMLStreamReader() {
return streamReader;
}
/**
* Throws a <code>UnsupportedOperationException</code>.
*
* @throws UnsupportedOperationException always
*/
public void setInputSource(InputSource inputSource) {
throw new UnsupportedOperationException("setInputSource is not supported");
}
/**
* Throws a <code>UnsupportedOperationException</code>.
*
* @throws UnsupportedOperationException always
*/
public void setXMLReader(XMLReader reader) {
throw new UnsupportedOperationException("setXMLReader is not supported");
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.transform;
import java.io.StringWriter;
import javax.xml.transform.stream.StreamResult;
/**
* Convenient subclass of <code>StreamResult</code> that writes to a <code>StringWriter</code>. The resulting string can
* be retrieved via <code>toString()</code>.
*
* @author Arjen Poutsma
* @see #toString()
*/
public class StringResult extends StreamResult {
public StringResult() {
super(new StringWriter());
}
/**
* Returns the written XML as a string.
*/
public String toString() {
return getWriter().toString();
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.transform;
import java.io.StringReader;
import javax.xml.transform.stream.StreamSource;
/**
* Convenient subclass of <code>StreamSource</code> that reads from a <code>StringReader</code>. The string to be read
* can be set via the constructor.
*
* @author Arjen Poutsma
*/
public class StringSource extends StreamSource {
/**
* Initializes a new instance of the <code>StringSource</code> with the given string content.
*
* @param content the content
*/
public StringSource(String content) {
super(new StringReader(content));
}
/**
* Initializes a new instance of the <code>StringSource</code> with the given string content and system id.
*
* @param content the content
* @param systemId a string that conforms to the URI syntax
*/
public StringSource(String content, String systemId) {
super(new StringReader(content), systemId);
}
}

View File

@@ -0,0 +1,5 @@
<html>
<body>
Provides classes that help with XML transformations. Mostly for internal use by the framework.
</body>
</html>

View File

@@ -0,0 +1,179 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.validation;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.springframework.core.io.Resource;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
/**
* Internal class that uses JAXP 1.0 features to create <code>XmlValidator</code> instances.
*
* @author Arjen Poutsma
*/
abstract class Jaxp10ValidatorFactory {
private static final String SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
private static final String SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
static XmlValidator createValidator(Resource[] schemaResources, String schemaLanguage) throws IOException {
InputSource[] inputSources = new InputSource[schemaResources.length];
for (int i = 0; i < schemaResources.length; i++) {
inputSources[i] = new InputSource(schemaResources[i].getInputStream());
inputSources[i].setSystemId(SchemaLoaderUtils.getSystemId(schemaResources[i]));
}
return new Jaxp10Validator(inputSources, schemaLanguage);
}
private static class Jaxp10Validator implements XmlValidator {
private SAXParserFactory parserFactory;
private TransformerFactory transformerFactory;
private InputSource[] schemaInputSources;
private String schemaLanguage;
private Jaxp10Validator(InputSource[] schemaInputSources, String schemaLanguage) {
this.schemaInputSources = schemaInputSources;
this.schemaLanguage = schemaLanguage;
transformerFactory = TransformerFactory.newInstance();
parserFactory = SAXParserFactory.newInstance();
parserFactory.setNamespaceAware(true);
parserFactory.setValidating(true);
}
public SAXParseException[] validate(Source source) throws IOException {
SAXParser parser = createSAXParser();
ValidationErrorHandler errorHandler = new ValidationErrorHandler();
try {
if (source instanceof SAXSource) {
validateSAXSource((SAXSource) source, parser, errorHandler);
}
else if (source instanceof StreamSource) {
validateStreamSource((StreamSource) source, parser, errorHandler);
}
else if (source instanceof DOMSource) {
validateDOMSource((DOMSource) source, parser, errorHandler);
}
else {
throw new IllegalArgumentException("Source [" + source.getClass().getName() +
"] is neither SAXSource, DOMSource, nor StreamSource");
}
return errorHandler.getErrors();
}
catch (SAXException ex) {
throw new XmlValidationException("Could not validate source: " + ex.getMessage(), ex);
}
}
private void validateDOMSource(DOMSource domSource, SAXParser parser, ValidationErrorHandler errorHandler)
throws IOException, SAXException {
try {
// Sadly, JAXP 1.0 DOM doesn't implement DOM level 3, so we cannot use Document.normalizeDocument()
// Instead, we write the Document to a Stream, and validate that
Transformer transformer = transformerFactory.newTransformer();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
transformer.transform(domSource, new StreamResult(outputStream));
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
validateStreamSource(new StreamSource(inputStream), parser, errorHandler);
}
catch (TransformerException ex) {
throw new XmlValidationException("Could not validate DOM source: " + ex.getMessage(), ex);
}
}
private void validateStreamSource(StreamSource streamSource,
SAXParser parser,
ValidationErrorHandler errorHandler) throws SAXException, IOException {
if (streamSource.getInputStream() != null) {
parser.parse(streamSource.getInputStream(), errorHandler);
}
else if (streamSource.getReader() != null) {
parser.parse(new InputSource(streamSource.getReader()), errorHandler);
}
else {
throw new IllegalArgumentException("StreamSource contains neither InputStream nor Reader");
}
}
private void validateSAXSource(SAXSource source, SAXParser parser, ValidationErrorHandler errorHandler)
throws SAXException, IOException {
parser.parse(source.getInputSource(), errorHandler);
}
private SAXParser createSAXParser() {
try {
SAXParser parser = parserFactory.newSAXParser();
parser.setProperty(SCHEMA_LANGUAGE, schemaLanguage);
parser.setProperty(SCHEMA_SOURCE, schemaInputSources);
return parser;
}
catch (ParserConfigurationException ex) {
throw new XmlValidationException("Could not create SAXParser: " + ex.getMessage(), ex);
}
catch (SAXException ex) {
throw new XmlValidationException("Could not create SAXParser: " + ex.getMessage(), ex);
}
}
}
/**
* <code>DefaultHandler</code> extension that stores errors and fatal errors in a list.
*/
private static class ValidationErrorHandler extends DefaultHandler {
private List errors = new ArrayList();
private SAXParseException[] getErrors() {
return (SAXParseException[]) errors.toArray(new SAXParseException[errors.size()]);
}
public void warning(SAXParseException ex) throws SAXException {
}
public void error(SAXParseException ex) throws SAXException {
errors.add(ex);
}
public void fatalError(SAXParseException ex) throws SAXException {
errors.add(ex);
}
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.validation;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.xml.transform.Source;
import javax.xml.validation.Schema;
import org.springframework.core.io.Resource;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/**
* Internal class that uses JAXP 1.0 features to create <code>XmlValidator</code> instances.
*
* @author Arjen Poutsma
*/
abstract class Jaxp13ValidatorFactory {
static XmlValidator createValidator(Resource[] resources, String schemaLanguage) throws IOException {
try {
Schema schema = SchemaLoaderUtils.loadSchema(resources, schemaLanguage);
return new Jaxp13Validator(schema);
}
catch (SAXException ex) {
throw new XmlValidationException("Could not create Schema: " + ex.getMessage(), ex);
}
}
private static class Jaxp13Validator implements XmlValidator {
private Schema schema;
public Jaxp13Validator(Schema schema) {
this.schema = schema;
}
public SAXParseException[] validate(Source source) throws IOException {
javax.xml.validation.Validator validator = schema.newValidator();
ValidationErrorHandler errorHandler = new ValidationErrorHandler();
validator.setErrorHandler(errorHandler);
try {
validator.validate(source);
return errorHandler.getErrors();
}
catch (SAXException ex) {
throw new XmlValidationException("Could not validate source: " + ex.getMessage(), ex);
}
}
}
/**
* <code>ErrorHandler</code> implementation that stores errors and fatal errors in a list.
*/
private static class ValidationErrorHandler implements ErrorHandler {
private List errors = new ArrayList();
private SAXParseException[] getErrors() {
return (SAXParseException[]) errors.toArray(new SAXParseException[errors.size()]);
}
public void warning(SAXParseException ex) throws SAXException {
}
public void error(SAXParseException ex) throws SAXException {
errors.add(ex);
}
public void fatalError(SAXParseException ex) throws SAXException {
errors.add(ex);
}
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.validation;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.xml.sax.SAXException;
/**
* Convenient utility methods for loading of <code>javax.xml.validation.Schema</code> objects, performing standard
* handling of input streams.
*
* @author Arjen Poutsma
*/
public abstract class SchemaLoaderUtils {
/**
* Load schema from the given resource.
*
* @param resource the resource to load from
* @param schemaLanguage the language of the schema. Can be <code>XMLConstants.W3C_XML_SCHEMA_NS_URI</code> or
* <code>XMLConstants.RELAXNG_NS_URI</code>.
* @throws IOException if loading failed
* @throws SAXException if loading failed
* @see javax.xml.XMLConstants#W3C_XML_SCHEMA_NS_URI
* @see javax.xml.XMLConstants#RELAXNG_NS_URI
*/
public static Schema loadSchema(Resource resource, String schemaLanguage) throws IOException, SAXException {
return loadSchema(new Resource[]{resource}, schemaLanguage);
}
/**
* Load schema from the given resource.
*
* @param resources the resources to load from
* @param schemaLanguage the language of the schema. Can be <code>XMLConstants.W3C_XML_SCHEMA_NS_URI</code> or
* <code>XMLConstants.RELAXNG_NS_URI</code>.
* @throws IOException if loading failed
* @throws SAXException if loading failed
* @see javax.xml.XMLConstants#W3C_XML_SCHEMA_NS_URI
* @see javax.xml.XMLConstants#RELAXNG_NS_URI
*/
public static Schema loadSchema(Resource[] resources, String schemaLanguage) throws IOException, SAXException {
Assert.notEmpty(resources, "No resources given");
Assert.hasLength(schemaLanguage, "No schema language provided");
StreamSource[] schemaSources = new StreamSource[resources.length];
try {
for (int i = 0; i < resources.length; i++) {
Assert.notNull(resources[i], "Resource is null");
Assert.isTrue(resources[i].exists(), "Resource " + resources[i] + " does not exist");
schemaSources[i] = new StreamSource(resources[i].getInputStream(), getSystemId(resources[i]));
}
SchemaFactory schemaFactory = SchemaFactory.newInstance(schemaLanguage);
return schemaFactory.newSchema(schemaSources);
}
finally {
for (int i = 0; i < schemaSources.length; i++) {
if (schemaSources[i] != null) {
InputStream inputStream = schemaSources[i].getInputStream();
if (inputStream != null) {
inputStream.close();
}
}
}
}
}
/**
* Retrieves the URL from the given resource as System ID. Returns <code>null</code> if it cannot be openened.
*/
public static String getSystemId(Resource resource) {
try {
return resource.getURL().toString();
}
catch (IOException e) {
return null;
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.validation;
import org.springframework.xml.XmlException;
/**
* Expection thrown when a validation error occurs
*
* @author Arjen Poutsma
*/
public class XmlValidationException extends XmlException {
public XmlValidationException(String s) {
super(s);
}
public XmlValidationException(String s, Throwable throwable) {
super(s, throwable);
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.validation;
import java.io.IOException;
import javax.xml.transform.Source;
import org.xml.sax.SAXParseException;
/**
* Simple processor that validates a given <code>Source</code>. Can be created via the
* <code>XmlValidatorFactory</code>.
* <p/>
* <code>XmlValidator</code> instances are designed to be thread safe.
*
* @author Arjen Poutsma
* @see XmlValidatorFactory#createValidator(org.springframework.core.io.Resource, String)
*/
public interface XmlValidator {
/**
* Validates the given <code>Source</code>, and returns an array of <code>SAXParseException</code>s as result. The
* array will be empty if no validation errors are found.
*
* @param source the input document
* @return an array of <code>SAXParseException</code>s
* @throws IOException if the <code>source</code> cannot be read
* @throws XmlValidationException if the <code>source</code> cannot be validated
*/
SAXParseException[] validate(Source source) throws IOException;
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.validation;
import java.io.IOException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.xml.JaxpVersion;
/**
* Factory for <code>XmlValidator</code>s, being aware of JAXP 1.3 <code>XmlValidator</code>s, and JAXP 1.0 parsing
* capababilities. Mainly for internal use within the framework.
* <p/>
* The goal of this class is to avoid runtime dependencies on JAXP 1.3 by using the best validation implementation that
* is available. Prefers JAXP 1.3 <code>XmlValidator</code> implementations to a custom, SAX-based implementation.
*
* @author Arjen Poutsma
* @see XmlValidator
*/
public abstract class XmlValidatorFactory {
private static final Log logger = LogFactory.getLog(XmlValidatorFactory.class);
/**
* Constant that defines a W3C XML Schema.
*/
public static final String SCHEMA_W3C_XML = "http://www.w3.org/2001/XMLSchema";
/**
* Constant that defines a RELAX NG Schema.
*/
public static final String SCHEMA_RELAX_NG = "http://relaxng.org/ns/structure/1.0";
/**
* Create a <code>XmlValidator</code> with the given schema resource and schema language type. The schema language
* must be one of the <code>SCHEMA_XXX</code> constants.
*
* @param schemaResource a resource that locates the schema to validate against
* @param schemaLanguage the language of the schema
* @return a validator
* @throws IOException if the schema resource cannot be read
* @throws IllegalArgumentException if the schema language is not supported
* @throws IllegalStateException if JAXP 1.0 cannot be located
* @throws XmlValidationException if a <code>XmlValidator</code> cannot be created
* @see #SCHEMA_RELAX_NG
* @see #SCHEMA_W3C_XML
*/
public static XmlValidator createValidator(Resource schemaResource, String schemaLanguage) throws IOException {
return createValidator(new Resource[]{schemaResource}, schemaLanguage);
}
/**
* Create a <code>XmlValidator</code> with the given schema resources and schema language type. The schema language
* must be one of the <code>SCHEMA_XXX</code> constants.
*
* @param schemaResources an array of resource that locate the schemas to validate against
* @param schemaLanguage the language of the schemas
* @return a validator
* @throws IOException if the schema resource cannot be read
* @throws IllegalArgumentException if the schema language is not supported
* @throws IllegalStateException if JAXP 1.0 cannot be located
* @throws XmlValidationException if a <code>XmlValidator</code> cannot be created
* @see #SCHEMA_RELAX_NG
* @see #SCHEMA_W3C_XML
*/
public static XmlValidator createValidator(Resource[] schemaResources, String schemaLanguage) throws IOException {
Assert.notEmpty(schemaResources, "No resources given");
Assert.hasLength(schemaLanguage, "No schema language provided");
Assert.isTrue(SCHEMA_W3C_XML.equals(schemaLanguage) || SCHEMA_RELAX_NG.equals(schemaLanguage),
"Invalid schema language: " + schemaLanguage);
for (int i = 0; i < schemaResources.length; i++) {
Assert.isTrue(schemaResources[i].exists(), "schema [" + schemaResources + "] does not exist");
}
if (JaxpVersion.getJaxpVersion() >= JaxpVersion.JAXP_13) {
logger.debug("Creating JAXP 1.3 XmlValidator");
return Jaxp13ValidatorFactory.createValidator(schemaResources, schemaLanguage);
}
else if (JaxpVersion.getJaxpVersion() >= JaxpVersion.JAXP_10) {
logger.debug("Creating JAXP 1.0 XmlValidator");
return Jaxp10ValidatorFactory.createValidator(schemaResources, schemaLanguage);
}
else {
throw new IllegalStateException("Could not locate JAXP 1.0 or higher.");
}
}
}

View File

@@ -0,0 +1,5 @@
<html>
<body>
Provides classes for XML validation in JAXP 1.0 and JAXP 1.3. Mostly for internal use by the framework.
</body>
</html>

View File

@@ -0,0 +1,131 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.xpath;
import java.util.List;
import java.util.Map;
import org.jaxen.JaxenException;
import org.jaxen.SimpleNamespaceContext;
import org.jaxen.XPath;
import org.jaxen.dom.DOMXPath;
import org.w3c.dom.Node;
/**
* Jaxen-specific factory for creating <code>XPathExpression</code>s.
*
* @author Arjen Poutsma
* @see #createXPathExpression(String)
*/
abstract class JaxenXPathExpressionFactory {
/**
* Creates a Jaxen <code>XPathExpression</code> from the given string expression.
*
* @param expression the XPath expression
* @return the compiled <code>XPathExpression</code>
* @throws XPathParseException when the given expression cannot be parsed
*/
static XPathExpression createXPathExpression(String expression) {
try {
XPath xpath = new DOMXPath(expression);
return new JaxenXpathExpression(xpath);
}
catch (JaxenException ex) {
throw new org.springframework.xml.xpath.XPathParseException(
"Could not compile [" + expression + "] to a XPathExpression: " + ex.getMessage(), ex);
}
}
/**
* Creates a Jaxen <code>XPathExpression</code> from the given string expression and prefixes.
*
* @param expression the XPath expression
* @param namespaces the namespaces
* @return the compiled <code>XPathExpression</code>
* @throws XPathParseException when the given expression cannot be parsed
*/
public static XPathExpression createXPathExpression(String expression, Map namespaces) {
try {
XPath xpath = new DOMXPath(expression);
xpath.setNamespaceContext(new SimpleNamespaceContext(namespaces));
return new JaxenXpathExpression(xpath);
}
catch (JaxenException ex) {
throw new org.springframework.xml.xpath.XPathParseException(
"Could not compile [" + expression + "] to a XPathExpression: " + ex.getMessage(), ex);
}
}
/**
* Jaxen implementation of the <code>XPathExpression</code> interface.
*/
private static class JaxenXpathExpression implements XPathExpression {
private XPath xpath;
private JaxenXpathExpression(XPath xpath) {
this.xpath = xpath;
}
public Node evaluateAsNode(Node node) {
try {
return (Node) xpath.selectSingleNode(node);
}
catch (JaxenException ex) {
throw new XPathException("Could not evaluate XPath expression [" + xpath + "] :" + ex.getMessage(), ex);
}
}
public boolean evaluateAsBoolean(Node node) {
try {
return xpath.booleanValueOf(node);
}
catch (JaxenException ex) {
throw new XPathException("Could not evaluate XPath expression [" + xpath + "] :" + ex.getMessage(), ex);
}
}
public double evaluateAsNumber(Node node) {
try {
return xpath.numberValueOf(node).doubleValue();
}
catch (JaxenException ex) {
throw new XPathException("Could not evaluate XPath expression [" + xpath + "] :" + ex.getMessage(), ex);
}
}
public String evaluateAsString(Node node) {
try {
return xpath.stringValueOf(node);
}
catch (JaxenException ex) {
throw new XPathException("Could not evaluate XPath expression [" + xpath + "] :" + ex.getMessage(), ex);
}
}
public Node[] evaluateAsNodes(Node node) {
try {
List result = xpath.selectNodes(node);
return (Node[]) result.toArray(new Node[result.size()]);
}
catch (JaxenException ex) {
throw new XPathException("Could not evaluate XPath expression [" + xpath + "] :" + ex.getMessage(), ex);
}
}
}
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.xpath;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.springframework.xml.namespace.SimpleNamespaceContext;
/**
* JAXP 1.3-specific factory creating <code>XPathExpression</code>s.
*
* @author Arjen Poutsma
* @see #createXPathExpression(String)
*/
abstract class Jaxp13XPathExpressionFactory {
private static XPathFactory xpathFactory;
static {
xpathFactory = XPathFactory.newInstance();
}
/**
* Creates a JAXP 1.3 <code>XPathExpression</code> from the given string expression.
*
* @param expression the XPath expression
* @return the compiled <code>XPathExpression</code>
* @throws XPathParseException when the given expression cannot be parsed
*/
static XPathExpression createXPathExpression(String expression) {
try {
XPath xpath = xpathFactory.newXPath();
javax.xml.xpath.XPathExpression xpathExpression = xpath.compile(expression);
return new Jaxp13XPathExpression(xpathExpression);
}
catch (XPathExpressionException ex) {
throw new org.springframework.xml.xpath.XPathParseException(
"Could not compile [" + expression + "] to a XPathExpression: " + ex.getMessage(), ex);
}
}
/**
* Creates a JAXP 1.3 <code>XPathExpression</code> from the given string expression and namespaces.
*
* @param expression the XPath expression
* @param namespaces the namespaces
* @return the compiled <code>XPathExpression</code>
* @throws XPathParseException when the given expression cannot be parsed
*/
public static XPathExpression createXPathExpression(String expression, Map namespaces) {
try {
XPath xpath = xpathFactory.newXPath();
SimpleNamespaceContext namespaceContext = new SimpleNamespaceContext();
namespaceContext.setBindings(namespaces);
xpath.setNamespaceContext(namespaceContext);
javax.xml.xpath.XPathExpression xpathExpression = xpath.compile(expression);
return new Jaxp13XPathExpression(xpathExpression);
}
catch (XPathExpressionException ex) {
throw new org.springframework.xml.xpath.XPathParseException(
"Could not compile [" + expression + "] to a XPathExpression: " + ex.getMessage(), ex);
}
}
/**
* JAXP 1.3 implementation of the <code>XPathExpression</code> interface.
*/
private static class Jaxp13XPathExpression implements XPathExpression {
javax.xml.xpath.XPathExpression xpathExpression;
private Jaxp13XPathExpression(javax.xml.xpath.XPathExpression xpathExpression) {
this.xpathExpression = xpathExpression;
}
public String evaluateAsString(Node node) {
return (String) evaluate(node, XPathConstants.STRING);
}
public Node[] evaluateAsNodes(Node node) {
NodeList nodeList = (NodeList) evaluate(node, XPathConstants.NODESET);
return toNodeArray(nodeList);
}
private Object evaluate(Node node, QName returnType) {
try {
return xpathExpression.evaluate(node, returnType);
}
catch (XPathExpressionException ex) {
throw new XPathException("Could not evaluate XPath expression:" + ex.getMessage(), ex);
}
}
private Node[] toNodeArray(NodeList nodeList) {
Node[] result = new Node[nodeList.getLength()];
for (int i = 0; i < nodeList.getLength(); i++) {
result[i] = nodeList.item(i);
}
return result;
}
public double evaluateAsNumber(Node node) {
Double result = (Double) evaluate(node, XPathConstants.NUMBER);
return result.doubleValue();
}
public boolean evaluateAsBoolean(Node node) {
Boolean result = (Boolean) evaluate(node, XPathConstants.BOOLEAN);
return result.booleanValue();
}
public Node evaluateAsNode(Node node) {
return (Node) evaluate(node, XPathConstants.NODE);
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.xpath;
import org.springframework.xml.XmlException;
/**
* Exception thrown when an error occurs during XPath processing.
*
* @author Arjen Poutsma
*/
public class XPathException extends XmlException {
/**
* Constructs a new instance of the <code>XPathException</code> with the specific detail message.
*
* @param message the detail message
*/
public XPathException(String message) {
super(message);
}
/**
* Constructs a new instance of the <code>XPathException</code> with the specific detail message and exception.
*
* @param message the detail message
* @param throwable the wrapped exception
*/
public XPathException(String message, Throwable throwable) {
super(message, throwable);
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.xpath;
import org.w3c.dom.Node;
/**
* Defines the contract for a precompiled XPath expression. Concrete instances can be obtained through the
* <code>XPathExpressionFactory</code>.
*
* @author Arjen Poutsma
* @see XPathExpressionFactory
*/
public interface XPathExpression {
/**
* Evaluates the given expression as a <code>Node</code>. Returns the evaluation of the expression, or
* <code>null</code> if it is invalid.
*
* @param node the starting point
* @return the result of the evaluation
*/
Node evaluateAsNode(Node node);
/**
* Evaluates the given expression as a <code>boolean</code>. Returns the boolean evaluation of the expression, or
* <code>false</code> if it is invalid.
*
* @param node the starting point
* @return the result of the evaluation
*/
boolean evaluateAsBoolean(Node node);
/**
* Evaluates the given expression as a number (<code>double</code>). Returns the numeric evaluation of the
* expression, or <code>Double.NaN</code> if it is invalid.
*
* @param node the starting point
* @return the result of the evaluation
* @see Double#NaN
*/
double evaluateAsNumber(Node node);
/**
* Evaluates the given expression, and returns the first <code>Node</code> that conforms to it. Returns
* <code>null</code> if no result could be found.
*
* @param node the starting point
* @return the first <code>Node</code> that is selected by the expression
*/
String evaluateAsString(Node node);
/**
* Evaluates the given expression, and returns all <code>Node</code>s that conform to it. Returns and empty array if
* no result could be found.
*
* @param node the starting point
* @return the <code>Node</code>s that are selected by the expression
*/
Node[] evaluateAsNodes(Node node);
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.xpath;
import java.util.Collections;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.xml.JaxpVersion;
/**
* Factory for compiled <code>XPathExpression</code>s, being aware of JAXP 1.3+ XPath functionality, Jaxen, and Xalan.
* Mainly for internal use of the framework.
* <p/>
* The goal of this class is to avoid runtime dependencies a specific XPath engine, simply using the best XPath
* implementation that is available. Prefers JAXP 1.3+ XPath implementations to Jaxen, and falls back to Xalan.
*
* @author Arjen Poutsma
* @see XPathExpression
*/
public abstract class XPathExpressionFactory {
private static final Log logger = LogFactory.getLog(XPathExpressionFactory.class);
private static final String XALAN_XPATH_CLASS_NAME = "org.apache.xpath.XPath";
private static boolean xalanXPathAvailable;
private static final String JAXEN_CLASS_NAME = "org.jaxen.XPath";
private static boolean jaxenAvailable;
static {
// Check whether JAXP 1.3, Resin, Jaxen, or Xalan are available
if (JaxpVersion.getJaxpVersion() >= JaxpVersion.JAXP_13) {
logger.info("JAXP 1.3 available");
}
try {
Class.forName(JAXEN_CLASS_NAME);
jaxenAvailable = true;
logger.info("Jaxen available");
}
catch (ClassNotFoundException ex) {
jaxenAvailable = false;
}
try {
Class.forName(XALAN_XPATH_CLASS_NAME);
xalanXPathAvailable = true;
logger.info("Xalan available");
}
catch (ClassNotFoundException ex) {
xalanXPathAvailable = false;
}
}
/**
* Create a compiled XPath expression using the given string.
*
* @param expression the XPath expression
* @return the compiled XPath expression
* @throws IllegalStateException if neither JAXP 1.3+, Jaxen, or Xalan are available
* @throws XPathParseException if the given expression cannot be parsed
*/
public static XPathExpression createXPathExpression(String expression)
throws IllegalStateException, XPathParseException {
return createXPathExpression(expression, Collections.EMPTY_MAP);
}
/**
* Create a compiled XPath expression using the given string and namespaces. The namespace map should consist of
* string prefixes mapped to string namespaces.
*
* @param expression the XPath expression
* @param namespaces a map that binds string prefixes to string namespaces
* @return the compiled XPath expression
* @throws IllegalStateException if neither JAXP 1.3+, Jaxen, or Xalan are available
* @throws XPathParseException if the given expression cannot be parsed
*/
public static XPathExpression createXPathExpression(String expression, Map namespaces)
throws IllegalStateException, XPathParseException {
Assert.hasLength(expression, "expression is empty");
if (JaxpVersion.getJaxpVersion() >= JaxpVersion.JAXP_13) {
logger.debug("Creating [javax.xml.xpath.XPathExpression]");
return Jaxp13XPathExpressionFactory.createXPathExpression(expression, namespaces);
}
else if (jaxenAvailable) {
logger.debug("Creating [org.jaxen.XPath]");
return JaxenXPathExpressionFactory.createXPathExpression(expression, namespaces);
}
else if (xalanXPathAvailable) {
logger.debug("Creating [org.apache.xpath.XPath]");
return XalanXPathExpressionFactory.createXPathExpression(expression, namespaces);
}
else {
throw new IllegalStateException(
"Could not create XPathExpression: could not locate JAXP 1.3, Resin, Xalan on the class path");
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.xpath;
/**
* Exception throws when a XPath expression cannot be parsed.
*
* @author Arjen Poutsma
*/
public class XPathParseException extends XPathException {
/**
* Constructs a new instance of the <code>XPathParseException</code> with the specific detail message.
*
* @param message the detail message
*/
public XPathParseException(String message) {
super(message);
}
/**
* Constructs a new instance of the <code>XPathParseException</code> with the specific detail message and
* exception.
*
* @param message the detail message
* @param throwable the wrapped exception
*/
public XPathParseException(String message, Throwable throwable) {
super(message, throwable);
}
}

View File

@@ -0,0 +1,173 @@
/*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.xml.xpath;
import java.util.Map;
import javax.xml.transform.TransformerException;
import org.apache.xml.utils.PrefixResolver;
import org.apache.xpath.XPath;
import org.apache.xpath.XPathContext;
import org.apache.xpath.objects.XObject;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.traversal.NodeIterator;
/**
* Xalan-specific factory creating <code>XPathExpression</code>s.
*
* @author Arjen Poutsma
* @see #createXPathExpression(String)
*/
abstract class XalanXPathExpressionFactory {
/**
* Creates a Xalan <code>XPathExpression</code> from the given string expression.
*
* @param expression the XPath expression
* @return the compiled <code>XPathExpression</code>
* @throws XPathParseException when the given expression cannot be parsed
*/
static XPathExpression createXPathExpression(String expression) {
try {
XPath xpath = new XPath(expression, null, null, XPath.SELECT);
return new XalanXPathExpression(xpath);
}
catch (TransformerException ex) {
throw new org.springframework.xml.xpath.XPathParseException(
"Could not compile [" + expression + "] to a XPathExpression: " + ex.getMessage(), ex);
}
}
/**
* Creates a Xalan <code>XPathExpression</code> from the given string expression and namespaces.
*
* @param expression the XPath expression
* @return the compiled <code>XPathExpression</code>
* @throws XPathParseException when the given expression cannot be parsed
*/
public static XPathExpression createXPathExpression(String expression, Map namespaces) {
try {
PrefixResolver prefixResolver = new SimplePrefixResolver(namespaces);
XPath xpath = new XPath(expression, null, prefixResolver, XPath.SELECT);
return new XalanXPathExpression(xpath);
}
catch (TransformerException ex) {
throw new org.springframework.xml.xpath.XPathParseException(
"Could not compile [" + expression + "] to a XPathExpression: " + ex.getMessage(), ex);
}
}
/**
* Xalan implementation of the <code>XPathExpression</code> interface.
*/
private static class XalanXPathExpression implements XPathExpression {
private XPath xpath;
private XPathContext context = new XPathContext();
private XalanXPathExpression(XPath xpath) {
this.xpath = xpath;
}
public Node evaluateAsNode(Node node) {
try {
XObject result = xpath.execute(context, node, null);
NodeIterator iterator = result.nodeset();
return iterator.nextNode();
}
catch (TransformerException ex) {
throw new XPathException("Could not evaluate XPath expression:" + ex.getMessage(), ex);
}
}
public boolean evaluateAsBoolean(Node node) {
try {
XObject result = xpath.execute(context, node, null);
return result.bool();
}
catch (TransformerException ex) {
throw new XPathException("Could not evaluate XPath expression:" + ex.getMessage(), ex);
}
}
public double evaluateAsNumber(Node node) {
try {
XObject result = xpath.execute(context, node, null);
return result.num();
}
catch (TransformerException ex) {
throw new XPathException("Could not evaluate XPath expression:" + ex.getMessage(), ex);
}
}
public String evaluateAsString(Node node) {
try {
XObject result = xpath.execute(context, node, null);
return result.str();
}
catch (TransformerException ex) {
throw new XPathException("Could not evaluate XPath expression:" + ex.getMessage(), ex);
}
}
public Node[] evaluateAsNodes(Node node) {
try {
XObject result = xpath.execute(context, node, null);
return toNodeArray(result.nodelist());
}
catch (TransformerException ex) {
throw new XPathException("Could not evaluate XPath expression:" + ex.getMessage(), ex);
}
}
private Node[] toNodeArray(NodeList nodeList) {
Node[] result = new Node[nodeList.getLength()];
for (int i = 0; i < nodeList.getLength(); i++) {
result[i] = nodeList.item(i);
}
return result;
}
}
private static class SimplePrefixResolver implements PrefixResolver {
private Map namespaces;
private SimplePrefixResolver(Map namespaces) {
this.namespaces = namespaces;
}
public String getNamespaceForPrefix(String prefix) {
return (String) namespaces.get(prefix);
}
public String getNamespaceForPrefix(String prefix, Node node) {
return (String) namespaces.get(prefix);
}
public String getBaseIdentifier() {
return null;
}
public boolean handlesNullPrefixes() {
return false;
}
}
}

View File

@@ -0,0 +1,5 @@
<html>
<body>
Provides classes for XPath evaluation using JAXP 1.3, Jaxen, and Xalan. Mostly for internal use by the framework.
</body>
</html>