org.hibernate.bytecode.enhance.internal.javassist.PersistentAttributesHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-core Show documentation
Show all versions of hibernate-core Show documentation
Hibernate's core ORM functionality
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or .
*/
package org.hibernate.bytecode.enhance.internal.javassist;
import java.beans.Introspector;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Map;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMember;
import javassist.CtMethod;
import javassist.NotFoundException;
import javassist.bytecode.BadBytecode;
import javassist.bytecode.SignatureAttribute;
import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
/**
* util methods to fetch attribute metadata. consistent for both field and property access types.
*
* @author Luis Barreiro
* @see org.hibernate.internal.util.ReflectHelper
*/
public class PersistentAttributesHelper {
private PersistentAttributesHelper() {
}
private static final CoreMessageLogger log = CoreLogging.messageLogger( PersistentAttributesHelper.class );
public static boolean hasAnnotation(CtField attribute, Class annotation) {
return getAnnotation( attribute, annotation ) != null;
}
public static boolean hasAnnotation(CtClass ctClass, String attributeName, Class annotation) {
return getAnnotation( ctClass, attributeName, annotation ) != null;
}
public static T getAnnotation(CtField attribute, Class annotation) {
return getAnnotation( attribute.getDeclaringClass(), attribute.getName(), annotation );
}
public static T getAnnotation(CtClass ctClass, String attributeName, Class annotation) {
AccessType classAccessType = getAccessTypeOrNull( ctClass );
CtField field = findFieldOrNull( ctClass, attributeName );
CtMethod getter = findGetterOrNull( ctClass, attributeName );
if ( classAccessType == AccessType.FIELD || ( field != null && getAccessTypeOrNull( field ) == AccessType.FIELD ) ) {
return field == null ? null : getAnnotationOrNull( field, annotation );
}
if ( classAccessType == AccessType.PROPERTY || ( getter != null && getAccessTypeOrNull( getter ) == AccessType.PROPERTY ) ) {
return getter == null ? null : getAnnotationOrNull( getter, annotation );
}
T found = ( getter == null ? null : getAnnotationOrNull( getter, annotation ) );
if ( found == null && field != null ) {
return getAnnotationOrNull( field, annotation );
}
return found;
}
private static T getAnnotationOrNull(CtMember ctMember, Class annotation) {
try {
if ( ctMember.hasAnnotation( annotation ) ) {
return annotation.cast( ctMember.getAnnotation( annotation ) );
}
}
catch (ClassNotFoundException cnfe) {
// should never happen
}
return null;
}
private static AccessType getAccessTypeOrNull(CtMember ctMember) {
Access access = getAnnotationOrNull( ctMember, Access.class );
return access == null ? null : access.value();
}
private static AccessType getAccessTypeOrNull(CtClass ctClass) {
try {
if ( ctClass.hasAnnotation( Access.class ) ) {
return ( (Access) ctClass.getAnnotation( Access.class ) ).value();
}
else {
CtClass extendsClass = ctClass.getSuperclass();
return extendsClass == null ? null : getAccessTypeOrNull( extendsClass );
}
}
catch (ClassNotFoundException e) {
return null;
}
catch (NotFoundException e) {
return null;
}
}
//
/**
* duplicated here to take CtClass instead of Class
* @see org.hibernate.internal.util.ReflectHelper#locateField
*/
private static CtField findFieldOrNull(CtClass ctClass, String propertyName) {
if ( ctClass == null ) {
return null;
}
try {
return ctClass.getField( propertyName );
}
catch ( NotFoundException nsfe ) {
try {
return findFieldOrNull( ctClass.getSuperclass(), propertyName );
}
catch (NotFoundException e) {
return null;
}
}
}
/**
* duplicated here to take CtClass instead of Class
* @see org.hibernate.internal.util.ReflectHelper#findGetterMethod
*/
private static CtMethod findGetterOrNull(CtClass ctClass, String propertyName) {
if ( ctClass == null ) {
return null;
}
CtMethod method = getterOrNull( ctClass, propertyName );
if ( method != null ) {
return method;
}
try {
// check if extends
method = findGetterOrNull( ctClass.getSuperclass(), propertyName );
if ( method != null ) {
return method;
}
// check if implements
for ( CtClass interfaceCtClass : ctClass.getInterfaces() ) {
method = getterOrNull( interfaceCtClass, propertyName );
if ( method != null ) {
return method;
}
}
}
catch (NotFoundException nfe) {
// give up
}
return null;
}
private static CtMethod getterOrNull(CtClass containerClass, String propertyName) {
for ( CtMethod method : containerClass.getDeclaredMethods() ) {
try {
// if the method has parameters, skip it
if ( method.isEmpty() || method.getParameterTypes().length != 0 ) {
continue;
}
}
catch (NotFoundException e) {
continue;
}
final String methodName = method.getName();
// try "get"
if ( methodName.startsWith( "get" ) ) {
String testStdMethod = Introspector.decapitalize( methodName.substring( 3 ) );
String testOldMethod = methodName.substring( 3 );
if ( testStdMethod.equals( propertyName ) || testOldMethod.equals( propertyName ) ) {
return method;
}
}
// if not "get", then try "is"
if ( methodName.startsWith( "is" ) ) {
String testStdMethod = Introspector.decapitalize( methodName.substring( 2 ) );
String testOldMethod = methodName.substring( 2 );
if ( testStdMethod.equals( propertyName ) || testOldMethod.equals( propertyName ) ) {
return method;
}
}
}
return null;
}
//
public static boolean isPossibleBiDirectionalAssociation(CtField persistentField) {
return PersistentAttributesHelper.hasAnnotation( persistentField, OneToOne.class ) ||
PersistentAttributesHelper.hasAnnotation( persistentField, OneToMany.class ) ||
PersistentAttributesHelper.hasAnnotation( persistentField, ManyToOne.class ) ||
PersistentAttributesHelper.hasAnnotation( persistentField, ManyToMany.class );
}
public static String getMappedBy(CtField persistentField, CtClass targetEntity, JavassistEnhancementContext context) throws NotFoundException {
final String local = getMappedByFromAnnotation( persistentField );
return local.isEmpty() ? getMappedByFromTargetEntity( persistentField, targetEntity, context ) : local;
}
private static String getMappedByFromAnnotation(CtField persistentField) {
OneToOne oto = PersistentAttributesHelper.getAnnotation( persistentField, OneToOne.class );
if ( oto != null ) {
return oto.mappedBy();
}
OneToMany otm = PersistentAttributesHelper.getAnnotation( persistentField, OneToMany.class );
if ( otm != null ) {
return otm.mappedBy();
}
// For @ManyToOne associations, mappedBy must come from the @OneToMany side of the association
ManyToMany mtm = PersistentAttributesHelper.getAnnotation( persistentField, ManyToMany.class );
return mtm == null ? "" : mtm.mappedBy();
}
private static String getMappedByFromTargetEntity(
CtField persistentField,
CtClass targetEntity,
JavassistEnhancementContext context) throws NotFoundException {
// get mappedBy value by searching in the fields of the target entity class
for ( CtField f : targetEntity.getDeclaredFields() ) {
if ( context.isPersistentField( f )
&& getMappedByFromAnnotation( f ).equals( persistentField.getName() )
&& isAssignable( persistentField.getDeclaringClass(), inferFieldTypeName( f ) ) ) {
log.debugf(
"mappedBy association for field [%s#%s] is [%s#%s]",
persistentField.getDeclaringClass().getName(),
persistentField.getName(),
targetEntity.getName(),
f.getName()
);
return f.getName();
}
}
return "";
}
public static CtClass getTargetEntityClass(CtClass managedCtClass, CtField persistentField) throws NotFoundException {
// get targetEntity defined in the annotation
try {
OneToOne oto = PersistentAttributesHelper.getAnnotation( persistentField, OneToOne.class );
OneToMany otm = PersistentAttributesHelper.getAnnotation( persistentField, OneToMany.class );
ManyToOne mto = PersistentAttributesHelper.getAnnotation( persistentField, ManyToOne.class );
ManyToMany mtm = PersistentAttributesHelper.getAnnotation( persistentField, ManyToMany.class );
Class targetClass = null;
if ( oto != null ) {
targetClass = oto.targetEntity();
}
if ( otm != null ) {
targetClass = otm.targetEntity();
}
if ( mto != null ) {
targetClass = mto.targetEntity();
}
if ( mtm != null ) {
targetClass = mtm.targetEntity();
}
if ( targetClass != null && targetClass != void.class ) {
return managedCtClass.getClassPool().get( targetClass.getName() );
}
}
catch (NotFoundException ignore) {
}
// infer targetEntity from generic type signature
String inferredTypeName = inferTypeName( managedCtClass, persistentField.getName() );
return inferredTypeName == null ? null : managedCtClass.getClassPool().get( inferredTypeName );
}
/**
* Consistent with hasAnnotation()
*/
private static String inferTypeName(CtClass ctClass, String attributeName ) {
AccessType classAccessType = getAccessTypeOrNull( ctClass );
CtField field = findFieldOrNull( ctClass, attributeName );
CtMethod getter = findGetterOrNull( ctClass, attributeName );
if ( classAccessType == AccessType.FIELD || ( field != null && getAccessTypeOrNull( field ) == AccessType.FIELD ) ) {
return field == null ? null : inferFieldTypeName( field );
}
if ( classAccessType == AccessType.PROPERTY || ( getter != null && getAccessTypeOrNull( getter ) == AccessType.PROPERTY ) ) {
return getter == null ? null : inferMethodTypeName( getter );
}
String found = ( getter == null ? null : inferMethodTypeName( getter ) );
if ( found == null && field != null ) {
return inferFieldTypeName( field );
}
return found;
}
private static String inferFieldTypeName(CtField field) {
try {
if ( field.getFieldInfo2().getAttribute( SignatureAttribute.tag ) == null ) {
return field.getType().getName();
}
return inferGenericTypeName(
field.getType(),
SignatureAttribute.toTypeSignature( field.getGenericSignature() )
);
}
catch (BadBytecode ignore) {
return null;
}
catch (NotFoundException e) {
return null;
}
}
private static String inferMethodTypeName(CtMethod method) {
try {
if ( method.getMethodInfo2().getAttribute( SignatureAttribute.tag ) == null ) {
return method.getReturnType().getName();
}
return inferGenericTypeName(
method.getReturnType(),
SignatureAttribute.toMethodSignature( method.getGenericSignature() ).getReturnType()
);
}
catch (BadBytecode ignore) {
return null;
}
catch (NotFoundException e) {
return null;
}
}
private static String inferGenericTypeName(CtClass ctClass, SignatureAttribute.Type genericSignature) {
// infer targetEntity from generic type signature
if ( isAssignable( ctClass, Collection.class.getName() ) ) {
return ( (SignatureAttribute.ClassType) genericSignature ).getTypeArguments()[0].getType().jvmTypeName();
}
if ( isAssignable( ctClass, Map.class.getName() ) ) {
return ( (SignatureAttribute.ClassType) genericSignature ).getTypeArguments()[1].getType().jvmTypeName();
}
return ctClass.getName();
}
//
public static boolean isAssignable(CtClass thisCtClass, String targetClassName) {
if ( thisCtClass == null ) {
return false;
}
if ( thisCtClass.getName().equals( targetClassName ) ) {
return true;
}
try {
// check if extends
if ( isAssignable( thisCtClass.getSuperclass(), targetClassName ) ) {
return true;
}
// check if implements
for ( CtClass interfaceCtClass : thisCtClass.getInterfaces() ) {
if ( isAssignable( interfaceCtClass, targetClassName ) ) {
return true;
}
}
}
catch (NotFoundException e) {
// keep going
}
return false;
}
public static boolean isAssignable(CtField thisCtField, String targetClassName) {
try {
return isAssignable( thisCtField.getType(), targetClassName );
}
catch (NotFoundException e) {
// keep going
}
return false;
}
}