blob: cbf2a748e672dda1c5fac3795f6751e3b44d2015 [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 jakarta.activation.DataHandler;
import javax.xml.namespace.QName;
import org.eclipse.persistence.core.sessions.CoreSession;
import org.eclipse.persistence.exceptions.XMLMarshalException;
import org.eclipse.persistence.internal.core.helper.CoreClassConstants;
import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession;
import org.eclipse.persistence.internal.oxm.mappings.BinaryDataMapping;
import org.eclipse.persistence.internal.oxm.mappings.Field;
import org.eclipse.persistence.internal.oxm.record.BinaryDataUnmarshalRecord;
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.deferred.BinaryMappingContentHandler;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
/**
* INTERNAL:
* <p><b>Purpose</b>: This is how the XML Binary Data Mapping is handled when
* used with the TreeObjectBuilder.</p>
* @author mmacivor
*/
public class XMLBinaryDataMappingNodeValue extends NodeValue implements NullCapableValue {
private BinaryDataMapping xmlBinaryDataMapping;
protected String getValueToWrite(QName schemaType, Object value, CoreAbstractSession session) {
return ((ConversionManager) session.getDatasourcePlatform().getConversionManager()).convertObject(value, CoreClassConstants.STRING, schemaType);
}
@Override
public boolean isOwningNode(XPathFragment xPathFragment) {
return (xPathFragment.getNextFragment() == null) || xPathFragment.isAttribute();
}
public XMLBinaryDataMappingNodeValue(BinaryDataMapping mapping) {
this.xmlBinaryDataMapping = mapping;
}
@Override
public boolean marshal(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, CoreAbstractSession session, NamespaceResolver namespaceResolver) {
return marshal(xPathFragment, marshalRecord, object, session, namespaceResolver, ObjectMarshalContext.getInstance(), null);
}
@Override
public boolean marshal(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, CoreAbstractSession session, NamespaceResolver namespaceResolver, MarshalContext marshalContext, XPathFragment rootFragment) {
if (xmlBinaryDataMapping.isReadOnly()) {
return false;
}
Object objectValue = marshalContext.getAttributeValue(object, xmlBinaryDataMapping);
return this.marshalSingleValue(xPathFragment, marshalRecord, object, objectValue, session, namespaceResolver, marshalContext, rootFragment);
}
@Override
public boolean marshalSingleValue(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, Object objectValue, CoreAbstractSession session, NamespaceResolver namespaceResolver, MarshalContext marshalContext) {
return marshalSingleValue(xPathFragment, marshalRecord, object, objectValue, session, namespaceResolver, marshalContext, null);
}
@Override
public boolean marshalSingleValue(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, Object objectValue,CoreAbstractSession session, NamespaceResolver namespaceResolver, MarshalContext marshalContext, XPathFragment rootFragment) {
XPathFragment xmlRootFrag = null;
if (objectValue instanceof Root) {
Root xmlRoot = (Root) objectValue;
xmlRootFrag = new XPathFragment();
if (xmlRoot.getNamespaceURI() != null && !xmlRoot.getNamespaceURI().equals(namespaceResolver.getDefaultNamespaceURI())) {
String prefix = namespaceResolver.resolveNamespaceURI(xmlRoot.getNamespaceURI());
xmlRootFrag.setXPath(prefix + Constants.COLON + xmlRoot.getLocalName());
xmlRootFrag.setNamespaceURI(xmlRoot.getNamespaceURI());
}else{
xmlRootFrag.setXPath(xmlRoot.getLocalName());
if(xmlRoot.getNamespaceURI() != null && xmlRoot.getNamespaceURI().length() > 0) {
xmlRootFrag.setNamespaceURI(xmlRoot.getNamespaceURI());
}
}
}
Marshaller marshaller = marshalRecord.getMarshaller();
objectValue = xmlBinaryDataMapping.convertObjectValueToDataValue(objectValue, session, marshaller);
XPathFragment groupingFragment = marshalRecord.openStartGroupingElements(namespaceResolver);
if(xPathFragment.isAttribute()){
if (objectValue == null) {
marshalRecord.closeStartGroupingElements(groupingFragment);
return true;
}
}else {
marshalRecord.closeStartGroupingElements(groupingFragment);
XPathFragment elementFragment = xPathFragment;
if(xmlRootFrag != null) {
elementFragment = xmlRootFrag;
}
if (objectValue == null) {
XPathNode holderXPathNode = new XPathNode();
holderXPathNode.setXPathFragment(elementFragment);
marshalRecord.addGroupingElement(holderXPathNode);
boolean returnVal = xmlBinaryDataMapping.getNullPolicy().directMarshal(xPathFragment, marshalRecord, object, session, namespaceResolver);
if(returnVal){
marshalRecord.endElement(elementFragment, namespaceResolver);
}
marshalRecord.removeGroupingElement(holderXPathNode);
return returnVal;
}else if(!xPathFragment.isSelfFragment){
marshalRecord.openStartElement(elementFragment, namespaceResolver);
marshalRecord.closeStartElement();
}
}
// figure out CID or bytes
String c_id = null;
byte[] bytes = null;
String mimeType = this.xmlBinaryDataMapping.getMimeType(object);
String attachmentType = mimeType;
if(mimeType == null) {
mimeType = "";
attachmentType = "application/octet-stream";
}
if (xmlBinaryDataMapping.isSwaRef() && (marshaller.getAttachmentMarshaller() != null)) {
//object value should be a DataHandler
if (xmlBinaryDataMapping.getAttributeClassification() == XMLBinaryDataHelper.getXMLBinaryDataHelper().DATA_HANDLER) {
c_id = marshaller.getAttachmentMarshaller().addSwaRefAttachment((DataHandler) objectValue);
} else {
XMLBinaryDataHelper.EncodedData data = XMLBinaryDataHelper.getXMLBinaryDataHelper().getBytesForBinaryValue(//
objectValue, marshaller, xmlBinaryDataMapping.getMimeType(object));
bytes = data.getData();
c_id = marshaller.getAttachmentMarshaller().addSwaRefAttachment(bytes, 0, bytes.length);
}
} else if (marshalRecord.isXOPPackage() && !xmlBinaryDataMapping.shouldInlineBinaryData()) {
XPathFragment lastFrag = ((Field) xmlBinaryDataMapping.getField()).getLastXPathFragment();
if(xmlRootFrag != null) {
lastFrag = xmlRootFrag;
}
String localName = null;
String namespaceUri = null;
if(rootFragment != null) {
localName = rootFragment.getLocalName();
namespaceUri = rootFragment.getNamespaceURI();
}
if(!lastFrag.isSelfFragment) {
localName = lastFrag.getLocalName();
namespaceUri = lastFrag.getNamespaceURI();
}
if (objectValue.getClass() == CoreClassConstants.APBYTE) {
bytes = (byte[]) objectValue;
c_id = marshaller.getAttachmentMarshaller().addMtomAttachment(bytes, 0, bytes.length, attachmentType, localName, namespaceUri);
} else if (xmlBinaryDataMapping.getAttributeClassification() == XMLBinaryDataHelper.getXMLBinaryDataHelper().DATA_HANDLER) {
c_id = marshaller.getAttachmentMarshaller().addMtomAttachment((DataHandler) objectValue, localName, namespaceUri);
} else {
XMLBinaryDataHelper.EncodedData data = XMLBinaryDataHelper.getXMLBinaryDataHelper().getBytesForBinaryValue(//
objectValue, marshaller, xmlBinaryDataMapping.getMimeType(object));
bytes = data.getData();
c_id = marshaller.getAttachmentMarshaller().addMtomAttachment(bytes, 0, bytes.length, data.getMimeType(), localName, namespaceUri);
}
}
// handle attributes
if (xPathFragment.isAttribute()) {
// if the CID is null there's nothing to write out
if (c_id != null) {
marshalRecord.attribute(xPathFragment, namespaceResolver, c_id);
}else {
String value = getValueToWrite(((Field) xmlBinaryDataMapping.getField()).getSchemaType(), objectValue, session);
marshalRecord.attribute(xPathFragment, namespaceResolver, value);
}
marshalRecord.closeStartGroupingElements(groupingFragment);
return true;
}
if (xmlBinaryDataMapping.isSwaRef() && (marshaller.getAttachmentMarshaller() != null)) {
if(c_id != null) {
marshalRecord.characters(c_id);
} else {
marshalRecord.characters(((Field) xmlBinaryDataMapping.getField()).getSchemaType(), objectValue, mimeType, false);
}
} else {
if (marshalRecord.isXOPPackage() && !xmlBinaryDataMapping.shouldInlineBinaryData()) {
if(c_id == null) {
marshalRecord.characters(((Field) xmlBinaryDataMapping.getField()).getSchemaType(), objectValue, mimeType, false);
} else {
String xopPrefix = null;
// If the field's resolver is non-null and has an entry for XOP,
// use it - otherwise, create a new resolver, set the XOP entry,
// on it, and use it instead.
// We do this to avoid setting the XOP namespace declaration on
// a given field or descriptor's resolver, as it is only required
// on the current element
// 20061023: handle NPE on null NSR
if (namespaceResolver != null) {
xopPrefix = namespaceResolver.resolveNamespaceURI(Constants.XOP_URL);
}
boolean addDeclaration = false;
if (xopPrefix == null || namespaceResolver == null) {
addDeclaration = true;
xopPrefix = Constants.XOP_PREFIX;
}
XPathFragment xopInclude = new XPathFragment();
xopInclude.setLocalName("Include");
xopInclude.setPrefix(xopPrefix);
xopInclude.setNamespaceURI(Constants.XOP_URL);
marshalRecord.openStartElement(xopInclude, namespaceResolver);
marshalRecord.attribute(Constants.EMPTY_STRING, "href", "href", c_id);
if (addDeclaration) {
marshalRecord.namespaceDeclaration(xopPrefix, Constants.XOP_URL);
}
marshalRecord.closeStartElement();
marshalRecord.endElement(xopInclude, namespaceResolver);
//marshal as an attachment
}
} else {
marshalRecord.characters(((Field)xmlBinaryDataMapping.getField()).getSchemaType(), objectValue, mimeType, false);
}
}
if(!xPathFragment.isSelfFragment()){
marshalRecord.endElement(xPathFragment, namespaceResolver);
}
return true;
}
@Override
public boolean startElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord, Attributes atts) {
try {
unmarshalRecord.removeNullCapableValue(this);
Field xmlField = (Field) xmlBinaryDataMapping.getField();
BinaryMappingContentHandler handler = new BinaryMappingContentHandler(unmarshalRecord, this, this.xmlBinaryDataMapping);
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 ex) {
throw XMLMarshalException.unmarshalException(ex);
}
}
@Override
public void endElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord) {
unmarshalRecord.resetStringBuffer();
}
/**
* Handle swaRef and inline attribute cases.
*/
@Override
public void attribute(UnmarshalRecord unmarshalRecord, String URI, String localName, String value) {
unmarshalRecord.removeNullCapableValue(this);
Object fieldValue = null;
if (xmlBinaryDataMapping.isSwaRef()) {
if (unmarshalRecord.getUnmarshaller().getAttachmentUnmarshaller() != null) {
if (xmlBinaryDataMapping.getAttributeClassification() == XMLBinaryDataHelper.getXMLBinaryDataHelper().DATA_HANDLER) {
fieldValue = unmarshalRecord.getUnmarshaller().getAttachmentUnmarshaller().getAttachmentAsDataHandler(value);
} else {
fieldValue = unmarshalRecord.getUnmarshaller().getAttachmentUnmarshaller().getAttachmentAsByteArray(value);
}
xmlBinaryDataMapping.setAttributeValueInObject(unmarshalRecord.getCurrentObject(), XMLBinaryDataHelper.getXMLBinaryDataHelper().convertObject(fieldValue, xmlBinaryDataMapping.getAttributeClassification(), unmarshalRecord.getSession(), null));
}
} else {
// value should be base64 binary string
fieldValue = ((ConversionManager) unmarshalRecord.getSession().getDatasourcePlatform().getConversionManager()).convertSchemaBase64ToByteArray(value);
xmlBinaryDataMapping.setAttributeValueInObject(unmarshalRecord.getCurrentObject(), XMLBinaryDataHelper.getXMLBinaryDataHelper().convertObject(fieldValue, xmlBinaryDataMapping.getAttributeClassification(), unmarshalRecord.getSession(), null));
}
}
@Override
public void setNullValue(Object object, CoreSession session) {
Object value = xmlBinaryDataMapping.getObjectValue(null, session);
xmlBinaryDataMapping.setAttributeValueInObject(object, value);
}
@Override
public boolean isNullCapableValue() {
return xmlBinaryDataMapping.getNullPolicy().getIsSetPerformedForAbsentNode();
}
public DataHandler getDataHandlerForObjectValue(Object obj, Class<?> classification) {
if (classification == DataHandler.class) {
return (DataHandler) obj;
}
return null;
}
public BinaryDataMapping getMapping() {
return this.xmlBinaryDataMapping;
}
@Override
public UnmarshalRecord buildSelfRecord(UnmarshalRecord unmarshalRecord, Attributes atts) {
unmarshalRecord.removeNullCapableValue(this);
BinaryDataUnmarshalRecord newRecord = new BinaryDataUnmarshalRecord(null, unmarshalRecord, this, xmlBinaryDataMapping);
return newRecord;
}
@Override
public void endSelfNodeValue(UnmarshalRecord unmarshalRecord, UnmarshalRecord selfRecord, Attributes attributes) {
unmarshalRecord.resetStringBuffer();
}
}