| /******************************************************************************* | |
| * Copyright (c) 1998, 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: | |
| * Oracle - initial API and implementation from Oracle TopLink | |
| ******************************************************************************/ | |
| package org.eclipse.persistence.internal.oxm; | |
| import java.util.Iterator; | |
| import java.util.HashMap; | |
| import java.util.Map; | |
| import java.util.Vector; | |
| import javax.xml.namespace.QName; | |
| import org.eclipse.persistence.descriptors.ClassDescriptor; | |
| import org.eclipse.persistence.descriptors.InheritancePolicy; | |
| import org.eclipse.persistence.descriptors.MultitenantPolicy; | |
| import org.eclipse.persistence.exceptions.DescriptorException; | |
| import org.eclipse.persistence.internal.core.helper.CoreClassConstants; | |
| import org.eclipse.persistence.internal.core.helper.CoreField; | |
| import org.eclipse.persistence.internal.helper.DatabaseTable; | |
| import org.eclipse.persistence.internal.helper.Helper; | |
| import org.eclipse.persistence.internal.sessions.AbstractRecord; | |
| import org.eclipse.persistence.internal.sessions.AbstractSession; | |
| import org.eclipse.persistence.oxm.NamespaceResolver; | |
| import org.eclipse.persistence.oxm.XMLDescriptor; | |
| import org.eclipse.persistence.oxm.XMLField; | |
| import org.eclipse.persistence.oxm.record.XMLRecord; | |
| /** | |
| * INTERNAL: | |
| * <p><b>Purpose</b>: A Subclass of Inheritance Policy to be used with XML | |
| * Descriptors. If the class indicator field is an xsi:type, the value of that | |
| * field may be a qualified type name. For example xsi:type="myns:my-type-name". | |
| * Since any given XML document can use different prefixes for these namespaces, | |
| * we must be able to find the class based on QName instead of just the string | |
| * "myns:my-type-name".</p> | |
| * @author mmacivor | |
| * @since 10.1.3 | |
| */ | |
| public class QNameInheritancePolicy extends InheritancePolicy { | |
| //used for initialization. Prefixed type names will be changed to QNames. | |
| private NamespaceResolver namespaceResolver; | |
| private boolean usesXsiType = false; | |
| public QNameInheritancePolicy() { | |
| super(); | |
| } | |
| public QNameInheritancePolicy(ClassDescriptor desc) { | |
| super(desc); | |
| } | |
| /** | |
| * Override to control order of uniqueTables, child tablenames should be first since | |
| * getDefaultRootElement on an XMLDescriptor will return the first table. | |
| */ | |
| protected void updateTables(){ | |
| // Unique is required because the builder can add the same table many times. | |
| Vector<DatabaseTable> childTables = getDescriptor().getTables(); | |
| Vector<DatabaseTable> parentTables = getParentDescriptor().getTables(); | |
| Vector<DatabaseTable> uniqueTables = Helper.concatenateUniqueVectors(childTables, parentTables); | |
| getDescriptor().setTables(uniqueTables); | |
| if(getDescriptor().isXMLDescriptor() && getParentDescriptor().isXMLDescriptor()){ | |
| if(((XMLDescriptor)getDescriptor()).getDefaultRootElementField() == null){ | |
| ((XMLDescriptor)getDescriptor()).setDefaultRootElementField(((XMLDescriptor)getParentDescriptor()).getDefaultRootElementField()); | |
| } | |
| } | |
| // After filtering out any duplicate tables, set the default table | |
| // if one is not already set. This must be done now before any other | |
| // initialization occurs. In a joined strategy case, the default | |
| // table will be at an index greater than 0. Which is where | |
| // setDefaultTable() assumes it is. Therefore, we need to send the | |
| // actual default table instead. | |
| if (childTables.isEmpty()) { | |
| getDescriptor().setInternalDefaultTable(); | |
| } else { | |
| getDescriptor().setInternalDefaultTable(uniqueTables.get(uniqueTables.indexOf(childTables.get(0)))); | |
| } | |
| } | |
| /** | |
| * INTERNAL: | |
| * Allow the inheritance properties of the descriptor to be initialized. | |
| * The descriptor's parent must first be initialized. | |
| */ | |
| @Override | |
| public void preInitialize(AbstractSession session) throws DescriptorException { | |
| // Override for OXM inheritence to avoid initializing unrequired fields | |
| // on the descriptor | |
| if (isChildDescriptor()) { | |
| updateTables(); | |
| // Clone the multitenant policy and set on child descriptor. | |
| if (getParentDescriptor().hasMultitenantPolicy()) { | |
| MultitenantPolicy clonedMultitenantPolicy = (MultitenantPolicy) getParentDescriptor().getMultitenantPolicy().clone(getDescriptor()); | |
| getDescriptor().setMultitenantPolicy(clonedMultitenantPolicy); | |
| } | |
| setClassIndicatorMapping(getParentDescriptor().getInheritancePolicy().getClassIndicatorMapping()); | |
| setShouldUseClassNameAsIndicator(getParentDescriptor().getInheritancePolicy().shouldUseClassNameAsIndicator()); | |
| // Initialize properties. | |
| getDescriptor().setPrimaryKeyFields(getParentDescriptor().getPrimaryKeyFields()); | |
| getDescriptor().setAdditionalTablePrimaryKeyFields(Helper.concatenateMaps(getParentDescriptor().getAdditionalTablePrimaryKeyFields(), getDescriptor().getAdditionalTablePrimaryKeyFields())); | |
| setClassIndicatorField(getParentDescriptor().getInheritancePolicy().getClassIndicatorField()); | |
| //if child has sequencing setting, do not bother to call the parent | |
| if (!getDescriptor().usesSequenceNumbers()) { | |
| getDescriptor().setSequenceNumberField(getParentDescriptor().getSequenceNumberField()); | |
| getDescriptor().setSequenceNumberName(getParentDescriptor().getSequenceNumberName()); | |
| } | |
| } else { | |
| // This must be done now before any other initialization occurs. | |
| getDescriptor().setInternalDefaultTable(); | |
| } | |
| initializeClassExtractor(session); | |
| if (!isChildDescriptor()) { | |
| // build abstract class indicator field. | |
| if ((getClassIndicatorField() == null) && (!hasClassExtractor())) { | |
| session.getIntegrityChecker().handleError(DescriptorException.classIndicatorFieldNotFound(getDescriptor(), getDescriptor())); | |
| } | |
| if (getClassIndicatorField() != null) { | |
| setClassIndicatorField(getDescriptor().buildField(getClassIndicatorField())); | |
| // Determine and set the class indicator classification. | |
| if (shouldUseClassNameAsIndicator()) { | |
| getClassIndicatorField().setType(CoreClassConstants.STRING); | |
| } else if (!getClassIndicatorMapping().isEmpty()) { | |
| Class type = null; | |
| Iterator fieldValuesEnum = getClassIndicatorMapping().values().iterator(); | |
| while (fieldValuesEnum.hasNext() && (type == null)) { | |
| Object value = fieldValuesEnum.next(); | |
| if (value.getClass() != getClass().getClass()) { | |
| type = value.getClass(); | |
| } | |
| } | |
| getClassIndicatorField().setType(type); | |
| } | |
| getDescriptor().getFields().addElement(getClassIndicatorField()); | |
| } | |
| } | |
| } | |
| /** | |
| * INTERNAL: | |
| * Initialized the inheritance properties of the descriptor once the mappings are initialized. | |
| * This is done before formal postInitialize during the end of mapping initialize. | |
| */ | |
| public void initialize(AbstractSession session) { | |
| super.initialize(session); | |
| // If we have a namespace resolver, check any of the class-indicator values | |
| // for prefixed type names and resolve the namespaces. | |
| if (!this.shouldUseClassNameAsIndicator()){ | |
| if(classIndicatorField != null){ | |
| XPathFragment frag = ((XMLField) classIndicatorField).getXPathFragment(); | |
| if (frag.getLocalName().equals(Constants.SCHEMA_TYPE_ATTRIBUTE) && javax.xml.XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI.equals(frag.getNamespaceURI())) { | |
| usesXsiType = true; | |
| } | |
| } | |
| // Must first clone the map to avoid concurrent modification. | |
| Iterator<Map.Entry> entries = new HashMap(getClassIndicatorMapping()).entrySet().iterator(); | |
| while (entries.hasNext()) { | |
| Map.Entry entry = entries.next(); | |
| Object key = entry.getKey(); | |
| if (key instanceof String) { | |
| XPathQName qname; | |
| String indicatorValue = (String) key; | |
| if (!usesXsiType || namespaceResolver == null) { | |
| qname = new XPathQName(indicatorValue, true); | |
| } else { | |
| int index = indicatorValue.indexOf(Constants.COLON); | |
| if (index != -1 && namespaceResolver != null) { | |
| String prefix = indicatorValue.substring(0, index); | |
| String localPart = indicatorValue.substring(index + 1); | |
| String uri = namespaceResolver.resolveNamespacePrefix(prefix); | |
| qname = new XPathQName(uri, localPart, true); | |
| } else { | |
| qname = new XPathQName(namespaceResolver.getDefaultNamespaceURI(), indicatorValue, true); | |
| } | |
| } | |
| getClassIndicatorMapping().put(qname, entry.getValue()); | |
| } else if (key instanceof QName) { | |
| XPathQName xpathQName = new XPathQName((QName) key, true); | |
| getClassIndicatorMapping().put(xpathQName, entry.getValue()); | |
| } | |
| } | |
| } | |
| //bug 6012173 - changed to initialize namespare uri on indicator field | |
| //need to be able to compare uri and local name during marshal to see if field is xsi type field | |
| if(getClassIndicatorField() != null){ | |
| XMLField classIndicatorXMLField; | |
| try { | |
| classIndicatorXMLField = (XMLField)getClassIndicatorField(); | |
| } catch (ClassCastException ex) { | |
| classIndicatorXMLField = new XMLField(getClassIndicatorField().getName()); | |
| setClassIndicatorField(classIndicatorXMLField); | |
| } | |
| XPathFragment frag = classIndicatorXMLField.getLastXPathFragment(); | |
| if ((frag != null) && frag.hasNamespace() && frag.getPrefix() !=null && (namespaceResolver != null)) { | |
| String uri = namespaceResolver.resolveNamespacePrefix(frag.getPrefix()); | |
| classIndicatorXMLField.getLastXPathFragment().setNamespaceURI(uri); | |
| } | |
| } | |
| } | |
| /** | |
| * INTERNAL: | |
| * This method is invoked only for the abstract descriptors. | |
| */ | |
| public Class classFromRow(AbstractRecord rowFromDatabase, AbstractSession session) throws DescriptorException { | |
| ((XMLRecord) rowFromDatabase).setSession(session); | |
| if (hasClassExtractor() || shouldUseClassNameAsIndicator()) { | |
| return super.classFromRow(rowFromDatabase, session); | |
| } | |
| Object indicator = rowFromDatabase.get(getClassIndicatorField()); | |
| if (indicator == AbstractRecord.noEntry) { | |
| return null; | |
| } | |
| if (indicator == null) { | |
| return null; | |
| } | |
| Class concreteClass; | |
| if (indicator instanceof String) { | |
| boolean namespaceAware = ((XMLRecord) rowFromDatabase).isNamespaceAware(); | |
| String indicatorValue = (String)indicator; | |
| int index = -1; | |
| if(namespaceAware){ | |
| index = indicatorValue.indexOf(((XMLRecord)rowFromDatabase).getNamespaceSeparator()); | |
| } | |
| if (index == -1) { | |
| if (namespaceAware && usesXsiType) { | |
| String uri = ((XMLRecord)rowFromDatabase).resolveNamespacePrefix(null); | |
| if (uri == null && ((XMLRecord)rowFromDatabase).getNamespaceResolver() != null) { | |
| uri = ((XMLRecord)rowFromDatabase).getNamespaceResolver().getDefaultNamespaceURI(); | |
| } | |
| XPathQName qname = new XPathQName(uri, indicatorValue, namespaceAware); | |
| concreteClass = (Class)this.classIndicatorMapping.get(qname); | |
| } else { | |
| XPathQName qname = new XPathQName(indicatorValue, namespaceAware); | |
| concreteClass = (Class)this.classIndicatorMapping.get(qname); | |
| } | |
| } else { | |
| String prefix = indicatorValue.substring(0, index); | |
| String localPart = indicatorValue.substring(index + 1); | |
| String uri = ((XMLRecord)rowFromDatabase).resolveNamespacePrefix(prefix); | |
| if (uri != null) { | |
| XPathQName qname = new XPathQName(uri, localPart, namespaceAware); | |
| concreteClass = (Class)this.classIndicatorMapping.get(qname); | |
| } else { | |
| concreteClass = (Class)this.classIndicatorMapping.get(indicatorValue); | |
| } | |
| } | |
| } else { | |
| concreteClass = (Class)this.classIndicatorMapping.get(indicator); | |
| } | |
| if (concreteClass == null) { | |
| throw DescriptorException.missingClassForIndicatorFieldValue(indicator, getDescriptor()); | |
| } | |
| return concreteClass; | |
| } | |
| public void setNamespaceResolver(NamespaceResolver resolver) { | |
| this.namespaceResolver = resolver; | |
| } | |
| /** | |
| * PUBLIC: | |
| * To set the class indicator field name. | |
| * This is the name of the field in the table that stores what type of object this is. | |
| */ | |
| public void setClassIndicatorFieldName(String fieldName) { | |
| if (fieldName == null) { | |
| setClassIndicatorField(null); | |
| } else { | |
| setClassIndicatorField(new XMLField(fieldName)); | |
| } | |
| } | |
| /** | |
| * INTERNAL: | |
| * Add abstract class indicator information to the database row. This is | |
| * required when building a row for an insert or an update of a concrete child | |
| * descriptor. | |
| */ | |
| public void addClassIndicatorFieldToRow(AbstractRecord databaseRow) { | |
| if (hasClassExtractor()) { | |
| return; | |
| } | |
| CoreField field = getClassIndicatorField(); | |
| Object value = getClassIndicatorValue(); | |
| if(usesXsiType){ | |
| boolean namespaceAware = ((XMLRecord)databaseRow).isNamespaceAware() || ((XMLRecord)databaseRow).hasCustomNamespaceMapper(); | |
| if(value instanceof String){ | |
| if(namespaceAware){ | |
| if(((XMLRecord)databaseRow).getNamespaceSeparator() != Constants.COLON){ | |
| value= ((String)value).replace(Constants.COLON, ((XMLRecord)databaseRow).getNamespaceSeparator()); | |
| } | |
| }else{ | |
| int colonIndex = ((String)value).indexOf(Constants.COLON); | |
| if(colonIndex > -1){ | |
| value = ((String)value).substring(colonIndex + 1); | |
| } | |
| } | |
| } | |
| } | |
| databaseRow.put(field, value); | |
| } | |
| } |