text: /*
   * 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.saaj.support;
  
  import java.util.Iterator;
  import javax.xml.soap.Name;
  import javax.xml.soap.Node;
  import javax.xml.soap.SOAPElement;
  import javax.xml.soap.Text;
  
  import org.xml.sax.Attributes;
  import org.xml.sax.InputSource;
  import org.xml.sax.SAXException;
  import org.xml.sax.SAXNotRecognizedException;
  import org.xml.sax.SAXNotSupportedException;
  import org.xml.sax.helpers.AttributesImpl;
  
  import org.springframework.util.StringUtils;
  import org.springframework.xml.sax.AbstractXmlReader;
  
  /**
   * SAX {@code XMLReader} that reads from a SAAJ {@code Node}. Consumes {@code XMLEvents} from an
   * {@code XMLEventReader}, and calls the corresponding methods on the SAX callback interfaces.
   *
   * @author Arjen Poutsma
   * @see javax.xml.soap.Node
   * @see javax.xml.soap.SOAPElement
   * @since 1.0.0
   */
  public class SaajXmlReader extends AbstractXmlReader {
  
  	private static final String NAMESPACES_FEATURE_NAME = "http://xml.org/sax/features/namespaces";
  
  	private static final String NAMESPACE_PREFIXES_FEATURE_NAME = "http://xml.org/sax/features/namespace-prefixes";
  
  	private final Node startNode;
  
  	private boolean namespacesFeature = true;
  
  	private boolean namespacePrefixesFeature = false;
  
  	/**
  	 * Constructs a new instance of the {@code SaajXmlReader} that reads from the given {@code Node}.
  	 *
  	 * @param startNode the SAAJ {@code Node} to read from
  	 */
  	public SaajXmlReader(Node startNode) {
  		this.startNode = startNode;
  	}
  
  	@Override
  	public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
  		if (NAMESPACES_FEATURE_NAME.equals(name)) {
  			return namespacesFeature;
  		}
  		else if (NAMESPACE_PREFIXES_FEATURE_NAME.equals(name)) {
  			return namespacePrefixesFeature;
  		}
  		else {
  			return super.getFeature(name);
  		}
  	}
  
  	@Override
  	public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
  		if (NAMESPACES_FEATURE_NAME.equals(name)) {
  			this.namespacesFeature = value;
  		}
  		else if (NAMESPACE_PREFIXES_FEATURE_NAME.equals(name)) {
  			this.namespacePrefixesFeature = value;
  		}
  		else {
  			super.setFeature(name, value);
  		}
  	}
  
  	/**
  	 * Parses the StAX XML reader passed at construction-time.
  	 *
  	 * <p><strong>Note</strong> that the given {@code InputSource} is not read, but ignored.
  	 *
  	 * @param ignored is ignored
  	 * @throws org.xml.sax.SAXException A SAX exception, possibly wrapping a {@code XMLStreamException}
  	 */
  	@Override
  	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}
  	 */
  	@Override
  	public final void parse(String ignored) throws SAXException {
  		parse();
  	}
  
  	private void parse() throws SAXException {
  		if (getContentHandler() != null) {
  			getContentHandler().startDocument();
  		}
  		handleNode(startNode);
  		if (getContentHandler() != null) {
  			getContentHandler().endDocument();
  		}
  	}
  
  	private void handleNode(Node node) throws SAXException {
  		if (node instanceof SOAPElement) {
  			handleElement((SOAPElement) node);
  		}
  		else if (node instanceof Text) {
  			Text text = (Text) node;
  			handleText(text);
  		}
  	}
  
  	private void handleElement(SOAPElement element) throws SAXException {
  		Name elementName = element.getElementName();
  		if (getContentHandler() != null) {
  			if (namespacesFeature) {
  				for (Iterator<?> iterator = element.getNamespacePrefixes(); iterator.hasNext();) {
  					String prefix = (String) iterator.next();
  					String namespaceUri = element.getNamespaceURI(prefix);
  					getContentHandler().startPrefixMapping(prefix, namespaceUri);
  				}
  				getContentHandler()
  						.startElement(elementName.getURI(), elementName.getLocalName(), elementName.getQualifiedName(),
  								getAttributes(element));
  			}
  			else {
  				getContentHandler().startElement("", "", elementName.getQualifiedName(), getAttributes(element));
  			}
  		}
  		for (Iterator<?> iterator = element.getChildElements(); iterator.hasNext();) {
  			Node child = (Node) iterator.next();
  			handleNode(child);
  		}
  		if (getContentHandler() != null) {
  			if (namespacesFeature) {
  				getContentHandler()
  						.endElement(elementName.getURI(), elementName.getLocalName(), elementName.getQualifiedName());
  				for (Iterator<?> iterator = element.getNamespacePrefixes(); iterator.hasNext();) {
  					String prefix = (String) iterator.next();
  					getContentHandler().endPrefixMapping(prefix);
  				}
  			}
  			else {
  				getContentHandler().endElement("", "", elementName.getQualifiedName());
  			}
  		}
  	}
  
  	private void handleText(Text text) throws SAXException {
  		if (getContentHandler() != null) {
  			char[] ch = text.getValue() != null ? text.getValue().toCharArray() : new char[0];
  			getContentHandler().characters(ch, 0, ch.length);
  		}
  	}
  
  	private Attributes getAttributes(SOAPElement element) {
  		AttributesImpl attributes = new AttributesImpl();
  
  		for (Iterator<?> iterator = element.getAllAttributes(); iterator.hasNext();) {
  			Name attributeName = (Name) iterator.next();
  			String namespace = attributeName.getURI();
  			if (namespace == null || !namespacesFeature) {
  				namespace = "";
  			}
  			String attributeValue = element.getAttributeValue(attributeName);
  			attributes.addAttribute(namespace, attributeName.getLocalName(), attributeName.getQualifiedName(), "CDATA",
  					attributeValue);
  		}
  		if (namespacePrefixesFeature) {
  			for (Iterator<?> iterator = element.getNamespacePrefixes(); iterator.hasNext();) {
  				String prefix = (String) iterator.next();
  				String namespaceUri = element.getNamespaceURI(prefix);
  				String qName;
  				if (StringUtils.hasLength(prefix)) {
  					qName = "xmlns:" + prefix;
  				}
  				else {
  					qName = "xmlns";
  				}
  				attributes.addAttribute("", "", qName, "CDATA", namespaceUri);
  			}
  		}
  		return attributes;
  	}
  
  }
  