blob: 88c4197fd99733f4bcae4aa74122ae8939bced5c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 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:
* Blaise Doughan - 2.5 - initial implementation
******************************************************************************/
package org.eclipse.persistence.internal.oxm.record;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.xml.namespace.QName;
import org.eclipse.persistence.core.descriptors.CoreInheritancePolicy;
import org.eclipse.persistence.internal.core.helper.CoreField;
import org.eclipse.persistence.internal.core.sessions.CoreAbstractRecord;
import org.eclipse.persistence.internal.core.sessions.CoreAbstractSession;
import org.eclipse.persistence.internal.oxm.Constants;
import org.eclipse.persistence.internal.oxm.Marshaller;
import org.eclipse.persistence.internal.oxm.MediaType;
import org.eclipse.persistence.internal.oxm.Namespace;
import org.eclipse.persistence.internal.oxm.NamespaceResolver;
import org.eclipse.persistence.internal.oxm.ObjectBuilder;
import org.eclipse.persistence.internal.oxm.Root;
import org.eclipse.persistence.internal.oxm.XPathQName;
import org.eclipse.persistence.internal.oxm.mappings.Descriptor;
import org.eclipse.persistence.internal.oxm.mappings.Field;
import org.eclipse.persistence.oxm.schema.XMLSchemaReference;
import org.w3c.dom.Node;
public class AbstractMarshalRecordImpl<
ABSTRACT_SESSION extends CoreAbstractSession,
FIELD extends CoreField,
MARSHALLER extends Marshaller,
NAMESPACE_RESOLVER extends NamespaceResolver> extends CoreAbstractRecord implements AbstractMarshalRecord<ABSTRACT_SESSION, FIELD, MARSHALLER, NAMESPACE_RESOLVER> {
protected boolean equalNamespaceResolvers;
protected boolean hasCustomNamespaceMapper;
private boolean isXOPPackage;
private XPathQName leafElementType;
protected MARSHALLER marshaller;
protected boolean namespaceAware = true;
protected NAMESPACE_RESOLVER namespaceResolver;
private Object owningObject;
private AbstractMarshalRecord<ABSTRACT_SESSION, FIELD, MARSHALLER, NAMESPACE_RESOLVER> realRecord;
protected ABSTRACT_SESSION session;
public AbstractMarshalRecordImpl(AbstractMarshalRecord realRecord) {
this.realRecord = realRecord;
}
@Override
public List addExtraNamespacesToNamespaceResolver(Descriptor descriptor,
CoreAbstractSession session, boolean allowOverride,
boolean ignoreEqualResolvers) {
if (equalNamespaceResolvers && !ignoreEqualResolvers) {
return null;
}
org.eclipse.persistence.internal.oxm.NamespaceResolver descriptorNamespaceResolver = descriptor.getNamespaceResolver();
if(null == descriptorNamespaceResolver || !descriptorNamespaceResolver.hasPrefixesToNamespaces()) {
return null;
}
Map<String, String> prefixesToNamespaces = descriptorNamespaceResolver.getPrefixesToNamespaces();
if(prefixesToNamespaces.size() == 0) {
return null;
}
List returnList = new ArrayList(prefixesToNamespaces.size());
org.eclipse.persistence.internal.oxm.NamespaceResolver marshalRecordNamespaceResolver = namespaceResolver;
for(Entry<String, String> entry: prefixesToNamespaces.entrySet()) {
//if isn't already on a parentadd namespace to this element
String prefix = marshalRecordNamespaceResolver.resolveNamespaceURI(entry.getValue());
if (prefix == null || prefix.length() == 0) {
//if there is no prefix already declared for this uri in the nr add this one
//unless that prefix is already bound to another namespace uri
prefix = entry.getKey();
if(hasCustomNamespaceMapper) {
String newPrefix = getMarshaller().getNamespacePrefixMapper().getPreferredPrefix(entry.getValue(), prefix, true);
if(newPrefix != null && !(newPrefix.length() == 0)) {
prefix = newPrefix;
}
}
String uri = marshalRecordNamespaceResolver.resolveNamespacePrefix(prefix);
if(hasCustomNamespaceMapper || allowOverride || uri == null || uri.length() == 0) {
//if this uri is unknown, the cutom mapper will return the preferred prefix for this uri
marshalRecordNamespaceResolver.put(entry.getKey(), entry.getValue());
returnList.add(new Namespace(prefix, entry.getValue()));
}
} else if(allowOverride) {
//if overrides are allowed, add the prefix if the URI is different
if (!prefix.equals(entry.getKey()) && !hasCustomNamespaceMapper) {
//if prefix exists for uri but is different then add this
//unless using a custom namespace prefix mapper. Then prefix is expected to be different
marshalRecordNamespaceResolver.put(entry.getKey(), entry.getValue());
returnList.add(new Namespace(entry.getKey(), entry.getValue()));
}
}
}
return returnList;
}
public boolean addXsiTypeAndClassIndicatorIfRequired(Descriptor descriptor, Descriptor referenceDescriptor, Field xmlField, boolean isRootElement) {
ObjectBuilder objectBuilder = (ObjectBuilder) descriptor.getObjectBuilder();
boolean xsiTypeIndicatorField = objectBuilder.isXsiTypeIndicatorField();
if(objectBuilder.addClassIndicatorFieldToRow(this)) {
return true;
}
QName leafType = null;
if (xmlField != null) {
leafType = xmlField.getLeafElementType();
XMLSchemaReference xmlRef = descriptor.getSchemaReference();
if (xmlRef != null) {
if (leafType == null) {
if (xmlRef.getType() == XMLSchemaReference.ELEMENT) {
return false;
}
if (referenceDescriptor == null) {
writeXsiTypeAttribute(descriptor, xmlRef, isRootElement);
return true;
}
} else if (((xmlRef.getType() == XMLSchemaReference.COMPLEX_TYPE) || (xmlRef.getType() == XMLSchemaReference.SIMPLE_TYPE)) && xmlRef.getSchemaContext() != null && xmlRef.isGlobalDefinition()) {
QName ctxQName = xmlRef.getSchemaContextAsQName(descriptor.getNamespaceResolver());
if (!ctxQName.equals(leafType)) {
writeXsiTypeAttribute(descriptor, xmlRef, isRootElement);
return true;
}
}
}
}
if (referenceDescriptor != null && referenceDescriptor == descriptor) {
return false;
}
if (descriptor.hasInheritance() && !descriptor.getInheritancePolicy().isRootParentDescriptor()) {
CoreInheritancePolicy inheritancePolicy = descriptor.getInheritancePolicy();
Field indicatorField = (Field) inheritancePolicy.getClassIndicatorField();
if (indicatorField != null && xsiTypeIndicatorField) {
Object classIndicatorValueObject = inheritancePolicy.getClassIndicatorMapping().get(descriptor.getJavaClass());
String classIndicatorUri = null;
String classIndicatorLocal= null;
String classIndicatorPrefix= null;
if (classIndicatorValueObject instanceof QName) {
QName classIndicatorQName = (QName) classIndicatorValueObject;
classIndicatorUri = classIndicatorQName.getNamespaceURI();
classIndicatorLocal = classIndicatorQName.getLocalPart();
classIndicatorPrefix = classIndicatorQName.getPrefix();
} else {
String classIndicatorValue = (String) inheritancePolicy.getClassIndicatorMapping().get(descriptor.getJavaClass());
int nsindex = classIndicatorValue.indexOf(Constants.COLON);
String prefix = null;
if (nsindex != -1) {
classIndicatorLocal = classIndicatorValue.substring(nsindex + 1);
prefix = classIndicatorValue.substring(0, nsindex);
} else {
classIndicatorLocal = classIndicatorValue;
}
classIndicatorUri = descriptor.getNonNullNamespaceResolver().resolveNamespacePrefix(prefix);
}
if(leafType == null
|| isRootElement && marshaller.getMediaType().isApplicationJSON() && !marshaller.isIncludeRoot()
|| !(leafType.getLocalPart().equals(classIndicatorLocal))
|| (classIndicatorUri == null && (leafType.getNamespaceURI() != null && leafType.getNamespaceURI().length() >0))
|| (classIndicatorUri != null && !classIndicatorUri.equals(leafType.getNamespaceURI()))
){
if (inheritancePolicy.hasClassExtractor()) {
objectBuilder.addClassIndicatorFieldToRow(this);
} else {
writeXsiTypeAttribute(descriptor, classIndicatorUri, classIndicatorLocal,classIndicatorPrefix, isRootElement);
}
return true;
}
return false;
}
}
return false;
}
/**
* INTERNAL
* @since EclipseLink 2.5.0
*/
public boolean addXsiTypeAndClassIndicatorIfRequired(Descriptor descriptor, Descriptor referenceDescriptor, Field xmlField,
Object originalObject, Object obj, boolean wasXMLRoot, boolean isRootElement) {
if (wasXMLRoot) {
XMLSchemaReference xmlRef = descriptor.getSchemaReference();
if (descriptor != null) {
Root xr = (Root) originalObject;
if (xmlRef == null) {
return false;
}
if (xr.getDeclaredType() != null && xr.getDeclaredType() == xr.getObject().getClass()) {
return false;
}
String xmlRootLocalName = xr.getLocalName();
String xmlRootUri = xr.getNamespaceURI();
XPathQName qName = new XPathQName(xmlRootUri, xmlRootLocalName, namespaceAware);
Descriptor xdesc = marshaller.getContext().getDescriptor(qName);
if (xdesc != null) {
boolean writeTypeAttribute = xdesc.getJavaClass() != descriptor.getJavaClass();
if (writeTypeAttribute) {
writeXsiTypeAttribute(descriptor, xmlRef, isRootElement);
return true;
}
return false;
}
boolean writeTypeAttribute = true;
int tableSize = descriptor.getTableNames().size();
for (int i = 0; i < tableSize; i++) {
if (!writeTypeAttribute) {
return false;
}
String defaultRootQualifiedName = (String) descriptor.getTableNames().get(i);
if (defaultRootQualifiedName != null) {
String defaultRootLocalName = null;
String defaultRootUri = null;
int colonIndex = defaultRootQualifiedName.indexOf(Constants.COLON);
if (colonIndex > 0) {
String defaultRootPrefix = defaultRootQualifiedName.substring(0, colonIndex);
defaultRootLocalName = defaultRootQualifiedName.substring(colonIndex + 1);
if (descriptor.getNamespaceResolver() != null) {
defaultRootUri = descriptor.getNamespaceResolver().resolveNamespacePrefix(defaultRootPrefix);
}
} else {
defaultRootLocalName = defaultRootQualifiedName;
}
if (xmlRootLocalName != null) {
if ((((defaultRootLocalName == null) && (xmlRootLocalName == null)) || (defaultRootLocalName.equals(xmlRootLocalName)))
&& (((defaultRootUri == null) && (xmlRootUri == null)) || ((xmlRootUri != null) && (defaultRootUri != null) && (defaultRootUri.equals(xmlRootUri))))) {
// if both local name and uris are equal then don't need to write type attribute
return false;
}
}
} else {
// no default rootElement was set
// if xmlRootName = null then writeTypeAttribute = false
if (xmlRootLocalName == null) {
return false;
}
}
}
if (writeTypeAttribute && xmlRef != null) {
writeXsiTypeAttribute(descriptor, xmlRef, isRootElement);
return true;
}
}
return false;
} else {
return addXsiTypeAndClassIndicatorIfRequired(descriptor, referenceDescriptor, xmlField, isRootElement);
}
}
public void attribute(String namespaceURI, String localName,
String qualifiedName, String value) {
if(null != realRecord) {
realRecord.attribute(namespaceURI, localName, qualifiedName, value);
}
}
@Override
public void attributeWithoutQName(String namespaceURI, String localName,
String prefix, String value) {
String qualifiedName = localName;
if(prefix != null && prefix.length() >0){
qualifiedName = prefix + getNamespaceSeparator() + qualifiedName;
}
attribute(namespaceURI, localName, qualifiedName, value);
}
@Override
public Node getDOM() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
public XPathQName getLeafElementType() {
return leafElementType;
}
@Override
public MARSHALLER getMarshaller() {
return marshaller;
}
@Override
public NAMESPACE_RESOLVER getNamespaceResolver() {
return namespaceResolver;
}
@Override
public char getNamespaceSeparator() {
return Constants.COLON;
}
@Override
public Object getOwningObject() {
return owningObject;
}
@Override
public ABSTRACT_SESSION getSession() {
return session;
}
@Override
public boolean hasCustomNamespaceMapper() {
return hasCustomNamespaceMapper;
}
@Override
public boolean hasEqualNamespaceResolvers() {
return equalNamespaceResolvers;
}
/**
* Determine if namespaces will be considered during marshal/unmarshal operations.
*/
@Override
public boolean isNamespaceAware() {
return namespaceAware;
}
@Override
public boolean isXOPPackage() {
return isXOPPackage;
}
@Override
public void namespaceDeclaration(String prefix, String typeUri) {
if(realRecord != null) {
realRecord.namespaceDeclaration(prefix, typeUri);
return;
}
throw new UnsupportedOperationException();
}
@Override
public Object put(FIELD field, Object object) {
if(null != realRecord) {
return realRecord.put(field, object);
}
throw new UnsupportedOperationException();
}
@Override
public void removeExtraNamespacesFromNamespaceResolver(List<Namespace> extraNamespaces, CoreAbstractSession session) {
if (extraNamespaces == null){
return;
}
for (int i = 0; i < extraNamespaces.size(); i++) {
Namespace nextExtraNamespace = (Namespace)extraNamespaces.get(i);
String uri = namespaceResolver.resolveNamespacePrefix(nextExtraNamespace.getPrefix());
if ((uri != null) && uri.equals(nextExtraNamespace.getNamespaceURI())) {
namespaceResolver.removeNamespace(nextExtraNamespace.getPrefix());
}
}
}
@Override
public String resolveNamespacePrefix(String prefix) {
return null;
}
@Override
public void setCustomNamespaceMapper(boolean customNamespaceMapper) {
this.hasCustomNamespaceMapper = customNamespaceMapper;
}
@Override
public void setEqualNamespaceResolvers(boolean equalNRs) {
this.equalNamespaceResolvers = equalNRs;
}
@Override
public void setLeafElementType(QName type) {
if(type != null){
setLeafElementType(new XPathQName(type, isNamespaceAware()));
}
}
@Override
public void setLeafElementType(XPathQName type) {
leafElementType = type;
}
@Override
public void setMarshaller(MARSHALLER marshaller) {
this.marshaller = marshaller;
if(marshaller != null){
MediaType mediaType = marshaller.getMediaType();
if(marshaller.getNamespacePrefixMapper() != null){
namespaceAware = true;
}else{
namespaceAware = mediaType.isApplicationXML();
}
}
}
@Override
public void setNamespaceResolver(NAMESPACE_RESOLVER namespaceResolver) {
this.namespaceResolver = namespaceResolver;
}
@Override
public void setOwningObject(Object owningObject) {
this.owningObject = owningObject;
}
@Override
public void setSession(ABSTRACT_SESSION session) {
this.session = session;
}
@Override
public void setXOPPackage(boolean isXOPPackage) {
this.isXOPPackage = isXOPPackage;
}
@Override
public void writeXsiTypeAttribute(Descriptor descriptor, String typeUri,
String typeLocal, String typePrefix, boolean addToNamespaceResolver) {
if (typeLocal == null){
return;
}
String typeValue = typeLocal;
if(isNamespaceAware() && typeUri != null && !typeUri.equals(Constants.EMPTY_STRING) && !typeUri.equals(namespaceResolver.getDefaultNamespaceURI())){
String prefix = namespaceResolver.resolveNamespaceURI(typeUri);
if(prefix != null && !prefix.equals(Constants.EMPTY_STRING)){
typeValue = prefix + getNamespaceSeparator() + typeValue;
} else if (typeUri.equals(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI)) {
prefix = namespaceResolver.generatePrefix(Constants.SCHEMA_PREFIX);
typeValue = prefix + getNamespaceSeparator() + typeValue;
namespaceDeclaration(prefix, typeUri);
} else if (typePrefix != null && !typePrefix.equals(Constants.EMPTY_STRING)){
String existingUri = namespaceResolver.resolveNamespacePrefix(typePrefix);
if(existingUri != null){
prefix = namespaceResolver.generatePrefix();
}else{
prefix = typePrefix;
}
typeValue = prefix + getNamespaceSeparator() + typeValue;
namespaceDeclaration(prefix, typeUri);
}else{
prefix = namespaceResolver.generatePrefix();
typeValue = prefix + getNamespaceSeparator() + typeValue;
namespaceDeclaration(prefix, typeUri);
}
}
String xsiPrefix = null;
if(isNamespaceAware()){
xsiPrefix = namespaceResolver.resolveNamespaceURI(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
if (xsiPrefix == null) {
xsiPrefix = namespaceResolver.generatePrefix(Constants.SCHEMA_INSTANCE_PREFIX);
namespaceDeclaration(xsiPrefix, javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
if(addToNamespaceResolver){
namespaceResolver.put(xsiPrefix, javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
}
}
}
attributeWithoutQName(javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, Constants.SCHEMA_TYPE_ATTRIBUTE, xsiPrefix, typeValue);
}
@Override
public void writeXsiTypeAttribute(Descriptor xmlDescriptor, XMLSchemaReference xmlRef, boolean addToNamespaceResolver) {
QName contextAsQName = xmlRef.getSchemaContextAsQName();
if(contextAsQName == null){
contextAsQName = xmlRef.getSchemaContextAsQName(namespaceResolver);
}
if (contextAsQName != null) {
writeXsiTypeAttribute(xmlDescriptor, contextAsQName.getNamespaceURI(), contextAsQName.getLocalPart(), null, addToNamespaceResolver);
}
}
}