diff --git a/build.gradle b/build.gradle index d8da775..6e14045 100644 --- a/build.gradle +++ b/build.gradle @@ -1,11 +1,15 @@ buildscript { repositories { mavenCentral() + maven { + url "https://plugins.gradle.org/m2/" + } } dependencies { classpath 'org.hibernate.build.gradle:gradle-maven-publish-auth:2.0.1' classpath 'org.hibernate.build.gradle:version-injection-plugin:1.0.0' classpath 'org.hibernate.build.gradle:gradle-animalSniffer-plugin:1.0.1.Final' + classpath "com.diffplug.spotless:spotless-plugin-gradle:4.5.1" } } @@ -16,6 +20,7 @@ repositories { apply plugin: 'java' apply plugin: 'osgi' +apply plugin: 'com.diffplug.gradle.spotless' apply plugin: 'version-injection' apply plugin: 'maven-publish' @@ -25,7 +30,7 @@ apply plugin: 'idea' apply plugin: 'eclipse' group = 'org.hibernate.common' -version = '6.0.0-SNAPSHOT' +version = '5.1.2.Final' buildDir = "target" @@ -176,6 +181,21 @@ wrapper { distributionType = Wrapper.DistributionType.ALL } +spotless { + //Only apply on files added, compared to this git source: + ratchetFrom 'origin/5.1' + //Don't fail during the check: rather than enforcing guidelines, we use this plugin to fix mistakes automatically. + enforceCheck false + java { + licenseHeaderFile 'spotless.license.java' + removeUnusedImports() + importOrder() + trimTrailingWhitespace() + endWithNewline() + } +} +tasks.compileJava.dependsOn(spotlessApply) + // OSGi manifest helpers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/readme.txt b/readme.txt index da67e99..9ebd260 100644 --- a/readme.txt +++ b/readme.txt @@ -13,7 +13,7 @@ It's second scope is to support Java Annotations overriding through XML files Requirements ------------ -Since version 6 this project requires Java 8. +Since version 5.1 this project requires Java 8. Instructions ------------ diff --git a/settings.gradle b/settings.gradle index c7f7eb4..6cabb64 100644 --- a/settings.gradle +++ b/settings.gradle @@ -8,4 +8,6 @@ if ( !JavaVersion.current().java8Compatible ) { throw new GradleException( "Gradle must be run with Java 8" ) } -enableFeaturePreview('STABLE_PUBLISHING') \ No newline at end of file +enableFeaturePreview('STABLE_PUBLISHING') + +rootProject.name = 'hibernate-commons-annotations' diff --git a/spotless.license.java b/spotless.license.java new file mode 100644 index 0000000..2602c3b --- /dev/null +++ b/spotless.license.java @@ -0,0 +1,6 @@ +/* + * 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 . + */ diff --git a/src/main/java/org/hibernate/annotations/common/AssertionFailure.java b/src/main/java/org/hibernate/annotations/common/AssertionFailure.java index e9d6d4c..e6b5cd2 100644 --- a/src/main/java/org/hibernate/annotations/common/AssertionFailure.java +++ b/src/main/java/org/hibernate/annotations/common/AssertionFailure.java @@ -15,7 +15,9 @@ * * @author Gavin King * @auhor Emmanuel Bernard + * @deprecated This is an old class which will be removed; this project no longer uses it. No specific alternative is recommended. */ +@Deprecated public class AssertionFailure extends RuntimeException { private static final Log log = LoggerFactory.make( AssertionFailure.class.getName() ); diff --git a/src/main/java/org/hibernate/annotations/common/annotationfactory/AnnotationDescriptor.java b/src/main/java/org/hibernate/annotations/common/annotationfactory/AnnotationDescriptor.java index b1d349c..4ced79a 100644 --- a/src/main/java/org/hibernate/annotations/common/annotationfactory/AnnotationDescriptor.java +++ b/src/main/java/org/hibernate/annotations/common/annotationfactory/AnnotationDescriptor.java @@ -21,30 +21,32 @@ * @author Paolo Perrotta * @author Davide Marchignoli */ -public class AnnotationDescriptor { +public final class AnnotationDescriptor { private final Class type; - - private final Map elements = new HashMap(); + private Map elements; public AnnotationDescriptor(Class annotationType) { type = annotationType; } public void setValue(String elementName, Object value) { + if ( elements == null ) { + elements = new HashMap<>( 4 ); //likely to be small + } elements.put( elementName, value ); } public Object valueOf(String elementName) { - return elements.get( elementName ); + return elements == null ? null : elements.get( elementName ); } public boolean containsElement(String elementName) { - return elements.containsKey( elementName ); + return elements == null ? false : elements.containsKey( elementName ); } public int numberOfElements() { - return elements.size(); + return elements == null ? 0 : elements.size(); } public Class type() { diff --git a/src/main/java/org/hibernate/annotations/common/annotationfactory/AnnotationFactory.java b/src/main/java/org/hibernate/annotations/common/annotationfactory/AnnotationFactory.java index d8bedfc..6f9f8e5 100644 --- a/src/main/java/org/hibernate/annotations/common/annotationfactory/AnnotationFactory.java +++ b/src/main/java/org/hibernate/annotations/common/annotationfactory/AnnotationFactory.java @@ -19,7 +19,7 @@ * @author Davide Marchignoli * @see AnnotationProxy */ -public class AnnotationFactory { +public final class AnnotationFactory { /** * Creates an Annotation proxy for the given annotation descriptor. diff --git a/src/main/java/org/hibernate/annotations/common/annotationfactory/AnnotationProxy.java b/src/main/java/org/hibernate/annotations/common/annotationfactory/AnnotationProxy.java index 487f33e..c0bf442 100644 --- a/src/main/java/org/hibernate/annotations/common/annotationfactory/AnnotationProxy.java +++ b/src/main/java/org/hibernate/annotations/common/annotationfactory/AnnotationProxy.java @@ -9,6 +9,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Map; @@ -39,7 +40,7 @@ * @author Davide Marchignoli * @see java.lang.annotation.Annotation */ -public class AnnotationProxy implements Annotation, InvocationHandler { +public final class AnnotationProxy implements Annotation, InvocationHandler { private final Class annotationType; //FIXME it's probably better to use String as a key rather than Method @@ -49,10 +50,12 @@ public class AnnotationProxy implements Annotation, InvocationHandler { public AnnotationProxy(AnnotationDescriptor descriptor) { this.annotationType = descriptor.type(); - values = getAnnotationValues( descriptor ); + this.values = getAnnotationValues( annotationType, descriptor ); } - private Map getAnnotationValues(AnnotationDescriptor descriptor) { + private static Map getAnnotationValues( + Class annotationType, + AnnotationDescriptor descriptor) { Map result = new HashMap(); int processedValuesFromDescriptor = 0; for ( Method m : annotationType.getDeclaredMethods() ) { @@ -70,7 +73,19 @@ else if ( m.getDefaultValue() != null ) { if ( processedValuesFromDescriptor != descriptor.numberOfElements() ) { throw new RuntimeException( "Trying to instanciate " + annotationType + " with unknown elements" ); } - return result; + return toSmallMap( result ); + } + + static Map toSmallMap(Map map) { + switch ( map.size() ) { + case 0: + return Collections.emptyMap(); + case 1: + Map.Entry entry = map.entrySet().iterator().next(); + return Collections.singletonMap( entry.getKey(), entry.getValue() ); + default: + return map; + } } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { @@ -110,7 +125,6 @@ public int compare(Method o1, Method o2) { } } ); - //List result = new LinkedList(); result.addAll( values.keySet() ); return result; } diff --git a/src/main/java/org/hibernate/annotations/common/reflection/ClassLoaderDelegate.java b/src/main/java/org/hibernate/annotations/common/reflection/ClassLoaderDelegate.java index 76ea588..1ffc604 100644 --- a/src/main/java/org/hibernate/annotations/common/reflection/ClassLoaderDelegate.java +++ b/src/main/java/org/hibernate/annotations/common/reflection/ClassLoaderDelegate.java @@ -10,7 +10,9 @@ * Delegate for interacting with {@link ClassLoader} methods. * * @author Steve Ebersole + * @deprecated This will be removed with no replacement: it will no longer be needed. */ +@Deprecated public interface ClassLoaderDelegate { /** * Locate a class by name. diff --git a/src/main/java/org/hibernate/annotations/common/reflection/ClassLoadingException.java b/src/main/java/org/hibernate/annotations/common/reflection/ClassLoadingException.java index b2367a9..cfa2ab1 100644 --- a/src/main/java/org/hibernate/annotations/common/reflection/ClassLoadingException.java +++ b/src/main/java/org/hibernate/annotations/common/reflection/ClassLoadingException.java @@ -8,7 +8,9 @@ /** * @author Steve Ebersole + * @deprecated This will be removed with no replacement: it will no longer be needed. */ +@Deprecated public class ClassLoadingException extends RuntimeException { public ClassLoadingException(String message) { super( message ); diff --git a/src/main/java/org/hibernate/annotations/common/reflection/MetadataProvider.java b/src/main/java/org/hibernate/annotations/common/reflection/MetadataProvider.java index 09f041f..4a0ad91 100644 --- a/src/main/java/org/hibernate/annotations/common/reflection/MetadataProvider.java +++ b/src/main/java/org/hibernate/annotations/common/reflection/MetadataProvider.java @@ -13,6 +13,7 @@ * Provides metadata * * @author Emmanuel Bernard + * @author Sanne Grinovero */ public interface MetadataProvider { @@ -22,7 +23,18 @@ public interface MetadataProvider { Map getDefaults(); /** - * provide metadata for a gien annotated element + * provide metadata for a given annotated element */ AnnotationReader getAnnotationReader(AnnotatedElement annotatedElement); + + /** + * Reset any internal caches. + * This will free up memory in some implementations, at the cost of + * possibly being a bit slower if its services are needed again. + * Other configuration aspects are not affected. + */ + default void reset() { + //By default a no-op + } + } diff --git a/src/main/java/org/hibernate/annotations/common/reflection/ReflectionManager.java b/src/main/java/org/hibernate/annotations/common/reflection/ReflectionManager.java index fc23401..0827bb4 100644 --- a/src/main/java/org/hibernate/annotations/common/reflection/ReflectionManager.java +++ b/src/main/java/org/hibernate/annotations/common/reflection/ReflectionManager.java @@ -15,20 +15,25 @@ * * @author Paolo Perrotta * @author Davide Marchignoli + * @author Sanne Grinovero */ public interface ReflectionManager { /** * Allows injection of a ClassLoaderDelegate into the ReflectionManager * * @param delegate The ClassLoaderDelegate to use + * @deprecated This will be removed with no replacement: it will no longer be needed. */ + @Deprecated public void injectClassLoaderDelegate(ClassLoaderDelegate delegate); /** * Access to the ClassLoaderDelegate currently associated with this ReflectionManager * * @return The current ClassLoaderDelegate + * @deprecated This will be removed with no replacement: it will no longer be needed. */ + @Deprecated public ClassLoaderDelegate getClassLoaderDelegate(); public XClass toXClass(Class clazz); @@ -55,6 +60,7 @@ public interface ReflectionManager { * @param * @return * @throws ClassNotFoundException + * @deprecated This will be removed with no replacement: it will no longer be needed. */ @Deprecated public XClass classForName(String name, Class caller) throws ClassNotFoundException; @@ -69,14 +75,49 @@ public interface ReflectionManager { * @return The XClass instance * * @throws ClassLoadingException Indicates a problem resolving the Class; see {@link ClassLoaderDelegate#classForName} + * @deprecated This will be removed with no replacement: it will no longer be needed. */ + @Deprecated public XClass classForName(String name) throws ClassLoadingException; + /** + * @deprecated This will be removed with no replacement: it will no longer be needed. + * @param packageName + * @return + * @throws ClassNotFoundException + */ + @Deprecated public XPackage packageForName(String packageName) throws ClassNotFoundException; + public XPackage toXPackage(Package pkg); + public boolean equals(XClass class1, Class class2); public AnnotationReader buildAnnotationReader(AnnotatedElement annotatedElement); public Map getDefaults(); + + /** + * This resets any internal caches. + * This will free up memory in some implementations, at the cost of + * possibly being a bit slower if its services are needed again. + *

+ * Ideally the ReflectionManager should be discarded after use, + * but sometimes that's not posible. + * This method is intended to be used when there is reasonable + * expectation for te ReflectionManager to no longer be needed, + * while having the option to still use it in case the assumption + * doesn't hold true. + *

+ *

+ * Careful: after invoking this method, returned X* instances will + * no longer honour any identity equality contract with X* instances + * which have been returned before resetting the cache. + *

+ * This operation does not affect the configuration. + */ + default void reset() { + //By default a no-op + } + } diff --git a/src/main/java/org/hibernate/annotations/common/reflection/java/JavaAnnotationReader.java b/src/main/java/org/hibernate/annotations/common/reflection/java/JavaAnnotationReader.java index 251ae06..5a1a7e7 100644 --- a/src/main/java/org/hibernate/annotations/common/reflection/java/JavaAnnotationReader.java +++ b/src/main/java/org/hibernate/annotations/common/reflection/java/JavaAnnotationReader.java @@ -17,7 +17,7 @@ * @author Paolo Perrotta * @author Davide Marchignoli */ -class JavaAnnotationReader implements AnnotationReader { +final class JavaAnnotationReader implements AnnotationReader { protected final AnnotatedElement element; diff --git a/src/main/java/org/hibernate/annotations/common/reflection/java/JavaMetadataProvider.java b/src/main/java/org/hibernate/annotations/common/reflection/java/JavaMetadataProvider.java index b0c3bb0..a7a8d11 100644 --- a/src/main/java/org/hibernate/annotations/common/reflection/java/JavaMetadataProvider.java +++ b/src/main/java/org/hibernate/annotations/common/reflection/java/JavaMetadataProvider.java @@ -16,7 +16,7 @@ /** * @author Emmanuel Bernard */ -public class JavaMetadataProvider implements MetadataProvider { +public final class JavaMetadataProvider implements MetadataProvider { public Map getDefaults() { return Collections.emptyMap(); @@ -25,4 +25,10 @@ public Map getDefaults() { public AnnotationReader getAnnotationReader(AnnotatedElement annotatedElement) { return new JavaAnnotationReader(annotatedElement); } + + @Override + public void reset() { + //no-op + } + } diff --git a/src/main/java/org/hibernate/annotations/common/reflection/java/JavaReflectionManager.java b/src/main/java/org/hibernate/annotations/common/reflection/java/JavaReflectionManager.java index 18f63fc..5ef5b04 100644 --- a/src/main/java/org/hibernate/annotations/common/reflection/java/JavaReflectionManager.java +++ b/src/main/java/org/hibernate/annotations/common/reflection/java/JavaReflectionManager.java @@ -13,6 +13,7 @@ import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; import org.hibernate.annotations.common.reflection.AnnotationReader; import org.hibernate.annotations.common.reflection.ClassLoaderDelegate; @@ -40,11 +41,17 @@ * @author Paolo Perrotta * @author Davide Marchignoli * @author Emmanuel Bernard + * @author Sanne Grinovero */ -public class JavaReflectionManager implements ReflectionManager, MetadataProviderInjector { +public final class JavaReflectionManager implements ReflectionManager, MetadataProviderInjector { + + private static final boolean METADATA_CACHE_DIAGNOSTICS = Boolean.getBoolean( "org.hibernate.annotations.common.METADATA_CACHE_DIAGNOSTICS" ); + private MetadataProvider metadataProvider; private ClassLoaderDelegate classLoaderDelegate = StandardClassLoaderDelegateImpl.INSTANCE; + private final AtomicBoolean empty = new AtomicBoolean(true); + static { LoggerFactory.make( Version.class.getName() ).version( Version.getVersionString() ); } @@ -75,27 +82,30 @@ public ClassLoaderDelegate getClassLoaderDelegate() { return classLoaderDelegate; } - private static class TypeKey extends Pair { - TypeKey(Type t, TypeEnvironment context) { - super( t, context ); - } - } + private final TypeEnvironmentMap xClasses = new TypeEnvironmentMap<>( this::javaXClassConstruction ); - private static class MemberKey extends Pair { - MemberKey(Member member, TypeEnvironment context) { - super( member, context ); - } + private JavaXClass javaXClassConstruction( + Class classType, + TypeEnvironment typeEnvironment) { + used(); + return new JavaXClass( classType, typeEnvironment, this ); } - private final Map xClasses = new HashMap(); + private Map packagesToXPackages; - private final Map packagesToXPackages = new HashMap(); + private final TypeEnvironmentMap xProperties = new TypeEnvironmentMap<>( this::javaXPropertyConstruction ); - private final Map xProperties = new HashMap(); + private JavaXProperty javaXPropertyConstruction(Member member, TypeEnvironment typeEnvironment) { + used(); + return JavaXProperty.create( member, typeEnvironment, this ); + } - private final Map xMethods = new HashMap(); + private final TypeEnvironmentMap xMethods = new TypeEnvironmentMap<>( this::javaJavaXMethodConstruction ); - private final TypeEnvironmentFactory typeEnvs = new TypeEnvironmentFactory(); + private JavaXMethod javaJavaXMethodConstruction(Member member, TypeEnvironment typeEnvironment) { + used(); + return JavaXMethod.create( member, typeEnvironment, this ); + } public XClass toXClass(Class clazz) { return toXClass( clazz, IdentityTypeEnvironment.INSTANCE ); @@ -127,7 +137,8 @@ public XClass classForName(String name) throws ClassLoadingException { return toXClass( getClassLoaderDelegate().classForName( name ) ); } - public XPackage packageForName(String packageName) throws ClassNotFoundException { + @Override + public XPackage packageForName(String packageName) { return getXAnnotatedElement( getClassLoaderDelegate().classForName( packageName + ".package-info" ).getPackage() ); } @@ -135,65 +146,60 @@ XClass toXClass(Type t, final TypeEnvironment context) { return new TypeSwitch() { @Override public XClass caseClass(Class classType) { - TypeKey key = new TypeKey( classType, context ); - JavaXClass result = xClasses.get( key ); - if ( result == null ) { - result = new JavaXClass( classType, context, JavaReflectionManager.this ); - xClasses.put( key, result ); - } - return result; + return xClasses.getOrCompute( context, classType ); } @Override public XClass caseParameterizedType(ParameterizedType parameterizedType) { return toXClass( parameterizedType.getRawType(), - typeEnvs.getEnvironment( parameterizedType, context ) + TypeEnvironmentFactory.getEnvironment( parameterizedType, context ) ); } }.doSwitch( context.bind( t ) ); } + @Deprecated XPackage getXAnnotatedElement(Package pkg) { - JavaXPackage xPackage = packagesToXPackages.get( pkg ); + return toXPackage( pkg ); + } + + @Override + public XPackage toXPackage(Package pkg) { + final Map packagesToXPackagesMap = getPackagesToXPackagesMap(); + JavaXPackage xPackage = packagesToXPackagesMap.get( pkg ); if ( xPackage == null ) { xPackage = new JavaXPackage( pkg, this ); - packagesToXPackages.put( pkg, xPackage ); + used(); + packagesToXPackagesMap.put( pkg, xPackage ); } return xPackage; } - XProperty getXProperty(Member member, TypeEnvironment context) { - MemberKey key = new MemberKey( member, context ); - //FIXME get is as expensive as create most time spent in hashCode and equals - JavaXProperty xProperty = xProperties.get( key ); - if ( xProperty == null ) { - xProperty = JavaXProperty.create( member, context, this ); - xProperties.put( key, xProperty ); + private Map getPackagesToXPackagesMap() { + if ( this.packagesToXPackages == null ) { + this.packagesToXPackages = new HashMap<>( 8, 0.5f ); } - return xProperty; + return this.packagesToXPackages; + } + + XProperty getXProperty(Member member, TypeEnvironment context) { + return xProperties.getOrCompute( context, member ); } XMethod getXMethod(Member member, TypeEnvironment context) { - MemberKey key = new MemberKey( member, context ); - //FIXME get is as expensive as create most time spent in hashCode and equals - JavaXMethod xMethod = xMethods.get( key ); - if ( xMethod == null ) { - xMethod = JavaXMethod.create( member, context, this ); - xMethods.put( key, xMethod ); - } - return xMethod; + return xMethods.getOrCompute( context, member ); } TypeEnvironment getTypeEnvironment(final Type t) { return new TypeSwitch() { @Override public TypeEnvironment caseClass(Class classType) { - return typeEnvs.getEnvironment( classType ); + return TypeEnvironmentFactory.getEnvironment( classType ); } @Override public TypeEnvironment caseParameterizedType(ParameterizedType parameterizedType) { - return typeEnvs.getEnvironment( parameterizedType ); + return TypeEnvironmentFactory.getEnvironment( parameterizedType ); } @Override @@ -225,7 +231,7 @@ public boolean equals(XClass class1, Class class2) { } public TypeEnvironment toApproximatingEnvironment(TypeEnvironment context) { - return typeEnvs.toApproximatingEnvironment( context ); + return TypeEnvironmentFactory.toApproximatingEnvironment( context ); } public AnnotationReader buildAnnotationReader(AnnotatedElement annotatedElement) { @@ -236,4 +242,28 @@ public Map getDefaults() { return getMetadataProvider().getDefaults(); } + @Override + public void reset() { + boolean wasEmpty = empty.getAndSet( true ); + if ( !wasEmpty ) { + this.xClasses.clear(); + this.packagesToXPackages = null; + this.xProperties.clear(); + this.xMethods.clear(); + if ( METADATA_CACHE_DIAGNOSTICS ) { + new RuntimeException( "Diagnostics message : Caches now empty" ).printStackTrace(); + } + } + if ( metadataProvider != null ) { + this.metadataProvider.reset(); + } + } + + private void used() { + boolean wasEmpty = empty.getAndSet( false ); + if ( wasEmpty && METADATA_CACHE_DIAGNOSTICS ) { + new RuntimeException( "Diagnostics message : Caches now being used" ).printStackTrace(); + } + } + } diff --git a/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXArrayType.java b/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXArrayType.java index ddb1b41..88fc316 100644 --- a/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXArrayType.java +++ b/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXArrayType.java @@ -19,7 +19,7 @@ * @author Emmanuel Bernard * @author Paolo Perrotta */ -class JavaXArrayType extends JavaXType { +final class JavaXArrayType extends JavaXType { public JavaXArrayType(Type type, TypeEnvironment context, JavaReflectionManager factory) { super( type, context, factory ); diff --git a/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXClass.java b/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXClass.java index d20b614..0607443 100644 --- a/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXClass.java +++ b/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXClass.java @@ -9,7 +9,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import org.hibernate.annotations.common.reflection.Filter; @@ -24,7 +24,7 @@ * @author Paolo Perrotta * @author Davide Marchignoli */ -class JavaXClass extends JavaXAnnotatedElement implements XClass { +final class JavaXClass extends JavaXAnnotatedElement implements XClass { private final TypeEnvironment context; private final Class clazz; @@ -81,22 +81,26 @@ public boolean isEnum() { } private List getDeclaredFieldProperties(Filter filter) { - List result = new LinkedList(); - for ( Field f : toClass().getDeclaredFields() ) { + Field[] declaredFields = toClass().getDeclaredFields(); + ArrayList result = new ArrayList<>(); + for ( Field f : declaredFields ) { if ( ReflectionUtil.isProperty( f, getTypeEnvironment().bind( f.getGenericType() ), filter ) ) { result.add( getFactory().getXProperty( f, getTypeEnvironment() ) ); } } + result.trimToSize(); return result; } private List getDeclaredMethodProperties(Filter filter) { - List result = new LinkedList(); - for ( Method m : toClass().getDeclaredMethods() ) { + ArrayList result = new ArrayList(); + Method[] declaredMethods = toClass().getDeclaredMethods(); + for ( Method m : declaredMethods ) { if ( ReflectionUtil.isProperty( m, getTypeEnvironment().bind( m.getGenericReturnType() ), filter ) ) { result.add( getFactory().getXProperty( m, getTypeEnvironment() ) ); } } + result.trimToSize(); return result; } @@ -115,8 +119,9 @@ public List getDeclaredProperties(String accessType, Filter filter) { } public List getDeclaredMethods() { - List result = new LinkedList(); - for ( Method m : toClass().getDeclaredMethods() ) { + Method[] declaredMethods = toClass().getDeclaredMethods(); + List result = new ArrayList<>( declaredMethods.length ); + for ( Method m : declaredMethods ) { result.add( getFactory().getXMethod( m, getTypeEnvironment() ) ); } return result; diff --git a/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXCollectionType.java b/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXCollectionType.java index 81bbf91..bc2fdad 100644 --- a/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXCollectionType.java +++ b/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXCollectionType.java @@ -24,7 +24,7 @@ * @author Paolo Perrotta */ @SuppressWarnings("unchecked") -class JavaXCollectionType extends JavaXType { +final class JavaXCollectionType extends JavaXType { public JavaXCollectionType(Type type, TypeEnvironment context, JavaReflectionManager factory) { super( type, context, factory ); diff --git a/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXMember.java b/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXMember.java index bab7f06..799bd00 100644 --- a/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXMember.java +++ b/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXMember.java @@ -59,7 +59,7 @@ protected TypeEnvironment getTypeEnvironment() { return env; } - protected Member getMember() { + public Member getMember() { return (Member) toAnnotatedElement(); } diff --git a/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXPackage.java b/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXPackage.java index 854f10f..1dbbc8a 100644 --- a/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXPackage.java +++ b/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXPackage.java @@ -12,7 +12,7 @@ * @author Paolo Perrotta * @author Davide Marchignoli */ -class JavaXPackage extends JavaXAnnotatedElement implements XPackage { +final class JavaXPackage extends JavaXAnnotatedElement implements XPackage { public JavaXPackage(Package pkg, JavaReflectionManager factory) { super( pkg, factory ); diff --git a/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXSimpleType.java b/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXSimpleType.java index 1ed9064..46738bc 100644 --- a/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXSimpleType.java +++ b/src/main/java/org/hibernate/annotations/common/reflection/java/JavaXSimpleType.java @@ -16,7 +16,7 @@ * @author Emmanuel Bernard * @author Paolo Perrotta */ -class JavaXSimpleType extends JavaXType { +final class JavaXSimpleType extends JavaXType { public JavaXSimpleType(Type type, TypeEnvironment context, JavaReflectionManager factory) { super( type, context, factory ); diff --git a/src/main/java/org/hibernate/annotations/common/reflection/java/Pair.java b/src/main/java/org/hibernate/annotations/common/reflection/java/Pair.java deleted file mode 100644 index 1dbbc65..0000000 --- a/src/main/java/org/hibernate/annotations/common/reflection/java/Pair.java +++ /dev/null @@ -1,64 +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.annotations.common.reflection.java; - -/** - * A pair of objects that can be used as a key in a Map. - * - * @author Paolo Perrotta - * @author Davide Marchignoli - */ -abstract class Pair { - - private final T o1; - - private final U o2; - private final int hashCode; - - Pair(T o1, U o2) { - this.o1 = o1; - this.o2 = o2; - this.hashCode = doHashCode(); - } - - @Override - public boolean equals(Object obj) { - if ( ! (obj instanceof Pair) ) { - return false; - } - Pair other = (Pair) obj; - return !differentHashCode( other ) && safeEquals( o1, other.o1 ) && safeEquals( o2, other.o2 ); - } - - private boolean differentHashCode(Pair other) { - return hashCode != other.hashCode; - } - - @Override - public int hashCode() { - //cached because the inheritance can be big - return hashCode; - } - - private int doHashCode() { - return safeHashCode( o1 ) ^ safeHashCode( o2 ); - } - - private int safeHashCode(Object o) { - if ( o == null ) { - return 0; - } - return o.hashCode(); - } - - private boolean safeEquals(Object obj1, Object obj2) { - if ( obj1 == null ) { - return obj2 == null; - } - return obj1.equals( obj2 ); - } -} diff --git a/src/main/java/org/hibernate/annotations/common/reflection/java/TypeEnvironmentMap.java b/src/main/java/org/hibernate/annotations/common/reflection/java/TypeEnvironmentMap.java new file mode 100644 index 0000000..ed4bd1f --- /dev/null +++ b/src/main/java/org/hibernate/annotations/common/reflection/java/TypeEnvironmentMap.java @@ -0,0 +1,82 @@ +/* + * 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.annotations.common.reflection.java; + +import java.util.HashMap; +import java.util.Objects; + +import org.hibernate.annotations.common.reflection.java.generics.TypeEnvironment; + +/** + * We need a two-level sparse tree for best efficiency in this case; + * the first split uses TypeEnvironment as a key, under which we store + * an additional Map to further organize by K. + * This used to be implemented as a HashMap with a composite key {TypeEnvironment,K}, + * but this wasn't memory efficient as all composite keys take too much space: + * the nested structure has some overhead as well, but has been shown to cost less + * overall. + * In addition, we strife to not need to compute hashkeys multiple times while + * not needing to allocate stateful lambdas; for this reason the tree nodes + * need to store some additional references. + * + * @param + * @param + * @author Sanne Grinovero + */ +final class TypeEnvironmentMap { + + //First level is optimised for fast access; it's expected to be small so a low load factor + //should be fine in terms of memory costs. + private HashMap rootMap; + private final XTypeConstruction constructionMethod; + + TypeEnvironmentMap(final XTypeConstruction constructionMethod) { + Objects.requireNonNull( constructionMethod ); + this.constructionMethod = constructionMethod; + } + + private HashMap getOrInitRootMap() { + if ( this.rootMap == null ) { + this.rootMap = new HashMap<>( 8, 0.5f ); + } + return this.rootMap; + } + + V getOrCompute(final TypeEnvironment context, final K subKey) { + final ContextScope contextualMap = getOrInitRootMap().computeIfAbsent( context, ContextScope::new ); + return contextualMap.getOrCompute( subKey ); + } + + void clear() { + final HashMap m = this.rootMap; + if ( m != null ) { + // Remove the reference to the rootMap, as very large arrays within HashMap + // are not resized when clearing. + this.rootMap = null; + m.clear(); + } + } + + private final class ContextScope extends HashMap { + + private final TypeEnvironment context; + + private ContextScope(TypeEnvironment context) { + //In the second level of the tree we expect many entries, but we can't have it consume too much memory: + super( 64, 0.85f ); + this.context = context; + } + + private V getOrCompute(K subKey) { + return computeIfAbsent( subKey, this::buildObject ); + } + + private V buildObject(K subKey) { + return constructionMethod.createInstance( subKey, context ); + } + } +} diff --git a/src/main/java/org/hibernate/annotations/common/reflection/java/XTypeConstruction.java b/src/main/java/org/hibernate/annotations/common/reflection/java/XTypeConstruction.java new file mode 100644 index 0000000..0bb6087 --- /dev/null +++ b/src/main/java/org/hibernate/annotations/common/reflection/java/XTypeConstruction.java @@ -0,0 +1,16 @@ +/* + * 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.annotations.common.reflection.java; + +import org.hibernate.annotations.common.reflection.java.generics.TypeEnvironment; + +@FunctionalInterface +interface XTypeConstruction { + + V createInstance(K typeKey, TypeEnvironment context); + +} diff --git a/src/main/java/org/hibernate/annotations/common/reflection/java/generics/ApproximatingTypeEnvironment.java b/src/main/java/org/hibernate/annotations/common/reflection/java/generics/ApproximatingTypeEnvironment.java index 5b0255d..9614cbc 100644 --- a/src/main/java/org/hibernate/annotations/common/reflection/java/generics/ApproximatingTypeEnvironment.java +++ b/src/main/java/org/hibernate/annotations/common/reflection/java/generics/ApproximatingTypeEnvironment.java @@ -43,7 +43,7 @@ * @author Paolo Perrotta * @return a type where the generic arguments have been replaced by raw classes. */ -class ApproximatingTypeEnvironment implements TypeEnvironment { +final class ApproximatingTypeEnvironment implements TypeEnvironment { public Type bind(final Type type) { Type result = fineApproximation( type ); diff --git a/src/main/java/org/hibernate/annotations/common/reflection/java/generics/CompoundTypeEnvironment.java b/src/main/java/org/hibernate/annotations/common/reflection/java/generics/CompoundTypeEnvironment.java index e24f976..df87e99 100644 --- a/src/main/java/org/hibernate/annotations/common/reflection/java/generics/CompoundTypeEnvironment.java +++ b/src/main/java/org/hibernate/annotations/common/reflection/java/generics/CompoundTypeEnvironment.java @@ -14,12 +14,10 @@ * @author Davide Marchignoli * @author Paolo Perrotta */ -public class CompoundTypeEnvironment implements TypeEnvironment { +public final class CompoundTypeEnvironment implements TypeEnvironment { private final TypeEnvironment f; - private final TypeEnvironment g; - private final int hashCode; public static TypeEnvironment create(TypeEnvironment f, TypeEnvironment g) { @@ -33,7 +31,7 @@ public static TypeEnvironment create(TypeEnvironment f, TypeEnvironment g) { private CompoundTypeEnvironment(TypeEnvironment f, TypeEnvironment g) { this.f = f; this.g = g; - hashCode = doHashCode(); + this.hashCode = doHashCode( f, g ); } public Type bind(Type type) { @@ -50,14 +48,15 @@ public boolean equals(Object o) { if ( !f.equals( that.f ) ) return false; return g.equals( that.g ); - } private boolean differentHashCode(CompoundTypeEnvironment that) { return hashCode != that.hashCode; } - private int doHashCode() { + private static int doHashCode( + TypeEnvironment f, + TypeEnvironment g) { int result; result = f.hashCode(); result = 29 * result + g.hashCode(); diff --git a/src/main/java/org/hibernate/annotations/common/reflection/java/generics/IdentityTypeEnvironment.java b/src/main/java/org/hibernate/annotations/common/reflection/java/generics/IdentityTypeEnvironment.java index 02897cb..b40dcdf 100644 --- a/src/main/java/org/hibernate/annotations/common/reflection/java/generics/IdentityTypeEnvironment.java +++ b/src/main/java/org/hibernate/annotations/common/reflection/java/generics/IdentityTypeEnvironment.java @@ -14,7 +14,7 @@ * @author Davide Marchignoli * @author Paolo Perrotta */ -public class IdentityTypeEnvironment implements TypeEnvironment { +public final class IdentityTypeEnvironment implements TypeEnvironment { public static final TypeEnvironment INSTANCE = new IdentityTypeEnvironment(); diff --git a/src/main/java/org/hibernate/annotations/common/reflection/java/generics/SimpleTypeEnvironment.java b/src/main/java/org/hibernate/annotations/common/reflection/java/generics/SimpleTypeEnvironment.java index 3a4cede..70913b4 100644 --- a/src/main/java/org/hibernate/annotations/common/reflection/java/generics/SimpleTypeEnvironment.java +++ b/src/main/java/org/hibernate/annotations/common/reflection/java/generics/SimpleTypeEnvironment.java @@ -19,7 +19,7 @@ * @author Davide Marchignoli * @author Paolo Perrotta */ -class SimpleTypeEnvironment extends HashMap implements TypeEnvironment { +final class SimpleTypeEnvironment extends HashMap implements TypeEnvironment { private static final long serialVersionUID = 1L; diff --git a/src/main/java/org/hibernate/annotations/common/reflection/java/generics/TypeEnvironmentFactory.java b/src/main/java/org/hibernate/annotations/common/reflection/java/generics/TypeEnvironmentFactory.java index 6903c2d..7847c02 100644 --- a/src/main/java/org/hibernate/annotations/common/reflection/java/generics/TypeEnvironmentFactory.java +++ b/src/main/java/org/hibernate/annotations/common/reflection/java/generics/TypeEnvironmentFactory.java @@ -18,35 +18,39 @@ * @author Davide Marchignoli * @author Paolo Perrotta */ -public class TypeEnvironmentFactory { +public final class TypeEnvironmentFactory { + + private TypeEnvironmentFactory() { + //no need to construct + } /** * @return Returns a type environment suitable for resolving types occurring * in subclasses of the context class. */ - public TypeEnvironment getEnvironment(Class context) { + public static TypeEnvironment getEnvironment(Class context) { if ( context == null ) { return IdentityTypeEnvironment.INSTANCE; } return createEnvironment( context ); } - public TypeEnvironment getEnvironment(Type context) { + public static TypeEnvironment getEnvironment(Type context) { if ( context == null ) { return IdentityTypeEnvironment.INSTANCE; } return createEnvironment( context ); } - public TypeEnvironment getEnvironment(Type t, TypeEnvironment context) { - return CompoundTypeEnvironment.create( getEnvironment(t), context ); + public static TypeEnvironment getEnvironment(Type t, TypeEnvironment context) { + return CompoundTypeEnvironment.create( getEnvironment( t ), context ); } - public TypeEnvironment toApproximatingEnvironment(TypeEnvironment context) { + public static TypeEnvironment toApproximatingEnvironment(TypeEnvironment context) { return CompoundTypeEnvironment.create( new ApproximatingTypeEnvironment(), context ); } - private TypeEnvironment createEnvironment(Type context) { + private static TypeEnvironment createEnvironment(Type context) { return new TypeSwitch() { @Override public TypeEnvironment caseClass(Class classType) { @@ -68,13 +72,12 @@ public TypeEnvironment defaultCase(Type t) { }.doSwitch( context ); } - private TypeEnvironment createSuperTypeEnvironment(Class clazz) { + private static TypeEnvironment createSuperTypeEnvironment(Class clazz) { Class superclass = clazz.getSuperclass(); if ( superclass == null ) { return IdentityTypeEnvironment.INSTANCE; } - Type[] formalArgs = superclass.getTypeParameters(); Type genericSuperclass = clazz.getGenericSuperclass(); if ( genericSuperclass instanceof Class ) { @@ -82,6 +85,7 @@ private TypeEnvironment createSuperTypeEnvironment(Class clazz) { } if ( genericSuperclass instanceof ParameterizedType ) { + Type[] formalArgs = superclass.getTypeParameters(); Type[] actualArgs = ( (ParameterizedType) genericSuperclass ).getActualTypeArguments(); return new SimpleTypeEnvironment( formalArgs, actualArgs ); } @@ -89,7 +93,7 @@ private TypeEnvironment createSuperTypeEnvironment(Class clazz) { throw new AssertionError( "Should be unreachable" ); } - private TypeEnvironment createEnvironment(ParameterizedType t) { + private static TypeEnvironment createEnvironment(ParameterizedType t) { Type[] tactuals = t.getActualTypeArguments(); Type rawType = t.getRawType(); if ( rawType instanceof Class ) { @@ -98,4 +102,5 @@ private TypeEnvironment createEnvironment(ParameterizedType t) { } return IdentityTypeEnvironment.INSTANCE; } + } diff --git a/src/main/java/org/hibernate/annotations/common/util/ReflectHelper.java b/src/main/java/org/hibernate/annotations/common/util/ReflectHelper.java index 9937de4..5d7ea55 100644 --- a/src/main/java/org/hibernate/annotations/common/util/ReflectHelper.java +++ b/src/main/java/org/hibernate/annotations/common/util/ReflectHelper.java @@ -8,7 +8,9 @@ /** * @author Emmanuel Bernard + * @deprecated This will be removed with no replacement: it will no longer be needed. */ +@Deprecated public final class ReflectHelper { /** diff --git a/src/main/java/org/hibernate/annotations/common/util/StandardClassLoaderDelegateImpl.java b/src/main/java/org/hibernate/annotations/common/util/StandardClassLoaderDelegateImpl.java index 1aed5f8..49767aa 100644 --- a/src/main/java/org/hibernate/annotations/common/util/StandardClassLoaderDelegateImpl.java +++ b/src/main/java/org/hibernate/annotations/common/util/StandardClassLoaderDelegateImpl.java @@ -14,7 +14,9 @@ /** * @author Steve Ebersole + * @deprecated This will be removed with no replacement: it will no longer be needed. */ +@Deprecated public class StandardClassLoaderDelegateImpl implements ClassLoaderDelegate { /** * Singleton access diff --git a/src/main/java/org/hibernate/annotations/common/util/StringHelper.java b/src/main/java/org/hibernate/annotations/common/util/StringHelper.java index d3b839d..68cd99c 100644 --- a/src/main/java/org/hibernate/annotations/common/util/StringHelper.java +++ b/src/main/java/org/hibernate/annotations/common/util/StringHelper.java @@ -12,10 +12,12 @@ /** - * Complete duplication of {@link org.hibernate.util.StringHelper}. + * This project no longer uses this class. + * @deprecated This will be removed. * * @author Emmanuel Bernard */ +@Deprecated public final class StringHelper { private static final int ALIAS_TRUNCATE_LENGTH = 10; diff --git a/src/test/java/org/hibernate/annotations/common/test/reflection/java/generics/ApproximatingTypeEnvironmentTest.java b/src/test/java/org/hibernate/annotations/common/test/reflection/java/generics/ApproximatingTypeEnvironmentTest.java index 4a1df65..10fd193 100644 --- a/src/test/java/org/hibernate/annotations/common/test/reflection/java/generics/ApproximatingTypeEnvironmentTest.java +++ b/src/test/java/org/hibernate/annotations/common/test/reflection/java/generics/ApproximatingTypeEnvironmentTest.java @@ -17,12 +17,11 @@ public class ApproximatingTypeEnvironmentTest extends TestCase { - TypeEnvironmentFactory teFactory = new TypeEnvironmentFactory(); - TypeEnvironment unboundContext = teFactory.getEnvironment( BigBlob.class ); - TypeEnvironment approximatingUnboundContext = teFactory.toApproximatingEnvironment( unboundContext ); + TypeEnvironment unboundContext = TypeEnvironmentFactory.getEnvironment( BigBlob.class ); + TypeEnvironment approximatingUnboundContext = TypeEnvironmentFactory.toApproximatingEnvironment( unboundContext ); - TypeEnvironment boundContext = teFactory.getEnvironment( SonOfBlob.class ); - TypeEnvironment approximatingBoundContext = teFactory.toApproximatingEnvironment( boundContext ); + TypeEnvironment boundContext = TypeEnvironmentFactory.getEnvironment( SonOfBlob.class ); + TypeEnvironment approximatingBoundContext = TypeEnvironmentFactory.toApproximatingEnvironment( boundContext ); public void testDoesNothingOnClasses() throws SecurityException { assertEquals( String[].class, approximatingUnboundContext.bind( String[].class ) ); diff --git a/src/test/java/org/hibernate/annotations/common/test/reflection/java/generics/TypeEnvironmentFactoryTest.java b/src/test/java/org/hibernate/annotations/common/test/reflection/java/generics/TypeEnvironmentFactoryTest.java index 9034733..3349474 100644 --- a/src/test/java/org/hibernate/annotations/common/test/reflection/java/generics/TypeEnvironmentFactoryTest.java +++ b/src/test/java/org/hibernate/annotations/common/test/reflection/java/generics/TypeEnvironmentFactoryTest.java @@ -22,34 +22,31 @@ public class TypeEnvironmentFactoryTest extends TestCase { public void testBindsGenericsToSuperclassEnvironment() throws SecurityException, NoSuchMethodException { - TypeEnvironmentFactory env = new TypeEnvironmentFactory(); Type type = Grandpa.class.getMethod( "returnsGeneric" ).getGenericReturnType(); - Type asSeenFromGrandpa = env.getEnvironment( Grandpa.class ).bind( type ); + Type asSeenFromGrandpa = TypeEnvironmentFactory.getEnvironment( Grandpa.class ).bind( type ); assertTrue( asSeenFromGrandpa instanceof TypeVariable ); assertEquals( "T", asSeenFromGrandpa.toString() ); - Type asSeenFromDad = env.getEnvironment( Dad.class ).bind( type ); + Type asSeenFromDad = TypeEnvironmentFactory.getEnvironment( Dad.class ).bind( type ); assertTrue( asSeenFromDad instanceof ParameterizedType ); assertEquals( "java.util.List", asSeenFromDad.toString() ); - ParameterizedType asSeenFromSon = (ParameterizedType) env.getEnvironment( Son.class ).bind( type ); + ParameterizedType asSeenFromSon = (ParameterizedType) TypeEnvironmentFactory.getEnvironment( Son.class ).bind( type ); assertType_isCollectionOfClass_withElementsOfClass( asSeenFromSon, List.class, String.class ); } @SuppressWarnings("unchecked") public void testBindsGenericsToOwnerEnvironment() throws SecurityException, NoSuchMethodException { - TypeEnvironmentFactory env = new TypeEnvironmentFactory(); - Type friendType = Dad.class.getMethod( "getFriend" ).getGenericReturnType(); - ParameterizedType friendTypeAsSeenFromDad = (ParameterizedType) env.getEnvironment( Dad.class ).bind( + ParameterizedType friendTypeAsSeenFromDad = (ParameterizedType) TypeEnvironmentFactory.getEnvironment( Dad.class ).bind( friendType ); Class friendClass = (Class) friendTypeAsSeenFromDad.getRawType(); Type returnType = friendClass.getMethod( "embeddedProperty" ).getGenericReturnType(); - ParameterizedType boundType = (ParameterizedType) env.getEnvironment( friendTypeAsSeenFromDad ).bind( + ParameterizedType boundType = (ParameterizedType) TypeEnvironmentFactory.getEnvironment( friendTypeAsSeenFromDad ).bind( returnType ); assertType_isCollectionOfClass_withElementsOfClass( boundType, Set.class, Integer.class ); diff --git a/src/test/java/org/hibernate/annotations/common/test/reflection/java/generics/TypeUtilsTest.java b/src/test/java/org/hibernate/annotations/common/test/reflection/java/generics/TypeUtilsTest.java index 9a88189..f7d0237 100644 --- a/src/test/java/org/hibernate/annotations/common/test/reflection/java/generics/TypeUtilsTest.java +++ b/src/test/java/org/hibernate/annotations/common/test/reflection/java/generics/TypeUtilsTest.java @@ -16,9 +16,8 @@ public class TypeUtilsTest extends TestCase { - TypeEnvironmentFactory env = new TypeEnvironmentFactory(); - TypeEnvironment dadContext = env.getEnvironment( Dad.class ); - TypeEnvironment sonContext = env.getEnvironment( Son.class ); + TypeEnvironment dadContext = TypeEnvironmentFactory.getEnvironment( Dad.class ); + TypeEnvironment sonContext = TypeEnvironmentFactory.getEnvironment( Son.class ); public void testAClassIsAlwaysFullyResolved() throws Exception { assertTrue( TypeUtils.isResolved( Dad.class ) );