diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/ConverterHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/ConverterHelper.java index b143c2b236..babf6c6b8b 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/ConverterHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/convert/internal/ConverterHelper.java @@ -65,7 +65,7 @@ public class ConverterHelper { } } else { - throw new HibernateException( "Unexpected java.lang.reflect.Member type from org.hibernate.annotations.common.reflection.java.JavaXMember : " + member ); + throw new HibernateException( "Unexpected java.lang.reflect.Member type from org.hibernate.models.spi.MemberDetails : " + member ); } throw new HibernateException( diff --git a/hibernate-envers/hibernate-envers.gradle b/hibernate-envers/hibernate-envers.gradle index 62d1d8e638..a6371f84e0 100644 --- a/hibernate-envers/hibernate-envers.gradle +++ b/hibernate-envers/hibernate-envers.gradle @@ -17,7 +17,7 @@ dependencies { implementation jakartaLibs.jaxbApi implementation jakartaLibs.jaxb implementation libs.jandex - implementation libs.hcann + implementation libs.hibernateModels compileOnly libs.ant diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/EnversMetadataBuildingContextImpl.java b/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/EnversMetadataBuildingContextImpl.java index debf6b4763..35b817c236 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/EnversMetadataBuildingContextImpl.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/EnversMetadataBuildingContextImpl.java @@ -6,19 +6,19 @@ */ package org.hibernate.envers.boot.internal; -import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.boot.model.TypeDefinitionRegistry; import org.hibernate.boot.model.naming.ObjectNameNormalizer; import org.hibernate.boot.spi.BootstrapContext; import org.hibernate.boot.spi.EffectiveMappingDefaults; import org.hibernate.boot.spi.InFlightMetadataCollector; -import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingOptions; import org.hibernate.envers.boot.spi.EnversMetadataBuildingContext; import org.hibernate.envers.configuration.Configuration; import org.hibernate.envers.configuration.internal.MappingCollector; import org.hibernate.envers.configuration.internal.metadata.AuditEntityConfigurationRegistry; import org.hibernate.envers.configuration.internal.metadata.AuditEntityNameRegister; +import org.hibernate.models.spi.ClassDetailsRegistry; +import org.hibernate.models.spi.SourceModelBuildingContext; import org.hibernate.service.ServiceRegistry; /** @@ -102,8 +102,13 @@ public class EnversMetadataBuildingContextImpl implements EnversMetadataBuilding } @Override - public ReflectionManager getReflectionManager() { - return configuration.getReflectionManager(); + public SourceModelBuildingContext getSourceModelBuildingContext() { + return metadataCollector.getSourceModelBuildingContext(); + } + + @Override + public ClassDetailsRegistry getClassDetailsRegistry() { + return metadataCollector.getClassDetailsRegistry(); } @Override diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/EnversServiceImpl.java b/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/EnversServiceImpl.java index ac81276b85..222c61cf7a 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/EnversServiceImpl.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/boot/internal/EnversServiceImpl.java @@ -98,6 +98,7 @@ public class EnversServiceImpl implements EnversService, Configurable, Stoppable initialized = true; + final InFlightMetadataCollector metadataCollector = (InFlightMetadataCollector) metadata; this.serviceRegistry = metadata.getMetadataBuildingOptions().getServiceRegistry(); this.classLoaderService = serviceRegistry.getService( ClassLoaderService.class ); @@ -105,12 +106,12 @@ public class EnversServiceImpl implements EnversService, Configurable, Stoppable final Properties properties = new Properties(); properties.putAll( cfgService.getSettings() ); - this.configuration = new Configuration( properties, this, metadata ); + this.configuration = new Configuration( properties, this, metadataCollector ); this.auditProcessManager = new AuditProcessManager( configuration.getRevisionInfo().getRevisionInfoGenerator() ); final EnversMetadataBuildingContext metadataBuildingContext = new EnversMetadataBuildingContextImpl( configuration, - (InFlightMetadataCollector) metadata, + metadataCollector, effectiveMappingDefaults, mappingCollector ); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/boot/spi/EnversMetadataBuildingContext.java b/hibernate-envers/src/main/java/org/hibernate/envers/boot/spi/EnversMetadataBuildingContext.java index 977702bd29..71ae02b871 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/boot/spi/EnversMetadataBuildingContext.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/boot/spi/EnversMetadataBuildingContext.java @@ -6,12 +6,13 @@ */ package org.hibernate.envers.boot.spi; -import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.envers.configuration.Configuration; import org.hibernate.envers.configuration.internal.MappingCollector; import org.hibernate.envers.configuration.internal.metadata.AuditEntityConfigurationRegistry; import org.hibernate.envers.configuration.internal.metadata.AuditEntityNameRegister; +import org.hibernate.models.spi.ClassDetailsRegistry; +import org.hibernate.models.spi.SourceModelBuildingContext; import org.hibernate.service.ServiceRegistry; /** @@ -26,7 +27,9 @@ public interface EnversMetadataBuildingContext extends MetadataBuildingContext { ServiceRegistry getServiceRegistry(); - ReflectionManager getReflectionManager(); + SourceModelBuildingContext getSourceModelBuildingContext(); + + ClassDetailsRegistry getClassDetailsRegistry(); AuditEntityNameRegister getAuditEntityNameRegistry(); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/Configuration.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/Configuration.java index 04ebc1e536..3bd9ec2b81 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/Configuration.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/Configuration.java @@ -11,11 +11,9 @@ import java.util.Map; import java.util.Properties; import java.util.concurrent.Callable; -import org.hibernate.annotations.common.reflection.ReflectionManager; -import org.hibernate.annotations.common.reflection.java.JavaReflectionManager; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.boot.registry.selector.spi.StrategySelector; -import org.hibernate.boot.spi.MetadataImplementor; +import org.hibernate.boot.spi.InFlightMetadataCollector; import org.hibernate.cfg.Environment; import org.hibernate.dialect.HSQLDialect; import org.hibernate.envers.RevisionListener; @@ -75,7 +73,6 @@ public class Configuration { private final boolean globalLegacyRelationTargetNotFound; private final boolean trackEntitiesChanged; - private final JavaReflectionManager reflectionManager; private boolean trackEntitiesOverride; private final String auditTablePrefix; @@ -98,7 +95,7 @@ public class Configuration { private final RevisionInfoConfiguration revisionInfo; - public Configuration(Properties properties, EnversService enversService, MetadataImplementor metadata) { + public Configuration(Properties properties, EnversService enversService, InFlightMetadataCollector metadata) { this.enversService = enversService; final ConfigurationProperties configProps = new ConfigurationProperties( properties ); @@ -185,14 +182,7 @@ public class Configuration { revisionPropertyBasePath = originalIdPropertyName + "." + revisionFieldName + "."; revisionNumberPath = revisionPropertyBasePath + "id"; - // todo: there are places that need bits built from the revinfo entity configuration - // this exists here as a way to pass it down in an immutable way to any consumer of this class - this.reflectionManager = new JavaReflectionManager(); - this.revisionInfo = new RevisionInfoConfiguration( this, metadata, reflectionManager ); - } - - public JavaReflectionManager getReflectionManager() { - return reflectionManager; + this.revisionInfo = new RevisionInfoConfiguration( this, metadata ); } public boolean isGenerateRevisionsForCollections() { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/ModelsHelper.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/ModelsHelper.java new file mode 100644 index 0000000000..0adae17587 --- /dev/null +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/ModelsHelper.java @@ -0,0 +1,92 @@ +/* + * 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 http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.envers.configuration.internal; + +import java.util.List; + +import org.hibernate.envers.configuration.internal.metadata.reader.PersistentPropertiesSource; +import org.hibernate.models.internal.ClassTypeDetailsImpl; +import org.hibernate.models.internal.ModifierUtils; +import org.hibernate.models.internal.dynamic.DynamicFieldDetails; +import org.hibernate.models.spi.ClassDetails; +import org.hibernate.models.spi.FieldDetails; +import org.hibernate.models.spi.MemberDetails; +import org.hibernate.models.spi.MethodDetails; +import org.hibernate.models.spi.SourceModelBuildingContext; +import org.hibernate.models.spi.TypeDetails; + +/** + * Simple helper class to streamline hibernate-models interactions. + * + * @author Marco Belladelli + */ +public class ModelsHelper { + /** + * Provides a list of the provided class' {@link MemberDetails} based on the provided access type. + * + * @param classDetails The class details to extract the members from + * @param accessType The access type to use, accepted values are {@code field}, {@code property} and {@code record} + * + * @return The list of member details + */ + public static List getMemberDetails(ClassDetails classDetails, String accessType) { + return switch ( accessType ) { + case "field" -> classDetails.getFields(); + case "property" -> classDetails.getMethods(); + case "record" -> classDetails.getRecordComponents(); + default -> throw new IllegalArgumentException( "Unknown access type " + accessType ); + }; + } + + /** + * Retrieves the {@link MemberDetails member} of the class, being either the field or the getter, + * with the provided name if one exists, {@code null} otherwise. + * + * @param classDetails The class details containing the desired member + * @param name The name of the member to find + * + * @return The requested member, null if not found + */ + public static MemberDetails getMember(ClassDetails classDetails, String name) { + final FieldDetails field = classDetails.findFieldByName( name ); + if ( field != null ) { + return field; + } + + for ( MethodDetails method : classDetails.getMethods() ) { + if ( method.resolveAttributeName().equals( name ) ) { + return method; + } + } + + return null; + } + + /** + * Instantiates a {@link DynamicFieldDetails} from the provided properties source with the specified name + * + * @param propertiesSource The property source containing the virtual field + * @param propertyName The property name of the dynamic field + * @param sourceModelBuildingContext Context object for models + * + * @return The newly created dynamic field details + */ + public static FieldDetails dynamicFieldDetails( + PersistentPropertiesSource propertiesSource, + String propertyName, + SourceModelBuildingContext sourceModelBuildingContext) { + return new DynamicFieldDetails( + propertyName, + new ClassTypeDetailsImpl( propertiesSource.getClassDetails(), TypeDetails.Kind.CLASS ), + propertiesSource.getClassDetails(), + ModifierUtils.DYNAMIC_ATTRIBUTE_MODIFIERS, + false, + false, + sourceModelBuildingContext + ); + } +} diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/RevisionInfoConfiguration.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/RevisionInfoConfiguration.java index beac5bccd4..f03d911274 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/RevisionInfoConfiguration.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/RevisionInfoConfiguration.java @@ -13,10 +13,7 @@ import java.util.Locale; import java.util.Set; import jakarta.persistence.Column; -import org.hibernate.annotations.common.reflection.ReflectionManager; -import org.hibernate.annotations.common.reflection.XClass; -import org.hibernate.annotations.common.reflection.XProperty; -import org.hibernate.boot.spi.MetadataImplementor; +import org.hibernate.boot.spi.InFlightMetadataCollector; import org.hibernate.envers.Audited; import org.hibernate.envers.DefaultRevisionEntity; import org.hibernate.envers.DefaultTrackingModifiedEntitiesRevisionEntity; @@ -48,8 +45,13 @@ import org.hibernate.envers.internal.revisioninfo.RevisionInfoQueryCreator; import org.hibernate.envers.internal.revisioninfo.RevisionTimestampValueResolver; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; +import org.hibernate.models.spi.ClassDetails; +import org.hibernate.models.spi.MemberDetails; +import org.hibernate.models.spi.TypeDetails; import org.hibernate.service.ServiceRegistry; +import static org.hibernate.envers.configuration.internal.ModelsHelper.getMemberDetails; + /** * @author Adam Warski (adam at warski dot org) * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) @@ -80,11 +82,11 @@ public class RevisionInfoConfiguration { private final Class revisionInfoClass; private final boolean useDefaultRevisionInfoMapping; - public RevisionInfoConfiguration(Configuration config, MetadataImplementor metadata, ReflectionManager reflectionManager) { + public RevisionInfoConfiguration(Configuration config, InFlightMetadataCollector metadata) { this.configuration = config; // Generate the resolver metadata - RevisionEntityResolver resolver = new RevisionEntityResolver( metadata, reflectionManager ); + final RevisionEntityResolver resolver = new RevisionEntityResolver( metadata ); // initialize attributes from resolver this.revisionInfoClass = resolver.revisionInfoClass; @@ -262,8 +264,7 @@ public class RevisionInfoConfiguration { private class RevisionEntityResolver { - private final MetadataImplementor metadata; - private final ReflectionManager reflectionManager; + private final InFlightMetadataCollector metadata; private boolean revisionEntityFound; private boolean revisionNumberFound; @@ -282,9 +283,8 @@ public class RevisionInfoConfiguration { private String revisionPropSqlType; private RevisionTimestampValueResolver timestampValueResolver; - public RevisionEntityResolver(MetadataImplementor metadata, ReflectionManager reflectionManager) { + public RevisionEntityResolver(InFlightMetadataCollector metadata) { this.metadata = metadata; - this.reflectionManager = reflectionManager; this.revisionInfoEntityName = getDefaultEntityName(); this.revisionInfoIdData = createPropertyData( "id", "field" ); this.revisionInfoTimestampData = createPropertyData( "timestamp", "field" ); @@ -312,8 +312,10 @@ public class RevisionInfoConfiguration { continue; } - XClass clazz = reflectionManager.toXClass( persistentClass.getMappedClass() ); - final RevisionEntity revisionEntity = clazz.getAnnotation( RevisionEntity.class ); + final ClassDetails classDetails = metadata.getClassDetailsRegistry().resolveClassDetails( + persistentClass.getClassName() + ); + final RevisionEntity revisionEntity = classDetails.getDirectAnnotationUsage( RevisionEntity.class ); if ( revisionEntity == null ) { // not annotated, skip continue; @@ -324,13 +326,13 @@ public class RevisionInfoConfiguration { } // Verify that the revision entity isn't audited - if ( clazz.getAnnotation( Audited.class ) != null ) { + if ( classDetails.hasDirectAnnotationUsage( Audited.class ) ) { throw new EnversMappingException( "The @RevisionEntity entity cannot be audited" ); } revisionEntityFound = true; - resolveConfiguration( clazz ); + resolveConfiguration( classDetails ); if ( !revisionNumberFound || !revisionTimestampFound ) { // A revision number and timestamp fields must be annotated or the revision entity mapping @@ -436,115 +438,116 @@ public class RevisionInfoConfiguration { || modifiedEntityNamesFound; } - private void resolveConfiguration(XClass clazz) { - final XClass superclazz = clazz.getSuperclass(); - if ( !Object.class.getName().equals( superclazz.getName() ) ) { + private void resolveConfiguration(ClassDetails classDetails) { + final ClassDetails superclass = classDetails.getSuperClass(); + if ( !Object.class.getName().equals( superclass.getName() ) ) { // traverse to the top of the entity hierarchy - resolveConfiguration( superclazz ); + resolveConfiguration( superclass ); } - resolveConfigurationFromProperties( clazz, "field" ); - resolveConfigurationFromProperties( clazz, "property" ); + resolveConfigurationFromProperties( classDetails, "field" ); + resolveConfigurationFromProperties( classDetails, "property" ); } - private void resolveConfigurationFromProperties(XClass clazz, String accessType) { - for ( XProperty property : clazz.getDeclaredProperties( accessType ) ) { - final RevisionNumber revisionNumber = property.getAnnotation( RevisionNumber.class ); + private void resolveConfigurationFromProperties(ClassDetails classDetails, String accessType) { + for ( MemberDetails member : getMemberDetails( classDetails, accessType ) ) { + final RevisionNumber revisionNumber = member.getDirectAnnotationUsage( RevisionNumber.class ); if ( revisionNumber != null ) { - resolveRevisionNumberFromProperty( property, accessType ); + resolveRevisionNumberFromProperty( member, accessType ); } - final RevisionTimestamp revisionTimestamp = property.getAnnotation( RevisionTimestamp.class ); + final RevisionTimestamp revisionTimestamp = member.getDirectAnnotationUsage( RevisionTimestamp.class ); if ( revisionTimestamp != null ) { - resolveRevisionTimestampFromProperty( property, accessType ); + resolveRevisionTimestampFromProperty( member, accessType ); } - final ModifiedEntityNames modifiedEntityNames = property.getAnnotation( ModifiedEntityNames.class ); + final ModifiedEntityNames modifiedEntityNames = member.getDirectAnnotationUsage( ModifiedEntityNames.class ); if ( modifiedEntityNames != null ) { - resolveModifiedEntityNamesFromProperty( property, accessType ); + resolveModifiedEntityNamesFromProperty( member, accessType ); } } } - private void resolveRevisionNumberFromProperty(XProperty property, String accessType) { + private void resolveRevisionNumberFromProperty(MemberDetails memberDetails, String accessType) { if ( revisionNumberFound ) { throw new EnversMappingException( "Only one property can be defined with @RevisionNumber" ); } - final XClass propertyType = property.getType(); - if ( isAnyType( propertyType, Integer.class, Integer.TYPE ) ) { - revisionInfoIdData = createPropertyData( property, accessType ); + final TypeDetails type = memberDetails.getType(); + if ( isAnyType( type, Integer.class, Integer.TYPE ) ) { + revisionInfoIdData = createPropertyData( memberDetails, accessType ); revisionNumberFound = true; } - else if ( isAnyType( propertyType, Long.class, Long.TYPE ) ) { - revisionInfoIdData = createPropertyData( property, accessType ); + else if ( isAnyType( type, Long.class, Long.TYPE ) ) { + revisionInfoIdData = createPropertyData( memberDetails, accessType ); revisionPropType = "long"; revisionNumberFound = true; } else { - throwUnexpectedAnnotatedType( property, RevisionNumber.class, "int, Integer, long, or Long" ); + throwUnexpectedAnnotatedType( memberDetails, RevisionNumber.class, "int, Integer, long, or Long" ); } // Getting the @Column definition of the revision number property, to later use that information // to generate the same mapping for the relation from an audit table's revision number to the // revision entity's revision number field. - final Column column = property.getAnnotation( Column.class ); + final Column column = memberDetails.getDirectAnnotationUsage( Column.class ); if ( column != null ) { revisionPropSqlType = column.columnDefinition(); } } - private void resolveRevisionTimestampFromProperty(XProperty property, String accessType) { + private void resolveRevisionTimestampFromProperty(MemberDetails memberDetails, String accessType) { if ( revisionTimestampFound ) { throw new EnversMappingException( "Only one property can be defined with @RevisionTimestamp" ); } - final XClass propertyType = property.getType(); - if ( isAnyType( propertyType, Long.class, Long.TYPE, Date.class, LocalDateTime.class, Instant.class, java.sql.Date.class ) ) { - revisionInfoTimestampData = createPropertyData( property, accessType ); + final TypeDetails type = memberDetails.getType(); + if ( isAnyType( type, Long.class, Long.TYPE, Date.class, LocalDateTime.class, Instant.class, java.sql.Date.class ) ) { + revisionInfoTimestampData = createPropertyData( memberDetails, accessType ); revisionTimestampFound = true; } else { - throwUnexpectedAnnotatedType( property, RevisionTimestamp.class, "long, Long, Date, LocalDateTime, Instant, or java.sql.Date" ); + throwUnexpectedAnnotatedType( memberDetails, RevisionTimestamp.class, "long, Long, Date, LocalDateTime, Instant, or java.sql.Date" ); } } - private void resolveModifiedEntityNamesFromProperty(XProperty property, String accessType) { + private void resolveModifiedEntityNamesFromProperty(MemberDetails memberDetails, String accessType) { if ( modifiedEntityNamesFound ) { throw new EnversMappingException( "Only one property can be defined with @ModifiedEntityNames" ); } - final XClass propertyType = property.getType(); - if ( isAnyType( propertyType, Set.class ) ) { - final XClass elementType = property.getElementClass(); + final TypeDetails type = memberDetails.getType(); + if ( isAnyType( type, Set.class ) ) { + final TypeDetails elementType = memberDetails.getElementType(); if ( isAnyType( elementType, String.class ) ) { - modifiedEntityNamesData = createPropertyData( property, accessType ); + modifiedEntityNamesData = createPropertyData( memberDetails, accessType ); modifiedEntityNamesFound = true; return; } } - throwUnexpectedAnnotatedType( property, ModifiedEntityNames.class, "Set" ); + throwUnexpectedAnnotatedType( memberDetails, ModifiedEntityNames.class, "Set" ); } - private PropertyData createPropertyData(XProperty property, String accessType) { - return createPropertyData( property.getName(), accessType ); + private PropertyData createPropertyData(MemberDetails memberDetails, String accessType) { + return createPropertyData( memberDetails.resolveAttributeName(), accessType ); } private PropertyData createPropertyData(String name, String accessType) { return new PropertyData( name, name, accessType ); } - private boolean isAnyType(XClass clazz, Class... types) { + private boolean isAnyType(TypeDetails typeDetails, Class... types) { for ( Class type : types ) { - if ( isType( clazz, type ) ) { + if ( isType( typeDetails, type ) ) { return true; } } return false; } - private boolean isType(XClass clazz, Class type) { - return reflectionManager.equals( clazz, type ); + private boolean isType(TypeDetails typeDetails, Class type) { + final String className = typeDetails != null ? typeDetails.determineRawClass().getClassName() : null; + return className != null && className.equals( type.getName() ); } private Class getRevisionListenerClass(Class defaultListener) { @@ -554,12 +557,12 @@ public class RevisionInfoConfiguration { return defaultListener; } - private void throwUnexpectedAnnotatedType(XProperty property, Class annotation, String allowedTypes) { + private void throwUnexpectedAnnotatedType(MemberDetails memberDetails, Class annotation, String allowedTypes) { throw new EnversMappingException( String.format( Locale.ENGLISH, "The field '%s' annotated with '@%s' must be of type: %s", - property.getName(), + memberDetails.resolveAttributeName(), annotation.getName(), allowedTypes ) diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/reader/AnnotationsMetadataReader.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/reader/AnnotationsMetadataReader.java index 838557517a..57d9682070 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/reader/AnnotationsMetadataReader.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/reader/AnnotationsMetadataReader.java @@ -8,7 +8,6 @@ package org.hibernate.envers.configuration.internal.metadata.reader; import java.lang.annotation.Annotation; -import org.hibernate.annotations.common.reflection.XClass; import org.hibernate.envers.AuditTable; import org.hibernate.envers.Audited; import org.hibernate.envers.RelationTargetAuditMode; @@ -16,6 +15,7 @@ import org.hibernate.envers.SecondaryAuditTable; import org.hibernate.envers.SecondaryAuditTables; import org.hibernate.envers.boot.spi.EnversMetadataBuildingContext; import org.hibernate.mapping.PersistentClass; +import org.hibernate.models.spi.ClassDetails; /** * A helper class to read versioning meta-data from annotations on a persistent class. @@ -31,8 +31,8 @@ public final class AnnotationsMetadataReader { this.metadataBuildingContext = metadataBuildingContext; } - private RelationTargetAuditMode getDefaultAudited(XClass clazz) { - final Audited defaultAudited = clazz.getAnnotation( Audited.class ); + private RelationTargetAuditMode getDefaultAudited(ClassDetails classDetails) { + final Audited defaultAudited = classDetails.getDirectAnnotationUsage( Audited.class ); if ( defaultAudited != null ) { return defaultAudited.targetAuditMode(); @@ -42,8 +42,8 @@ public final class AnnotationsMetadataReader { } } - private void addAuditTable(ClassAuditingData auditData, XClass clazz) { - final AuditTable auditTable = clazz.getAnnotation( AuditTable.class ); + private void addAuditTable(ClassAuditingData auditData, ClassDetails classDetails) { + final AuditTable auditTable = classDetails.getDirectAnnotationUsage( AuditTable.class ); if ( auditTable != null ) { auditData.setAuditTable( auditTable ); } @@ -52,9 +52,9 @@ public final class AnnotationsMetadataReader { } } - private void addAuditSecondaryTables(ClassAuditingData auditData, XClass clazz) { + private void addAuditSecondaryTables(ClassAuditingData auditData, ClassDetails classDetails) { // Getting information on secondary tables - final SecondaryAuditTable secondaryVersionsTable1 = clazz.getAnnotation( SecondaryAuditTable.class ); + final SecondaryAuditTable secondaryVersionsTable1 = classDetails.getDirectAnnotationUsage( SecondaryAuditTable.class ); if ( secondaryVersionsTable1 != null ) { auditData.getSecondaryTableDictionary().put( secondaryVersionsTable1.secondaryTableName(), @@ -62,7 +62,7 @@ public final class AnnotationsMetadataReader { ); } - final SecondaryAuditTables secondaryAuditTables = clazz.getAnnotation( SecondaryAuditTables.class ); + final SecondaryAuditTables secondaryAuditTables = classDetails.getDirectAnnotationUsage( SecondaryAuditTables.class ); if ( secondaryAuditTables != null ) { for ( SecondaryAuditTable secondaryAuditTable2 : secondaryAuditTables.value() ) { auditData.getSecondaryTableDictionary().put( @@ -75,21 +75,23 @@ public final class AnnotationsMetadataReader { public ClassAuditingData getAuditData(PersistentClass persistentClass) { final ClassAuditingData auditData = new ClassAuditingData( persistentClass ); - final XClass xclass = metadataBuildingContext.getReflectionManager().toXClass( persistentClass.getMappedClass() ); + final ClassDetails classDetails = metadataBuildingContext.getClassDetailsRegistry().resolveClassDetails( + persistentClass.getClassName() + ); - final RelationTargetAuditMode auditMode = getDefaultAudited( xclass ); + final RelationTargetAuditMode auditMode = getDefaultAudited( classDetails ); if ( auditMode != null ) { auditData.setDefaultAudited( true ); } new AuditedPropertiesReader( metadataBuildingContext, - PersistentPropertiesSource.forClass( persistentClass, xclass ), + PersistentPropertiesSource.forClass( persistentClass, classDetails ), auditData ).read(); - addAuditTable( auditData, xclass ); - addAuditSecondaryTables( auditData, xclass ); + addAuditTable( auditData, classDetails ); + addAuditSecondaryTables( auditData, classDetails ); return auditData; } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/reader/AuditedPropertiesReader.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/reader/AuditedPropertiesReader.java index 38bfb7ceb2..47782cee46 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/reader/AuditedPropertiesReader.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/reader/AuditedPropertiesReader.java @@ -17,9 +17,6 @@ import java.util.Set; import java.util.function.Function; import org.hibernate.HibernateException; -import org.hibernate.annotations.common.reflection.ReflectionManager; -import org.hibernate.annotations.common.reflection.XClass; -import org.hibernate.annotations.common.reflection.XProperty; import org.hibernate.envers.AuditJoinTable; import org.hibernate.envers.AuditMappedBy; import org.hibernate.envers.AuditOverride; @@ -33,12 +30,15 @@ import org.hibernate.envers.boot.EnversMappingException; import org.hibernate.envers.boot.internal.ModifiedColumnNameResolver; import org.hibernate.envers.boot.spi.EnversMetadataBuildingContext; import org.hibernate.envers.internal.tools.MappingTools; -import org.hibernate.envers.internal.tools.ReflectionTools; import org.hibernate.envers.internal.tools.StringTools; import org.hibernate.internal.util.StringHelper; import org.hibernate.mapping.Component; import org.hibernate.mapping.Property; import org.hibernate.mapping.Value; +import org.hibernate.models.spi.ClassDetails; +import org.hibernate.models.spi.FieldDetails; +import org.hibernate.models.spi.MemberDetails; +import org.hibernate.models.spi.SourceModelBuildingContext; import org.hibernate.spi.NavigablePath; import jakarta.persistence.ElementCollection; @@ -46,8 +46,11 @@ import jakarta.persistence.Lob; import jakarta.persistence.MapKey; import jakarta.persistence.MapKeyEnumerated; import jakarta.persistence.OneToMany; +import jakarta.persistence.Transient; import jakarta.persistence.Version; +import static org.hibernate.envers.configuration.internal.ModelsHelper.dynamicFieldDetails; +import static org.hibernate.envers.configuration.internal.ModelsHelper.getMember; import static org.hibernate.envers.internal.tools.Tools.newHashMap; import static org.hibernate.envers.internal.tools.Tools.newHashSet; @@ -79,12 +82,12 @@ public class AuditedPropertiesReader { // Mapping class field to corresponding element. private final Map propertiesGroupMapping; - private final Set overriddenAuditedProperties; - private final Set overriddenNotAuditedProperties; - private final Map overriddenAuditedPropertiesJoinTables; + private final Set overriddenAuditedProperties; + private final Set overriddenNotAuditedProperties; + private final Map overriddenAuditedPropertiesJoinTables; - private final Set overriddenAuditedClasses; - private final Set overriddenNotAuditedClasses; + private final Set overriddenAuditedClasses; + private final Set overriddenNotAuditedClasses; public AuditedPropertiesReader( EnversMetadataBuildingContext metadataBuildingContext, @@ -129,7 +132,7 @@ public class AuditedPropertiesReader { else { // Retrieve classes and properties that are explicitly marked for auditing process by any superclass // of currently mapped entity or itself. - final XClass clazz = persistentPropertiesSource.getXClass(); + final ClassDetails classDetails = persistentPropertiesSource.getClassDetails(); if ( persistentPropertiesSource.hasCompositeUserType() ) { for ( String propertyName : fieldAccessedPersistentProperties ) { final Property property = persistentPropertiesSource.getProperty( propertyName ); @@ -153,9 +156,9 @@ public class AuditedPropertiesReader { } } else { - readAuditOverrides( clazz ); + readAuditOverrides( classDetails ); // Adding all properties from the given class. - addPropertiesFromClass( clazz ); + addPropertiesFromClass( classDetails ); } } } @@ -164,16 +167,16 @@ public class AuditedPropertiesReader { * Recursively constructs sets of audited and not audited properties and classes which behavior has been overridden * using {@link AuditOverride} annotation. * - * @param clazz Class that is being processed. Currently mapped entity shall be passed during first invocation. + * @param classDetails Class that is being processed. Currently mapped entity shall be passed during first invocation. */ - private void readAuditOverrides(XClass clazz) { + private void readAuditOverrides(ClassDetails classDetails) { /* TODO: Code to remove with @Audited.auditParents - start. */ - final ReflectionManager reflectionManager = metadataBuildingContext.getReflectionManager(); - final Audited allClassAudited = clazz.getAnnotation( Audited.class ); + final Audited allClassAudited = classDetails.getDirectAnnotationUsage( Audited.class ); if ( allClassAudited != null && allClassAudited.auditParents().length > 0 ) { for ( Class c : allClassAudited.auditParents() ) { - final XClass parentClass = reflectionManager.toXClass( c ); - checkSuperclass( clazz, parentClass ); + final ClassDetails parentClass = metadataBuildingContext.getClassDetailsRegistry() + .resolveClassDetails( c.getName() ); + checkSuperclass( classDetails, parentClass ); if ( !overriddenNotAuditedClasses.contains( parentClass ) ) { // If the class has not been marked as not audited by the subclass. overriddenAuditedClasses.add( parentClass ); @@ -181,15 +184,16 @@ public class AuditedPropertiesReader { } } /* TODO: Code to remove with @Audited.auditParents - finish. */ - final List auditOverrides = computeAuditOverrides( clazz ); + final List auditOverrides = computeAuditOverrides( classDetails ); for ( AuditOverride auditOverride : auditOverrides ) { if ( auditOverride.forClass() != void.class ) { - final XClass overrideClass = reflectionManager.toXClass( auditOverride.forClass() ); - checkSuperclass( clazz, overrideClass ); + final ClassDetails overrideClass = metadataBuildingContext.getClassDetailsRegistry() + .resolveClassDetails( auditOverride.forClass().getName() ); + checkSuperclass( classDetails, overrideClass ); final String propertyName = auditOverride.name(); if ( !StringTools.isEmpty( propertyName ) ) { // Override @Audited annotation on property level. - final XProperty property = getProperty( overrideClass, propertyName ); + final MemberDetails property = getProperty( overrideClass, propertyName ); if ( auditOverride.isAudited() ) { if ( !overriddenNotAuditedProperties.contains( property ) ) { // If the property has not been marked as not audited by the subclass. @@ -221,20 +225,20 @@ public class AuditedPropertiesReader { } } } - final XClass superclass = clazz.getSuperclass(); - if ( !clazz.isInterface() && !Object.class.getName().equals( superclass.getName() ) ) { + final ClassDetails superclass = classDetails.getSuperClass(); + if ( !classDetails.isInterface() && !Object.class.getName().equals( superclass.getName() ) ) { readAuditOverrides( superclass ); } } /** - * @param clazz Source class. + * @param classDetails Source class. * * @return List of @AuditOverride annotations applied at class level. */ - private List computeAuditOverrides(XClass clazz) { - final AuditOverrides auditOverrides = clazz.getAnnotation( AuditOverrides.class ); - final AuditOverride auditOverride = clazz.getAnnotation( AuditOverride.class ); + private List computeAuditOverrides(ClassDetails classDetails) { + final AuditOverrides auditOverrides = classDetails.getDirectAnnotationUsage( AuditOverrides.class ); + final AuditOverride auditOverride = classDetails.getDirectAnnotationUsage( AuditOverride.class ); if ( auditOverrides == null && auditOverride != null ) { return Arrays.asList( auditOverride ); } @@ -244,7 +248,7 @@ public class AuditedPropertiesReader { else if ( auditOverrides != null && auditOverride != null ) { throw new EnversMappingException( "@AuditOverrides annotation should encapsulate all @AuditOverride declarations. " + - "Please revise Envers annotations applied to class " + clazz.getName() + "." + "Please revise Envers annotations applied to class " + classDetails.getName() + "." ); } return Collections.emptyList(); @@ -256,8 +260,8 @@ public class AuditedPropertiesReader { * @param child Subclass. * @param parent Superclass. */ - private void checkSuperclass(XClass child, XClass parent) { - if ( !parent.isAssignableFrom( child ) ) { + private void checkSuperclass(ClassDetails child, ClassDetails parent) { + if ( !child.isImplementor( parent.toJavaClass() ) ) { throw new EnversMappingException( "Class " + parent.getName() + " is not assignable from " + child.getName() + ". " + "Please revise Envers annotations applied to " + child.getName() + " type." @@ -268,20 +272,20 @@ public class AuditedPropertiesReader { /** * Checks whether class contains property with a given name. If not {@link EnversMappingException} is thrown. * - * @param clazz Class. + * @param classDetails Class. * @param propertyName Property name. * * @return Property object. */ - private XProperty getProperty(XClass clazz, String propertyName) { - final XProperty property = ReflectionTools.getProperty( clazz, propertyName ); - if ( property == null ) { + private MemberDetails getProperty(ClassDetails classDetails, String propertyName) { + final MemberDetails member = getMember( classDetails, propertyName ); + if ( member == null ) { throw new EnversMappingException( - "Property '" + propertyName + "' not found in class " + clazz.getName() + ". " + - "Please revise Envers annotations applied to class " + persistentPropertiesSource.getXClass() + "." + "Property '" + propertyName + "' not found in class " + classDetails.getName() + ". " + + "Please revise Envers annotations applied to class " + persistentPropertiesSource.getClassDetails() + "." ); } - return property; + return member; } private void readPersistentPropertiesAccess() { @@ -313,7 +317,7 @@ public class AuditedPropertiesReader { } /** - * @param clazz Class which properties are currently being added. + * @param classDetails Class which properties are currently being added. * * @return {@link Audited} annotation of specified class. If processed type hasn't been explicitly marked, method * checks whether given class exists in {@link AuditedPropertiesReader#overriddenAuditedClasses} collection. @@ -321,27 +325,27 @@ public class AuditedPropertiesReader { * {@code null}. If processed type exists in {@link AuditedPropertiesReader#overriddenNotAuditedClasses} * collection, the result is also {@code null}. */ - private Audited computeAuditConfiguration(XClass clazz) { - Audited allClassAudited = clazz.getAnnotation( Audited.class ); + private Audited computeAuditConfiguration(ClassDetails classDetails) { + Audited allClassAudited = classDetails.getDirectAnnotationUsage( Audited.class ); // If processed class is not explicitly marked with @Audited annotation, check whether auditing is // forced by any of its child entities configuration (@AuditedOverride.forClass). - if ( allClassAudited == null && overriddenAuditedClasses.contains( clazz ) ) { + if ( allClassAudited == null && overriddenAuditedClasses.contains( classDetails ) ) { // Declared audited parent copies @Audited.modStore and @Audited.targetAuditMode configuration from // currently mapped entity. - allClassAudited = persistentPropertiesSource.getXClass().getAnnotation( Audited.class ); + allClassAudited = persistentPropertiesSource.getClassDetails().getDirectAnnotationUsage( Audited.class ); if ( allClassAudited == null ) { // If parent class declares @Audited on the field/property level. allClassAudited = DEFAULT_AUDITED; } } - else if ( allClassAudited != null && overriddenNotAuditedClasses.contains( clazz ) ) { + else if ( allClassAudited != null && overriddenNotAuditedClasses.contains( classDetails ) ) { return null; } return allClassAudited; } private void addPropertiesFromDynamicComponent(PersistentPropertiesSource propertiesSource) { - Audited audited = computeAuditConfiguration( propertiesSource.getXClass() ); + Audited audited = computeAuditConfiguration( propertiesSource.getClassDetails() ); if ( !fieldAccessedPersistentProperties.isEmpty() ) { throw new EnversMappingException( "Audited dynamic component cannot have properties with access=\"field\" for properties: " + @@ -354,20 +358,18 @@ public class AuditedPropertiesReader { String accessType = entry.getValue(); if ( !auditedPropertiesHolder.contains( property ) ) { final Value propertyValue = persistentPropertiesSource.getProperty( property ).getValue(); + final SourceModelBuildingContext buildingContext = metadataBuildingContext.getSourceModelBuildingContext(); + final FieldDetails fieldDetails = dynamicFieldDetails( propertiesSource, property, buildingContext ); if ( propertyValue instanceof Component ) { this.addFromComponentProperty( - new DynamicProperty( propertiesSource, property ), + fieldDetails, accessType, (Component) propertyValue, audited ); } else { - this.addFromNotComponentProperty( - new DynamicProperty( propertiesSource, property ), - accessType, - audited - ); + this.addFromNotComponentProperty( fieldDetails, accessType, audited ); } } } @@ -376,29 +378,29 @@ public class AuditedPropertiesReader { /** * Recursively adds all audited properties of entity class and its superclasses. * - * @param clazz Currently processed class. + * @param classDetails Currently processed class. */ - private void addPropertiesFromClass(XClass clazz) { - final Audited allClassAudited = computeAuditConfiguration( clazz ); + private void addPropertiesFromClass(ClassDetails classDetails) { + final Audited allClassAudited = computeAuditConfiguration( classDetails ); //look in the class - addFromProperties( - clazz.getDeclaredProperties( "field" ), + classDetails.forEachField( (i, field) -> addFromProperty( + field, it -> "field", fieldAccessedPersistentProperties, allClassAudited - ); - addFromProperties( - clazz.getDeclaredProperties( "property" ), + ) ); + classDetails.forEachMethod( (i, method) -> addFromProperty( + method, propertyAccessedPersistentProperties::get, propertyAccessedPersistentProperties.keySet(), allClassAudited - ); + ) ); if ( isClassHierarchyTraversalNeeded( allClassAudited ) ) { - final XClass superclazz = clazz.getSuperclass(); - if ( !clazz.isInterface() && !"java.lang.Object".equals( superclazz.getName() ) ) { - addPropertiesFromClass( superclazz ); + final ClassDetails superclass = classDetails.getSuperClass(); + if ( !classDetails.isInterface() && !"java.lang.Object".equals( superclass.getName() ) ) { + addPropertiesFromClass( superclass ); } } } @@ -407,53 +409,56 @@ public class AuditedPropertiesReader { return allClassAudited != null || !auditedPropertiesHolder.isEmpty(); } - private void addFromProperties( - Iterable properties, + private void addFromProperty( + MemberDetails memberDetails, Function accessTypeProvider, Set persistentProperties, Audited allClassAudited) { - for ( XProperty property : properties ) { - final String accessType = accessTypeProvider.apply( property.getName() ); + if ( !memberDetails.isPersistable() || memberDetails.hasDirectAnnotationUsage( Transient.class ) ) { + return; + } - // If this is not a persistent property, with the same access type as currently checked, - // it's not audited as well. - // If the property was already defined by the subclass, is ignored by superclasses - if ( persistentProperties.contains( property.getName() ) - && !auditedPropertiesHolder.contains( property.getName() ) ) { - final Value propertyValue = persistentPropertiesSource.getProperty( property.getName() ).getValue(); - if ( propertyValue instanceof Component ) { - this.addFromComponentProperty( property, accessType, (Component) propertyValue, allClassAudited ); - } - else { - this.addFromNotComponentProperty( property, accessType, allClassAudited ); - } + final String attributeName = memberDetails.resolveAttributeName(); + final String accessType = accessTypeProvider.apply( attributeName ); + + // If this is not a persistent property, with the same access type as currently checked, + // it's not audited as well. + // If the property was already defined by the subclass, is ignored by superclasses + if ( persistentProperties.contains( attributeName ) + && !auditedPropertiesHolder.contains( attributeName ) ) { + final Value propertyValue = persistentPropertiesSource.getProperty( attributeName ).getValue(); + if ( propertyValue instanceof Component ) { + this.addFromComponentProperty( memberDetails, accessType, (Component) propertyValue, allClassAudited ); } - else if ( propertiesGroupMapping.containsKey( property.getName() ) ) { - // Retrieve embedded component name based on class field. - final String embeddedName = propertiesGroupMapping.get( property.getName() ); - if ( !auditedPropertiesHolder.contains( embeddedName ) ) { - // Manage properties mapped within tag. - final Value propertyValue = persistentPropertiesSource.getProperty( embeddedName ).getValue(); - this.addFromPropertiesGroup( - embeddedName, - property, - accessType, - (Component) propertyValue, - allClassAudited - ); - } + else { + this.addFromNotComponentProperty( memberDetails, accessType, allClassAudited ); + } + } + else if ( propertiesGroupMapping.containsKey( attributeName ) ) { + // Retrieve embedded component name based on class field. + final String embeddedName = propertiesGroupMapping.get( attributeName ); + if ( !auditedPropertiesHolder.contains( embeddedName ) ) { + // Manage properties mapped within tag. + final Value propertyValue = persistentPropertiesSource.getProperty( embeddedName ).getValue(); + this.addFromPropertiesGroup( + embeddedName, + memberDetails, + accessType, + (Component) propertyValue, + allClassAudited + ); } } } private void addFromPropertiesGroup( String embeddedName, - XProperty property, + MemberDetails memberDetails, String accessType, Component propertyValue, Audited allClassAudited) { final ComponentAuditingData componentData = new ComponentAuditingData(); - final boolean isAudited = fillPropertyData( property, componentData, accessType, allClassAudited ); + final boolean isAudited = fillPropertyData( memberDetails, componentData, accessType, allClassAudited ); if ( isAudited ) { // EntityPersister.getPropertyNames() returns name of embedded component instead of class field. componentData.setName( embeddedName ); @@ -477,17 +482,18 @@ public class AuditedPropertiesReader { } private void addFromComponentProperty( - XProperty property, + MemberDetails memberDetails, String accessType, Component propertyValue, Audited allClassAudited) { final ComponentAuditingData componentData = new ComponentAuditingData(); - final boolean isAudited = fillPropertyData( property, componentData, accessType, allClassAudited ); + final boolean isAudited = fillPropertyData( memberDetails, componentData, accessType, allClassAudited ); final PersistentPropertiesSource componentPropertiesSource; if ( propertyValue.isDynamic() ) { - final XClass xClass = metadataBuildingContext.getReflectionManager().toXClass( Map.class ); - componentPropertiesSource = PersistentPropertiesSource.forComponent( propertyValue, xClass, true ); + final ClassDetails mapClassDetails = metadataBuildingContext.getClassDetailsRegistry() + .getClassDetails( Map.class.getName() ); + componentPropertiesSource = PersistentPropertiesSource.forComponent( propertyValue, mapClassDetails, true ); } else { componentPropertiesSource = PersistentPropertiesSource.forComponent( metadataBuildingContext, propertyValue ); @@ -497,23 +503,23 @@ public class AuditedPropertiesReader { metadataBuildingContext, componentPropertiesSource, componentData, - propertyNamePrefix + MappingTools.createComponentPrefix( property.getName() ) + propertyNamePrefix + MappingTools.createComponentPrefix( memberDetails.resolveAttributeName() ) ); audPropReader.read( allClassAudited ); if ( isAudited ) { // Now we know that the property is audited - auditedPropertiesHolder.addPropertyAuditingData( property.getName(), componentData ); + auditedPropertiesHolder.addPropertyAuditingData( memberDetails.resolveAttributeName(), componentData ); } } - private void addFromNotComponentProperty(XProperty property, String accessType, Audited allClassAudited) { + private void addFromNotComponentProperty(MemberDetails memberDetails, String accessType, Audited allClassAudited) { final PropertyAuditingData propertyData = new PropertyAuditingData(); - final boolean isAudited = fillPropertyData( property, propertyData, accessType, allClassAudited ); + final boolean isAudited = fillPropertyData( memberDetails, propertyData, accessType, allClassAudited ); if ( isAudited ) { // Now we know that the property is audited - auditedPropertiesHolder.addPropertyAuditingData( property.getName(), propertyData ); + auditedPropertiesHolder.addPropertyAuditingData( memberDetails.resolveAttributeName(), propertyData ); } } @@ -521,59 +527,59 @@ public class AuditedPropertiesReader { /** * Checks if a property is audited and if yes, fills all of its data. * - * @param property Property to check. + * @param memberDetails Property to check. * @param propertyData Property data, on which to set this property's modification store. * @param accessType Access type for the property. * * @return False if this property is not audited. */ private boolean fillPropertyData( - XProperty property, + MemberDetails memberDetails, PropertyAuditingData propertyData, String accessType, Audited allClassAudited) { // check if a property is declared as not audited to exclude it // useful if a class is audited but some properties should be excluded - final NotAudited unVer = property.getAnnotation( NotAudited.class ); + final NotAudited unVer = memberDetails.getDirectAnnotationUsage( NotAudited.class ); if ( ( unVer != null - && !overriddenAuditedProperties.contains( property ) ) - || overriddenNotAuditedProperties.contains( property ) ) { + && !overriddenAuditedProperties.contains( memberDetails ) ) + || overriddenNotAuditedProperties.contains( memberDetails ) ) { return false; } else { // if the optimistic locking field has to be unversioned and the current property // is the optimistic locking field, don't audit it if ( metadataBuildingContext.getConfiguration().isDoNotAuditOptimisticLockingField() ) { - final Version jpaVer = property.getAnnotation( Version.class ); + final Version jpaVer = memberDetails.getDirectAnnotationUsage( Version.class ); if ( jpaVer != null ) { return false; } } } - final String propertyName = propertyNamePrefix + property.getName(); + final String propertyName = propertyNamePrefix + memberDetails.resolveAttributeName(); final String modifiedFlagsSuffix = metadataBuildingContext.getConfiguration().getModifiedFlagsSuffix(); - if ( !this.checkAudited( property, propertyData,propertyName, allClassAudited, modifiedFlagsSuffix ) ) { + if ( !this.checkAudited( memberDetails, propertyData,propertyName, allClassAudited, modifiedFlagsSuffix ) ) { return false; } - validateLobMappingSupport( property ); + validateLobMappingSupport( memberDetails ); propertyData.setName( propertyName ); - propertyData.setBeanName( property.getName() ); + propertyData.setBeanName( memberDetails.resolveAttributeName() ); propertyData.setAccessType( accessType ); - addPropertyJoinTables( property, propertyData ); - addPropertyCollectionAuditTable( property, propertyData ); - addPropertyAuditingOverrides( property, propertyData ); - if ( !processPropertyAuditingOverrides( property, propertyData ) ) { + addPropertyJoinTables( memberDetails, propertyData ); + addPropertyCollectionAuditTable( memberDetails, propertyData ); + addPropertyAuditingOverrides( memberDetails, propertyData ); + if ( !processPropertyAuditingOverrides( memberDetails, propertyData ) ) { // not audited due to AuditOverride annotation return false; } - addPropertyMapKey( property, propertyData ); - setPropertyAuditMappedBy( property, propertyData ); - setPropertyRelationMappedBy( property, propertyData ); + addPropertyMapKey( memberDetails, propertyData ); + setPropertyAuditMappedBy( memberDetails, propertyData ); + setPropertyRelationMappedBy( memberDetails, propertyData ); return true; } @@ -588,8 +594,9 @@ public class AuditedPropertiesReader { final PersistentPropertiesSource componentPropertiesSource; if ( propertyValue.isDynamic() ) { - final XClass xClass = metadataBuildingContext.getReflectionManager().toXClass( Map.class ); - componentPropertiesSource = PersistentPropertiesSource.forComponent( propertyValue, xClass, true ); + final ClassDetails mapClassDetails = metadataBuildingContext.getClassDetailsRegistry() + .getClassDetails( Map.class.getName() ); + componentPropertiesSource = PersistentPropertiesSource.forComponent( propertyValue, mapClassDetails, true ); } else { componentPropertiesSource = PersistentPropertiesSource.forComponent( metadataBuildingContext, propertyValue ); @@ -654,12 +661,12 @@ public class AuditedPropertiesReader { return true; } - private void validateLobMappingSupport(XProperty property) { + private void validateLobMappingSupport(MemberDetails memberDetails) { // HHH-9834 - Sanity check try { - if ( property.isAnnotationPresent( ElementCollection.class ) ) { - if ( property.isAnnotationPresent( Lob.class ) ) { - if ( !property.getCollectionClass().isAssignableFrom( Map.class ) ) { + if ( memberDetails.hasDirectAnnotationUsage( ElementCollection.class ) ) { + if ( memberDetails.hasDirectAnnotationUsage( Lob.class ) ) { + if ( !memberDetails.getType().isImplementor( Map.class ) ) { throw new EnversMappingException( "@ElementCollection combined with @Lob is only supported for Map collection types." ); @@ -672,8 +679,8 @@ public class AuditedPropertiesReader { String.format( Locale.ENGLISH, "Invalid mapping in [%s] for property [%s]", - property.getDeclaringClass().getName(), - property.getName() + memberDetails.getDeclaringType().getName(), + memberDetails.resolveAttributeName() ), e ); @@ -681,23 +688,23 @@ public class AuditedPropertiesReader { } protected boolean checkAudited( - XProperty property, + MemberDetails memberDetails, PropertyAuditingData propertyData, String propertyName, Audited allClassAudited, String modifiedFlagSuffix) { // Checking if this property is explicitly audited or if all properties are. - Audited aud = ( property.isAnnotationPresent( Audited.class ) ) - ? property.getAnnotation( Audited.class ) + Audited aud = ( memberDetails.hasDirectAnnotationUsage( Audited.class ) ) + ? memberDetails.getDirectAnnotationUsage( Audited.class ) : allClassAudited; if ( aud == null - && overriddenAuditedProperties.contains( property ) - && !overriddenNotAuditedProperties.contains( property ) ) { + && overriddenAuditedProperties.contains( memberDetails ) + && !overriddenNotAuditedProperties.contains( memberDetails ) ) { // Assigning @Audited defaults. If anyone needs to customize those values in the future, // appropriate fields shall be added to @AuditOverride annotation. aud = DEFAULT_AUDITED; } if ( aud != null ) { propertyData.setRelationTargetAuditMode( aud.targetAuditMode() ); - propertyData.setRelationTargetNotFoundAction( getRelationNotFoundAction( property, allClassAudited ) ); + propertyData.setRelationTargetNotFoundAction( getRelationNotFoundAction( memberDetails, allClassAudited ) ); propertyData.setUsingModifiedFlag( checkUsingModifiedFlag( aud ) ); propertyData.setModifiedFlagName( ModifiedColumnNameResolver.getName( propertyName, modifiedFlagSuffix ) ); if ( !StringTools.isEmpty( aud.modifiedColumnName() ) ) { @@ -748,15 +755,15 @@ public class AuditedPropertiesReader { return aud.withModifiedFlag(); } - private void setPropertyRelationMappedBy(XProperty property, PropertyAuditingData propertyData) { - final OneToMany oneToMany = property.getAnnotation( OneToMany.class ); + private void setPropertyRelationMappedBy(MemberDetails memberDetails, PropertyAuditingData propertyData) { + final OneToMany oneToMany = memberDetails.getDirectAnnotationUsage( OneToMany.class ); if ( oneToMany != null && StringHelper.isNotEmpty( oneToMany.mappedBy() ) ) { propertyData.setRelationMappedBy( oneToMany.mappedBy() ); } } - private void setPropertyAuditMappedBy(XProperty property, PropertyAuditingData propertyData) { - final AuditMappedBy auditMappedBy = property.getAnnotation( AuditMappedBy.class ); + private void setPropertyAuditMappedBy(MemberDetails memberDetails, PropertyAuditingData propertyData) { + final AuditMappedBy auditMappedBy = memberDetails.getDirectAnnotationUsage( AuditMappedBy.class ); if ( auditMappedBy != null ) { propertyData.setAuditMappedBy( auditMappedBy.mappedBy() ); if ( StringHelper.isNotEmpty( auditMappedBy.positionMappedBy() ) ) { @@ -765,20 +772,20 @@ public class AuditedPropertiesReader { } } - private void addPropertyMapKey(XProperty property, PropertyAuditingData propertyData) { - final MapKey mapKey = property.getAnnotation( MapKey.class ); + private void addPropertyMapKey(MemberDetails memberDetails, PropertyAuditingData propertyData) { + final MapKey mapKey = memberDetails.getDirectAnnotationUsage( MapKey.class ); if ( mapKey != null ) { propertyData.setMapKey( mapKey.name() ); } else { - final MapKeyEnumerated mapKeyEnumerated = property.getAnnotation( MapKeyEnumerated.class ); + final MapKeyEnumerated mapKeyEnumerated = memberDetails.getDirectAnnotationUsage( MapKeyEnumerated.class ); if ( mapKeyEnumerated != null ) { propertyData.setMapKeyEnumType( mapKeyEnumerated.value() ); } } } - private void addPropertyJoinTables(XProperty property, PropertyAuditingData propertyData) { + private void addPropertyJoinTables(MemberDetails memberDetails, PropertyAuditingData propertyData) { // The AuditJoinTable annotation source will follow the following priority rules // 1. Use the override if one is specified // 2. Use the site annotation if one is specified @@ -788,12 +795,12 @@ public class AuditedPropertiesReader { // the join-table specified there should have a higher priority in the event the // super-class defines an equivalent @AuditJoinTable at the site/property level. - final AuditJoinTable overrideJoinTable = overriddenAuditedPropertiesJoinTables.get( property ); + final AuditJoinTable overrideJoinTable = overriddenAuditedPropertiesJoinTables.get( memberDetails ); if ( overrideJoinTable != null ) { propertyData.setJoinTable( new AuditJoinTableData( overrideJoinTable ) ); } else { - final AuditJoinTable propertyJoinTable = property.getAnnotation( AuditJoinTable.class ); + final AuditJoinTable propertyJoinTable = memberDetails.getDirectAnnotationUsage( AuditJoinTable.class ); if ( propertyJoinTable != null ) { propertyData.setJoinTable( new AuditJoinTableData( propertyJoinTable ) ); } @@ -803,8 +810,8 @@ public class AuditedPropertiesReader { } } - private void addPropertyCollectionAuditTable(XProperty property, PropertyAuditingData propertyAuditingData) { - final CollectionAuditTable collectionAuditTableAnn = property.getAnnotation( CollectionAuditTable.class ); + private void addPropertyCollectionAuditTable(MemberDetails memberDetails, PropertyAuditingData propertyAuditingData) { + final CollectionAuditTable collectionAuditTableAnn = memberDetails.getDirectAnnotationUsage( CollectionAuditTable.class ); if ( collectionAuditTableAnn != null ) { propertyAuditingData.setCollectionAuditTable( collectionAuditTableAnn ); } @@ -813,15 +820,15 @@ public class AuditedPropertiesReader { /** * Add the {@link AuditOverride} annotations. * - * @param property the property being processed + * @param memberDetails the property being processed * @param propertyData the Envers auditing data for this property */ - private void addPropertyAuditingOverrides(XProperty property, PropertyAuditingData propertyData) { - final AuditOverride annotationOverride = property.getAnnotation( AuditOverride.class ); + private void addPropertyAuditingOverrides(MemberDetails memberDetails, PropertyAuditingData propertyData) { + final AuditOverride annotationOverride = memberDetails.getDirectAnnotationUsage( AuditOverride.class ); if ( annotationOverride != null ) { propertyData.addAuditingOverride( annotationOverride ); } - final AuditOverrides annotationOverrides = property.getAnnotation( AuditOverrides.class ); + final AuditOverrides annotationOverrides = memberDetails.getDirectAnnotationUsage( AuditOverrides.class ); if ( annotationOverrides != null ) { propertyData.addAuditingOverrides( annotationOverrides ); } @@ -830,16 +837,16 @@ public class AuditedPropertiesReader { /** * Process the {@link AuditOverride} annotations for this property. * - * @param property the property for which the {@link AuditOverride} + * @param memberDetails the property for which the {@link AuditOverride} * annotations are being processed * @param propertyData the Envers auditing data for this property * * @return {@code false} if isAudited() of the override annotation was set to */ - private boolean processPropertyAuditingOverrides(XProperty property, PropertyAuditingData propertyData) { + private boolean processPropertyAuditingOverrides(MemberDetails memberDetails, PropertyAuditingData propertyData) { // Only components register audit overrides, classes will have no entries. for ( AuditOverrideData override : auditedPropertiesHolder.getAuditingOverrides() ) { - if ( property.getName().equals( override.getName() ) ) { + if ( memberDetails.resolveAttributeName().equals( override.getName() ) ) { // the override applies to this property if ( !override.isAudited() ) { return false; @@ -872,24 +879,24 @@ public class AuditedPropertiesReader { return true; } - protected boolean isOverriddenNotAudited(XProperty property) { - return overriddenNotAuditedProperties.contains( property ); + protected boolean isOverriddenNotAudited(MemberDetails memberDetails) { + return overriddenNotAuditedProperties.contains( memberDetails ); } - protected boolean isOverriddenNotAudited(XClass clazz) { - return overriddenNotAuditedClasses.contains( clazz ); + protected boolean isOverriddenNotAudited(ClassDetails classDetails) { + return overriddenNotAuditedClasses.contains( classDetails ); } - protected boolean isOverriddenAudited(XProperty property) { - return overriddenAuditedProperties.contains( property ); + protected boolean isOverriddenAudited(MemberDetails memberDetails) { + return overriddenAuditedProperties.contains( memberDetails ); } - protected boolean isOverriddenAudited(XClass clazz) { - return overriddenAuditedClasses.contains( clazz ); + protected boolean isOverriddenAudited(ClassDetails classDetails) { + return overriddenAuditedClasses.contains( classDetails ); } - private RelationTargetNotFoundAction getRelationNotFoundAction(XProperty property, Audited classAudited) { - final Audited propertyAudited = property.getAnnotation( Audited.class ); + private RelationTargetNotFoundAction getRelationNotFoundAction(MemberDetails memberDetails, Audited classAudited) { + final Audited propertyAudited = memberDetails.getDirectAnnotationUsage( Audited.class ); // class isn't annotated, check property if ( classAudited == null ) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/reader/ComponentAuditedPropertiesReader.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/reader/ComponentAuditedPropertiesReader.java index 37f3420243..c802d383b9 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/reader/ComponentAuditedPropertiesReader.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/reader/ComponentAuditedPropertiesReader.java @@ -6,13 +6,12 @@ */ package org.hibernate.envers.configuration.internal.metadata.reader; -import org.hibernate.annotations.common.reflection.XClass; -import org.hibernate.annotations.common.reflection.XProperty; -import org.hibernate.envers.AuditOverride; import org.hibernate.envers.Audited; import org.hibernate.envers.boot.internal.ModifiedColumnNameResolver; import org.hibernate.envers.boot.spi.EnversMetadataBuildingContext; import org.hibernate.internal.util.StringHelper; +import org.hibernate.models.spi.ClassDetails; +import org.hibernate.models.spi.MemberDetails; import jakarta.persistence.MappedSuperclass; @@ -56,13 +55,13 @@ public class ComponentAuditedPropertiesReader extends AuditedPropertiesReader { @Override protected boolean checkAudited( - XProperty property, + MemberDetails memberDetails, PropertyAuditingData propertyData, String propertyName, Audited allClassAudited, String modifiedFlagSuffix) { // Checking if this property is explicitly audited or if all properties are. - final Audited aud = property.getAnnotation( Audited.class ); + final Audited aud = memberDetails.getDirectAnnotationUsage( Audited.class ); if ( aud != null ) { propertyData.setRelationTargetAuditMode( aud.targetAuditMode() ); propertyData.setUsingModifiedFlag( checkUsingModifiedFlag( aud ) ); @@ -74,7 +73,7 @@ public class ComponentAuditedPropertiesReader extends AuditedPropertiesReader { else { // get declaring class for property - final XClass declaringClass = property.getDeclaringClass(); + final ClassDetails declaringClass = memberDetails.getDeclaringType(); // check component data to make sure that no audit overrides were not defined at // the parent class or the embeddable at the property level. @@ -88,7 +87,7 @@ public class ComponentAuditedPropertiesReader extends AuditedPropertiesReader { if ( "".equals( auditOverride.getName() ) ) { classNotAuditedOverride = true; } - if ( property.getName().equals( auditOverride.getName() ) ) { + if ( memberDetails.resolveAttributeName().equals( auditOverride.getName() ) ) { return false; } } @@ -96,7 +95,7 @@ public class ComponentAuditedPropertiesReader extends AuditedPropertiesReader { if ( "".equals( auditOverride.getName() ) ) { classAuditedOverride = true; } - if ( property.getName().equals( auditOverride.getName() ) ) { + if ( memberDetails.resolveAttributeName().equals( auditOverride.getName() ) ) { return true; } } @@ -105,12 +104,12 @@ public class ComponentAuditedPropertiesReader extends AuditedPropertiesReader { } // make sure taht if the class or property are explicitly 'isAudited=false', use that. - if ( classNotAuditedOverride || isOverriddenNotAudited( property) || isOverriddenNotAudited( declaringClass ) ) { + if ( classNotAuditedOverride || isOverriddenNotAudited( memberDetails ) || isOverriddenNotAudited( declaringClass ) ) { return false; } // make sure that if the class or property are explicitly 'isAudited=true', use that. - if ( classAuditedOverride || isOverriddenAudited( property ) || isOverriddenAudited( declaringClass ) ) { + if ( classAuditedOverride || isOverriddenAudited( memberDetails ) || isOverriddenAudited( declaringClass ) ) { return true; } @@ -122,7 +121,7 @@ public class ComponentAuditedPropertiesReader extends AuditedPropertiesReader { // assumption here is if a component reader is looking at a @MappedSuperclass, it should be treated // as not being audited if we have reached htis point; allowing components and any @Embeddable // class being audited by default. - if ( declaringClass.isAnnotationPresent( MappedSuperclass.class ) ) { + if ( declaringClass.hasDirectAnnotationUsage( MappedSuperclass.class ) ) { return false; } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/reader/DynamicProperty.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/reader/DynamicProperty.java deleted file mode 100644 index 3c4d7b69a8..0000000000 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/reader/DynamicProperty.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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.envers.configuration.internal.metadata.reader; - -import java.lang.annotation.Annotation; -import java.util.Collection; - -import org.hibernate.annotations.common.reflection.XClass; -import org.hibernate.annotations.common.reflection.XProperty; - -/** - * This class pretends to be property but in fact it represents entry in the map (for dynamic component) - * - * @author Lukasz Zuchowski (author at zuchos dot com) - * @author Chris Cranford - */ -public class DynamicProperty implements XProperty { - - private final PersistentPropertiesSource source; - private final String propertyName; - - public DynamicProperty(PersistentPropertiesSource source, String propertyName) { - this.source = source; - this.propertyName = propertyName; - } - - @Override - public XClass getDeclaringClass() { - return source.getXClass(); - } - - @Override - public String getName() { - return propertyName; - } - - @Override - public boolean isCollection() { - return false; - } - - @Override - public boolean isArray() { - return false; - } - - @Override - public Class> getCollectionClass() { - return null; - } - - @Override - public XClass getType() { - return source.getXClass(); - } - - @Override - public XClass getElementClass() { - return null; - } - - @Override - public XClass getClassOrElementClass() { - return null; - } - - @Override - public XClass getMapKey() { - return null; - } - - @Override - public int getModifiers() { - return 0; - } - - @Override - public void setAccessible(boolean accessible) { - } - - @Override - public Object invoke(Object target, Object... parameters) { - return null; - } - - @Override - public Object invoke(Object target) { - return null; - } - - @Override - public boolean isTypeResolved() { - return false; - } - - @Override - public T getAnnotation(Class annotationType) { - return null; - } - - @Override - public boolean isAnnotationPresent(Class annotationType) { - return false; - } - - @Override - public Annotation[] getAnnotations() { - return new Annotation[0]; - } -} diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/reader/PersistentPropertiesSource.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/reader/PersistentPropertiesSource.java index 18321aedff..d7146a80e1 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/reader/PersistentPropertiesSource.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/internal/metadata/reader/PersistentPropertiesSource.java @@ -8,14 +8,13 @@ package org.hibernate.envers.configuration.internal.metadata.reader; import java.util.Iterator; -import org.hibernate.annotations.common.reflection.XClass; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.envers.boot.EnversMappingException; -import org.hibernate.envers.boot.registry.classloading.ClassLoaderAccessHelper; import org.hibernate.envers.boot.spi.EnversMetadataBuildingContext; import org.hibernate.mapping.Component; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; +import org.hibernate.models.spi.ClassDetails; /** * A source of data on persistent properties of a class or component. @@ -28,7 +27,7 @@ public interface PersistentPropertiesSource { Property getProperty(String propertyName); - XClass getXClass(); + ClassDetails getClassDetails(); boolean isDynamicComponent(); @@ -38,10 +37,10 @@ public interface PersistentPropertiesSource { * Get a persistent properties source for a persistent class. * * @param persistentClass the persistent class - * @param clazz the class + * @param classDetails the class details * @return the properties source */ - static PersistentPropertiesSource forClass(PersistentClass persistentClass, XClass clazz) { + static PersistentPropertiesSource forClass(PersistentClass persistentClass, ClassDetails classDetails) { return new PersistentPropertiesSource() { @Override public Iterator getPropertyIterator() { @@ -54,8 +53,8 @@ public interface PersistentPropertiesSource { } @Override - public XClass getXClass() { - return clazz; + public ClassDetails getClassDetails() { + return classDetails; } @Override @@ -80,9 +79,9 @@ public interface PersistentPropertiesSource { */ static PersistentPropertiesSource forComponent(EnversMetadataBuildingContext context, Component component, boolean dynamic) { try { - Class componentClass = ClassLoaderAccessHelper.loadClass( context, component.getComponentClassName() ); - XClass clazz = context.getReflectionManager().toXClass( componentClass ); - return forComponent( component, clazz, dynamic ); + final ClassDetails classDetails = context.getClassDetailsRegistry() + .resolveClassDetails( component.getComponentClassName() ); + return forComponent( component, classDetails, dynamic ); } catch (ClassLoadingException e) { throw new EnversMappingException( e ); @@ -97,11 +96,11 @@ public interface PersistentPropertiesSource { * Get a persistent properties source for a component with its class already resolved. * * @param component the component - * @param clazz the class + * @param classDetails the class details * @param dynamic whether the component is dynamic or not * @return the properties source */ - static PersistentPropertiesSource forComponent(Component component, XClass clazz, boolean dynamic) { + static PersistentPropertiesSource forComponent(Component component, ClassDetails classDetails, boolean dynamic) { return new PersistentPropertiesSource() { @Override public Iterator getPropertyIterator() { @@ -114,8 +113,8 @@ public interface PersistentPropertiesSource { } @Override - public XClass getXClass() { - return clazz; + public ClassDetails getClassDetails() { + return classDetails; } @Override diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/ReflectionTools.java b/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/ReflectionTools.java index 0c67f2faec..68e465cdea 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/ReflectionTools.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/internal/tools/ReflectionTools.java @@ -10,8 +10,6 @@ import java.lang.reflect.Field; import java.util.Locale; import java.util.Map; -import org.hibernate.annotations.common.reflection.XClass; -import org.hibernate.annotations.common.reflection.XProperty; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.envers.exception.AuditException; @@ -130,36 +128,6 @@ public abstract class ReflectionTools { ); } - /** - * @param clazz Source class. - * @param propertyName Property name. - * - * @return Property object or {@code null} if none with expected name has been found. - */ - public static XProperty getProperty(XClass clazz, String propertyName) { - XProperty property = getProperty( clazz, propertyName, "field" ); - if ( property == null ) { - property = getProperty( clazz, propertyName, "property" ); - } - return property; - } - - /** - * @param clazz Source class. - * @param propertyName Property name. - * @param accessType Expected access type. Legal values are field and property. - * - * @return Property object or {@code null} if none with expected name and access type has been found. - */ - public static XProperty getProperty(XClass clazz, String propertyName, String accessType) { - for ( XProperty property : clazz.getDeclaredProperties( accessType ) ) { - if ( propertyName.equals( property.getName() ) ) { - return property; - } - } - return null; - } - /** * Locate class with a given name. *