blob: 7aef98a6cf05ca3a55238b3f7d4d25dbd7ffbd42 [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:
// Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.mappings.querykeys;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.expressions.*;
import org.eclipse.persistence.internal.expressions.DataExpression;
import org.eclipse.persistence.internal.expressions.ExpressionIterator;
import org.eclipse.persistence.internal.expressions.ParameterExpression;
import org.eclipse.persistence.internal.expressions.TableExpression;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.helper.DatabaseTable;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.security.PrivilegedClassForName;
/**
* <p>
* <b>Purpose</b>: Define an alias to a foreign object.
* <p>
* <b> Responsibilities</b>:
* <ul>
* <li> Define the reference class of the foreign object.
* </ul>
*/
public class ForeignReferenceQueryKey extends QueryKey {
protected Class referenceClass;
protected String referenceClassName;
protected Expression joinCriteria;
/**
* INTERNAL:
* Convert all the class-name-based settings in this project to actual class-based
* settings
*/
@Override
public void convertClassNamesToClasses(ClassLoader classLoader){
Class referenceClass = null;
try{
if (referenceClassName != null){
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
referenceClass = AccessController.doPrivileged(new PrivilegedClassForName(referenceClassName, true, classLoader));
} catch (PrivilegedActionException exception) {
throw ValidationException.classNotFoundWhileConvertingClassNames(referenceClassName, exception.getException());
}
} else {
referenceClass = PrivilegedAccessHelper.getClassForName(referenceClassName, true, classLoader);
}
}
setReferenceClass(referenceClass);
} catch (ClassNotFoundException exc){
throw ValidationException.classNotFoundWhileConvertingClassNames(referenceClassName, exc);
}
}
/**
* PUBLIC:
* Return the join expression for the relationship defined by the query key.
*/
public Expression getJoinCriteria() {
return joinCriteria;
}
/**
* PUBLIC:
* Return the reference class of the relationship.
*/
public Class getReferenceClass() {
return referenceClass;
}
/**
* PUBLIC:
* Return the reference class name of the relationship.
*/
public String getReferenceClassName() {
if (referenceClassName == null && referenceClass != null){
referenceClassName = referenceClass.getName();
}
return referenceClassName;
}
/**
* INTERNAL:
* override the isForeignReferenceQueryKey() method in the superclass to return true.
* @return boolean
*/
@Override
public boolean isForeignReferenceQueryKey() {
return true;
}
/**
* PUBLIC:
* Set the join expression for the relationship defined by the query key.
* <p>Example:
* <blockquote><pre>
* builder.getField("ADDRESS.ADDRESS_ID").equal(builder.getParameter("EMPLOYEE.ADDR_ID");
* </pre></blockquote>
*/
public void setJoinCriteria(Expression joinCriteria) {
this.joinCriteria = joinCriteria;
}
/**
* PUBLIC:
* Set the reference class of the relationship.
* This is not required for direct collection query keys.
*/
public void setReferenceClass(Class referenceClass) {
this.referenceClass = referenceClass;
}
/**
* PUBLIC:
* Set the reference class name for this relationship
* This is used when projects are built without using classes
*/
public void setReferenceClassName(String referenceClassName) {
this.referenceClassName = referenceClassName;
}
/**
* PUBLIC:
* Returns the source table.
*/
public DatabaseTable getSourceTable() {
// TODO: Should extract the target table from joinCriteria (if it's not null),
// like ManyToManyQueryKey.getRelationTable does.
return this.descriptor.getTables().firstElement();
}
/**
* PUBLIC:
* Returns the reference table.
*/
public DatabaseTable getReferenceTable(ClassDescriptor desc) {
// TODO: This won't work for direct collection.
// Should extract the target table from joinCriteria (if it's not null),
// like ManyToManyQueryKey.getRelationTable does.
return desc.getTables().firstElement();
}
/**
* PUBLIC:
* Returns the relation table.
* Currently only ManyToMany and OneToOne may have relation table.
* The method is overridden to return null for other subclasses.
* The returned relationTable still could be null.
*/
public DatabaseTable getRelationTable(ClassDescriptor referenceDescriptor) {
ExpressionIterator expIterator = new ExpressionIterator() {
@Override
public void iterate(Expression each) {
if(each.isTableExpression()) {
((Collection)this.getResult()).add(((TableExpression)each).getTable());
}
else if(each.isDataExpression()) {
DatabaseField field = ((DataExpression)each).getField();
if(field != null && field.hasTableName()) {
((Collection)this.getResult()).add(field.getTable());
}
} else if(each.isParameterExpression()) {
DatabaseField field = ((ParameterExpression)each).getField();
if(field != null && field.hasTableName()) {
((Collection)this.getResult()).add(field.getTable());
}
}
}
};
expIterator.setResult(new HashSet());
expIterator.iterateOn(this.joinCriteria);
HashSet<DatabaseTable> tables = (HashSet)expIterator.getResult();
DatabaseTable relationTable = null;
Iterator<DatabaseTable> it = tables.iterator();
while(it.hasNext()) {
DatabaseTable table = it.next();
// neither source nor reference descriptor contains table - must be relationTable
if(!descriptor.getTables().contains(table) && !referenceDescriptor.getTables().contains(table)) {
relationTable = table;
break;
}
}
return relationTable;
}
}