org.apache.axiom.om.impl.serialize.OMXMLReader Maven / Gradle / Ivy
/*
* 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.axiom.om.impl.serialize;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.axiom.om.OMAttribute;
import org.apache.axiom.om.OMComment;
import org.apache.axiom.om.OMContainer;
import org.apache.axiom.om.OMDocument;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.om.OMNode;
import org.apache.axiom.om.OMProcessingInstruction;
import org.apache.axiom.om.OMText;
import org.apache.axiom.util.sax.AbstractXMLReader;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
/**
* SAX {@link XMLReader} implementation that traverses a given OM tree and invokes the
* callback methods on the configured {@link ContentHandler}. This can be used to
* serialize an Axiom tree to SAX.
*
* Note that this class doesn't support serializing {@link org.apache.axiom.om.OMDocType}
* nodes. They will be silently skipped.
*
* This class can also generate SAX events for a subtree. This is the case if the
* element passed to the constructor is not the root element of the document. In this
* case, care is taken to properly generate startPrefixMapping
and
* endPrefixMapping
events also for namespace mappings declared on the ancestors
* of the element.
*
* To understand why this is important, consider the following example:
*
<root xmlns:ns="urn:ns"><element attr="ns:someThing"/><root>
* In that case, to correctly interpret the attribute value, the SAX content handler must be
* aware of the namespace mapping for the ns prefix, even if the serialization starts
* only at the child element.
*/
public class OMXMLReader extends AbstractXMLReader {
private final OMContainer root;
private final AttributesAdapter attributesAdapter = new AttributesAdapter();
public OMXMLReader(OMContainer root) {
this.root = root;
}
public void parse(InputSource input) throws IOException, SAXException {
parse();
}
public void parse(String systemId) throws IOException, SAXException {
parse();
}
private void parse() throws SAXException {
if (root instanceof OMDocument) {
generateEvents((OMDocument)root);
} else {
OMElement element = (OMElement)root;
contentHandler.startDocument();
generateParentPrefixMappingEvents(element, true);
generateEvents(element);
generateParentPrefixMappingEvents(element, false);
contentHandler.endDocument();
}
}
private void generateEvents(OMDocument document) throws SAXException {
contentHandler.startDocument();
generateEventsForChildren(document);
contentHandler.endDocument();
}
private void generatePrefixMappingEvents(OMNamespace ns, boolean start) throws SAXException {
String prefix = ns.getPrefix();
if (prefix != null) {
if (start) {
contentHandler.startPrefixMapping(prefix, ns.getNamespaceURI());
} else {
contentHandler.endPrefixMapping(prefix);
}
}
}
private void generatePrefixMappingEvents(OMElement omElement, boolean start)
throws SAXException {
for (Iterator it = omElement.getAllDeclaredNamespaces(); it.hasNext(); ) {
generatePrefixMappingEvents((OMNamespace)it.next(), start);
}
}
private void generateParentPrefixMappingEvents(OMElement omElement, boolean start)
throws SAXException {
if (!(omElement.getParent() instanceof OMElement)) {
return;
}
// Maintain a set of the prefixes we have already seen. This is required to take into
// account that a namespace mapping declared on an element can hide another one declared
// for the same prefix on an ancestor of the element.
Set/**/ seenPrefixes = new HashSet();
for (Iterator it = omElement.getAllDeclaredNamespaces(); it.hasNext(); ) {
seenPrefixes.add(((OMNamespace)it.next()).getPrefix());
}
OMElement current = omElement;
while (true) {
OMContainer parent = current.getParent();
if (!(parent instanceof OMElement)) {
return;
}
current = (OMElement)parent;
for (Iterator it = current.getAllDeclaredNamespaces(); it.hasNext(); ) {
OMNamespace ns = (OMNamespace)it.next();
if (seenPrefixes.add(ns.getPrefix())) {
generatePrefixMappingEvents(ns, start);
}
}
}
}
private void generateEvents(OMElement omElement) throws SAXException {
generatePrefixMappingEvents(omElement, true);
OMNamespace omNamespace = omElement.getNamespace();
String uri;
String prefix;
if (omNamespace != null) {
uri = omNamespace.getNamespaceURI();
prefix = omNamespace.getPrefix();
} else {
uri = "";
prefix = null;
}
String localName = omElement.getLocalName();
String qName;
if (prefix == null || prefix.length() == 0) {
qName = localName;
} else {
qName = prefix + ":" + localName;
}
// For performance reasons, we always reuse the same instance of AttributesAdapter.
// This is explicitely allowed by the specification of the startElement method.
attributesAdapter.setAttributes(omElement);
contentHandler.startElement(uri, localName, qName, attributesAdapter);
generateEventsForChildren(omElement);
contentHandler.endElement(uri, localName, qName);
generatePrefixMappingEvents(omElement, false);
}
private void generateEventsForChildren(OMContainer parent) throws SAXException {
for (Iterator it = parent.getChildren(); it.hasNext(); ) {
OMNode node = (OMNode)it.next();
switch (node.getType()) {
case OMNode.ELEMENT_NODE:
generateEvents((OMElement)node);
break;
case OMNode.TEXT_NODE:
generateEvents((OMText)node, false);
break;
case OMNode.SPACE_NODE:
generateEvents((OMText)node, true);
break;
case OMNode.CDATA_SECTION_NODE:
if (lexicalHandler != null) {
lexicalHandler.startCDATA();
}
generateEvents((OMText)node, false);
if (lexicalHandler != null) {
lexicalHandler.endCDATA();
}
break;
case OMNode.COMMENT_NODE:
if (lexicalHandler != null) {
char[] ch = ((OMComment)node).getValue().toCharArray();
lexicalHandler.comment(ch, 0, ch.length);
}
break;
case OMNode.PI_NODE:
OMProcessingInstruction pi = (OMProcessingInstruction)node;
contentHandler.processingInstruction(pi.getTarget(), pi.getValue());
}
}
}
private void generateEvents(OMText omText, boolean space) throws SAXException {
char[] ch = omText.getTextCharacters();
if (space) {
contentHandler.ignorableWhitespace(ch, 0, ch.length);
} else {
contentHandler.characters(ch, 0, ch.length);
}
}
protected static class AttributesAdapter implements Attributes {
private List/**/ attributes = new ArrayList(5);
public void setAttributes(OMElement element) {
attributes.clear();
for (Iterator it = element.getAllAttributes(); it.hasNext(); ) {
attributes.add(it.next());
}
}
public int getLength() {
return attributes.size();
}
public int getIndex(String qName) {
for (int i=0, len=attributes.size(); i
© 2015 - 2025 Weber Informatics LLC | Privacy Policy