All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.datanucleus.metadata.ClassMetaData Maven / Gradle / Ivy

Go to download

DataNucleus Core provides the primary components of a heterogenous Java persistence solution. It supports persistence API's being layered on top of the core functionality.

There is a newer version: 6.0.7
Show newest version
/**********************************************************************
Copyright (c) 2004 Andy Jefferson and others. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Contributors:
2004 Marco Schulze (NightLabs) - changed the behaviour to warn only if
        an inherited class declares an own objectid and it equals the
        one of the superclass.
2004 Andy Jefferson - Added discriminator/inheritance checks
2004 Erik Bengtson - changes for application identity
2004 Andy Jefferson - moved PK class checks out into JDOUtils
2007 Xuan Baldauf - little reduction in code duplication to anticipate changes regarding issue http://www.jpox.org/servlet/jira/browse/CORE-3272
    ...
**********************************************************************/
package org.datanucleus.metadata;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.exceptions.ClassNotResolvedException;
import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.identity.IdentityUtils;
import org.datanucleus.util.ClassUtils;
import org.datanucleus.util.Localiser;
import org.datanucleus.util.NucleusLogger;

/**
 * Representation of the MetaData of a class. Extends the abstract definition to include
 * implementations, fields, embedded-only tags. Has a parent PackageMetaData that
 * can contain the metadata for several classes.
 *
 * 

Lifecycle state

* This object supports 3 lifecycle states. The first is the raw * constructed object which represents pure MetaData (maybe from a MetaData * file). The second is a "populated" object which represents MetaData for a * Class with the metadata aligned to be appropriate for that Class. * The third is "initialised" once the internal arrays are created. * This object, once populated, will represent ALL fields in the class * (including static, final and transient fields). * *

Fields/Properties

* This object keeps a list of FieldMetaData/PropertyMetaData objects for the fields of this class. * In addition it has an array of FieldMetaData * objects representing those that are actually managed by JDO * ("managedFields"). This second set does not contain things like static, final * or transient fields since JDO doesn't support those yet. *

Fields are of 2 types. The first are normal fields of this class. * These have their own "relative" field number, relative to this class. * The second type are "overriding" fields which override the baseline field * in a superclass. These fields have no "relative" field number since they are * relative to this class (and such a relative field number would make no sense). * Fields are all added through addField() during the parse process, and * are updated during the populate/initialise process to define their relative field * numbers. Please refer to FieldMetaData for more details of fields. * *

Numbering of fields

* Fields of the class are numbered in 2 ways. The first way is the numbering * within a class. In a class, the field 'id's will start at 0. If a class is * inherited, it will also have a second numbering for its fields - the * "absolute" numbering. With "absolute" numbering, the fields start at the * first field in the root superclass which has absolute number 0, and they are * numbered from there, navigating down the hierarchy. In terms of what is * stored in the records, the FieldMetaData stores fieldId as the first * method (relative to the class it is in). The "absolute" numbering is * always derived from this and the inheritance hierarchy. */ public class ClassMetaData extends AbstractClassMetaData { private static final long serialVersionUID = -1029032058753152022L; /** List of implements. */ protected List implementations = null; // ------------------------------------------------------------------------- // Fields below here are not represented in the output MetaData. They are // for use internally in the operation of the JDO system. The majority are // for convenience to save iterating through the fields since the fields // are fixed once initialised. /*** ImplementsMetaData */ protected ImplementsMetaData[] implementsMetaData; /** is the persistable class abstract. */ protected boolean isAbstract; /** * Constructor. * Takes the basic string information found in the MetaData file. * @param parent The package to which this class belongs * @param name Name of class */ public ClassMetaData(final PackageMetaData parent, final String name) { super(parent, name); } /** * Constructor for creating the ClassMetaData for an implementation of a "persistent-interface". * @param imd MetaData for the "persistent-interface" * @param implClassName Name of the implementation class * @param copyFields Whether to copy the fields of the interface too */ public ClassMetaData(final InterfaceMetaData imd, String implClassName, boolean copyFields) { super(imd, implClassName, copyFields); } /** * Constructor for creating the ClassMetaData for an implementation of a "persistent-abstract-class". * @param cmd MetaData for the implementation of the "persistent-abstract-class" * @param implClassName Name of the implementation class */ public ClassMetaData(final ClassMetaData cmd, String implClassName) { super(cmd, implClassName); } /** * Method to provide the details of the class being represented by this * MetaData. This can be used to firstly provide defaults for attributes * that aren't specified in the MetaData, and secondly to report any errors * with attributes that have been specified that are inconsistent with the * class being represented. *

* One possible use of this method would be to take a basic ClassMetaData * for a class and call this, passing in the users class. This would then * add FieldMetaData for all fields in this class providing defaults for * all of these. * @param clr ClassLoaderResolver to use in loading any classes * @param primary the primary ClassLoader to use (or null) * @param mmgr MetaData manager */ public synchronized void populate(ClassLoaderResolver clr, ClassLoader primary, MetaDataManager mmgr) { if (isInitialised() || isPopulated()) { NucleusLogger.METADATA.error(Localiser.msg("044068", name)); throw new NucleusException(Localiser.msg("044068", fullName)).setFatal(); } if (populating) { return; } try { if (NucleusLogger.METADATA.isDebugEnabled()) { NucleusLogger.METADATA.debug(Localiser.msg("044075", fullName)); } populating = true; Class cls = loadClass(clr, primary, mmgr); isAbstract = Modifier.isAbstract(cls.getModifiers()); // Load any Annotations definition for this class if (!isMetaDataComplete()) { mmgr.addAnnotationsDataToClass(cls, this, clr); } // Load any ORM definition for this class mmgr.addORMDataToClass(cls, clr); // If a class is an inner class and is non-static it is invalid if (ClassUtils.isInnerClass(fullName) && !Modifier.isStatic(cls.getModifiers()) && persistenceModifier == ClassPersistenceModifier.PERSISTENCE_CAPABLE) { throw new InvalidClassMetaDataException("044063", fullName); } if (entityName == null) { // No entity name given so just default to the name of the class (without package) this.entityName = name; } determineSuperClassName(clr, cls, mmgr); inheritIdentity(); determineIdentity(); validateUserInputForIdentity(); addMetaDataForMembersNotInMetaData(cls, mmgr); // Set inheritance validateUserInputForInheritanceMetaData(isAbstract()); determineInheritanceMetaData(mmgr); applyDefaultDiscriminatorValueWhenNotSpecified(mmgr); if (objectidClass == null) { // No user-defined objectid-class but potentially have SingleFieldIdentity so make sure PK fields are set populateMemberMetaData(clr, cls, true, primary, mmgr); // PK fields determineObjectIdClass(mmgr); populateMemberMetaData(clr, cls, false, primary, mmgr); // Non-PK fields } else { populateMemberMetaData(clr, cls, true, primary, mmgr); populateMemberMetaData(clr, cls, false, primary, mmgr); determineObjectIdClass(mmgr); } validateUnmappedColumns(); // populate the implements if (implementations != null) { for (int i=0; i superintf : ClassUtils.getSuperinterfaces(cls)) { AbstractClassMetaData acmd = mmgr.getMetaDataForInterface(superintf, clr); if (acmd != null && acmd.getInheritanceMetaData() != null) { if (acmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.NEW_TABLE) { // Found it return acmd; } else if (acmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUPERCLASS_TABLE) { // Try further up the hierarchy return getMetaDataForSuperinterfaceManagingTable(superintf, clr, mmgr); } } } return null; } /** * Add MetaData of fields/properties not declared in MetaData. * @param cls Class represented by this metadata * @param mmgr MetaData manager */ protected void addMetaDataForMembersNotInMetaData(Class cls, MetaDataManager mmgr) { // Access API since we treat things differently for JPA and JDO String api = mmgr.getNucleusContext().getApiName(); // Add fields/properties for the class that don't have MetaData. // We use Reflection here since JDOImplHelper would only give use info // for enhanced files (and the enhancer needs unenhanced as well). // NOTE 1 : We ignore fields/properties in superclasses // NOTE 2 : We ignore "enhanced" fields/properties (added by the enhancer) // NOTE 3 : We ignore inner class fields/properties (containing "$") // NOTE 4 : We sort the fields/properties into ascending alphabetical order Collections.sort(members); try { // check if we have any persistent properties boolean hasProperties = false; for (int i=0; i j) { NucleusLogger.GENERAL.debug(">> Class=" + cls.getName() + " method=" + allclsMethods[i].getName() + " declared to return " + methodTypeVar + ", namely TypeVariable(" + j + ") of " + declCls.getName() + " so using " + paramTypeArgs[j]); String propertyName = allclsMethods[i].getDeclaringClass().getName() + "." + ClassUtils.getFieldNameForJavaBeanGetter(allclsMethods[i].getName()); if (Collections.binarySearch(members, propertyName) < 0) { // No property of this name - add a default PropertyMetaData for this method with the type set to what we need NucleusLogger.METADATA.debug(Localiser.msg("044060", fullName, propertyName)); AbstractMemberMetaData mmd = new PropertyMetaData(this, propertyName); mmd.type = (Class) paramTypeArgs[j]; members.add(mmd); Collections.sort(members); } else { // TODO Cater for the user overriding it } } } } } } } } // Process all (reflected) fields in the populating class Field[] clsFields = cls.getDeclaredFields(); for (int i=0;i j) { NucleusLogger.GENERAL.debug(">> Class=" + cls.getName() + " field=" + theclsFields[i].getName() + " declared to be " + fieldTypeVar + ", namely TypeVariable(" + j + ") of " + declCls.getName() + " so using " + paramTypeArgs[j]); String fieldName = declCls.getName() + "." + theclsFields[i].getName(); if (Collections.binarySearch(members, fieldName) < 0) { // No property of this name - add a default FieldMetaData for this method with the type set to what we need NucleusLogger.METADATA.debug(Localiser.msg("044060", fullName, fieldName)); AbstractMemberMetaData mmd = new FieldMetaData(this, fieldName); if (hasProperties && api.equalsIgnoreCase("JPA")) { // JPA : Class has properties but field not present, so add as transient field (JPA) mmd.setNotPersistent(); } mmd.type = (Class) paramTypeArgs[j]; members.add(mmd); Collections.sort(members); } else { // TODO Cater for the user overriding it } } } } } } } } } catch (Exception e) { NucleusLogger.METADATA.error(e.getMessage(), e); throw new RuntimeException(e.getMessage()); } } /** * Populate MetaData for all members. * @param clr The ClassLoaderResolver * @param cls This class * @param pkMembers Process pk fields/properties (or non-PK if false) * @param primary the primary ClassLoader to use (or null) * @param mmgr MetaData manager * @throws InvalidMetaDataException if the Class for a declared type in a field cannot be loaded by the clr * @throws InvalidMetaDataException if a field declared in the MetaData does not exist in the Class */ protected void populateMemberMetaData(ClassLoaderResolver clr, Class cls, boolean pkMembers, ClassLoader primary, MetaDataManager mmgr) { Collections.sort(members); // Populate the real field values. This will populate any containers in these members also Iterator memberIter = members.iterator(); while (memberIter.hasNext()) { AbstractMemberMetaData mmd = (AbstractMemberMetaData)memberIter.next(); if (pkMembers == mmd.isPrimaryKey()) { Class fieldCls = cls; if (mmd.className != null && mmd.className.equals("#UNKNOWN")) { // Field is for a superclass but we didn't know which at creation so resolve it if (pcSuperclassMetaData != null) { AbstractMemberMetaData superFmd = pcSuperclassMetaData.getMetaDataForMember(mmd.getName()); if (superFmd != null) { // Field is for a superclass so set its "className" mmd.className = (superFmd.className != null) ? superFmd.className : superFmd.getClassName(); } } else { // No superclass so it doesn't make sense so assume to be for this class mmd.className = null; } } if (!mmd.fieldBelongsToClass()) { // Field overrides a field in a superclass, so find the class try { fieldCls = clr.classForName(mmd.getClassName()); } catch (ClassNotResolvedException cnre) { // Not found at specified location, so try the same package as this class String fieldClassName = getPackageName() + "." + mmd.getClassName(); try { fieldCls = clr.classForName(fieldClassName); mmd.setClassName(fieldClassName); } catch (ClassNotResolvedException cnre2) { NucleusLogger.METADATA.error(Localiser.msg("044092", fullName, mmd.getFullFieldName(), fieldClassName)); throw new InvalidClassMetaDataException("044092", fullName, mmd.getFullFieldName(), fieldClassName); } } } boolean populated = false; if (mmd instanceof PropertyMetaData) { // User class must have a getter and setter for this property as per Java Beans Method getMethod = null; try { // Find the getter // a). Try as a standard form of getter (getXXX) getMethod = fieldCls.getDeclaredMethod(ClassUtils.getJavaBeanGetterName(mmd.getName(), false)); // Only public? } catch (Exception e) { try { // b). Try as a boolean form of getter (isXXX) getMethod = fieldCls.getDeclaredMethod(ClassUtils.getJavaBeanGetterName(mmd.getName(), true)); // Only public? } catch (Exception e2) { } } if (getMethod == null && mmd.getPersistenceModifier() != FieldPersistenceModifier.NONE) { // Property is persistent yet no getter! throw new InvalidClassMetaDataException("044073", fullName, mmd.getName()); } Method setMethod = null; try { // Find the setter String setterName = ClassUtils.getJavaBeanSetterName(mmd.getName()); Method[] methods = fieldCls.getMethods(); // Only gives public methods for (int i=0;i\n"); // Identity if (identityMetaData != null) { sb.append(identityMetaData.toString(prefix + indent,indent)); } // PrimaryKey if (primaryKeyMetaData != null) { sb.append(primaryKeyMetaData.toString(prefix + indent,indent)); } // Inheritance if (inheritanceMetaData != null) { sb.append(inheritanceMetaData.toString(prefix + indent,indent)); } // Add Version if (versionMetaData != null) { sb.append(versionMetaData.toString(prefix + indent,indent)); } // Add joins if (joinMetaData != null) { for (int i=0; i 0) { // Not yet initialised so use input members Iterator iter = members.iterator(); while (iter.hasNext()) { AbstractMemberMetaData mmd = (AbstractMemberMetaData)iter.next(); sb.append(mmd.toString(prefix + indent,indent)); } } // Add unmapped columns if (unmappedColumns != null) { for (int i=0;i iter = queries.iterator(); while (iter.hasNext()) { QueryMetaData q = iter.next(); sb.append(q.toString(prefix + indent,indent)); } } // Add fetch-groups if (fetchGroups != null) { for (FetchGroupMetaData fgmd : fetchGroups) { sb.append(fgmd.toString(prefix + indent,indent)); } } // Add extensions sb.append(super.toString(prefix + indent,indent)); sb.append(prefix + "\n"); return sb.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy