All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.ws.commons.schema.docpath.SaxWalkerOverDom Maven / Gradle / Ivy

There is a newer version: 2.3.1
Show newest version
/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.apache.ws.commons.schema.docpath;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;

import org.apache.ws.commons.schema.constants.Constants;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;

/**
 * Walks over a {@link Document} in a SAX style, notifying listeners with SAX
 * events.
 * 

* Because the document has already been processed, only the following methods * in the {@link ContentHandler} will be called: *

    *
  • {@link ContentHandler#startDocument()}
  • *
  • {@link ContentHandler#startPrefixMapping(String, String)}
  • *
  • {@link ContentHandler#startElement(String, String, String, Attributes)}
  • *
  • {@link ContentHandler#characters(char[], int, int)}
  • *
  • {@link ContentHandler#endElement(String, String, String)}
  • *
  • {@link ContentHandler#endPrefixMapping(String)}
  • *
  • {@link ContentHandler#endDocument()}
  • *
*

*/ public final class SaxWalkerOverDom { private List listeners; private static class Attr { final QName qName; final String qualifiedName; final String value; Attr(String namespace, String localName, String qualName, String val) { qName = new QName(namespace, localName); qualifiedName = qualName; value = val; } Attr(Node node) { this(node.getNamespaceURI(), node.getLocalName(), node.getNodeName(), node.getNodeValue()); } } private static class DomAttrsAsSax implements org.xml.sax.Attributes { private final List attributes; private final Map attrsByQualifiedName; private final Map attrsByQName; private final Map indexByQualifiedName; private final Map indexByQName; DomAttrsAsSax(NamedNodeMap domAttrs) throws SAXException { attributes = new ArrayList(); attrsByQualifiedName = new HashMap(); attrsByQName = new HashMap(); indexByQualifiedName = new HashMap(); indexByQName = new HashMap(); if (domAttrs != null) { for (int attrIdx = 0; attrIdx < domAttrs.getLength(); ++attrIdx) { final Node domAttr = domAttrs.item(attrIdx); if (Constants.XMLNS_ATTRIBUTE_NS_URI.equals(domAttr.getNamespaceURI())) { // Namespace declarations will be handled separately. continue; } final Attr attribute = new Attr(domAttr); attributes.add(attribute); attrsByQualifiedName.put(attribute.qualifiedName, attribute); attrsByQName.put(attribute.qName, attribute); indexByQualifiedName.put(attribute.qualifiedName, attrIdx); indexByQName.put(attribute.qName, attrIdx); } } } @Override public int getLength() { return attributes.size(); } @Override public String getURI(int index) { if (attributes.size() <= index) { return null; } else { return attributes.get(index).qName.getNamespaceURI(); } } @Override public String getLocalName(int index) { if (attributes.size() <= index) { return null; } else { return attributes.get(index).qName.getLocalPart(); } } @Override public String getQName(int index) { if (attributes.size() <= index) { return null; } else { return attributes.get(index).qualifiedName; } } @Override public String getType(int index) { if (attributes.size() <= index) { return null; } else { return "CDATA"; // We do not know the type information. } } @Override public String getValue(int index) { if (attributes.size() <= index) { return null; } else { return attributes.get(index).value; } } @Override public int getIndex(String uri, String localName) { if ((uri == null) || (localName == null)) { return -1; } final QName qName = new QName(uri, localName); final Integer index = indexByQName.get(qName); if (index == null) { return -1; } else { return index; } } @Override public int getIndex(String qName) { if (qName == null) { return -1; } final Integer index = indexByQualifiedName.get(qName); if (index == null) { return -1; } else { return index; } } @Override public String getType(String uri, String localName) { if ((uri == null) || (localName == null)) { return null; } else { final Attr attr = attrsByQName.get(new QName(uri, localName)); return (attr == null) ? null : "CDATA"; } } @Override public String getType(String qName) { if (qName == null) { return null; } else { final Attr attr = attrsByQualifiedName.get(qName); return (attr == null) ? null : "CDATA"; } } @Override public String getValue(String uri, String localName) { if ((uri == null) || (localName == null)) { return null; } else { final Attr attr = attrsByQName.get(new QName(uri, localName)); return (attr == null) ? null : attr.value; } } @Override public String getValue(String qName) { if (qName == null) { return null; } else { final Attr attr = attrsByQualifiedName.get(qName); return (attr == null) ? null : attr.value; } } } /** * Constructs a new SaxWalkerOverDom. */ public SaxWalkerOverDom() { listeners = null; } /** * Constructs a new SaxWalkerOverDom with the provided * {@link ContentHandler} to send SAX events. * * @param contentHandler The content handler to send events to. */ public SaxWalkerOverDom(ContentHandler contentHandler) { this(); listeners = new ArrayList(1); listeners.add(contentHandler); } /** * Constructs a new SaxWalkerOverDom, taking ownership of the * list of {@link ContentHandler}s to send events to. * * @param contentHandlers The list of content handlers to send events to. */ public SaxWalkerOverDom(List contentHandlers) { this(); listeners = contentHandlers; } /** * Adds the provided {@link ContentHandler} to the list of content handlers * to send events to. If this content handler was already added, it will be * sent events twice (or more often). * * @param contentHandler The content handler to send events to. */ public void addContentHandler(ContentHandler contentHandler) { if (listeners == null) { listeners = new ArrayList(1); } listeners.add(contentHandler); } /** * Removes the first instance of the provided {@link ContentHandler} from * the set of handlers to send events to. If the content handler was added * more than once, it will continue to receive events. * * @param contentHandler The content handler to stop sending events to. * @return true if it was found, false if not. */ public boolean removeContentHandler(ContentHandler contentHandler) { if (listeners != null) { return listeners.remove(contentHandler); } return false; } /** * Walks the provided {@link Document}, sending events to all of the * {@link ContentHandler}s as it traverses. If there are no content * handlers, this method is a no-op. * * @param document The {@link Document} to traverse. * @param systemId The system ID of this {@link Document}. * @throws SAXException if an exception occurs when notifying the handlers. */ public void walk(Document document) throws SAXException { if (document == null) { throw new IllegalArgumentException("Document cannot be null."); } if ((listeners == null) || listeners.isEmpty()) { return; } for (ContentHandler listener : listeners) { listener.startDocument(); } final List prefixes = startPrefixMappings(document); walk(document.getDocumentElement()); for (ContentHandler listener : listeners) { for (String prefix : prefixes) { listener.endPrefixMapping(prefix); } listener.endDocument(); } } private void walk(Element element) throws SAXException { DomAttrsAsSax attrs = new DomAttrsAsSax(element.getAttributes()); final List prefixes = startPrefixMappings(element); for (ContentHandler listener : listeners) { listener.startElement(convertNullToEmptyString(element.getNamespaceURI()), convertNullToEmptyString(element.getLocalName()), convertNullToEmptyString(element.getNodeName()), attrs); } NodeList children = element.getChildNodes(); for (int childIndex = 0; childIndex < children.getLength(); ++childIndex) { Node node = children.item(childIndex); if (node instanceof Element) { walk((Element)node); } else if (node instanceof Text) { walk((Text)node); } else if (node instanceof org.w3c.dom.Comment) { // Ignored. } else { throw new SAXException("Unrecognized child of " + element.getTagName() + " of type " + node.getClass().getName()); } } for (ContentHandler listener : listeners) { listener.endElement(convertNullToEmptyString(element.getNamespaceURI()), convertNullToEmptyString(element.getLocalName()), convertNullToEmptyString(element.getNodeName())); for (String prefix : prefixes) { listener.endPrefixMapping(prefix); } } } private void walk(Text text) throws SAXException { /* * TODO: getData() may throw a org.w3c.dom.DOMException if the actual * text data is too large to fit into a single DOMString (the DOM impl's * internal storage of text data). If that's the case, substringData() * must be called to retrieve the data in pieces. The documentation does * not supply information on the maximum DOMString size; it appears to * require trial & error. */ if (text.getLength() > 0) { char[] data = text.getData().toCharArray(); for (ContentHandler listener : listeners) { listener.characters(data, 0, data.length); } } } private static String convertNullToEmptyString(String input) { if (input == null) { return ""; } return input; } private List startPrefixMappings(Node node) throws DOMException, SAXException { switch (node.getNodeType()) { case Node.DOCUMENT_NODE: case Node.ELEMENT_NODE: break; default: throw new IllegalArgumentException("Cannot start prefix mappings for a node of type " + node.getNodeType()); } final ArrayList prefixes = new ArrayList(); final NamedNodeMap attrs = node.getAttributes(); if (attrs == null) { return prefixes; } for (int attrIndex = 0; attrIndex < attrs.getLength(); ++attrIndex) { final Node attr = attrs.item(attrIndex); final String attrUri = attr.getNamespaceURI(); if (Constants.XMLNS_ATTRIBUTE_NS_URI.equals(attrUri)) { final String localName = attr.getLocalName(); String prefix = null; if (Constants.XMLNS_ATTRIBUTE.equals(localName)) { prefix = Constants.DEFAULT_NS_PREFIX; } else { prefix = localName; } prefixes.add(prefix); for (ContentHandler listener : listeners) { listener.startPrefixMapping(prefix, attr.getNodeValue()); } } } return prefixes; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy