/* * Copyright 2005-2014 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.ws.soap.axiom; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.xml.XMLConstants; import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamConstants; import org.apache.axiom.om.OMAttribute; import org.apache.axiom.om.OMContainer; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.OMFactory; import org.apache.axiom.om.OMNamespace; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.ext.LexicalHandler; import org.springframework.util.Assert; import org.springframework.xml.namespace.QNameUtils; /** * Specific SAX {@link ContentHandler} and {@link LexicalHandler} that adds the resulting * AXIOM OMElement to a specified parent element when {@code endDocument} is called. * Used for returing {@code SAXResult}s from Axiom elements. * @author Arjen Poutsma * @author Tommy Winther * @since 1.0.0 */ class AxiomHandler implements ContentHandler, LexicalHandler { private final OMFactory factory; private final List<OMContainer> elements = new ArrayList<OMContainer>(); private final OMContainer container; private int charactersType = XMLStreamConstants.CHARACTERS; private List<Map<String, String>> namespaceMappings = new ArrayList<Map<String, String>>(); AxiomHandler(OMContainer container, OMFactory factory) { Assert.notNull(container, "'container' must not be null"); Assert.notNull(factory, "'factory' must not be null"); this.factory = factory; this.container = container; } private OMContainer getParent() { if (!elements.isEmpty()) { return elements.get(elements.size() - 1); } else { return container; } } @Override public void startDocument() throws SAXException { removeAllNamespaceMappings(); newNamespaceMapping(); } @Override public void endDocument() throws SAXException { removeAllNamespaceMappings(); } @Override public void startPrefixMapping(String prefix, String uri) throws SAXException { currentNamespaceMapping().put(prefix, uri); } @Override public void endPrefixMapping(String prefix) throws SAXException { currentNamespaceMapping().remove(prefix); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { OMContainer parent = getParent(); OMNamespace ns = factory.createOMNamespace(uri, QNameUtils.toQName(uri, qName).getPrefix()); OMElement element = factory.createOMElement(localName, ns, parent); // declare namespaces Map<String, String> namespaceMappings = currentNamespaceMapping(); for (Map.Entry<String, String> entry : namespaceMappings.entrySet()) { String prefix = entry.getKey(); String namespaceUri = entry.getValue(); if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)) { element.declareDefaultNamespace(namespaceUri); } else { element.declareNamespace(namespaceUri, prefix); } } // declare attributes for (int i = 0; i < attributes.getLength(); i++) { QName attrName = QNameUtils.toQName(attributes.getURI(i), attributes.getQName(i)); if (!isNamespaceDeclaration(attrName)) { OMNamespace namespace = factory.createOMNamespace(attrName.getNamespaceURI(), attrName.getPrefix()); OMAttribute attribute = factory.createOMAttribute(attrName.getLocalPart(), namespace, attributes.getValue(i)); element.addAttribute(attribute); } } elements.add(element); newNamespaceMapping(); } private boolean isNamespaceDeclaration(QName qName) { String prefix = qName.getPrefix(); String localPart = qName.getLocalPart(); return (XMLConstants.XMLNS_ATTRIBUTE.equals(localPart) && prefix.length() == 0) || (XMLConstants.XMLNS_ATTRIBUTE.equals(prefix) && localPart.length() != 0); } @Override public void endElement(String uri, String localName, String qName) throws SAXException { elements.remove(elements.size() - 1); removeNamespaceMapping(); } @Override public void characters(char ch[], int start, int length) throws SAXException { String data = new String(ch, start, length); OMContainer parent = getParent(); factory.createOMText(parent, data, charactersType); } @Override public void ignorableWhitespace(char ch[], int start, int length) throws SAXException { charactersType = XMLStreamConstants.SPACE; characters(ch, start, length); charactersType = XMLStreamConstants.CHARACTERS; } @Override public void processingInstruction(String target, String data) throws SAXException { OMContainer parent = getParent(); factory.createOMProcessingInstruction(parent, target, data); } @Override public void comment(char ch[], int start, int length) throws SAXException { String content = new String(ch, start, length); OMContainer parent = getParent(); factory.createOMComment(parent, content); } @Override public void startCDATA() throws SAXException { charactersType = XMLStreamConstants.CDATA; } @Override public void endCDATA() throws SAXException { charactersType = XMLStreamConstants.CHARACTERS; } @Override public void startEntity(String name) throws SAXException { if (!isPredefinedEntityReference(name)) { charactersType = XMLStreamConstants.ENTITY_REFERENCE; } } @Override public void endEntity(String name) throws SAXException { charactersType = XMLStreamConstants.CHARACTERS; } private boolean isPredefinedEntityReference(String name) { return "lt".equals(name) || "gt".equals(name) || "amp".equals(name) || "quot".equals(name) || "apos".equals(name); } /* * Unsupported */ @Override public void setDocumentLocator(Locator locator) { } @Override public void skippedEntity(String name) throws SAXException { } @Override public void startDTD(String name, String publicId, String systemId) throws SAXException { } @Override public void endDTD() throws SAXException { } private Map<String, String> currentNamespaceMapping() { return namespaceMappings.get(namespaceMappings.size() - 1); } private void newNamespaceMapping() { namespaceMappings.add(new HashMap<String, String>()); } private void removeNamespaceMapping() { namespaceMappings.remove(namespaceMappings.size() - 1); } private void removeAllNamespaceMappings() { namespaceMappings.clear(); } }