blob: 34ee13368d2ad6abfc0503c6b066d2bac913122b [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.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.eclipse.persistence.exceptions.XMLMarshalException;
import org.eclipse.persistence.internal.core.helper.CoreClassConstants;
import org.eclipse.persistence.internal.core.queries.CoreContainerPolicy;
import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession;
import org.eclipse.persistence.internal.oxm.mappings.BinaryDataCollectionMapping;
import org.eclipse.persistence.internal.oxm.mappings.Field;
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.eclipse.persistence.oxm.mappings.nullpolicy.AbstractNullPolicy;
import org.eclipse.persistence.oxm.mappings.nullpolicy.XMLNullRepresentationType;
/**
* INTERNAL:
* <p><b>Purpose</b>: This is how the XML Binary Data Collection Mapping is
* handled when used with the TreeObjectBuilder.</p>
*/
public class XMLBinaryDataCollectionMappingNodeValue extends MappingNodeValue implements ContainerValue {
private BinaryDataCollectionMapping xmlBinaryDataCollectionMapping;
private int index = -1;
public XMLBinaryDataCollectionMappingNodeValue(BinaryDataCollectionMapping mapping) {
this.xmlBinaryDataCollectionMapping = mapping;
}
@Override
public void setContainerInstance(Object object, Object containerInstance) {
xmlBinaryDataCollectionMapping.setAttributeValueInObject(object, containerInstance);
}
@Override
public Object getContainerInstance() {
return getContainerPolicy().containerInstance();
}
@Override
public CoreContainerPolicy getContainerPolicy() {
return xmlBinaryDataCollectionMapping.getContainerPolicy();
}
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();
}
@Override
public boolean isWrapperAllowedAsCollectionName() {
return true;
}
@Override
public boolean marshal(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, CoreAbstractSession session, NamespaceResolver namespaceResolver) {
if (xmlBinaryDataCollectionMapping.isReadOnly()) {
return false;
}
Object collection = xmlBinaryDataCollectionMapping.getAttributeAccessor().getAttributeValueFromObject(object);
if (null == collection) {
AbstractNullPolicy wrapperNP = xmlBinaryDataCollectionMapping.getWrapperNullPolicy();
if (wrapperNP != null && wrapperNP.getMarshalNullRepresentation() == XMLNullRepresentationType.XSI_NIL) {
marshalRecord.nilSimple(namespaceResolver);
return true;
} else {
return false;
}
}
CoreContainerPolicy cp = getContainerPolicy();
Object iterator = cp.iteratorFor(collection);
if (!cp.hasNext(iterator)) {
return marshalRecord.emptyCollection(xPathFragment, namespaceResolver, xmlBinaryDataCollectionMapping.getWrapperNullPolicy() != null);
}
XPathFragment groupingFragment = marshalRecord.openStartGroupingElements(namespaceResolver);
marshalRecord.closeStartGroupingElements(groupingFragment);
int valueSize = cp.sizeFor(collection);
if(marshalRecord.getMarshaller().isApplicationJSON() && (valueSize > 1 || !marshalRecord.getMarshaller().isReduceAnyArrays())) {
marshalRecord.startCollection();
}
while (cp.hasNext(iterator)) {
Object objectValue = cp.next(iterator, session);
marshalSingleValue(xPathFragment, marshalRecord, object, objectValue, session, namespaceResolver, ObjectMarshalContext.getInstance());
}
if(marshalRecord.getMarshaller().isApplicationJSON() && (valueSize > 1 || !marshalRecord.getMarshaller().isReduceAnyArrays())) {
marshalRecord.endCollection();
}
return true;
}
@Override
public boolean startElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord, Attributes atts) {
try {
Field xmlField = (Field)xmlBinaryDataCollectionMapping.getField();
XPathFragment lastFragment = xmlField.getLastXPathFragment();
if(!lastFragment.isAttribute()) {
//set a new content handler to deal with the Include element's event.
BinaryMappingContentHandler handler = new BinaryMappingContentHandler(unmarshalRecord, this, this.xmlBinaryDataCollectionMapping);
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);
} else if (lastFragment.isAttribute()) {
//handle swaRef and inline attribute cases here:
String value = atts.getValue(lastFragment.getNamespaceURI(), lastFragment.getLocalName());
Object fieldValue = null;
if (xmlBinaryDataCollectionMapping.isSwaRef()) {
if (unmarshalRecord.getUnmarshaller().getAttachmentUnmarshaller() != null) {
if (xmlBinaryDataCollectionMapping.getAttributeClassification() == XMLBinaryDataHelper.getXMLBinaryDataHelper().DATA_HANDLER) {
fieldValue = unmarshalRecord.getUnmarshaller().getAttachmentUnmarshaller().getAttachmentAsDataHandler(value);
} else {
fieldValue = unmarshalRecord.getUnmarshaller().getAttachmentUnmarshaller().getAttachmentAsByteArray(value);
}
xmlBinaryDataCollectionMapping.setAttributeValueInObject(unmarshalRecord.getCurrentObject(), XMLBinaryDataHelper.getXMLBinaryDataHelper().convertObject(fieldValue, xmlBinaryDataCollectionMapping.getAttributeClassification(),
unmarshalRecord.getSession(), xmlBinaryDataCollectionMapping.getContainerPolicy()));
}
} else {
//value should be base64 binary string
fieldValue = ((ConversionManager) unmarshalRecord.getSession().getDatasourcePlatform().getConversionManager()).convertSchemaBase64ToByteArray(value);
xmlBinaryDataCollectionMapping.setAttributeValueInObject(unmarshalRecord.getCurrentObject(), XMLBinaryDataHelper.getXMLBinaryDataHelper().convertObject(fieldValue, xmlBinaryDataCollectionMapping.getAttributeClassification(),
unmarshalRecord.getSession(),xmlBinaryDataCollectionMapping.getContainerPolicy()));
}
}
return true;
} catch(SAXException ex) {
throw XMLMarshalException.unmarshalException(ex);
}
}
@Override
public void endElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord) {
unmarshalRecord.resetStringBuffer();
}
@Override
public void endElement(XPathFragment xPathFragment, UnmarshalRecord unmarshalRecord, Object container) {
this.endElement(xPathFragment, unmarshalRecord);
}
public DataHandler getDataHandlerForObjectValue(Object obj, Class<?> classification) {
if (classification == DataHandler.class) {
return (DataHandler) obj;
}
return null;
}
@Override
public boolean isContainerValue() {
return true;
}
@Override
public boolean marshalSingleValue(XPathFragment xPathFragment, MarshalRecord marshalRecord, Object object, Object objectValue, CoreAbstractSession session, NamespaceResolver namespaceResolver, MarshalContext marshalContext) {
Marshaller marshaller = marshalRecord.getMarshaller();
objectValue = xmlBinaryDataCollectionMapping.convertObjectValueToDataValue(objectValue, session, marshaller);
if(objectValue == null) {
AbstractNullPolicy nullPolicy = xmlBinaryDataCollectionMapping.getNullPolicy();
if (nullPolicy.getMarshalNullRepresentation() != XMLNullRepresentationType.ABSENT_NODE) {
XPathNode holderXPathNode = new XPathNode();
holderXPathNode.setXPathFragment(xPathFragment);
marshalRecord.addGroupingElement(holderXPathNode);
boolean returnVal = xmlBinaryDataCollectionMapping.getNullPolicy().directMarshal(xPathFragment, marshalRecord, object, session, namespaceResolver);
if(returnVal){
marshalRecord.endElement(xPathFragment, namespaceResolver);
}
marshalRecord.removeGroupingElement(holderXPathNode);
return returnVal;
}
return true;
}
String mimeType = this.xmlBinaryDataCollectionMapping.getMimeType(object);
String attachmentType = mimeType;
if(mimeType == null) {
mimeType = Constants.EMPTY_STRING;
attachmentType = "application/octet-stream";
}
marshalRecord.openStartElement(xPathFragment, namespaceResolver);
marshalRecord.closeStartElement();
if (xmlBinaryDataCollectionMapping.isSwaRef() && marshaller.getAttachmentMarshaller() != null) {
//object value should be a DataHandler
String c_id = null;
byte[] bytes = null;
if (xmlBinaryDataCollectionMapping.getAttributeElementClass() == XMLBinaryDataHelper.getXMLBinaryDataHelper().DATA_HANDLER) {
c_id = marshaller.getAttachmentMarshaller().addSwaRefAttachment((DataHandler) objectValue);
} else {
XMLBinaryDataHelper.EncodedData data = XMLBinaryDataHelper.getXMLBinaryDataHelper().getBytesForBinaryValue(//
objectValue, marshaller, xmlBinaryDataCollectionMapping.getMimeType(object));
bytes = data.getData();
c_id = marshaller.getAttachmentMarshaller().addSwaRefAttachment(bytes, 0, bytes.length);
}
if(c_id != null) {
marshalRecord.characters(c_id);
} else {
marshalRecord.characters(((Field) xmlBinaryDataCollectionMapping.getField()).getSchemaType(), objectValue, mimeType, false);
}
} else {
if (marshalRecord.isXOPPackage() && !xmlBinaryDataCollectionMapping.shouldInlineBinaryData()) {
XPathFragment lastFrag = ((Field) xmlBinaryDataCollectionMapping.getField()).getLastXPathFragment();
String c_id = Constants.EMPTY_STRING;
byte[] bytes = null;
if (objectValue.getClass() == CoreClassConstants.APBYTE) {
bytes = (byte[]) objectValue;
c_id = marshaller.getAttachmentMarshaller().addMtomAttachment(bytes, 0, bytes.length, attachmentType, lastFrag.getLocalName(), lastFrag.getNamespaceURI());
} else if (xmlBinaryDataCollectionMapping.getAttributeElementClass() == XMLBinaryDataHelper.getXMLBinaryDataHelper().DATA_HANDLER) {
c_id = marshaller.getAttachmentMarshaller().addMtomAttachment((DataHandler) objectValue, lastFrag.getLocalName(), lastFrag.getNamespaceURI());
} else {
XMLBinaryDataHelper.EncodedData data = XMLBinaryDataHelper.getXMLBinaryDataHelper().getBytesForBinaryValue(//
objectValue, marshaller, xmlBinaryDataCollectionMapping.getMimeTypePolicy().getMimeType(object));
bytes = data.getData();
c_id = marshaller.getAttachmentMarshaller().addMtomAttachment(bytes, 0, bytes.length, //
data.getMimeType(), lastFrag.getLocalName(), lastFrag.getNamespaceURI());
}
if(c_id == null) {
marshalRecord.characters(((Field) xmlBinaryDataCollectionMapping.getField()).getSchemaType(), objectValue, mimeType, false);
} else {
boolean addDeclaration = false;
String xopPrefix = null;
if(marshalRecord.getNamespaceResolver() != null){
xopPrefix = marshalRecord.getNamespaceResolver().resolveNamespaceURI(Constants.XOP_URL);
}
if (xopPrefix == null) {
addDeclaration = true;
xopPrefix = marshalRecord.getNamespaceResolver().generatePrefix(Constants.XOP_PREFIX);
marshalRecord.getNamespaceResolver().put(xopPrefix, Constants.XOP_URL);
namespaceResolver = marshalRecord.getNamespaceResolver();
}
XPathFragment xopInclude = new XPathFragment(xopPrefix + ":Include");
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(xPathFragment, namespaceResolver);
//marshal as an attachment.
if (addDeclaration) {
marshalRecord.getNamespaceResolver().removeNamespace(Constants.XOP_PREFIX);
}
}
} else {
marshalRecord.characters(((Field)xmlBinaryDataCollectionMapping.getField()).getSchemaType(), objectValue, mimeType, false);
}
}
marshalRecord.endElement(xPathFragment, namespaceResolver);
return true;
}
@Override
public BinaryDataCollectionMapping getMapping() {
return xmlBinaryDataCollectionMapping;
}
@Override
public boolean getReuseContainer() {
return getMapping().getReuseContainer();
}
/**
* INTERNAL:
* Used to track the index of the corresponding containerInstance in the containerInstances Object[] on UnmarshalRecord
*/
@Override
public void setIndex(int index){
this.index = index;
}
/**
* INTERNAL:
* Set to track the index of the corresponding containerInstance in the containerInstances Object[] on UnmarshalRecord
* Set during TreeObjectBuilder initialization
*/
@Override
public int getIndex(){
return index;
}
/**
* INTERNAL
* Return true if an empty container should be set on the object if there
* is no presence of the collection in the XML document.
* @since EclipseLink 2.3.3
*/
@Override
public boolean isDefaultEmptyContainer() {
return getMapping().isDefaultEmptyContainer();
}
}