blob: 0cae9c2cfa3fa3db7ca4fc40bc4418b56654a5a0 [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;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.persistence.internal.oxm.record.UnmarshalRecord;
import org.eclipse.persistence.platform.xml.XMLPlatform;
import org.eclipse.persistence.platform.xml.XMLPlatformFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Text;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.eclipse.persistence.platform.xml.SAXDocumentBuilder;
/**
* @version $Header: SAXFragmentBuilder.java 18-sep-2007.14:36:11 dmahar Exp $
* @author mmacivor
* @since release specific (what release of product did this appear in)
*/
public class SAXFragmentBuilder extends SAXDocumentBuilder {
private UnmarshalRecord owningRecord;
private boolean mixedContent;
public SAXFragmentBuilder(UnmarshalRecord unmarshalRecord) {
super();
owningRecord = unmarshalRecord;
}
@Override
public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
if (!mixedContent) {
boolean bufferContainsOnlyWhitespace = stringBuffer.toString().trim().length() == 0;
if (bufferContainsOnlyWhitespace) {
stringBuffer.reset();
}
}
if ((stringBuffer.length() > 0) && !(nodes.size() == 1)) {
Text text = getInitializedDocument().createTextNode(stringBuffer.toString());
Node parent = this.nodes.get(nodes.size() - 1);
parent.appendChild(text);
processNamespacesForText(text.getTextContent(), (Element)parent);
stringBuffer.reset();
}
if (null != namespaceURI && namespaceURI.length() == 0) {
namespaceURI = null;
}
if(qName == null){
qName = localName;
if(namespaceURI != null){
if(owningRecord != null){
String prefix = owningRecord.resolveNamespaceUri(namespaceURI);
if(prefix != null && prefix.length() > 0){
qName = prefix +Constants.COLON+ qName;
}
}
}
}
int qNameColonIndex = qName.indexOf(Constants.COLON);
if ((namespaceURI != null) && (qNameColonIndex == -1)) {
//check for a prefix from the unmarshal record:
String prefix = owningRecord.resolveNamespaceUri(namespaceURI);
if (prefix != null && prefix.length() >0){
qName = prefix + Constants.COLON + qName;
qNameColonIndex = prefix.length();
}
}
Element element = getInitializedDocument().createElementNS(namespaceURI, qName);
Node parentNode = nodes.get(nodes.size() - 1);
appendChildNode(parentNode, element);
nodes.add(element);
if (qNameColonIndex > -1) {
String prefix = qName.substring(0, qNameColonIndex);
String parentUri = null;
if (element.getParentNode() != null) {
parentUri = XMLPlatformFactory.getInstance().getXMLPlatform().resolveNamespacePrefix(element.getParentNode(), prefix);
}
if ((parentUri == null) || parentUri.length() == 0) {
startPrefixMapping(prefix, namespaceURI);
}
}
if (null != namespaceDeclarations) {
Iterator namespaces = namespaceDeclarations.entrySet().iterator();
while (namespaces.hasNext()) {
Map.Entry entry = (Map.Entry)namespaces.next();
addNamespaceDeclaration(element, (String)entry.getKey(), (String)entry.getValue());
}
namespaceDeclarations = null;
}
int numberOfAttributes = atts.getLength();
String attributeNamespaceURI, attributeQName, attributeValue;
for (int x = 0; x < numberOfAttributes; x++) {
attributeNamespaceURI = atts.getURI(x);
attributeQName = atts.getQName(x);
attributeValue = atts.getValue(x);
// Empty string will be treated as a null URI
if (null != attributeNamespaceURI && attributeNamespaceURI.length() == 0) {
attributeNamespaceURI = null;
}
// Handle case where prefix/uri are not set on an xmlns prefixed attribute
if (attributeNamespaceURI == null && attributeQName.startsWith(javax.xml.XMLConstants.XMLNS_ATTRIBUTE)) {
attributeNamespaceURI = javax.xml.XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
}
element.setAttributeNS(attributeNamespaceURI, attributeQName, attributeValue == null ? Constants.EMPTY_STRING : attributeValue);
if (attributeValue != null) {
processNamespacesForText(attributeValue, element);
}
}
}
@Override
public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
if (super.nodes.size() == 2) {
Element endedElement = (Element)nodes.get(nodes.size() -1);
if (stringBuffer.length() > 0) {
Text text = getInitializedDocument().createTextNode(stringBuffer.toString());
endedElement.appendChild(text);
stringBuffer.reset();
processNamespacesForText(text.getTextContent(), endedElement);
}
while(owningRecord.isSelfRecord() && owningRecord.getParentRecord() != null){
owningRecord = owningRecord.getParentRecord();
}
//just the doc left in the stack. Finish this off.
owningRecord.getXMLReader().setContentHandler(owningRecord);
owningRecord.endElement(namespaceURI, localName, qName);
} else {
super.endElement(namespaceURI, localName, qName);
}
}
public void endSelfElement(String namespaceURI, String localName, String qName) throws SAXException {
if (super.nodes.size() == 2) {
Element endedElement = (Element)nodes.get(nodes.size() -1);
if (stringBuffer.length() > 0) {
Text text = getInitializedDocument().createTextNode(stringBuffer.toString());
endedElement.appendChild(text);
stringBuffer.reset();
}
} else {
super.endElement(namespaceURI, localName, qName);
}
}
public List<Node> getNodes() {
return super.nodes;
}
public void setOwningRecord(UnmarshalRecord record) {
this.owningRecord = record;
}
@Override
public void appendChildNode(Node parent, Node child) {
if (parent != this.getDocument()) {
parent.appendChild(child);
}
}
public Attr buildAttributeNode(String namespaceURI, String localName, String value) {
try {
Attr attribute = getInitializedDocument().createAttributeNS(namespaceURI, localName);
attribute.setValue(value);
return attribute;
} catch (SAXException ex) {
}
return null;
}
public Text buildTextNode(String textValue) {
try {
Text text = getInitializedDocument().createTextNode(textValue);
return text;
} catch (SAXException ex) {
}
return null;
}
/**
* Adds a namespace declaration to the parent element if the textValue represents a
* prefixed qualified name. The determination of a qname is based on the existance of a
* colon character and the ability to resolve the characters before the colon to a
* namespace uri.
*/
private void processNamespacesForText(String textValue, Element parentNode) {
//If the text value is a qname, we may need to do namespace processing
int colon = textValue.indexOf(':');
if(colon != -1) {
String prefix = textValue.substring(0, colon);
XMLPlatform platform = XMLPlatformFactory.getInstance().getXMLPlatform();
String uri = platform.resolveNamespacePrefix(parentNode, prefix);
if(uri == null) {
uri = this.owningRecord.resolveNamespacePrefix(prefix);
if(uri != null) {
//add namespace declaration
addNamespaceDeclaration(parentNode, prefix, uri);
}
}
}
}
public void setMixedContent(boolean mixedContent) {
this.mixedContent = mixedContent;
}
}