blob: 35bc4764344886436292d502d834e7c61f444eea [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:
// 05/16/2008-1.0M8 Guy Pelletier
// - 218084: Implement metadata merging functionality between mapping files
// 12/12/2008-1.1 Guy Pelletier
// - 249860: Implement table per class inheritance support.
// 03/27/2009-2.0 Guy Pelletier
// - 241413: JPA 2.0 Add EclipseLink support for Map type attributes
// 03/08/2010-2.1 Guy Pelletier
// - 303632: Add attribute-type for mapping attributes to EclipseLink-ORM
// 04/27/2010-2.1 Guy Pelletier
// - 309856: MappedSuperclasses from XML are not being initialized properly
// 05/14/2010-2.1 Guy Pelletier
// - 253083: Add support for dynamic persistence using ORM.xml/eclipselink-orm.xml
// 08/04/2010-2.1.1 Guy Pelletier
// - 315782: JPA2 derived identity metadata processing validation doesn't account for autoboxing
// 01/25/2011-2.3 Guy Pelletier
// - 333913: @OrderBy and <order-by/> without arguments should order by primary
// 03/24/2011-2.3 Guy Pelletier
// - 337323: Multi-tenant with shared schema support (part 1)
// 04/05/2011-2.3 Guy Pelletier
// - 337323: Multi-tenant with shared schema support (part 3)
// 07/03/2011-2.3.1 Guy Pelletier
// - 348756: m_cascadeOnDelete boolean should be changed to Boolean
// 07/06/2011-2.3.1 Guy Pelletier
// - 349906: NPE while using eclipselink in the application
// // 30/05/2012-2.4 Guy Pelletier
// - 354678: Temp classloader is still being used during metadata processing
// 06/20/2012-2.5 Guy Pelletier
// - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
// 10/09/2012-2.5 Guy Pelletier
// - 374688: JPA 2.1 Converter support
// 11/19/2012-2.5 Guy Pelletier
// - 389090: JPA 2.1 DDL Generation Support (foreign key metadata support)
// 11/28/2012-2.5 Guy Pelletier
// - 374688: JPA 2.1 Converter support
package org.eclipse.persistence.internal.jpa.metadata;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.helper.DatabaseType;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.jpa.metadata.accessors.MetadataAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EntityAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.classes.MappedSuperclassAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAccessibleObject;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAnnotation;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataClass;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataFactory;
import org.eclipse.persistence.internal.jpa.metadata.queries.ComplexTypeMetadata;
import org.eclipse.persistence.internal.jpa.metadata.xml.XMLEntityMappings;
import org.eclipse.persistence.platform.database.jdbc.JDBCTypes;
import org.eclipse.persistence.platform.database.oracle.plsql.OraclePLSQLTypes;
import org.eclipse.persistence.platform.database.oracle.plsql.PLSQLrecord;
/**
* INTERNAL:
* Abstract/common level for JPA Object/Relational metadata. This class handles
* the merging and overriding details for those metadata objects who care about
* it. For consistency, and ease of future work, all metadata objects added
* should extend this class even though they may not currently have a need for
* merging and overriding.
*
* Subclasses that care about merging need to concern themselves with the
* following methods:
* - getIdentifier() used to compare two named objects.
* - equals() used to compare if two objects have similar metadata.
* - setLocation() must be set on the accessible object. From annotations this
* is handled in the constructor. For XML objects you need to ensure their
* init method or processing method sets the location (that is, a mapping
* file) where the element was found.
*
* @author Guy Pelletier
* @since EclipseLink 1.0
*/
public abstract class ORMetadata {
// If loaded from an annotation this will be set and is used in the
// ignore logging message. Note: in a defaulted annotation case, this
// annotation will be null. This is not an issue though since we're
// obviously not going to ignore and log a message for this case.
private MetadataAnnotation m_annotation;
// The accessible object this metadata is tied to.
private MetadataAccessibleObject m_accessibleObject;
// Location could be 2 things:
// 1 - URL to a mapping file
// 2 - Annotated element (Class, Method or Field)
private Object m_location;
// The project this metadata belongs to. Having the project can facilitate
// individual metadata process methods since it contains the logger,
// persistence unit property metadata, the session etc.
protected MetadataProject m_project;
// If this metadata was loaded from XML the entity mappings will be set.
private XMLEntityMappings m_entityMappings;
// The tag name of the XML element. Used in logging messages and validation
// exceptions.
private String m_xmlElement;
// Lookup of classname to Class to resolve primitive classes
private static final Map<String, Class<?>> primitiveClasses = Collections.unmodifiableMap(getPrimitiveClassesMap());
// Lookup of boxed types of primitive classes.
private static final Map<String, String> boxedTypes = Collections.unmodifiableMap(getBoxedTypesMap());
/**
* INTERNAL:
* Used for defaulting case.
*/
protected ORMetadata() {}
/**
* INTERNAL:
* Used for OX loading.
*/
public ORMetadata(String xmlElement) {
m_xmlElement = xmlElement;
}
/**
* INTERNAL:
* For merging and overriding to work properly, all ORMetadata must be able
* to compare themselves for metadata equality.
*
* equals plays a big role in the shouldOverride() method from this class.
*/
@Override
public abstract boolean equals(Object objectToCompare);
/**
* INTERNAL:
* Used for defaulting. At minimum, all metadata should have this
* information available to them at process time (to ensure no errors during
* processing). Depending on the metadata processing needs you can get away
* with not having them set, however, any dependencies on the loader,
* metadata logger or the metadata project etc. will require these.
*/
protected ORMetadata(MetadataAccessibleObject accessibleObject, MetadataProject project, Object location) {
m_location = location;
m_accessibleObject = accessibleObject;
m_project = project;
}
/**
* INTERNAL:
* Used for annotation loading of metadata objects.
*/
public ORMetadata(MetadataAnnotation annotation, MetadataAccessor accessor) {
this(accessor.getAccessibleObject(), accessor.getProject(), accessor.getLocation());
m_annotation = annotation;
}
/**
* INTERNAL:
* Used for annotation loading of class and mapping accessors.
*/
public ORMetadata(MetadataAnnotation annotation, MetadataAccessibleObject accessibleObject, MetadataProject project) {
this(accessibleObject, project, accessibleObject);
m_annotation = annotation;
}
/**
* INTERNAL:
* Used for annotation loading and switching from one metadata object to
* a more specific one.
*/
public ORMetadata(ORMetadata orm) {
m_location = orm.getLocation();
m_accessibleObject = orm.getAccessibleObject();
m_project = orm.getProject();
m_annotation = orm.getAnnotation();
}
/**
* INTERNAL:
* Returns the accessible object for this accessor.
*/
protected MetadataAccessibleObject getAccessibleObject() {
return m_accessibleObject;
}
/**
* INTERNAL:
* Returns the name of the accessible object. If it is a field, it will
* return the field name. For a method it will return the method name.
*/
public String getAccessibleObjectName() {
return m_accessibleObject.getName();
}
/**
* INTERNAL:
* This is a value is that is used when logging messages for overriding.
* @see shouldOverride
*/
public MetadataAnnotation getAnnotation() {
return m_annotation;
}
/**
* INTERNAL:
* Quick lookup of a primitive boxed type.
*/
protected String getBoxedType(String type) {
String value = boxedTypes.get(type);
return (value != null) ? value : type;
}
/**
* INTERNAL:
* This method will return the current loader from the metadata factory used
* to created this ORMetadata.
*/
public ClassLoader getLoader() {
return getMetadataFactory().getLoader();
}
/**
* Return the DataType enum constant for the String type name.
* If not a type defined by the enums, then return a record type.
*/
protected DatabaseType getDatabaseTypeEnum(String type) {
if (type == null) {
return JDBCTypes.VARCHAR_TYPE;
}
try {
return JDBCTypes.valueOf(type);
} catch (Exception invalid) {
try {
return OraclePLSQLTypes.valueOf(type);
} catch (Exception alsoInvalid) {
ComplexTypeMetadata typeMetadata = getProject().getComplexTypeMetadata(type);
if (typeMetadata != null) {
return typeMetadata.process();
}
PLSQLrecord record = new PLSQLrecord();
record.setTypeName(type);
return record;
}
}
}
/**
* INTERNAL:
*/
public XMLEntityMappings getEntityMappings() {
return m_entityMappings;
}
/**
* INTERNAL:
* Return the fully qualified className using the package (if any) setting
* from XML.
*/
protected String getFullyQualifiedClassName(String className) {
Class<?> primitiveClass = getPrimitiveClassForName(className);
if (primitiveClass == null) {
if (loadedFromXML()) {
return getEntityMappings().getPackageQualifiedClassName(className);
}
return className;
} else {
return primitiveClass.getName();
}
}
/**
* INTERNAL:
* Sub classes that can uniquely be identified must override this method to
* allow the overriding and merging to uniquely identify objects. It will
* also be used when logging messages, that is, to provide a more detailed
* message.
*
* @see #shouldOverride(ORMetadata)
* @see #mergeORObjects(ORMetadata, ORMetadata)
* @see #mergeORObjectLists(List, List)
*/
protected String getIdentifier() {
return "";
}
/**
* INTERNAL:
* Return the Java class for the metadata class using the metadata loader.
* Callers to this method should only do so when the application loader
* (from deploy) is available. This method should not be called with the
* temp loader, see getJavaClassName instead which will provide a valid
* string class name that can be initialized at runtime instead.
*/
protected Class<?> getJavaClass(MetadataClass metadataClass) {
String className = metadataClass.getName();
Class<?> primitiveClass = getPrimitiveClassForName(className);
if (primitiveClass == null) {
String convertedClassName = className;
// Array type names need to be converted so they can be used with Class.forName()
int index = className.indexOf('[');
if ((index > 0) && (className.charAt(index + 1) == ']')){
convertedClassName = "[L" + className.substring(0, index) + ";";
}
// If we have an entity mappings object we need to append the
// package specification if it is specified.
convertedClassName = getFullyQualifiedClassName(convertedClassName);
return MetadataHelper.getClassForName(convertedClassName, getLoader());
} else {
return primitiveClass;
}
}
/**
* INTERNAL:
* Return the Java class name for the metadata class. This is the class name
* name metadata processing should set on descriptors and mappings to be
* initialized during the convertClassNamesToClasses call at runtime.
*/
public String getJavaClassName(MetadataClass metadataClass) {
String className = metadataClass.getName();
Class<?> primitiveClass = getPrimitiveClassForName(className);
if (primitiveClass == null) {
String convertedClassName = className;
// Array type names need to be converted so they can be used with Class.forName()
int index = className.indexOf('[');
if ((index > 0) && (className.charAt(index + 1) == ']')){
convertedClassName = "[L" + className.substring(0, index) + ";";
}
// If we have an entity mappings object we need to append the
// package specification if it is specified.
return getFullyQualifiedClassName(convertedClassName);
} else {
return primitiveClass.getName();
}
}
/**
* INTERNAL:
*/
public Object getLocation() {
return m_location;
}
/**
* INTERNAL:
* Return the metadata logger.
*/
public MetadataLogger getLogger() {
return m_project.getLogger();
}
/**
* INTERNAL:
* Return the MetadataClass for the class.
*/
public MetadataClass getMetadataClass(Class<?> javaClass) {
if (javaClass == null) {
return null;
}
return getMetadataClass(javaClass.getName());
}
/**
* INTERNAL:
* Return the MetadataClass for the class name.
*/
public MetadataClass getMetadataClass(String className) {
return getMetadataClass(className, true);
}
/**
* INTERNAL:
* Return the MetadataClass for the class name.
*/
public MetadataClass getMetadataClass(String className, boolean isLazy) {
return getMetadataFactory().getMetadataClass(getFullyQualifiedClassName(className), isLazy);
}
/**
* INTERNAL:
*/
public MetadataFactory getMetadataFactory() {
if (getAccessibleObject() != null) {
return getAccessibleObject().getMetadataFactory();
}
return getEntityMappings().getMetadataFactory();
}
/**
* INTERNAL:
* Helper method to return a field name from a candidate field name and a
* default field name.
*
* Requires the context from where this method is called to output the
* correct logging message when defaulting the field name.
*
* In some cases, both the name and defaultName could be "" or null,
* therefore, don't log a message and return name.
*/
protected String getName(String name, String defaultName, String context) {
return MetadataHelper.getName(name, defaultName, context, getLogger(), getAccessibleObjectName());
}
/**
* INTERNAL:
*/
protected Class<?> getPrimitiveClassForName(String className){
return (className == null) ? void.class : primitiveClasses.get(className);
}
/**
* INTERNAL:
* Return the MetadataProject.
*/
public MetadataProject getProject() {
return m_project;
}
/**
* INTERNAL:
* Any ORMetadata that supported mixed types, that is, text or other
* metadata should override this method.
*/
protected String getText() {
return null;
}
/**
* INTERNAL:
* This is a value is that is used when logging messages for overriding.
* @see #shouldOverride(ORMetadata)
*/
protected String getXMLElement() {
return m_xmlElement;
}
/**
* INTERNAL:
*/
protected boolean hasIdentifier() {
return ! getIdentifier().equals("");
}
/**
* INTERNAL:
* Any ORMetadata that supported mixed types, that is, text or other
* metadata should override this method.
*/
protected boolean hasText() {
return getText() != null && ! getText().equals("");
}
/**
* INTERNAL:
* This method should only be called on those objects that were loaded
* from XML and that need to initialize a class name. The assumption
* here is that an entity mappings object will be available.
*/
protected MetadataClass initXMLClassName(String className) {
return getMetadataClass(className);
}
/**
* INTERNAL:
* Any subclass that cares to do any more initialization (e.g. initialize a
* class) should override this method.
*/
public void initXMLObject(MetadataAccessibleObject accessibleObject, XMLEntityMappings entityMappings) {
m_project = entityMappings.getProject();
m_accessibleObject = accessibleObject;
setEntityMappings(entityMappings);
}
/**
* INTERNAL:
*/
protected void initXMLObject(ORMetadata metadata, MetadataAccessibleObject accessibleObject) {
if (metadata != null) {
metadata.initXMLObject(accessibleObject, m_entityMappings);
}
}
/**
* INTERNAL:
* It is assumed this is a list of ORMetadata
*/
protected <T extends ORMetadata> void initXMLObjects(List<T> metadatas, MetadataAccessibleObject accessibleObject) {
if (metadatas != null) {
for (T metadata : metadatas) {
metadata.initXMLObject(accessibleObject, m_entityMappings);
}
}
}
/**
* INTERNAL:
* This is to support legacy orm instance docs. In some cases, previous
* simple text elements may have been changed to a list of ORMetadata
* through spec churn. (e.g. {@code <convert>}). Method helps support backwards
* compatibility. If the text object is initialized the metadata list is
* set to null to ease further processing (logging, warnings, overrides etc.)
*/
protected <T extends ORMetadata> String initXMLTextObject(List<T> metadatas) {
if (metadatas != null && metadatas.size() == 1) {
T metadata = metadatas.get(0);
if (metadata.hasText()) {
String text = metadata.getText();
metadatas = null;
return text;
}
}
return null;
}
/**
* INTERNAL:
* Note: That annotations can default so the annotation may be null.
*/
public boolean loadedFromAnnotation() {
return m_annotation != null || ! loadedFromXML();
}
/**
* INTERNAL:
*/
public boolean loadedFromEclipseLinkXML() {
if (loadedFromXML()) {
return m_entityMappings.isEclipseLinkORMFile();
}
return false;
}
/**
* INTERNAL:
*/
public boolean loadedFromXML() {
return m_entityMappings != null;
}
/**
* INTERNAL:
* Subclasses that care to handle deeper merges should extend this method.
*/
protected void merge(ORMetadata metadata) {
// Does nothing at this level ...
}
/**
* INTERNAL:
* Convenience method to merge two lists of metadata objects. This does
* not check for duplicates or any overrides at this time. Just appends
* all items from list2 to list1.
*/
protected <T extends ORMetadata> List<T> mergeORObjectLists(List<T> list1, List<T> list2) {
List<T> newList = new ArrayList<>();
for (T obj1 : list1) {
boolean found = false;
for (T obj2 : list2) {
if (obj2.getIdentifier().equals(obj1.getIdentifier())) {
if (obj2.shouldOverride(obj1)) {
newList.add(obj2);
} else {
newList.add(obj1);
}
found = true;
break;
}
}
if (!found) {
newList.add(obj1);
}
}
// Now go through m2 and see what is not in m1
for (T obj2 : list2) {
boolean found = false;
for (ORMetadata obj1 : list1) {
if (obj2.getIdentifier().equals(obj1.getIdentifier())) {
found = true;
break;
}
}
if (!found) {
newList.add(obj2);
}
}
// Assign the first list to the newly built (merged and overridden list)
return newList;
}
/**
* INTERNAL:
* Convenience method to merge two objects that were loaded from XML. The
* merge is complete. If value2 is specified it will override value1,
* otherwise, value1 does not change.
*/
protected ORMetadata mergeORObjects(ORMetadata obj1, ORMetadata obj2) {
if (obj2 != null) {
if (obj1 != null) {
if (obj2.shouldOverride(obj1)) {
return obj2;
}
} else {
return obj2;
}
}
return obj1;
}
/**
* INTERNAL:
* Convenience method to merge two objects that were loaded from XML. The
* merge is complete. If value2 is specified it will override value1,
* otherwise, value1 does not.
*/
protected Object mergeSimpleObjects(Object obj1, Object obj2, ORMetadata otherMetadata, String xmlElement) {
if (obj1 == null && obj2 == null) {
return null;
} else {
SimpleORMetadata object1 = (obj1 == null) ? null : new SimpleORMetadata(obj1, getAccessibleObject(), getEntityMappings(), xmlElement);
SimpleORMetadata object2 = (obj2 == null) ? null : new SimpleORMetadata(obj2, otherMetadata.getAccessibleObject(), otherMetadata.getEntityMappings(), xmlElement);
// After this call return the value from the returned simple object.
return ((SimpleORMetadata) mergeORObjects(object1, object2)).getValue();
}
}
/**
* INTERNAL:
* This method should be called to reload an entity (that was either
* loaded from XML or an annotation) as a way of cloning it. This is needed
* when we process TABLE_PER_CLASS inheritance. We must process the parent
* classes for every subclasses descriptor. The processing is similar to
* that of processing a mapped superclass, in that we process the parents
* with the subclasses context (that is, the descriptor we are given).
*/
protected EntityAccessor reloadEntity(EntityAccessor entity, MetadataDescriptor descriptor) {
if (entity.loadedFromAnnotation()) {
// Create a new EntityAccesor.
EntityAccessor entityAccessor = new EntityAccessor(entity.getAnnotation(), entity.getJavaClass(), entity.getProject());
// Things we care about ...
descriptor.setDefaultAccess(entity.getDescriptor().getDefaultAccess());
entityAccessor.setDescriptor(descriptor);
return entityAccessor;
} else {
return entity.getEntityMappings().reloadEntity(entity, descriptor);
}
}
/**
* INTERNAL:
* This method should be called to reload a mapped superclass (that was either
* loaded from XML or an annotation) as a way of cloning it. This is needed
* when processing TABLE_PER_CLASS inheritance and when building individual
* entity accessor's mapped superclass list.
*/
protected MappedSuperclassAccessor reloadMappedSuperclass(MappedSuperclassAccessor mappedSuperclass, MetadataDescriptor descriptor) {
if (mappedSuperclass.loadedFromAnnotation()) {
// The descriptor for the mapped superclass is the one passed in
// which should be a valid entity accessor's descriptor.
MappedSuperclassAccessor mappedSuperclassAccessor = new MappedSuperclassAccessor(mappedSuperclass.getAnnotation(), mappedSuperclass.getJavaClass(), descriptor);
return mappedSuperclassAccessor;
} else {
return mappedSuperclass.getEntityMappings().reloadMappedSuperclass(mappedSuperclass, descriptor);
}
}
/**
* INTERNAL:
* Set the accessible object for this accessor.
*/
public void setAccessibleObject(MetadataAccessibleObject accessibleObject) {
m_accessibleObject = accessibleObject;
}
/**
* INTERNAL:
* Set the entity mappings (mapping file) for this OR object.
*/
public void setEntityMappings(XMLEntityMappings entityMappings) {
m_entityMappings = entityMappings;
m_location = entityMappings.getMappingFileOrURL();
}
/**
* INTERNAL:
* All field names should be set through this method to ensure delimited
* identifiers and upper casing defaults are set.
*/
protected void setFieldName(DatabaseField field, String name) {
// This may set the use delimited identifier flag to true.
field.setName(name, Helper.getDefaultStartDatabaseDelimiter(), Helper.getDefaultEndDatabaseDelimiter());
// The check is necessary to avoid overriding a true setting (set after
// setting the name of the field). We don't want to override it at this
// point if the global flag is set to false.
if (m_project.useDelimitedIdentifier()) {
field.setUseDelimiters(true);
} else if (m_project.getShouldForceFieldNamesToUpperCase() && ! field.shouldUseDelimiters()) {
field.useUpperCaseForComparisons(true);
}
}
/**
* INTERNAL:
* Go through this method if you can default a name. Provide the defaulting
* context to log to the correct context message to the user.
*/
protected void setFieldName(DatabaseField field, String defaultName, String context) {
setFieldName(field, getName(field.getName(), defaultName, context));
}
/**
* INTERNAL:
* Set the metadata project.
*/
public void setProject(MetadataProject project) {
m_project = project;
}
/**
* INTERNAL:
* Method to determine if this ORMetadata should override another. Assumes
* all ORMetadata that call this method have correctly implemented their
* equals method.
*/
public boolean shouldOverride(ORMetadata existing) {
MetadataLogger logger = getAccessibleObject().getLogger();
if (existing == null) {
// There is no existing, no override occurs, just use it!
return true;
} else if (existing.equals(this)) {
// The objects are the same. Could be that the user accidently
// cut and paste from one file to another or that we are processing
// an object from a mapped superclass which we have already
// processed. Therefore, log no messages, ignore it and fall
// through to return false.
} else {
// The objects are not the same ... need to look at them further.
if (loadedFromXML() && existing.loadedFromAnnotation()) {
// Need to override, log a message and return true;
if (hasIdentifier()) {
logger.logConfigMessage(MetadataLogger.OVERRIDE_NAMED_ANNOTATION_WITH_XML, existing.getAnnotation(), getIdentifier(), existing.getLocation(), getLocation());
} else {
logger.logConfigMessage(MetadataLogger.OVERRIDE_ANNOTATION_WITH_XML, existing.getAnnotation(), existing.getLocation(), getLocation());
}
return true;
} else if (loadedFromAnnotation() && existing.loadedFromXML()) {
// Log an override warning.
if (hasIdentifier()) {
logger.logConfigMessage(MetadataLogger.OVERRIDE_NAMED_ANNOTATION_WITH_XML, m_annotation, getIdentifier(), getLocation(), existing.getLocation());
} else {
logger.logConfigMessage(MetadataLogger.OVERRIDE_ANNOTATION_WITH_XML, m_annotation, getLocation(), existing.getLocation());
}
} else {
// Before throwing an exception we need to examine where the
// objects came from a little further. We know at this point
// that both objects were either loaded from XML or from
// annotations.
if (loadedFromEclipseLinkXML() && ! existing.loadedFromEclipseLinkXML()) {
// Need to override, log a message and return true.
if (hasIdentifier()) {
logger.logConfigMessage(MetadataLogger.OVERRIDE_NAMED_XML_WITH_ECLIPSELINK_XML, existing.getXMLElement(), getIdentifier(), existing.getLocation(), getLocation());
} else {
logger.logConfigMessage(MetadataLogger.OVERRIDE_XML_WITH_ECLIPSELINK_XML, existing.getXMLElement(), existing.getLocation(), getLocation());
}
return true;
} else if (! loadedFromEclipseLinkXML() && existing.loadedFromEclipseLinkXML()) {
// Log an override warning.
if (hasIdentifier()) {
logger.logConfigMessage(MetadataLogger.OVERRIDE_NAMED_XML_WITH_ECLIPSELINK_XML, existing.getXMLElement(), getIdentifier(), getLocation(), existing.getLocation());
} else {
logger.logConfigMessage(MetadataLogger.OVERRIDE_XML_WITH_ECLIPSELINK_XML, existing.getXMLElement(), getLocation(), existing.getLocation());
}
} else {
if (loadedFromAnnotation()) {
if (hasIdentifier()) {
throw ValidationException.conflictingNamedAnnotations(getIdentifier(), m_annotation, getLocation(), existing.getAnnotation(), existing.getLocation());
} else {
throw ValidationException.conflictingAnnotations(m_annotation, getLocation(), existing.getAnnotation(), existing.getLocation());
}
} else {
// To this point, if the objects are loaded from the
// same place and were loaded for the canonical model
// generation, assume the user has changed the xml and
// override it.
if (existing.getLocation().equals(getLocation()) && existing.getEntityMappings().loadedForCanonicalModel()) {
return true;
} else {
if (hasIdentifier()) {
throw ValidationException.conflictingNamedXMLElements(getIdentifier(), m_xmlElement, getLocation(), existing.getLocation());
} else {
throw ValidationException.conflictingXMLElements(m_xmlElement, getAccessibleObject(), getLocation(), existing.getLocation());
}
}
}
}
}
}
return false;
}
/**
* INTERNAL:
* Two lists are the same if they are the same size and their ordered
* elements are the same.
*/
protected boolean valuesMatch(List<Object> list1, List<Object> list2) {
if (list1.size() == list2.size()) {
for (Object obj1 : list1) {
if (! list2.contains(obj1)) {
return false;
}
}
return true;
} else {
return false;
}
}
/**
* INTERNAL:
*/
protected boolean valuesMatch(Object value1, Object value2) {
if ((value1 == null && value2 != null) || (value2 == null && value1 != null)) {
return false;
} else if (value1 == null && value2 == null) {
return true;
} else {
return value1.equals(value2);
}
}
private static Map<String, Class<?>> getPrimitiveClassesMap() {
Map<String, Class<?>> mappings = new HashMap<>(28);
mappings.put("", void.class);
mappings.put("void", void.class);
mappings.put("Boolean", Boolean.class);
mappings.put("Byte", Byte.class);
mappings.put("Character", Character.class);
mappings.put("Double", Double.class);
mappings.put("Float", Float.class);
mappings.put("Integer", Integer.class);
mappings.put("Long", Long.class);
mappings.put("Number", Number.class);
mappings.put("Short", Short.class);
mappings.put("String", String.class);
mappings.put("boolean", boolean.class);
mappings.put("byte", byte.class);
mappings.put("char", char.class);
mappings.put("double", double.class);
mappings.put("float", float.class);
mappings.put("int", int.class);
mappings.put("long", long.class);
mappings.put("short", short.class);
mappings.put("byte[]", new byte[0].getClass());
mappings.put("char[]", new char[0].getClass());
mappings.put("boolean[]", new boolean[0].getClass());
mappings.put("double[]", new double[0].getClass());
mappings.put("float[]", new float[0].getClass());
mappings.put("int[]", new int[0].getClass());
mappings.put("long[]", new long[0].getClass());
mappings.put("short[]", new short[0].getClass());
return mappings;
}
private static Map<String, String> getBoxedTypesMap() {
Map<String, String> mappings = new HashMap<>(17);
mappings.put("void", Void.class.getName());
mappings.put("boolean", Boolean.class.getName());
mappings.put("byte", Byte.class.getName());
mappings.put("char", Character.class.getName());
mappings.put("double", Double.class.getName());
mappings.put("float", Float.class.getName());
mappings.put("int", Integer.class.getName());
mappings.put("long", Long.class.getName());
mappings.put("short", Short.class.getName());
mappings.put("byte[]", new Byte[0].getClass().getName());
mappings.put("char[]", new Character[0].getClass().getName());
mappings.put("boolean[]", new Boolean[0].getClass().getName());
mappings.put("double[]", new Double[0].getClass().getName());
mappings.put("float[]", new Float[0].getClass().getName());
mappings.put("int[]", new Integer[0].getClass().getName());
mappings.put("long[]", new Long[0].getClass().getName());
mappings.put("short[]", new Short[0].getClass().getName());
return mappings;
}
// Made static final for performance reasons.
/**
* INTERNAL:
* Internal class to represent java type objects. XML only.
*/
private static final class SimpleORMetadata extends ORMetadata {
// Final only for style and performance reasons.
private final Object m_value;
/**
* INTERNAL:
*/
public SimpleORMetadata(Object value, MetadataAccessibleObject accessibleObject, XMLEntityMappings entityMappings, String xmlElement) {
super(xmlElement);
setAccessibleObject(accessibleObject);
setEntityMappings(entityMappings);
m_value = value;
}
/**
* INTERNAL:
*/
@Override
public boolean equals(Object objectToCompare) {
if (objectToCompare instanceof SimpleORMetadata) {
return valuesMatch(getValue(), ((SimpleORMetadata) objectToCompare).getValue());
}
return false;
}
@Override
public int hashCode() {
return m_value != null ? m_value.hashCode() : 0;
}
/**
* INTERNAL:
*/
public Object getValue() {
return m_value;
}
}
}