blob: c0e3b3bd4e030dce30fa07d5cddf03903d2e2764 [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.internal.jpa.parsing;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.DirectCollectionMapping;
import org.eclipse.persistence.mappings.querykeys.ForeignReferenceQueryKey;
import org.eclipse.persistence.mappings.querykeys.QueryKey;
import org.eclipse.persistence.internal.queries.ContainerPolicy;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.security.PrivilegedClassForName;
import org.eclipse.persistence.internal.helper.BasicTypeHelperImpl;
/**
* INTERNAL
* <p><b>Purpose</b>: Implement type helper methods specified by TypeHelper.
* This implementation uses Class instances to represent a type.
*/
public class TypeHelperImpl
extends BasicTypeHelperImpl implements TypeHelper {
/** The session. */
private final AbstractSession session;
/** The class loader used to resolve type names. */
private final ClassLoader classLoader;
/** */
public TypeHelperImpl(AbstractSession session, ClassLoader classLoader) {
this.session = session;
this.classLoader = classLoader;
}
/** Returns a type representation for the specified type name or null if
* there is no such type.
*/
@Override
public Object resolveTypeName(String typeName) {
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
return AccessController.doPrivileged(
new PrivilegedClassForName<>(typeName, true, classLoader));
} catch (PrivilegedActionException exception) {
return null;
}
} else {
return PrivilegedAccessHelper.getClassForName(typeName, true, classLoader);
}
} catch (ClassNotFoundException ex) {
return null;
}
}
/** Returns the type of the attribute with the specified name in the
* specified owner class.
*/
@Override
public Object resolveAttribute(Object ownerClass, String attribute) {
DatabaseMapping mapping = resolveAttributeMapping(ownerClass, attribute);
if (mapping != null) {
return getType(mapping);
}
QueryKey queryKey = resolveQueryKey(ownerClass, attribute);
if (queryKey == null) {
return null;
} else if (queryKey.isForeignReferenceQueryKey()) {
return ((ForeignReferenceQueryKey)queryKey).getReferenceClass();
} else {
return Object.class;
}
}
/** Returns the type of the map key for the mapping on ownerClass named attribute
* Returns null if that mapping does not exist or does not contain a map key
*/
@Override
public Object resolveMapKey(Object ownerClass, String attribute) {
Object type = null;
DatabaseMapping mapping = resolveAttributeMapping(ownerClass, attribute);
if (mapping.isCollectionMapping()){
ContainerPolicy cp = mapping.getContainerPolicy();
type = cp.getKeyType();
}
return type;
}
/** Returns the type of the class corresponding to the specified abstract
* schema type.
*/
@Override
public Object resolveSchema(String schemaName) {
ClassDescriptor descriptor = session.getDescriptorForAlias(schemaName);
return (descriptor != null) ? descriptor.getJavaClass() : null;
}
/** Returns the enum constant if the specified type denotes an enum type
* and the specified constant denotes a constant of the enum type.
*/
@Override
public Object resolveEnumConstant(Object type, String constant) {
Class<?> clazz = getJavaClass(type);
Object[] constants = clazz.getEnumConstants();
if (constants != null) {
for (int i = 0; i < constants.length; i++) {
Enum<?> enumConstant = (Enum<?>) constants[i];
if (enumConstant.name().equals(constant)) {
return enumConstant;
}
}
}
return null;
}
/** Returns true if the specified type denotes an entity class. */
@Override
public boolean isEntityClass(Object type) {
ClassDescriptor desc = getDescriptor(type);
return (desc != null) && !desc.isAggregateDescriptor();
}
/** Returns true if the specified type denotes an orderable type */
@Override
public boolean isOrderableType(Object type) {
return !(isEntityClass(type) || isEmbeddable(type));
}
/** Returns true if the specified type denotes an embedded class. */
@Override
public boolean isEmbeddable(Object type) {
ClassDescriptor desc = getDescriptor(type);
return (desc != null) && desc.isAggregateDescriptor();
}
/** Returns true if the specified type denotes an embedded attribute. */
@Override
public boolean isEmbeddedAttribute(Object ownerClass, String attribute) {
DatabaseMapping mapping =
resolveAttributeMapping(ownerClass, attribute);
return (mapping != null) && mapping.isAggregateObjectMapping();
}
/** Returns true if the specified type denotes a simple state attribute. */
@Override
public boolean isSimpleStateAttribute(Object ownerClass, String attribute) {
DatabaseMapping mapping =
resolveAttributeMapping(ownerClass, attribute);
return (mapping != null) && mapping.isDirectToFieldMapping();
}
/** Returns true if the specified attribute denotes a single valued
* or collection valued relationship attribute.
*/
@Override
public boolean isRelationship(Object ownerClass, String attribute) {
DatabaseMapping mapping =
resolveAttributeMapping(ownerClass, attribute);
return (mapping != null) && mapping.isForeignReferenceMapping();
}
/** Returns true if the specified attribute denotes a single valued
* relationship attribute.
*/
@Override
public boolean isSingleValuedRelationship(Object ownerClass,
String attribute) {
DatabaseMapping mapping =
resolveAttributeMapping(ownerClass, attribute);
return (mapping != null) && mapping.isObjectReferenceMapping();
}
/** Returns true if the specified attribute denotes a collection valued
* relationship attribute.
*/
@Override
public boolean isCollectionValuedRelationship(Object ownerClass,
String attribute) {
DatabaseMapping mapping =
resolveAttributeMapping(ownerClass, attribute);
return (mapping != null) &&
(mapping.isOneToManyMapping() || mapping.isManyToManyMapping());
}
// ===== Internal helper methods =====
/** Returns the class descriptor if the specified non-null type is a class
* object.
*/
private ClassDescriptor getDescriptor(Object type) {
ClassDescriptor desc = null;
if (type instanceof Class) {
desc = session.getDescriptor((Class<?>)type);
} else if (type instanceof ClassDescriptor) {
desc = (ClassDescriptor)type;
}
return desc;
}
/** Returns the mapping for the specified attribute of the specified
* class. The method returns null if the class is not known or if the
* class does not have an attribute of the specified name.
*/
private DatabaseMapping resolveAttributeMapping(Object ownerClass, String attribute) {
ClassDescriptor descriptor = getDescriptor(ownerClass);
return (descriptor == null) ? null : descriptor.getObjectBuilder().getMappingForAttributeName(attribute);
}
@Override
public QueryKey resolveQueryKey(Object ownerClass, String attribute) {
ClassDescriptor descriptor = getDescriptor(ownerClass);
if (descriptor == null) {
return null;
}
return descriptor.getQueryKeyNamed(attribute);
}
private Object getType(DatabaseMapping mapping) {
if (mapping == null) {
return null;
}
Object type = null;
if (mapping.isDirectCollectionMapping()){
type = ((DirectCollectionMapping)mapping).getDirectField().getType();
if (type == null){
type = Object.class;
}
} else if (mapping.isAggregateCollectionMapping()) {
// Return the ClassDescriptor as the type representation in case
// of an embedded. This makes sure that any property or field
// access of the embedded uses the correct mapping information and
// not the shell of the descriptors as stored by the session.
type = mapping.getReferenceDescriptor();
} else if (mapping.isForeignReferenceMapping()) {
ClassDescriptor descriptor = mapping.getReferenceDescriptor();
type = descriptor == null ? null : descriptor.getJavaClass();
} else if (mapping.isAggregateMapping()) {
// Return the ClassDescriptor as the type representation in case
// of an embedded. This makes sure that any property or field
// access of the embedded uses the correct mapping information and
// not the shell of the descriptors as stored by the session.
type = mapping.getReferenceDescriptor();
} else {
type = mapping.getAttributeAccessor().getAttributeClass();
}
return type;
}
}