/******************************************************************************* | |
* 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; | |
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; | |
public SAXFragmentBuilder(UnmarshalRecord unmarshalRecord) { | |
super(); | |
owningRecord = unmarshalRecord; | |
} | |
public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { | |
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); | |
} | |
} | |
} | |
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; | |
} | |
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. | |
* @param textValue | |
* @param parentNode | |
*/ | |
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); | |
} | |
} | |
} | |
} | |
} |