blob: bbbe73c3067cbd537e5fdb230d86327ceec7a3a6 [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 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 v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// 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>
* </ul>
*/
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<>();
}
@Override
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);
}
}
@Override
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.
*/
@Override
protected void endDocument() throws SAXException {
// NOT APPLICABLE FOR FRAGMENTS - DO NOTHING
}
/**
* Trigger a startDocument event on the contenthandler.
*/
@Override
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
*/
@Override
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
*
* @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.
*
*/
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.
* @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.
*
*/
@Override
protected void processParentNamespaces(Element element) throws SAXException {
// DO NOTHING FOR FRAGMENTS
}
@Override
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);
}
}
}
}
}