/******************************************************************************* * Copyright (c) 2012, 2015 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.ConversionManager; import org.eclipse.persistence.internal.oxm.Marshaller; 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> { private ConversionManager conversionManager; 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.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); } /** * @since EclipseLink 2.6.0 */ @Override public ConversionManager getConversionManager() { if(null == conversionManager) { conversionManager = (ConversionManager) session.getDatasourcePlatform().getConversionManager(); } return conversionManager; } @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){ if(marshaller.getNamespacePrefixMapper() != null){ namespaceAware = true; }else{ namespaceAware = marshaller.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); } } }