/******************************************************************************* | |
* Copyright (c) 1998, 2013 Oracle and/or its affiliates. All rights reserved. | |
* This program and the accompanying materials are made available under the | |
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 | |
* which accompanies this distribution. | |
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html | |
* and the Eclipse Distribution License is available at | |
* http://www.eclipse.org/org/documents/edl-v10.php. | |
* | |
* Contributors: | |
* Oracle - initial API and implementation from Oracle TopLink | |
******************************************************************************/ | |
package org.eclipse.persistence.internal.oxm.record; | |
import java.util.ArrayList; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Map.Entry; | |
import javax.xml.XMLConstants; | |
import org.eclipse.persistence.internal.oxm.Constants; | |
import org.eclipse.persistence.internal.oxm.NamespaceResolver; | |
import org.eclipse.persistence.platform.xml.XMLPlatformFactory; | |
import org.w3c.dom.Attr; | |
import org.w3c.dom.Element; | |
import org.w3c.dom.NamedNodeMap; | |
import org.w3c.dom.Node; | |
import org.xml.sax.ContentHandler; | |
import org.xml.sax.SAXException; | |
/** | |
* Internal: | |
* <p><b>Purpose:</b> An implementation of XMLReader for parsing XMLFragment Nodes | |
* into SAX events. | |
* <p><b>Responsibilities:</b><ul> | |
* <li>Walk the XMLFragment node's DOM tree and report sax events to the provided | |
* content handler</li> | |
* <li>Report lexical events to the lexical handler if it's provided</li> | |
* | |
*/ | |
public class XMLFragmentReader extends DOMReader { | |
protected NamespaceResolver nsresolver; | |
protected List<NamespaceResolver> nsresolverList; | |
protected Map<Element, NamespaceResolver> tmpresolverMap; | |
public XMLFragmentReader(NamespaceResolver namespaceResolver) { | |
nsresolverList = new ArrayList(); | |
if(null != namespaceResolver) { | |
nsresolverList.add(namespaceResolver); | |
} | |
tmpresolverMap = new HashMap<Element, NamespaceResolver>(); | |
} | |
public void parse (Node node, String uri, String name) throws SAXException { | |
if (node.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) { | |
handleChildNodes(node.getChildNodes()); | |
} else { | |
super.parse(node, uri, name); | |
} | |
} | |
protected void reportElementEvents(Element elem) throws SAXException { | |
super.reportElementEvents(elem); | |
// Clean up any temporary namespace resolvers created while processing this element | |
cleanupNamespaceResolvers(elem); | |
} | |
/** | |
* Trigger an endDocument event on the contenthandler. | |
*/ | |
protected void endDocument() throws SAXException { | |
// NOT APPLICABLE FOR FRAGMENTS - DO NOTHING | |
} | |
/** | |
* Trigger a startDocument event on the contenthandler. | |
*/ | |
protected void startDocument() throws SAXException { | |
// NOT APPLICABLE FOR FRAGMENTS - DO NOTHING | |
} | |
@Override | |
protected void handleNewNamespaceDeclaration(Element elem, String prefix, String namespaceURI) { | |
String uri = resolveNamespacePrefix(prefix); | |
if (uri == null || !uri.equals(namespaceURI)) { | |
NamespaceResolver tmpresolver = getTempResolver(elem); | |
tmpresolver.put(prefix, namespaceURI); | |
if (!nsresolverList.contains(tmpresolver)) { | |
nsresolverList.add(tmpresolver); | |
} | |
} | |
} | |
/** | |
* Handle prefixed attribute - may need to declare the namespace | |
* URI locally. | |
* | |
*/ | |
@Override | |
protected void handlePrefixedAttribute(Element elem) throws SAXException { | |
// Need to determine if the URI for the prefix needs to be | |
// declared locally: | |
// If the prefix or URI are not in the resolver, or the URI | |
// associated with the prefix (in the resolver) is different | |
// than node's URI, write out the node's URI locally | |
String prefix = elem.getPrefix(); | |
if(prefix == null) { | |
prefix = Constants.EMPTY_STRING; | |
} | |
String uri = resolveNamespacePrefix(prefix); | |
if((uri == null && elem.getNamespaceURI() != null) || (uri != null && !uri.equals(elem.getNamespaceURI()))) { | |
NamespaceResolver tmpresolver = getTempResolver(elem); | |
tmpresolver.put(prefix, elem.getNamespaceURI()); | |
if (!nsresolverList.contains(tmpresolver)) { | |
nsresolverList.add(tmpresolver); | |
} | |
String namespaceURI = elem.getNamespaceURI(); | |
if(null == namespaceURI) { | |
namespaceURI = Constants.EMPTY_STRING; | |
} | |
getContentHandler().startPrefixMapping(prefix, namespaceURI); | |
} | |
NamedNodeMap attributes = elem.getAttributes(); | |
if(attributes != null) { | |
for(int x=0; x < attributes.getLength(); x++) { | |
Node attribute = attributes.item(x); | |
if(XMLConstants.XMLNS_ATTRIBUTE.equals(attribute.getPrefix())) { | |
NamespaceResolver tmpresolver = getTempResolver(elem); | |
tmpresolver.put(attribute.getLocalName(), attribute.getNodeValue()); | |
if (!nsresolverList.contains(tmpresolver)) { | |
nsresolverList.add(tmpresolver); | |
} | |
} else if(XMLConstants.XMLNS_ATTRIBUTE.equals(attribute.getNodeName())) { | |
NamespaceResolver tmpresolver = getTempResolver(elem); | |
String namespace = attribute.getNodeValue(); | |
if(null == namespace) { | |
namespace = Constants.EMPTY_STRING; | |
} | |
tmpresolver.put(Constants.EMPTY_STRING, namespace); | |
if (!nsresolverList.contains(tmpresolver)) { | |
nsresolverList.add(tmpresolver); | |
} | |
} | |
} | |
} | |
} | |
/** | |
* If there is a temporary namespace resolver for a given element, | |
* each entry contains a prefix that requires an endPrefixMapping | |
* event to be triggered | |
*/ | |
protected void endPrefixMappings(Element elem) throws SAXException { | |
NamespaceResolver tmpresolver = getTempResolver(elem); | |
if (tmpresolver != null) { | |
ContentHandler contentHandler = getContentHandler(); | |
String defaultNamespace = tmpresolver.getDefaultNamespaceURI(); | |
if(null != defaultNamespace) { | |
contentHandler.endPrefixMapping(Constants.EMPTY_STRING); | |
} | |
if(tmpresolver.hasPrefixesToNamespaces()) { | |
for(Entry<String, String> entry : tmpresolver.getPrefixesToNamespaces().entrySet()) { | |
contentHandler.endPrefixMapping(entry.getKey()); | |
} | |
} | |
} | |
} | |
/** | |
* Returns the namespace resolver in the map of temporary namespace | |
* resolvers for a given element | |
* | |
* @param elem | |
* @return the namespace resolver in the map for elem, or a new | |
* resolver if none exists | |
*/ | |
protected NamespaceResolver getTempResolver(Element elem) { | |
NamespaceResolver tmpresolver = tmpresolverMap.get(elem); | |
if (tmpresolver == null) { | |
tmpresolver = new NamespaceResolver(); | |
tmpresolverMap.put(elem, tmpresolver); | |
} | |
return tmpresolver; | |
} | |
/** | |
* Remove any temporary namespace resolvers created while processing | |
* a given element. | |
* | |
* @param elem | |
*/ | |
protected void cleanupNamespaceResolvers(Element elem) { | |
NamespaceResolver tmpresolver = tmpresolverMap.get(elem); | |
if (tmpresolver != null) { | |
tmpresolverMap.remove(elem); | |
nsresolverList.remove(tmpresolver); | |
} | |
} | |
/** | |
* Convenience method that iterates over each namespace resolver | |
* in the resolver list until it locates a uri for 'prefix' or | |
* the final resolver is reached w/o success. | |
* @param prefix | |
* @return true if a URI exists in one of the resolvers in the | |
* list, false otherwise | |
*/ | |
protected String resolveNamespacePrefix(String prefix) { | |
String uri = null; | |
if (null == prefix || prefix.length() == 0) { | |
for (int i = nsresolverList.size() - 1; i >= 0; i--) { | |
NamespaceResolver next = nsresolverList.get(i); | |
uri = next.getDefaultNamespaceURI(); | |
if ((uri != null) && uri.length() > 0) { | |
break; | |
} | |
} | |
} else { | |
for (int i = nsresolverList.size() - 1; i >= 0; i--) { | |
NamespaceResolver next = nsresolverList.get(i); | |
uri = next.resolveNamespacePrefix(prefix); | |
if ((uri != null) && uri.length() > 0) { | |
break; | |
} | |
} | |
} | |
return uri; | |
} | |
/** | |
* Process namespace declarations on parent elements if not the root. | |
* For each parent node from current to root place puch each onto a | |
* stack, then pop each off, calling startPrefixMapping for each | |
* XMLNS attribute. Using a stack ensures that the parent nodes are | |
* processed top down. | |
* | |
* @param element | |
*/ | |
protected void processParentNamespaces(Element element) throws SAXException { | |
// DO NOTHING FOR FRAGMENTS | |
} | |
protected void handleXsiTypeAttribute(Attr attr) throws SAXException { | |
String value = attr.getValue(); | |
int colon = value.indexOf(':'); | |
if(colon != -1) { | |
String prefix = value.substring(0, colon); | |
String uri = this.resolveNamespacePrefix(prefix); | |
if(uri == null) { | |
uri = XMLPlatformFactory.getInstance().getXMLPlatform().resolveNamespacePrefix(attr.getOwnerElement(), prefix); | |
if(uri != null) { | |
this.contentHandler.startPrefixMapping(prefix, uri); | |
} | |
} | |
} | |
} | |
} |