| /* |
| * Copyright (c) 1998, 2020 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.List; |
| |
| import javax.xml.namespace.QName; |
| |
| import org.eclipse.persistence.core.queries.CoreAttributeGroup; |
| import org.eclipse.persistence.core.queries.CoreAttributeItem; |
| import org.eclipse.persistence.core.sessions.CoreSession; |
| import org.eclipse.persistence.exceptions.XMLMarshalException; |
| import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession; |
| import org.eclipse.persistence.internal.oxm.mappings.AnyObjectMapping; |
| import org.eclipse.persistence.internal.oxm.mappings.Descriptor; |
| import org.eclipse.persistence.internal.oxm.mappings.Field; |
| import org.eclipse.persistence.internal.oxm.mappings.Mapping; |
| import org.eclipse.persistence.internal.oxm.mappings.UnmarshalKeepAsElementPolicy; |
| import org.eclipse.persistence.internal.oxm.record.MarshalContext; |
| import org.eclipse.persistence.internal.oxm.record.MarshalRecord; |
| import org.eclipse.persistence.internal.oxm.record.ObjectMarshalContext; |
| import org.eclipse.persistence.internal.oxm.record.UnmarshalRecord; |
| import org.eclipse.persistence.internal.oxm.record.XMLReader; |
| import org.eclipse.persistence.internal.oxm.record.XMLRecord; |
| import org.eclipse.persistence.internal.oxm.record.deferred.AnyMappingContentHandler; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * INTERNAL: |
| * <p><b>Purpose</b>: This is how the XML Any Object Mapping is handled when |
| * used with the TreeObjectBuilder.</p> |
| */ |
| public class XMLAnyObjectMappingNodeValue extends XMLRelationshipMappingNodeValue { |
| private AnyObjectMapping xmlAnyObjectMapping; |
| |
| public XMLAnyObjectMappingNodeValue(AnyObjectMapping xmlAnyObjectMapping) { |
| super(); |
| this.xmlAnyObjectMapping = xmlAnyObjectMapping; |
| } |
| |
| @Override |
| public boolean isOwningNode(XPathFragment xPathFragment) { |
| return null == xPathFragment; |
| } |
| |
| @Override |
| public boolean marshal(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, CoreAbstractSession session, NamespaceResolver namespaceResolver) { |
| return marshal(xPathFragment, marshalRecord, object, session, namespaceResolver, ObjectMarshalContext.getInstance()); |
| } |
| |
| @Override |
| public boolean marshal(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, CoreAbstractSession session, NamespaceResolver namespaceResolver, MarshalContext marshalContext) { |
| if (xmlAnyObjectMapping.isReadOnly()) { |
| return false; |
| } |
| Object objectValue = marshalContext.getAttributeValue(object, xmlAnyObjectMapping); |
| return this.marshalSingleValue(xPathFragment, marshalRecord, object, objectValue, session, namespaceResolver, marshalContext); |
| } |
| |
| @Override |
| public boolean marshalSingleValue(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, Object objectValue, CoreAbstractSession session, NamespaceResolver namespaceResolver, MarshalContext marshalContext) { |
| XPathFragment rootFragment = null; |
| |
| Marshaller marshaller = marshalRecord.getMarshaller(); |
| objectValue = xmlAnyObjectMapping.convertObjectValueToDataValue(objectValue, session, marshalRecord.getMarshaller()); |
| |
| if (null == objectValue) { |
| return false; |
| } |
| XPathFragment groupingFragment = marshalRecord.openStartGroupingElements(namespaceResolver); |
| marshalRecord.closeStartGroupingElements(groupingFragment); |
| |
| boolean wasXMLRoot = false; |
| XPathFragment xmlRootFragment = null; |
| Object originalValue = objectValue; |
| if (xmlAnyObjectMapping.usesXMLRoot() && (objectValue instanceof Root)) { |
| xmlRootFragment = new XPathFragment(); |
| xmlRootFragment.setNamespaceAware(marshalRecord.isNamespaceAware()); |
| wasXMLRoot = true; |
| objectValue = ((Root) objectValue).getObject(); |
| if(objectValue == null){ |
| setupFragment(((Root) originalValue), xmlRootFragment, marshalRecord); |
| marshalRecord.nilComplex(xmlRootFragment, namespaceResolver); |
| return true; |
| } |
| } |
| |
| if (objectValue instanceof String) { |
| marshalSimpleValue(xmlRootFragment, marshalRecord, originalValue, object, objectValue, session, namespaceResolver); |
| } else { |
| CoreSession childSession = null; |
| try { |
| childSession = marshaller.getContext().getSession(objectValue); |
| } catch (XMLMarshalException e) { |
| marshalSimpleValue(xmlRootFragment, marshalRecord, originalValue, object, objectValue, session, namespaceResolver); |
| return true; |
| } |
| |
| Descriptor descriptor = (Descriptor) childSession.getDescriptor(objectValue); |
| CoreAttributeGroup group = marshalRecord.getCurrentAttributeGroup(); |
| CoreAttributeItem item = group.getItem(getMapping().getAttributeName()); |
| CoreAttributeGroup nestedGroup = XMLRecord.DEFAULT_ATTRIBUTE_GROUP; |
| if(item != null) { |
| if(item.getGroups() != null) { |
| nestedGroup = item.getGroup(descriptor.getJavaClass()); |
| } |
| if(nestedGroup == null) { |
| nestedGroup = item.getGroup() == null?XMLRecord.DEFAULT_ATTRIBUTE_GROUP:item.getGroup(); |
| } |
| } |
| |
| marshalRecord.pushAttributeGroup(nestedGroup); |
| ObjectBuilder objectBuilder = (ObjectBuilder) descriptor.getObjectBuilder(); |
| |
| List extraNamespaces = objectBuilder.addExtraNamespacesToNamespaceResolver(descriptor, marshalRecord, session, true, true); |
| if(wasXMLRoot){ |
| setupFragment(((Root) originalValue), xmlRootFragment, marshalRecord); |
| } |
| /* |
| * B5112171: 25 Apr 2006 |
| * During marshalling - XML AnyObject and AnyCollection |
| * mappings throw a NullPointerException when the |
| * "document root element" on child object descriptors are not |
| * all defined. These nodes will be ignored with a warning. |
| */ |
| String defaultRootElementString = descriptor.getDefaultRootElement(); |
| if (!wasXMLRoot && (defaultRootElementString == null)) { |
| throw XMLMarshalException.defaultRootElementNotSpecified(descriptor); |
| } else { |
| marshalRecord.beforeContainmentMarshal(objectValue); |
| |
| if (xmlRootFragment != null) { |
| rootFragment = xmlRootFragment; |
| } else { |
| rootFragment = new XPathFragment(defaultRootElementString); |
| //resolve URI |
| if (rootFragment.getNamespaceURI() == null) { |
| if(rootFragment.getPrefix() != null) { |
| String uri = descriptor.getNonNullNamespaceResolver().resolveNamespacePrefix(rootFragment.getPrefix()); |
| rootFragment.setNamespaceURI(uri); |
| } else { |
| rootFragment.setNamespaceURI(descriptor.getNonNullNamespaceResolver().getDefaultNamespaceURI()); |
| } |
| } |
| } |
| |
| if (!wasXMLRoot) { |
| marshalRecord.setLeafElementType(descriptor.getDefaultRootElementType()); |
| } |
| getXPathNode().startElement(marshalRecord, rootFragment, object, session, descriptor.getNonNullNamespaceResolver(), objectBuilder, objectValue); |
| |
| writeExtraNamespaces(extraNamespaces, marshalRecord, session); |
| marshalRecord.addXsiTypeAndClassIndicatorIfRequired(descriptor, descriptor, (Field)xmlAnyObjectMapping.getField(), originalValue, objectValue, wasXMLRoot, false); |
| objectBuilder.buildRow(marshalRecord, objectValue, (CoreAbstractSession) childSession, marshaller, null); |
| marshalRecord.afterContainmentMarshal(object, objectValue); |
| marshalRecord.endElement(rootFragment, namespaceResolver); |
| marshalRecord.removeExtraNamespacesFromNamespaceResolver(extraNamespaces, session); |
| } |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public boolean startElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord, Attributes atts) { |
| try { |
| Descriptor workingDescriptor = findReferenceDescriptor(xPathFragment, unmarshalRecord, atts, xmlAnyObjectMapping, xmlAnyObjectMapping.getKeepAsElementPolicy()); |
| |
| UnmarshalKeepAsElementPolicy policy = xmlAnyObjectMapping.getKeepAsElementPolicy(); |
| if (null != policy && ((workingDescriptor == null && policy.isKeepUnknownAsElement()) || policy.isKeepAllAsElement())) { |
| setupHandlerForKeepAsElementPolicy(unmarshalRecord, xPathFragment, atts); |
| }else if (workingDescriptor != null) { |
| processChild(xPathFragment, unmarshalRecord, atts, workingDescriptor, xmlAnyObjectMapping); |
| }else{ |
| AnyMappingContentHandler handler = new AnyMappingContentHandler(unmarshalRecord, xmlAnyObjectMapping.usesXMLRoot()); |
| String qnameString = xPathFragment.getLocalName(); |
| if (xPathFragment.getPrefix() != null) { |
| qnameString = xPathFragment.getPrefix() + Constants.COLON + qnameString; |
| } |
| handler.startElement(xPathFragment.getNamespaceURI(), xPathFragment.getLocalName(), qnameString, atts); |
| XMLReader xmlReader = unmarshalRecord.getXMLReader(); |
| xmlReader.setContentHandler(handler); |
| xmlReader.setLexicalHandler(handler); |
| return true; |
| } |
| |
| } catch (SAXException e) { |
| throw XMLMarshalException.unmarshalException(e); |
| } |
| return true; |
| } |
| |
| @Override |
| public void endElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord) { |
| UnmarshalRecord childRecord = unmarshalRecord.getChildRecord(); |
| if (null != childRecord) { |
| Object childObject = childRecord.getCurrentObject(); |
| // OBJECT VALUE |
| if (xmlAnyObjectMapping.usesXMLRoot()) { |
| Descriptor workingDescriptor = childRecord.getDescriptor(); |
| if (workingDescriptor != null) { |
| String prefix = xPathFragment.getPrefix(); |
| if ((prefix == null) && (xPathFragment.getNamespaceURI() != null)) { |
| prefix = unmarshalRecord.resolveNamespaceUri(xPathFragment.getNamespaceURI()); |
| } |
| childObject = workingDescriptor.wrapObjectInXMLRoot(childObject, xPathFragment.getNamespaceURI(), xPathFragment.getLocalName(), prefix, false, unmarshalRecord.isNamespaceAware(), unmarshalRecord.getUnmarshaller()); |
| workingDescriptor = null; |
| } |
| } |
| childObject = xmlAnyObjectMapping.convertDataValueToObjectValue(childObject, unmarshalRecord.getSession(), unmarshalRecord.getUnmarshaller()); |
| unmarshalRecord.setAttributeValue(childObject, xmlAnyObjectMapping); |
| } else { |
| SAXFragmentBuilder builder = unmarshalRecord.getFragmentBuilder(); |
| |
| UnmarshalKeepAsElementPolicy keepAsElementPolicy = xmlAnyObjectMapping.getKeepAsElementPolicy(); |
| |
| if (null != keepAsElementPolicy && (keepAsElementPolicy.isKeepUnknownAsElement() || keepAsElementPolicy.isKeepAllAsElement()) && builder.getNodes().size() > 1) { |
| setOrAddAttributeValueForKeepAsElement(builder, xmlAnyObjectMapping, xmlAnyObjectMapping, unmarshalRecord, false, null); |
| } else { |
| // TEXT VALUE |
| if(xmlAnyObjectMapping.isMixedContent()) { |
| endElementProcessText(unmarshalRecord, xmlAnyObjectMapping, xPathFragment, null); |
| } else { |
| unmarshalRecord.resetStringBuffer(); |
| } |
| } |
| } |
| } |
| |
| @Override |
| protected void setOrAddAttributeValue(UnmarshalRecord unmarshalRecord, Object value, XPathFragment xPathFragment, Object collection){ |
| if (!xmlAnyObjectMapping.usesXMLRoot()) { |
| unmarshalRecord.setAttributeValue(value, xmlAnyObjectMapping); |
| } else { |
| Root xmlRoot = unmarshalRecord.createRoot(); |
| xmlRoot.setNamespaceURI(xPathFragment.getNamespaceURI()); |
| xmlRoot.setSchemaType(unmarshalRecord.getTypeQName()); |
| xmlRoot.setLocalName(xPathFragment.getLocalName()); |
| xmlRoot.setObject(value); |
| // xmlRoot.setDeclaredType(type); |
| unmarshalRecord.setAttributeValue(xmlRoot, xmlAnyObjectMapping); |
| } |
| } |
| |
| |
| private Namespace setupFragment(Root originalValue, XPathFragment xmlRootFragment, MarshalRecord marshalRecord) { |
| Namespace generatedNamespace = null; |
| String xpath = originalValue.getLocalName(); |
| if (originalValue.getNamespaceURI() != null) { |
| xmlRootFragment.setNamespaceURI((originalValue).getNamespaceURI()); |
| String prefix = marshalRecord.getNamespaceResolver().resolveNamespaceURI((originalValue).getNamespaceURI()); |
| if (prefix == null || prefix.length() == 0) { |
| prefix = marshalRecord.getNamespaceResolver().generatePrefix("ns0"); |
| generatedNamespace = new Namespace(prefix, xmlRootFragment.getNamespaceURI()); |
| xmlRootFragment.setGeneratedPrefix(true); |
| } |
| xpath = prefix + Constants.COLON + xpath; |
| |
| } |
| xmlRootFragment.setXPath(xpath); |
| return generatedNamespace; |
| } |
| |
| private void marshalSimpleValue(XPathFragment xmlRootFragment, MarshalRecord marshalRecord, Object originalValue, Object object, Object value, CoreAbstractSession session, NamespaceResolver namespaceResolver) { |
| QName qname = null; |
| if (xmlRootFragment != null) { |
| qname = ((Root) originalValue).getSchemaType(); |
| setupFragment(((Root) originalValue), xmlRootFragment, marshalRecord); |
| getXPathNode().startElement(marshalRecord, xmlRootFragment, object, session, namespaceResolver, null, null); |
| updateNamespaces(qname, marshalRecord, null); |
| } |
| |
| if (value instanceof org.w3c.dom.Node) { |
| marshalRecord.node((org.w3c.dom.Node) value, marshalRecord.getNamespaceResolver()); |
| } else { |
| marshalRecord.characters(qname, value, null, false); |
| } |
| |
| if (xmlRootFragment != null) { |
| marshalRecord.endElement(xmlRootFragment, namespaceResolver); |
| } |
| } |
| |
| @Override |
| public AnyObjectMapping getMapping() { |
| return xmlAnyObjectMapping; |
| } |
| |
| @Override |
| public boolean isWhitespaceAware() { |
| return false; |
| } |
| |
| @Override |
| public boolean isAnyMappingNodeValue() { |
| return true; |
| } |
| |
| @Override |
| public boolean isMixedContentNodeValue() { |
| return this.xmlAnyObjectMapping.isMixedContent(); |
| } |
| |
| @Override |
| protected Descriptor findReferenceDescriptor(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord, Attributes atts, Mapping mapping, UnmarshalKeepAsElementPolicy policy) { |
| Descriptor referenceDescriptor = super.findReferenceDescriptor(xPathFragment, unmarshalRecord, atts, mapping, policy); |
| if (referenceDescriptor == null) { |
| Context xmlContext = unmarshalRecord.getUnmarshaller().getContext(); |
| XPathQName xpathQName = new XPathQName(xPathFragment.getNamespaceURI(), xPathFragment.getLocalName(), unmarshalRecord.isNamespaceAware()); |
| referenceDescriptor = xmlContext.getDescriptor(xpathQName); |
| // Check if descriptor is for a wrapper, if it is null it out and let continue |
| if (referenceDescriptor != null && referenceDescriptor.isWrapper()) { |
| referenceDescriptor = null; |
| } |
| } |
| return referenceDescriptor; |
| } |
| |
| } |