| /* |
| * 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; |
| } |
| } |