blob: 0670e30812b1e82011da90e7b1b1138d27ad0b0d [file] [log] [blame]
/*
* Copyright (c) 1998, 2020 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
// 05/16/2008-1.0M8 Guy Pelletier
// - 218084: Implement metadata merging functionality between mapping files
// 03/08/2010-2.1 Guy Pelletier
// - 303632: Add attribute-type for mapping attributes to EclipseLink-ORM
// 05/04/2010-2.1 Guy Pelletier
// - 309373: Add parent class attribute to EclipseLink-ORM
// 05/14/2010-2.1 Guy Pelletier
// - 253083: Add support for dynamic persistence using ORM.xml/eclipselink-orm.xml
// 01/25/2011-2.3 Guy Pelletier
// - 333488: Serializable attribute being defaulted to a variable one to one mapping and causing exception
// 07/16/2013-2.5.1 Guy Pelletier
// - 412384: Applying Converter for parameterized basic-type for joda-time's DateTime does not work
package org.eclipse.persistence.internal.jpa.metadata.accessors.objects;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.libraries.asm.Opcodes;
/**
* INTERNAL:
* An object to hold onto a valid JPA decorated class.
*
* @author Guy Pelletier
* @since TopLink 10.1.3/EJB 3.0 Preview
*/
public class MetadataClass extends MetadataAnnotatedElement {
protected boolean m_isLazy;
protected boolean m_isAccessible;
protected boolean m_isPrimitive;
protected boolean m_isJDK;
protected int m_modifiers;
// Stores the implements interfaces of this class.
protected List<String> m_interfaces;
// Stores a list of enclosed classes found inside this metadata class.
// E.g. inner classes, enums etc.
protected List<MetadataClass> m_enclosedClasses;
// Store the classes field metadata, keyed by the field's name.
protected Map<String, MetadataField> m_fields;
// Store the classes method metadata, keyed by the method's name.
// Method's next is used if multiple method with the same name.
protected Map<String, MetadataMethod> m_methods;
protected MetadataClass m_superclass;
protected String m_superclassName;
/**
* Create the metadata class with the class name.
*/
public MetadataClass(MetadataFactory factory, String name, boolean isLazy) {
super(factory);
setName(name);
// By default, set the type to be the same as the name. The canonical
// model generator relies on types which in most cases is the name, but
// the generator resolves generic types a little differently to
// correctly generate model classes.
setType(name);
m_isAccessible = true;
m_isLazy = isLazy;
}
/**
* Create the metadata class with the class name.
*/
public MetadataClass(MetadataFactory factory, String name) {
this(factory, name, false);
}
/**
* Create the metadata class based on the class.
* Mainly used for primitive defaults.
*/
public MetadataClass(MetadataFactory factory, Class cls) {
this(factory, cls.getName(), false);
m_isPrimitive = cls.isPrimitive();
}
/**
* INTERNAL:
*/
public void addEnclosedClass(MetadataClass enclosedClass) {
if (m_enclosedClasses == null) {
m_enclosedClasses = new ArrayList<MetadataClass>();
}
m_enclosedClasses.add(enclosedClass);
}
/**
* INTERNAL:
*/
public void addField(MetadataField field) {
if (m_fields == null) {
m_fields = new HashMap<String, MetadataField>();
}
m_fields.put(field.getName(), field);
}
/**
* INTERNAL:
*/
public void addInterface(String interfaceName) {
if (m_interfaces == null) {
m_interfaces = new ArrayList<String>();
}
m_interfaces.add(interfaceName);
}
/**
* INTERNAL:
*/
public void addMethod(MetadataMethod method) {
if (m_methods == null) {
m_methods = new HashMap<String, MetadataMethod>();
}
m_methods.put(method.getName(), method);
}
/**
* Allow comparison to Java classes and Metadata classes.
*/
@Override
public boolean equals(Object object) {
if (object instanceof Class) {
if (getName() == null) {
// Void's name is null.
return ((Class)object).getName() == null;
}
return getName().equals(((Class)object).getName());
}
return super.equals(object);
}
/**
* INTERNAL:
* Return if this class is or extends, or super class extends the class.
*/
public boolean extendsClass(Class javaClass) {
return extendsClass(javaClass.getName());
}
/**
* INTERNAL:
* Return if this class is or extends, or super class extends the class.
*/
public boolean extendsClass(String className) {
if (getName() == null) {
return className == null;
}
if (getName().equals(className)) {
return true;
}
if (getSuperclassName() == null) {
return false;
}
if (getSuperclassName().equals(className)) {
return true;
}
return getSuperclass().extendsClass(className);
}
/**
* INTERNAL:
* Return if this class is or extends, or super class extends the interface.
*/
public boolean extendsInterface(Class javaClass) {
return extendsInterface(javaClass.getName());
}
/**
* INTERNAL:
* Return if this class is or extends, or super class extends the interface.
*/
public boolean extendsInterface(String className) {
if (getName() == null) {
return false;
}
if (getName().equals(className)) {
return true;
}
if (getInterfaces().contains(className)) {
return true;
}
for (String interfaceName : getInterfaces()) {
if (getMetadataClass(interfaceName).extendsInterface(className)) {
return true;
}
}
if (getSuperclassName() == null) {
return false;
}
return getSuperclass().extendsInterface(className);
}
/**
* INTERNAL:
* Return the list of classes defined within this metadata class. E.g.
* enums and inner classes.
*/
public List<MetadataClass> getEnclosedClasses() {
if (m_enclosedClasses == null) {
m_enclosedClasses = new ArrayList<MetadataClass>();
}
return m_enclosedClasses;
}
/**
* INTERNAL:
* Return the field with the name.
* Search for any declared or inherited field.
*/
public MetadataField getField(String name) {
return getField(name, true);
}
/**
* INTERNAL:
* Return the field with the name.
* Search for any declared or inherited field.
*/
public MetadataField getField(String name, boolean checkSuperClass) {
MetadataField field = getFields().get(name);
if (checkSuperClass && (field == null) && (getSuperclassName() != null)) {
return getSuperclass().getField(name);
}
return field;
}
/**
* INTERNAL:
*/
public Map<String, MetadataField> getFields() {
if (m_fields == null) {
m_fields = new HashMap<String, MetadataField>();
if (m_isLazy) {
m_factory.getMetadataClass(getName(), false);
}
}
return m_fields;
}
/**
* INTERNAL:
*/
public List<String> getInterfaces() {
if (m_interfaces == null) {
m_interfaces = new ArrayList<String>();
}
return m_interfaces;
}
/**
* INTERNAL:
* Return the method with the name and no arguments.
*/
protected MetadataMethod getMethod(String name) {
return getMethods().get(name);
}
/**
* INTERNAL:
* Return the method with the name and argument types.
*/
public MetadataMethod getMethod(String name, Class[] arguments) {
List<String> argumentNames = new ArrayList<String>(arguments.length);
for (int index = 0; index < arguments.length; index++) {
argumentNames.add(arguments[index].getName());
}
return getMethod(name, argumentNames);
}
/**
* INTERNAL:
* Return the method with the name and argument types (class names).
*/
public MetadataMethod getMethod(String name, List<String> arguments) {
return getMethod(name, arguments, true);
}
/**
* INTERNAL:
* Return the method with the name and argument types (class names).
*/
public MetadataMethod getMethod(String name, List<String> arguments, boolean checkSuperClass) {
MetadataMethod method = getMethods().get(name);
while ((method != null) && !method.getParameters().equals(arguments)) {
method = method.getNext();
}
if (checkSuperClass && (method == null) && (getSuperclassName() != null)) {
return getSuperclass().getMethod(name, arguments);
}
return method;
}
/**
* INTERNAL:
* Return the method with the name and argument types (class names).
*/
public MetadataMethod getMethod(String name, String[] arguments) {
return getMethod(name, Arrays.asList(arguments));
}
/**
* INTERNAL:
* Return the method for the given property name.
*/
public MetadataMethod getMethodForPropertyName(String propertyName) {
MetadataMethod method;
String leadingChar = String.valueOf(propertyName.charAt(0)).toUpperCase();
String restOfName = propertyName.substring(1);
// Look for a getPropertyName() method
method = getMethod(Helper.GET_PROPERTY_METHOD_PREFIX.concat(leadingChar).concat(restOfName), new String[]{});
if (method == null) {
// Look for an isPropertyName() method
method = getMethod(Helper.IS_PROPERTY_METHOD_PREFIX.concat(leadingChar).concat(restOfName), new String[]{});
}
if (method != null) {
method.setSetMethod(method.getSetMethod(this));
}
return method;
}
/**
* INTERNAL:
*/
public Map<String, MetadataMethod> getMethods() {
if (m_methods == null) {
m_methods = new HashMap<String, MetadataMethod>();
if (m_isLazy) {
m_factory.getMetadataClass(getName(), false);
}
}
return m_methods;
}
/**
* INTERNAL:
*/
@Override
public int getModifiers() {
return m_modifiers;
}
/**
* INTERNAL:
*/
public MetadataClass getSuperclass() {
if (m_superclass == null) {
m_superclass = getMetadataClass(m_superclassName);
}
return m_superclass;
}
/**
* INTERNAL:
*/
public String getSuperclassName() {
return m_superclassName;
}
/**
* Return the ASM type name.
*/
public String getTypeName() {
if (isArray()) {
return getName().replace('.', '/');
} else if (isPrimitive()) {
if (getName().equals("int")) {
return "I";
} else if (getName().equals("long")) {
return "J";
} else if (getName().equals("short")) {
return "S";
} else if (getName().equals("boolean")) {
return "Z";
} else if (getName().equals("float")) {
return "F";
} else if (getName().equals("double")) {
return "D";
} else if (getName().equals("char")) {
return "C";
} else if (getName().equals("byte")) {
return "B";
}
}
return "L" + getName().replace('.', '/') + ";";
}
/**
* INTERNAL:
* Return true is this class accessible to be found.
*/
public boolean isAccessible() {
return m_isAccessible;
}
/**
* INTERNAL:
* Return if this class is an array type.
*/
public boolean isArray() {
return (getName() != null) && (getName().charAt(0) == '[');
}
/**
* INTERNAL:
* Return if this is extends Collection.
*/
public boolean isCollection() {
return extendsInterface(Collection.class);
}
/**
* INTERNAL:
* Return if this is extends Enum.
*/
public boolean isEnum() {
return extendsClass(Enum.class);
}
/**
* INTERNAL:
* Return if this is an interface (super is null).
*/
public boolean isInterface() {
return (Opcodes.ACC_INTERFACE & m_modifiers) != 0;
}
/**
* INTERNAL:
* Return if this is a JDK (java/javax) class.
*/
public boolean isJDK() {
return m_isJDK;
}
/**
* INTERNAL:
*/
public boolean isLazy() {
return m_isLazy;
}
/**
* INTERNAL:
* Return if this is extends List.
*/
public boolean isList() {
return extendsInterface(List.class);
}
/**
* INTERNAL:
* Return if this is extends Map.
*/
public boolean isMap() {
return extendsInterface(Map.class);
}
/**
* INTERNAL:
* Return if this is Object class.
*/
public boolean isObject() {
return getName().equals(Object.class.getName());
}
/**
* INTERNAL:
* Return if this is a primitive.
*/
public boolean isPrimitive() {
return m_isPrimitive;
}
/**
* INTERNAL:
* Return if this class extends Serializable or is an array type.
*/
public boolean isSerializable() {
if (isArray()) {
return true;
}
return extendsInterface(Serializable.class);
}
/**
* INTENAL:
* Return true is this class is the Serializable.class interface.
*/
public boolean isSerializableInterface() {
return getName().equals(Serializable.class.getName());
}
/**
* INTERNAL:
* Return true if this extends Set.
*/
public boolean isSet() {
return extendsInterface(Set.class);
}
/**
* INTERNAL:
* Return if this is the void class.
*/
public boolean isVoid() {
return getName().equals(void.class.getName()) || getName().equals(Void.class.getName());
}
/**
* INTERNAL:
*/
public void setIsAccessible(boolean isAccessible) {
m_isAccessible = isAccessible;
}
/**
* INTERNAL:
*/
public void setIsJDK(boolean isJDK) {
m_isJDK = isJDK;
}
/**
* INTERNAL:
*/
public void setIsLazy(boolean isLazy) {
m_isLazy = isLazy;
}
/**
* INTERNAL:
*/
@Override
public void setModifiers(int modifiers) {
m_modifiers = modifiers;
}
/**
* INTERNAL:
*/
@Override
public void setName(String name) {
super.setName(name);
if ((!MetadataFactory.ALLOW_JDK) && (name.startsWith("java.")
|| name.startsWith("javax.") || name.startsWith("jakarta.")
|| name.startsWith("org.eclipse.persistence.internal."))) {
setIsJDK(true);
}
}
/**
* INTERNAL:
*/
public void setSuperclass(MetadataClass superclass) {
m_superclass = superclass;
}
/**
* INTERNAL:
*/
public void setSuperclassName(String superclass) {
m_superclassName = superclass;
}
}