/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * // Copyright (c) 1998, 2007, Oracle. All rights reserved. * * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can obtain * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt. * Sun designates this particular file as subject to the "Classpath" exception * as provided by Sun in the GPL Version 2 section of the License file that * accompanied this code. If applicable, add the following below the License * Header, with the fields enclosed by brackets [] replaced by your own * identifying information: "Portions Copyrighted [year] * [name of copyright owner]" * * Contributor(s): * * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package oracle.toplink.essentials.descriptors; import java.io.*; import java.lang.reflect.*; import java.security.AccessController; import java.security.PrivilegedActionException; import java.util.*; import oracle.toplink.essentials.exceptions.*; import oracle.toplink.essentials.expressions.*; import oracle.toplink.essentials.internal.descriptors.OptimisticLockingPolicy; import oracle.toplink.essentials.internal.expressions.*; import oracle.toplink.essentials.internal.helper.*; import oracle.toplink.essentials.internal.queryframework.*; import oracle.toplink.essentials.mappings.*; import oracle.toplink.essentials.queryframework.*; import oracle.toplink.essentials.internal.sessions.AbstractRecord; import oracle.toplink.essentials.internal.sessions.AbstractSession; import oracle.toplink.essentials.internal.security.PrivilegedAccessHelper; import oracle.toplink.essentials.internal.security.PrivilegedClassForName; /** *
Purpose: Allows customization of an object's inheritance. * The primary supported inheritance model uses a class type indicator * column in the table that stores the object's class type. * The class-to-type mapping is specified on this policy. * The full class name can also be used for the indicator instead of the mapping. *
Each subclass can either share their parents table, or in addition add their * own table(s). *
For legacy models a customized inheritance class-extractor can be provided.
* This allows Java code to be used to compute the class type to use for a row.
* When this customized inheritance model is used an only-instances and with-all-subclasses
* filter expression may be required for concrete and branch querying.
*/
public class InheritancePolicy implements Serializable, Cloneable {
protected Class parentClass;
protected String parentClassName;
protected ClassDescriptor parentDescriptor;
protected Vector childDescriptors;
protected transient DatabaseField classIndicatorField;
protected transient Map classIndicatorMapping;
protected transient Map classNameIndicatorMapping;
protected transient boolean shouldUseClassNameAsIndicator;
protected transient Boolean shouldReadSubclasses;
protected transient DatabaseTable readAllSubclassesView;
protected transient Vector allChildClassIndicators;
protected transient Expression onlyInstancesExpression;
protected transient Expression withAllSubclassesExpression;
// null if there are no childrenTables, otherwise all tables for reference class plus childrenTables
protected transient Vector allTables;
// all tables for all subclasses (subclasses of subclasses included), should be in sync with childrenTablesJoinExpressions.
protected transient List childrenTables;
// join expression for each child table, keyed by the table, should be in sync with childrenTables.
protected transient Map childrenTablesJoinExpressions;
// all expressions from childrenTablesJoinExpressions ANDed together
protected transient Expression childrenJoinExpression;
/** Allow for class extraction method to be specified. */
protected transient ClassExtractor classExtractor;
protected ClassDescriptor descriptor;
protected boolean shouldAlwaysUseOuterJoin;
//CR 4005
protected boolean useDescriptorsToValidateInheritedObjects;
// used by the entity-mappings XML writer to determine inheritance strategy
protected boolean isJoinedStrategy;
/**
* INTERNAL:
* Create a new policy.
* Only descriptors involved in inheritence should have a policy.
*/
public InheritancePolicy() {
this.classIndicatorMapping = new HashMap(10);
this.classNameIndicatorMapping = new HashMap(10);
this.shouldUseClassNameAsIndicator = false;
this.allChildClassIndicators = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance();
this.childDescriptors = oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(5);
this.setJoinedStrategy();
}
/**
* INTERNAL:
* Create a new policy.
* Only descriptors involved in inheritence should have a policy.
*/
public InheritancePolicy(ClassDescriptor descriptor) {
this();
this.descriptor = descriptor;
}
/**
* INTERNAL:
* Add child descriptor to the parent descriptor.
*/
public void addChildDescriptor(ClassDescriptor childDescriptor) {
getChildDescriptors().addElement(childDescriptor);
}
/**
* INTERNAL:
* childrenTablesJoinExpressions, childrenTables, allTables and childrenJoinExpression
* are created simultaneously and kept in sync.
*/
protected void addChildTableJoinExpression(DatabaseTable table, Expression expression) {
if(childrenTablesJoinExpressions == null) {
childrenTablesJoinExpressions = new HashMap();
// childrenTables should've been null, too
childrenTables = new ArrayList();
// allTables should've been null, too
allTables = new Vector(getDescriptor().getTables());
}
childrenTables.add(table);
allTables.add(table);
childrenTablesJoinExpressions.put(table, expression);
childrenJoinExpression = expression.and(childrenJoinExpression);
}
/**
* INTERNAL:
* call addChildTableJoinExpression on all parents
*/
public void addChildTableJoinExpressionToAllParents(DatabaseTable table, Expression expression) {
ClassDescriptor parentDescriptor = getParentDescriptor();
while(parentDescriptor != null) {
InheritancePolicy parentPolicy = parentDescriptor.getInheritancePolicy();
parentPolicy.addChildTableJoinExpression(table, expression);
parentDescriptor = parentPolicy.getParentDescriptor();
}
}
/**
* PUBLIC:
* Add a class indicator for the root classes subclass.
* The indicator is used to determine the class to use for a row read from the database,
* and to query only instances of a class from the database.
* Every concrete persistent subclass must have a single unique indicator defined for it.
* If the root class is concrete then it must also define an indicator.
* Only the root class's descriptor of the entire inheritance hierarchy can define the class indicator mapping.
*/
public void addClassIndicator(Class childClass, Object typeValue) {
// Note we should think about supporting null values.
// Store as key and value for bi-diractional lookup.
getClassIndicatorMapping().put(typeValue, childClass);
getClassIndicatorMapping().put(childClass, typeValue);
}
/**
* INTERNAL:
* Add the class name reference by class name, used by the MW.
*/
public void addClassNameIndicator(String childClassName, Object typeValue) {
getClassNameIndicatorMapping().put(childClassName, typeValue);
}
/**
* 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.
* This is only used to build a template row.
*/
public void addClassIndicatorFieldToInsertRow(AbstractRecord databaseRow) {
if (hasClassExtractor()) {
return;
}
DatabaseField field = getClassIndicatorField();
databaseRow.put(field, null);
}
/**
* 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;
}
DatabaseField field = getClassIndicatorField();
Object value = getClassIndicatorValue();
databaseRow.put(field, value);
}
/**
* INTERNAL:
* Post initialize the child descriptors
*/
protected void addClassIndicatorTypeToParent(Object indicator) {
ClassDescriptor parentDescriptor = getDescriptor().getInheritancePolicy().getParentDescriptor();
if (parentDescriptor.getInheritancePolicy().isChildDescriptor()) {
if (parentDescriptor.getInheritancePolicy().shouldReadSubclasses()) {
parentDescriptor.getInheritancePolicy().getAllChildClassIndicators().addElement(indicator);
}
parentDescriptor.getInheritancePolicy().addClassIndicatorTypeToParent(indicator);
}
}
/**
* INTERNAL:
* Recursively adds fields to all the parents
*/
protected void addFieldsToParent(Vector fields) {
if (isChildDescriptor()) {
if (getParentDescriptor().isInvalid()) {
return;
}
ClassDescriptor parentDescriptor = getParentDescriptor();
if (parentDescriptor.getInheritancePolicy().shouldReadSubclasses()) {
Helper.addAllUniqueToVector(parentDescriptor.getAllFields(), fields);
}
parentDescriptor.getInheritancePolicy().addFieldsToParent(fields);
}
}
/**
* INTERNAL:
* Return a select statement that will be used to query the class indicators required to query.
* This is used in the abstract-multiple read.
*/
public SQLSelectStatement buildClassIndicatorSelectStatement(ObjectLevelReadQuery query) {
SQLSelectStatement selectStatement;
selectStatement = new SQLSelectStatement();
selectStatement.useDistinct();
selectStatement.addTable(classIndicatorField.getTable());
selectStatement.addField(getClassIndicatorField());
// 2612538 - the default size of IdentityHashtable (32) is appropriate
IdentityHashtable clonedExpressions = new IdentityHashtable();
selectStatement.setWhereClause(((ExpressionQueryMechanism)query.getQueryMechanism()).buildBaseSelectionCriteria(false, clonedExpressions));
appendWithAllSubclassesExpression(selectStatement);
selectStatement.setTranslationRow(query.getTranslationRow());
selectStatement.normalize(query.getSession(), getDescriptor(), clonedExpressions);
ExpressionQueryMechanism m = (ExpressionQueryMechanism)query.getQueryMechanism();
return selectStatement;
}
/**
* INTERNAL:
* Append the branch with all subclasses expression to the statement.
*/
public void appendWithAllSubclassesExpression(SQLSelectStatement selectStatement) {
if (getWithAllSubclassesExpression() != null) {
// For Flashback: Must always rebuild with simple expression on right.
if (selectStatement.getWhereClause() == null) {
selectStatement.setWhereClause((Expression)getWithAllSubclassesExpression().clone());
} else {
selectStatement.setWhereClause(selectStatement.getWhereClause().and(getWithAllSubclassesExpression()));
}
}
}
/**
* INTERNAL:
* Build a select statement for all subclasses on the view using the same
* selection criteria as the query.
*/
public SQLSelectStatement buildViewSelectStatement(ObjectLevelReadQuery query) {
// 2612538 - the default size of IdentityHashtable (32) is appropriate
IdentityHashtable clonedExpressions = new IdentityHashtable();
ExpressionQueryMechanism mechanism = (ExpressionQueryMechanism)query.getQueryMechanism();
// CR#3166555 - Have the mechanism build the statement to avoid duplicating code and ensure that lock-mode, hints, hierarchical, etc. are set.
SQLSelectStatement selectStatement = mechanism.buildBaseSelectStatement(false, clonedExpressions);
selectStatement.setTables(oracle.toplink.essentials.internal.helper.NonSynchronizedVector.newInstance(1));
selectStatement.addTable(getReadAllSubclassesView());
// Case, normal read for branch inheritence class that reads subclasses all in its own table(s).
if (getWithAllSubclassesExpression() != null) {
Expression branchIndicator = (Expression)getWithAllSubclassesExpression().clone();
if (branchIndicator != null) {
selectStatement.setWhereClause(branchIndicator.and(selectStatement.getWhereClause()));
}
}
selectStatement.setFields(mechanism.getSelectionFields(selectStatement, true));
selectStatement.normalizeForView(query.getSession(), getDescriptor(), clonedExpressions);
// Allow for joining indexes to be computed to ensure distinct rows
((ObjectLevelReadQuery)query).getJoinedAttributeManager().computeJoiningMappingIndexes(false, query.getSession(), 0);
return selectStatement;
}
/**
* INTERNAL:
* This method is invoked only for the abstract descriptors.
*/
public Class classFromRow(AbstractRecord rowFromDatabase, AbstractSession session) throws DescriptorException {
if (hasClassExtractor()) {
return getClassExtractor().extractClassFromRow(rowFromDatabase, session);
}
Object classFieldValue = session.getDatasourcePlatform().getConversionManager().convertObject(rowFromDatabase.get(getClassIndicatorField()), getClassIndicatorField().getType());
if (classFieldValue == null) {
throw DescriptorException.missingClassIndicatorField(rowFromDatabase, getDescriptor());
}
Class concreteClass;
if (!shouldUseClassNameAsIndicator()) {
concreteClass = (Class)getClassIndicatorMapping().get(classFieldValue);
if (concreteClass == null) {
throw DescriptorException.missingClassForIndicatorFieldValue(classFieldValue, getDescriptor());
}
} else {
try {
String className = (String)classFieldValue;
//PWK 2.5.1.7 can not use class for name, must go through conversion manager.
//Should use the root Descriptor's classloader to avoid loading from a loader other
//than the one that loaded the project
concreteClass = getDescriptor().getJavaClass().getClassLoader().loadClass(className);
if (concreteClass == null) {
throw DescriptorException.missingClassForIndicatorFieldValue(classFieldValue, getDescriptor());
}
} catch (ClassNotFoundException e) {
throw DescriptorException.missingClassForIndicatorFieldValue(classFieldValue, getDescriptor());
} catch (ClassCastException e) {
throw DescriptorException.missingClassForIndicatorFieldValue(classFieldValue, getDescriptor());
}
}
return concreteClass;
}
/**
* INTERNAL:
* Clone the policy
*/
public Object clone() {
InheritancePolicy clone = null;
try {
clone = (InheritancePolicy)super.clone();
if (hasClassIndicator()) {
clone.setClassIndicatorField((DatabaseField)clone.getClassIndicatorField().clone());
}
} catch (Exception exception) {
throw new InternalError("clone failed");
}
return clone;
}
/**
* INTERNAL:
* Convert all the class-name-based settings in this InheritancePolicy to actual class-based
* settings. This method is used when converting a project that has been built
* with class names to a project with classes.
* @param classLoader
*/
public void convertClassNamesToClasses(ClassLoader classLoader){
if (parentClassName == null){
return;
}
Class parentClass = null;
try{
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
parentClass = (Class)AccessController.doPrivileged(new PrivilegedClassForName(parentClassName, true, classLoader));
} catch (PrivilegedActionException exception) {
throw ValidationException.classNotFoundWhileConvertingClassNames(parentClassName, exception.getException());
}
} else {
parentClass = oracle.toplink.essentials.internal.security.PrivilegedAccessHelper.getClassForName(parentClassName, true, classLoader);
}
} catch (ClassNotFoundException exc){
throw ValidationException.classNotFoundWhileConvertingClassNames(parentClassName, exc);
}
setParentClass(parentClass);
}
/**
* PUBLIC:
* Set the descriptor to only read instance of itself when queried.
* This is used with inheritance to configure the result of queries.
* By default this is true for root inheritance descriptors, and false for all others.
*/
public void dontReadSubclassesOnQueries() {
setShouldReadSubclasses(false);
}
/**
* PUBLIC:
* Set the descriptor not to use the class' full name as the indicator.
* The class indicator is used with inheritance to determine the class from a row.
* By default a class indicator mapping is required, this can be set to true if usage of the class name is desired.
* The field must be of a large enough size to store the fully qualified class name.
*/
public void dontUseClassNameAsIndicator() {
setShouldUseClassNameAsIndicator(false);
}
/**
* INTERNAL:
* Stores class indicators for all child and children's children.
* Used for queries on branch classes only.
*/
protected Vector getAllChildClassIndicators() {
return allChildClassIndicators;
}
/**
* INTERNAL:
* Returns all the child descriptors, even descriptors for subclasses of
* subclasses.
* Required for bug 3019934.
*/
public Vector getAllChildDescriptors() {
// Guess the number of child descriptors...
Vector allChildDescriptors = new Vector(this.getAllChildClassIndicators().size());
return getAllChildDescriptors(allChildDescriptors);
}
/**
* INTERNAL:
* Recursive subroutine of getAllChildDescriptors.
*/
protected Vector getAllChildDescriptors(Vector allChildDescriptors) {
for (Enumeration enumtr = getChildDescriptors().elements(); enumtr.hasMoreElements();) {
ClassDescriptor childDescriptor = (ClassDescriptor)enumtr.nextElement();
allChildDescriptors.addElement(childDescriptor);
childDescriptor.getInheritancePolicyOrNull().getAllChildDescriptors(allChildDescriptors);
}
return allChildDescriptors;
}
/**
* INTERNAL:
* if reads subclasses, all tables for all read subclasses (indirect included).
*/
public List getChildrenTables() {
return childrenTables;
}
/**
* INTERNAL:
* join expression for each child table, keyed by the table
*/
public Map getChildrenTablesJoinExpressions() {
return childrenTablesJoinExpressions;
}
/**
* INTERNAL:
* all expressions from childrenTablesJoinExpressions ANDed together
*/
public Expression getChildrenJoinExpression() {
return childrenJoinExpression;
}
/**
* INTERNAL:
* all tables for reference class plus childrenTables
*/
public Vector getAllTables() {
if(allTables == null) {
return this.getDescriptor().getTables();
} else {
return allTables;
}
}
/**
* INTERNAL:
* Return all the immediate child descriptors. Only descriptors from
* direct subclasses are returned.
*/
public Vector getChildDescriptors() {
return childDescriptors;
}
/**
* INTERNAL:
* Return all the classExtractionMethod
*/
protected Method getClassExtractionMethod() {
if (classExtractor instanceof MethodClassExtractor) {
return ((MethodClassExtractor)classExtractor).getClassExtractionMethod();
} else {
return null;
}
}
/**
* ADVANCED:
* A class extraction method can be registered with the descriptor to override the default inheritance mechanism.
* This allows for the class indicator field to not be used, and a user defined one instead.
* The method registered must be a static method on the class that the descriptor is for,
* the method must take DatabaseRow as argument, and must return the class to use for that row.
* This method will be used to decide which class to instantiate when reading from the database.
* It is the application's responsiblity to populate any typing information in the database required
* to determine the class from the row.
* If this method is used then the class indicator field and mapping cannot be used,
* also the descriptor's withAllSubclasses and onlyInstances expressions must also be setup correctly.
*
* @see #setWithAllSubclassesExpression(Expression)
* @see #setOnlyInstancesExpression(Expression)
*/
public String getClassExtractionMethodName() {
if (classExtractor instanceof MethodClassExtractor) {
return ((MethodClassExtractor)classExtractor).getClassExtractionMethodName();
} else {
return null;
}
}
/**
* ADVANCED:
* A class extractor can be registered with the descriptor to override the default inheritance mechanism.
* This allows for the class indicator field to not be used, and a user defined one instead.
* The instance registered must extend the ClassExtractor class and implement the extractClass(Map) method,
* the method must take database row (Map) as argument, and must return the class to use for that row.
* This method will be used to decide which class to instantiate when reading from the database.
* It is the application's responsiblity to populate any typing information in the database required
* to determine the class from the row, such as usage of a direct or transformation mapping for the type fields.
* If this method is used then the class indicator field and mapping cannot be used,
* also the descriptor's withAllSubclasses and onlyInstances expressions must also be setup correctly.
*
* @see #setWithAllSubclassesExpression(Expression)
* @see #setOnlyInstancesExpression(Expression)
*/
public ClassExtractor getClassExtractor() {
return classExtractor;
}
/**
* ADVANCED:
* A class extractor can be registered with the descriptor to override the default inheritance mechanism.
* This allows for the class indicator field to not be used, and a user defined one instead.
* The instance registered must extend the ClassExtractor class and implement the extractClass(Map) method,
* the method must take database row (Map) as argument, and must return the class to use for that row.
* This method will be used to decide which class to instantiate when reading from the database.
* It is the application's responsiblity to populate any typing information in the database required
* to determine the class from the row, such as usage of a direct or transformation mapping for the type fields.
* If this method is used then the class indicator field and mapping cannot be used,
* also the descriptor's withAllSubclasses and onlyInstances expressions must also be setup correctly.
*
* @see #setWithAllSubclassesExpression(Expression)
* @see #setOnlyInstancesExpression(Expression)
*/
public void setClassExtractor(ClassExtractor classExtractor) {
this.classExtractor = classExtractor;
}
/**
* INTERNAL:
* Return the class indicator associations for XML.
* List of class-name/value associations.
*/
public Vector getClassIndicatorAssociations() {
Vector associations = new Vector(getClassNameIndicatorMapping().size() / 2);
Iterator classesEnum = getClassNameIndicatorMapping().keySet().iterator();
Iterator valuesEnum = getClassNameIndicatorMapping().values().iterator();
while (classesEnum.hasNext()) {
Object className = classesEnum.next();
// If the project was built in runtime is a class, MW is a string.
if (className instanceof Class) {
className = ((Class)className).getName();
}
Object value = valuesEnum.next();
associations.addElement(new TypedAssociation(className, value));
}
return associations;
}
/**
* INTERNAL:
* Returns field that the class type indicator is store when using inheritence.
*/
public DatabaseField getClassIndicatorField() {
return classIndicatorField;
}
/**
* PUBLIC:
* Return the class indicator field name.
* This is the name of the field in the table that stores what type of object this is.
*/
public String getClassIndicatorFieldName() {
if (getClassIndicatorField() == null) {
return null;
} else {
return getClassIndicatorField().getQualifiedName();
}
}
/**
* INTERNAL:
* Return the association of indicators and classes
*/
public Map getClassIndicatorMapping() {
return getClassIndicatorMapping(ConversionManager.getDefaultManager());
}
/**
* INTERNAL:
* Return the association of indicators and classes using specified ConversionManager
*/
public Map getClassIndicatorMapping(ConversionManager conversionManager) {
if (classIndicatorMapping.isEmpty() && !classNameIndicatorMapping.isEmpty()) {
Iterator keysEnum = classNameIndicatorMapping.keySet().iterator();
Iterator valuesEnum = classNameIndicatorMapping.values().iterator();
while (keysEnum.hasNext()) {
Object key = keysEnum.next();
Object value = valuesEnum.next();
Class theClass = (Class)conversionManager.convertObject((String)key, ClassConstants.CLASS);
classIndicatorMapping.put(theClass, value);
classIndicatorMapping.put(value, theClass);
}
}
return classIndicatorMapping;
}
/**
* INTERNAL:
* Return the mapping from class name to indicator, used by MW.
*/
public Map getClassNameIndicatorMapping() {
if (classNameIndicatorMapping.isEmpty() && !classIndicatorMapping.isEmpty()) {
Iterator keysEnum = classIndicatorMapping.keySet().iterator();
Iterator valuesEnum = classIndicatorMapping.values().iterator();
while (keysEnum.hasNext()) {
Object key = keysEnum.next();
Object value = valuesEnum.next();
if (key instanceof Class) {
String className = ((Class)key).getName();
classNameIndicatorMapping.put(className, value);
}
}
}
return classNameIndicatorMapping;
}
/**
* INTERNAL:
* Returns value of the abstract class indicator for the Java class.
*/
protected Object getClassIndicatorValue() {
Object value = getClassIndicatorValue(getDescriptor().getJavaClass());
if ( classIndicatorField.type == Integer.class ) {
try {
value = new Integer( ( String ) value );
} catch ( NumberFormatException nfE ) {
// Leave value set to string.
}
}
return value;
}
/**
* INTERNAL:
* Returns the indicator field value for the given class
* If no abstract indicator mapping is specified, use the class name.
*/
protected Object getClassIndicatorValue(Class javaClass) {
if (shouldUseClassNameAsIndicator()) {
return javaClass.getName();
} else {
return getClassIndicatorMapping().get(javaClass);
}
}
/**
* INTERNAL:
* Returns the descriptor which the policy belongs to.
*/
public ClassDescriptor getDescriptor() {
return descriptor;
}
/**
* ADVANCED:
* Return the 'only instances expression'.
*/
public Expression getOnlyInstancesExpression() {
return onlyInstancesExpression;
}
/**
* PUBLIC:
* Return the parent class.
*/
public Class getParentClass() {
return parentClass;
}
/**
* INTERNAL:
* Return the parent class name.
*/
public String getParentClassName() {
if ((parentClassName == null) && (parentClass != null)) {
parentClassName = parentClass.getName();
}
return parentClassName;
}
/**
* INTERNAL:
* Return the parent descirptor
*/
public ClassDescriptor getParentDescriptor() {
return parentDescriptor;
}
/**
* INTERNAL:
* The view can be used to optimize/customize the query for all subclasses where they have multiple tables.
* This view can do the outer join, we require the view because we cannot generate dynmic platform independent SQL
* for outer joins (i.e. not possible to do so either).
*/
public DatabaseTable getReadAllSubclassesView() {
return readAllSubclassesView;
}
/**
* ADVANCED:
* The view can be used to optimize/customize the query for all subclasses where they have multiple tables.
* This view can use outer joins or unions to combine the results of selecting from all of the subclass tables.
* If a view is not given then TopLink must make an individual call for each subclass.
*/
public String getReadAllSubclassesViewName() {
if (getReadAllSubclassesView() == null) {
return null;
}
return getReadAllSubclassesView().getName();
}
/**
* INTERNAL:
* Return the root parent descriptor
*/
public ClassDescriptor getRootParentDescriptor() {
if (isRootParentDescriptor()) {
return getDescriptor();
} else {
return getParentDescriptor().getInheritancePolicy().getRootParentDescriptor();
}
}
/**
* INTERNAL:
* use aggregate in inheritance
*/
public ClassDescriptor getSubclassDescriptor(Class theClass) {
if (hasChildren()) {
for (Iterator enumtr = getChildDescriptors().iterator(); enumtr.hasNext();) {
ClassDescriptor childDescriptor = (ClassDescriptor)enumtr.next();
if (childDescriptor.getJavaClass().equals(theClass)) {
return childDescriptor;
} else {
ClassDescriptor descriptor = childDescriptor.getInheritancePolicy().getSubclassDescriptor(theClass);
if (descriptor != null) {
return descriptor;
}
}
}
}
return null;
}
/**
* INTERNAL:
* return if we should use the descriptor inheritance to determine
* if an object can be returned from the identity map or not.
*/
public boolean getUseDescriptorsToValidateInheritedObjects() {
return useDescriptorsToValidateInheritedObjects;
}
/**
* ADVANCED:
* Return the Expression which gets all subclasses.
*/
public Expression getWithAllSubclassesExpression() {
return withAllSubclassesExpression;
}
/**
* INTERNAL:
* Check if descriptor has children
*/
public boolean hasChildren() {
return !getChildDescriptors().isEmpty();
}
/**
* INTERNAL:
*/
public boolean hasClassExtractor() {
return getClassExtractor() != null;
}
/**
* INTERNAL:
* Checks if the class is invloved in inheritence
*/
public boolean hasClassIndicator() {
return getClassIndicatorField() != null;
}
/**
* INTERNAL:
* Return if any children of this descriptor require information from another table
* not specified at the parent level.
*/
public boolean hasMultipleTableChild() {
return childrenTables != null;
}
/**
* INTERNAL:
* Return if a view is used for inheritance reads.
*/
public boolean hasView() {
return getReadAllSubclassesView() != null;
}
/**
* INTERNAL:
* Initialized the inheritence 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) {
// Must reset this in the case that a child thinks it wants to read its subclasses.
if ((shouldReadSubclasses == null) || shouldReadSubclasses()) {
setShouldReadSubclasses(!getChildDescriptors().isEmpty());
}
if (isChildDescriptor()) {
getDescriptor().setMappings(Helper.concatenateVectors(getParentDescriptor().getMappings(), getDescriptor().getMappings()));
getDescriptor().setQueryKeys(Helper.concatenateMaps(getParentDescriptor().getQueryKeys(), getDescriptor().getQueryKeys()));
addFieldsToParent(getDescriptor().getFields());
// Parents fields must be first for indexing to work.
Vector parentsFields = (Vector)getParentDescriptor().getFields().clone();
//bug fix on Oracle duplicate field SQL using "order by"
Helper.addAllUniqueToVector(parentsFields, getDescriptor().getFields());
getDescriptor().setFields(parentsFields);
if (getClassIndicatorValue() != null) {
if (shouldReadSubclasses()) {
getAllChildClassIndicators().addElement(getClassIndicatorValue());
}
addClassIndicatorTypeToParent(getClassIndicatorValue());
}
// CR#3214106, do not override if specified in subclass.
if (!getDescriptor().usesOptimisticLocking() && getParentDescriptor().usesOptimisticLocking()) {
getDescriptor().setOptimisticLockingPolicy((OptimisticLockingPolicy)getParentDescriptor().getOptimisticLockingPolicy().clone());
getDescriptor().getOptimisticLockingPolicy().setDescriptor(getDescriptor());
}
// create CMPPolicy on child if parent has one and it does not. Then copy individual fields
CMPPolicy parentCMPPolicy = getDescriptor().getInheritancePolicy().getParentDescriptor().getCMPPolicy();
if (parentCMPPolicy != null) {
CMPPolicy cmpPolicy = getDescriptor().getCMPPolicy();
if (cmpPolicy == null) {
cmpPolicy = new CMPPolicy();
getDescriptor().setCMPPolicy(cmpPolicy);
}
}
}
initializeOnlyInstancesExpression();
initializeWithAllSubclassesExpression();
}
/**
* INTERNAL:
* Setup the default classExtractionMethod, or if one was specified by the user make sure it is valid.
*/
protected void initializeClassExtractor(AbstractSession session) throws DescriptorException {
if (getClassExtractor() == null) {
if (isChildDescriptor()) {
setClassExtractor(getParentDescriptor().getInheritancePolicy().getClassExtractor());
}
} else {
getClassExtractor().initialize(getDescriptor(), session);
}
}
/**
* INTERNAL:
* Initialize the expression to use to check the specific type field.
*/
protected void initializeOnlyInstancesExpression() throws DescriptorException {
if (getOnlyInstancesExpression() == null) {
if (hasClassExtractor()) {
return;
}
Object typeValue = getClassIndicatorValue();
if (typeValue == null) {
if (shouldReadSubclasses()) {
return;// No indicator is allowed in this case.
}
throw DescriptorException.valueNotFoundInClassIndicatorMapping(getParentDescriptor(), getDescriptor());
}
DatabaseField typeField = getClassIndicatorField();
if (typeField == null) {
throw DescriptorException.classIndicatorFieldNotFound(getParentDescriptor(), getDescriptor());
}
// cr3546
if (shouldAlwaysUseOuterJoin()) {
setOnlyInstancesExpression(new ExpressionBuilder().getField(typeField).equalOuterJoin(typeValue));
} else {
setOnlyInstancesExpression(new ExpressionBuilder().getField(typeField).equal(typeValue));
}
}
// If subclasses are read, this is anded dynamically.
if (!shouldReadSubclasses()) {
getDescriptor().getQueryManager().setAdditionalJoinExpression(getOnlyInstancesExpression().and(getDescriptor().getQueryManager().getAdditionalJoinExpression()));
}
}
/**
* INTERNAL:
* Initialize the expression to use for queries to the class and its subclasses.
*/
protected void initializeWithAllSubclassesExpression() throws DescriptorException {
if (getWithAllSubclassesExpression() == null) {
if (hasClassExtractor()) {
return;
}
if (isChildDescriptor() && shouldReadSubclasses()) {
setWithAllSubclassesExpression(new ExpressionBuilder().getField(getClassIndicatorField()).in(getAllChildClassIndicators()));
}
}
}
/**
* INTERNAL:
* Check if it is a child descriptor.
*/
public boolean isChildDescriptor() {
return getParentClassName() != null;
}
/**
* INTERNAL:
* Indicate whether a single table or joined inheritance strategy is being used. Since we currently do
* not support TABLE_PER_CLASS, indicating either joined/not joined is sufficient.
*
* @return isJoinedStrategy value
*/
public boolean isJoinedStrategy() {
return isJoinedStrategy;
}
/**
* INTERNAL:
* Return whether or not is root parent descriptor
*/
public boolean isRootParentDescriptor() {
return getParentDescriptor() == null;
}
/**
* INTERNAL:
* Initialized the inheritence properties that cannot be initialized
* unitl after the mappings have been.
*/
public void postInitialize(AbstractSession session) {
}
/**
* INTERNAL:
* Allow the inheritence properties of the descriptor to be initialized.
* The descriptor's parent must first be initialized.
*/
public void preInitialize(AbstractSession session) throws DescriptorException {
// Make sure that parent is already preinitialized.
if (isChildDescriptor()) {
// Unique is required because the builder can add the same table many times.
Vector