/******************************************************************************* * Copyright (c) 1998, 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: * 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.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. */ 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: */ 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: */ public void setModifiers(int modifiers) { m_modifiers = modifiers; } /** * INTERNAL: */ public void setName(String name) { super.setName(name); if ((!MetadataFactory.ALLOW_JDK) && (name.startsWith("java.") || name.startsWith("javax.") || 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; } }