diff --git a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/model/impl/HibernateOrmBootstrapIntrospector.java b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/model/impl/HibernateOrmBootstrapIntrospector.java index 0b132b087ec..40230ecaf44 100644 --- a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/model/impl/HibernateOrmBootstrapIntrospector.java +++ b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/model/impl/HibernateOrmBootstrapIntrospector.java @@ -113,30 +113,23 @@ protected ValueCreateHandle createValueCreateHandle(Constructor constr return valueHandleFactory.createForConstructor( constructor ); } + @Override + protected ValueReadHandle createValueReadHandle(Member member) throws IllegalAccessException { + setAccessible( member ); + return super.createValueReadHandle( member ); + } + ValueReadHandle createValueReadHandle(Class holderClass, Member member, HibernateOrmBasicClassPropertyMetadata ormPropertyMetadata) throws IllegalAccessException { - if ( member instanceof Method ) { - Method method = (Method) member; - setAccessible( method ); - return valueHandleFactory.createForMethod( method ); - } - else if ( member instanceof Field ) { - Field field = (Field) member; - if ( ormPropertyMetadata != null && !ormPropertyMetadata.isId() ) { - Method bytecodeEnhancerReaderMethod = getBytecodeEnhancerReaderMethod( holderClass, field ); - if ( bytecodeEnhancerReaderMethod != null ) { - setAccessible( bytecodeEnhancerReaderMethod ); - return valueHandleFactory.createForMethod( bytecodeEnhancerReaderMethod ); - } + if ( member instanceof Field && ormPropertyMetadata != null && !ormPropertyMetadata.isId() ) { + Method bytecodeEnhancerReaderMethod = getBytecodeEnhancerReaderMethod( holderClass, (Field) member ); + if ( bytecodeEnhancerReaderMethod != null ) { + return createValueReadHandle( bytecodeEnhancerReaderMethod ); } - - setAccessible( field ); - return valueHandleFactory.createForField( field ); - } - else { - throw new AssertionFailure( "Unexpected type for a " + Member.class.getName() + ": " + member ); } + + return createValueReadHandle( member ); } @SuppressWarnings("rawtypes") @@ -160,14 +153,15 @@ private HibernateOrmClassRawTypeModel createClassTypeModel(Class type) ); } - private static void setAccessible(AccessibleObject member) { + private static void setAccessible(Member member) { try { - // always set accessible to true as it bypass the security model checks - // at execution time and is faster. - member.setAccessible( true ); + // always try to set accessible to true regardless of visibility + // as it's faster even for public fields: + // it bypasses the security model checks at execution time. + ( (AccessibleObject) member ).setAccessible( true ); } catch (SecurityException se) { - if ( !Modifier.isPublic( ( (Member) member ).getModifiers() ) ) { + if ( !Modifier.isPublic( member.getModifiers() ) ) { throw se; } } diff --git a/mapper/pojo-base/src/main/java/org/hibernate/search/mapper/pojo/model/hcann/spi/AbstractPojoHCAnnBootstrapIntrospector.java b/mapper/pojo-base/src/main/java/org/hibernate/search/mapper/pojo/model/hcann/spi/AbstractPojoHCAnnBootstrapIntrospector.java index 82388b967ce..f8d64337711 100644 --- a/mapper/pojo-base/src/main/java/org/hibernate/search/mapper/pojo/model/hcann/spi/AbstractPojoHCAnnBootstrapIntrospector.java +++ b/mapper/pojo-base/src/main/java/org/hibernate/search/mapper/pojo/model/hcann/spi/AbstractPojoHCAnnBootstrapIntrospector.java @@ -8,6 +8,9 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -26,6 +29,7 @@ import org.hibernate.search.util.common.impl.StreamHelper; import org.hibernate.search.util.common.reflect.spi.ValueCreateHandle; import org.hibernate.search.util.common.reflect.spi.ValueHandleFactory; +import org.hibernate.search.util.common.reflect.spi.ValueReadHandle; public abstract class AbstractPojoHCAnnBootstrapIntrospector implements PojoBootstrapIntrospector { @@ -77,6 +81,20 @@ protected ValueCreateHandle createValueCreateHandle(Constructor constr + " '" + getClass().getName() + " should be updated to implement createValueCreateHandle(Constructor)." ); } + protected ValueReadHandle createValueReadHandle(Member member) throws IllegalAccessException { + if ( member instanceof Method ) { + Method method = (Method) member; + return valueHandleFactory.createForMethod( method ); + } + else if ( member instanceof Field ) { + Field field = (Field) member; + return valueHandleFactory.createForField( field ); + } + else { + throw new AssertionFailure( "Unexpected type for a " + Member.class.getName() + ": " + member ); + } + } + public Class toClass(XClass xClass) { return reflectionManager.toClass( xClass ); } diff --git a/mapper/pojo-standalone/src/main/java/org/hibernate/search/mapper/pojo/standalone/model/impl/StandalonePojoPropertyModel.java b/mapper/pojo-base/src/main/java/org/hibernate/search/mapper/pojo/model/hcann/spi/PojoSimpleHCAnnPropertyModel.java similarity index 68% rename from mapper/pojo-standalone/src/main/java/org/hibernate/search/mapper/pojo/standalone/model/impl/StandalonePojoPropertyModel.java rename to mapper/pojo-base/src/main/java/org/hibernate/search/mapper/pojo/model/hcann/spi/PojoSimpleHCAnnPropertyModel.java index 4c7f70185ed..4f0e4c5841c 100644 --- a/mapper/pojo-standalone/src/main/java/org/hibernate/search/mapper/pojo/standalone/model/impl/StandalonePojoPropertyModel.java +++ b/mapper/pojo-base/src/main/java/org/hibernate/search/mapper/pojo/model/hcann/spi/PojoSimpleHCAnnPropertyModel.java @@ -4,19 +4,18 @@ * 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.search.mapper.pojo.standalone.model.impl; +package org.hibernate.search.mapper.pojo.model.hcann.spi; import java.lang.reflect.Member; import java.util.List; import org.hibernate.annotations.common.reflection.XProperty; -import org.hibernate.search.mapper.pojo.model.hcann.spi.AbstractPojoHCAnnPropertyModel; import org.hibernate.search.util.common.reflect.spi.ValueReadHandle; -class StandalonePojoPropertyModel extends AbstractPojoHCAnnPropertyModel { +final class PojoSimpleHCAnnPropertyModel extends AbstractPojoHCAnnPropertyModel { - StandalonePojoPropertyModel(StandalonePojoBootstrapIntrospector introspector, - StandalonePojoRawTypeModel holderTypeModel, + PojoSimpleHCAnnPropertyModel(AbstractPojoHCAnnBootstrapIntrospector introspector, + PojoSimpleHCAnnRawTypeModel holderTypeModel, String name, List declaredXProperties, List members) { super( introspector, holderTypeModel, name, declaredXProperties, members ); diff --git a/mapper/pojo-standalone/src/main/java/org/hibernate/search/mapper/pojo/standalone/model/impl/StandalonePojoRawTypeModel.java b/mapper/pojo-base/src/main/java/org/hibernate/search/mapper/pojo/model/hcann/spi/PojoSimpleHCAnnRawTypeModel.java similarity index 71% rename from mapper/pojo-standalone/src/main/java/org/hibernate/search/mapper/pojo/standalone/model/impl/StandalonePojoRawTypeModel.java rename to mapper/pojo-base/src/main/java/org/hibernate/search/mapper/pojo/model/hcann/spi/PojoSimpleHCAnnRawTypeModel.java index 30a7ed9adaf..1d4ccef2310 100644 --- a/mapper/pojo-standalone/src/main/java/org/hibernate/search/mapper/pojo/standalone/model/impl/StandalonePojoRawTypeModel.java +++ b/mapper/pojo-base/src/main/java/org/hibernate/search/mapper/pojo/model/hcann/spi/PojoSimpleHCAnnRawTypeModel.java @@ -4,7 +4,7 @@ * 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.search.mapper.pojo.standalone.model.impl; +package org.hibernate.search.mapper.pojo.model.hcann.spi; import java.lang.reflect.Member; import java.util.ArrayList; @@ -15,33 +15,35 @@ import java.util.stream.Stream; import org.hibernate.annotations.common.reflection.XProperty; -import org.hibernate.search.mapper.pojo.model.hcann.spi.AbstractPojoHCAnnRawTypeModel; import org.hibernate.search.mapper.pojo.model.spi.GenericContextAwarePojoGenericTypeModel.RawTypeDeclaringContext; +import org.hibernate.search.mapper.pojo.model.spi.PojoPropertyModel; import org.hibernate.search.mapper.pojo.model.spi.PojoRawTypeIdentifier; -class StandalonePojoRawTypeModel extends AbstractPojoHCAnnRawTypeModel { +public final class PojoSimpleHCAnnRawTypeModel + extends AbstractPojoHCAnnRawTypeModel { - StandalonePojoRawTypeModel(StandalonePojoBootstrapIntrospector introspector, PojoRawTypeIdentifier typeIdentifier, + public PojoSimpleHCAnnRawTypeModel(AbstractPojoHCAnnBootstrapIntrospector introspector, + PojoRawTypeIdentifier typeIdentifier, RawTypeDeclaringContext rawTypeDeclaringContext) { super( introspector, typeIdentifier, rawTypeDeclaringContext ); } @Override @SuppressWarnings("unchecked") // xClass represents T, so its supertypes represent ? super T - public Stream> ascendingSuperTypes() { + public Stream> ascendingSuperTypes() { return introspector.ascendingSuperClasses( xClass ) - .map( xc -> (StandalonePojoRawTypeModel) introspector.typeModel( xc ) ); + .map( xc -> (PojoSimpleHCAnnRawTypeModel) introspector.typeModel( xc ) ); } @Override @SuppressWarnings("unchecked") // xClass represents T, so its supertypes represent ? super T - public Stream> descendingSuperTypes() { + public Stream> descendingSuperTypes() { return introspector.descendingSuperClasses( xClass ) - .map( xc -> (StandalonePojoRawTypeModel) introspector.typeModel( xc ) ); + .map( xc -> (PojoSimpleHCAnnRawTypeModel) introspector.typeModel( xc ) ); } @Override - protected StandalonePojoPropertyModel createPropertyModel(String propertyName) { + protected PojoPropertyModel createPropertyModel(String propertyName) { List declaredXProperties = new ArrayList<>( 2 ); List methodAccessXProperties = declaredMethodAccessXPropertiesByName().get( propertyName ); if ( methodAccessXProperties != null ) { @@ -57,7 +59,7 @@ protected StandalonePojoPropertyModel createPropertyModel(String propertyName return null; } - return new StandalonePojoPropertyModel<>( introspector, this, propertyName, + return new PojoSimpleHCAnnPropertyModel<>( introspector, this, propertyName, declaredXProperties, members ); } @@ -72,7 +74,7 @@ private List findPropertyMember(String propertyName) { return field == null ? null : Collections.singletonList( field ); } - private T2 findInSelfOrParents(Function, T2> getter) { + private T2 findInSelfOrParents(Function, T2> getter) { return ascendingSuperTypes() .map( getter ) .filter( Objects::nonNull ) diff --git a/mapper/pojo-base/src/test/java/org/hibernate/search/mapper/pojo/model/typepattern/impl/TypePatternMatcherFactoryTest.java b/mapper/pojo-base/src/test/java/org/hibernate/search/mapper/pojo/model/typepattern/impl/TypePatternMatcherFactoryTest.java index db2d8992631..121ec7c40a2 100644 --- a/mapper/pojo-base/src/test/java/org/hibernate/search/mapper/pojo/model/typepattern/impl/TypePatternMatcherFactoryTest.java +++ b/mapper/pojo-base/src/test/java/org/hibernate/search/mapper/pojo/model/typepattern/impl/TypePatternMatcherFactoryTest.java @@ -8,72 +8,50 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import java.io.Serializable; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Type; +import java.util.Collection; +import java.util.EnumSet; import java.util.List; import java.util.Map; -import java.util.Optional; -import org.hibernate.search.mapper.pojo.model.spi.PojoBootstrapIntrospector; -import org.hibernate.search.mapper.pojo.model.spi.PojoRawTypeModel; -import org.hibernate.search.mapper.pojo.model.spi.PojoTypeModel; +import org.hibernate.search.mapper.pojo.testsupport.TestIntrospector; +import org.hibernate.search.util.common.reflect.spi.ValueHandleFactory; import org.hibernate.search.util.impl.test.reflect.TypeCapture; import org.hibernate.search.util.impl.test.reflect.WildcardTypeCapture; import org.hibernate.search.util.impl.test.reflect.WildcardTypeCapture.Of; import org.junit.jupiter.api.Test; -@SuppressWarnings({ "unchecked", "rawtypes" }) // Raw types are the only way to mock parameterized types -class TypePatternMatcherFactoryTest { +import org.assertj.core.api.InstanceOfAssertFactories; - private final PojoBootstrapIntrospector introspectorMock = mock( PojoBootstrapIntrospector.class ); +class TypePatternMatcherFactoryTest { - private final TypePatternMatcherFactory factory = new TypePatternMatcherFactory( introspectorMock ); + private final TestIntrospector introspector = + new TestIntrospector( ValueHandleFactory.usingMethodHandle( MethodHandles.lookup() ) ); + private final TypePatternMatcherFactory factory = new TypePatternMatcherFactory( introspector ); @Test void exactType() { - PojoRawTypeModel typeToMatchMock = mock( PojoRawTypeModel.class ); - PojoTypeModel typeToInspectMock = mock( PojoTypeModel.class ); - PojoRawTypeModel typeToInspectRawTypeMock = mock( PojoRawTypeModel.class ); - - when( introspectorMock.typeModel( String.class ) ) - .thenReturn( typeToMatchMock ); - TypePatternMatcher matcher = factory.createExactRawTypeMatcher( String.class ); + TypePatternMatcher matcher = factory.createExactRawTypeMatcher( CharSequence.class ); assertThat( matcher ).isNotNull(); - when( typeToMatchMock.name() ) - .thenReturn( "THE_TYPE_TO_MATCH" ); - assertThat( matcher.toString() ) - .isEqualTo( "hasExactRawType(THE_TYPE_TO_MATCH)" ); - - when( typeToInspectMock.rawType() ) - .thenReturn( typeToInspectRawTypeMock ); - when( typeToMatchMock.isSubTypeOf( typeToInspectRawTypeMock ) ) - .thenReturn( true ); - when( typeToInspectRawTypeMock.isSubTypeOf( typeToMatchMock ) ) - .thenReturn( true ); - assertThat( matcher.matches( typeToInspectMock ) ).isTrue(); - - when( typeToMatchMock.isSubTypeOf( typeToInspectRawTypeMock ) ) - .thenReturn( false ); - when( typeToInspectRawTypeMock.isSubTypeOf( typeToMatchMock ) ) - .thenReturn( true ); - assertThat( matcher.matches( typeToInspectMock ) ).isFalse(); - - when( typeToMatchMock.isSubTypeOf( typeToInspectRawTypeMock ) ) - .thenReturn( true ); - when( typeToInspectRawTypeMock.isSubTypeOf( typeToMatchMock ) ) - .thenReturn( false ); - assertThat( matcher.matches( typeToInspectMock ) ).isFalse(); - - when( typeToMatchMock.isSubTypeOf( typeToInspectRawTypeMock ) ) - .thenReturn( false ); - when( typeToInspectRawTypeMock.isSubTypeOf( typeToMatchMock ) ) - .thenReturn( false ); - assertThat( matcher.matches( typeToInspectMock ) ).isFalse(); + assertThat( matcher ) + .hasToString( "hasExactRawType(java.lang.CharSequence)" ); + + // Exact type => match + assertThat( matcher.matches( introspector.typeModel( CharSequence.class ) ) ).isTrue(); + + // Supertype => no match + assertThat( matcher.matches( introspector.typeModel( Object.class ) ) ).isFalse(); + + // Subtype => no match + assertThat( matcher.matches( introspector.typeModel( String.class ) ) ).isFalse(); + + // Unrelated type => no match + assertThat( matcher.matches( introspector.typeModel( Number.class ) ) ).isFalse(); } /** @@ -82,45 +60,25 @@ void exactType() { */ @Test void concreteEnumType() { - PojoRawTypeModel enumTypeMock = mock( PojoRawTypeModel.class ); - PojoTypeModel typeToInspectMock = mock( PojoTypeModel.class ); - PojoRawTypeModel typeToInspectRawTypeMock = mock( PojoRawTypeModel.class ); - - when( introspectorMock.typeModel( Enum.class ) ) - .thenReturn( enumTypeMock ); TypePatternMatcher matcher = factory.createRawSuperTypeMatcher( Enum.class ) .and( factory.createExactRawTypeMatcher( Enum.class ).negate() ); assertThat( matcher ).isNotNull(); // Strict Enum subtype => match - when( typeToInspectMock.rawType() ) - .thenReturn( typeToInspectRawTypeMock ); - when( enumTypeMock.isSubTypeOf( typeToInspectRawTypeMock ) ) - .thenReturn( false ); - when( typeToInspectRawTypeMock.isSubTypeOf( enumTypeMock ) ) - .thenReturn( true ); - assertThat( matcher.matches( typeToInspectMock ) ).isTrue(); + assertThat( matcher.matches( introspector.typeModel( MyEnum.class ) ) ).isTrue(); // Enum class itself => no match - when( enumTypeMock.isSubTypeOf( typeToInspectRawTypeMock ) ) - .thenReturn( true ); - when( typeToInspectRawTypeMock.isSubTypeOf( enumTypeMock ) ) - .thenReturn( true ); - assertThat( matcher.matches( typeToInspectMock ) ).isFalse(); + assertThat( matcher.matches( introspector.typeModel( Enum.class ) ) ).isFalse(); // Enum supertype => no match - when( enumTypeMock.isSubTypeOf( typeToInspectRawTypeMock ) ) - .thenReturn( true ); - when( typeToInspectRawTypeMock.isSubTypeOf( enumTypeMock ) ) - .thenReturn( false ); - assertThat( matcher.matches( typeToInspectMock ) ).isFalse(); + assertThat( matcher.matches( introspector.typeModel( Object.class ) ) ).isFalse(); // Unrelated type => no match - when( enumTypeMock.isSubTypeOf( typeToInspectRawTypeMock ) ) - .thenReturn( false ); - when( typeToInspectRawTypeMock.isSubTypeOf( enumTypeMock ) ) - .thenReturn( false ); - assertThat( matcher.matches( typeToInspectMock ) ).isFalse(); + assertThat( matcher.matches( introspector.typeModel( Number.class ) ) ).isFalse(); + } + + private enum MyEnum { + FOO, BAR; } @Test @@ -145,41 +103,18 @@ void typeVariable() { @Test void rawSuperType() { - PojoRawTypeModel typeToMatchMock = mock( PojoRawTypeModel.class ); - PojoRawTypeModel resultTypeMock = mock( PojoRawTypeModel.class ); - PojoTypeModel typeToInspectMock = mock( PojoTypeModel.class ); - PojoRawTypeModel typeToInspectRawTypeMock = mock( PojoRawTypeModel.class ); - - when( introspectorMock.typeModel( String.class ) ) - .thenReturn( typeToMatchMock ); - when( introspectorMock.typeModel( Integer.class ) ) - .thenReturn( resultTypeMock ); - ExtractingTypePatternMatcher matcher = factory.createExtractingMatcher( String.class, Integer.class ); + ExtractingTypePatternMatcher matcher = factory.createExtractingMatcher( Collection.class, Integer.class ); assertThat( matcher ).isNotNull(); - when( typeToMatchMock.name() ) - .thenReturn( "THE_TYPE_TO_MATCH" ); - when( resultTypeMock.name() ) - .thenReturn( "THE_RESULT_TYPE" ); - assertThat( matcher.toString() ) - .isEqualTo( "hasRawSuperType(THE_TYPE_TO_MATCH) => THE_RESULT_TYPE" ); - - Optional> actualReturn; - - when( typeToInspectMock.rawType() ) - .thenReturn( typeToInspectRawTypeMock ); - when( typeToInspectRawTypeMock.isSubTypeOf( typeToMatchMock ) ) - .thenReturn( true ); - actualReturn = matcher.extract( typeToInspectMock ); - assertThat( actualReturn ).isNotNull(); - assertThat( actualReturn.isPresent() ).isTrue(); - assertThat( actualReturn.get() ).isSameAs( resultTypeMock ); - - when( typeToInspectRawTypeMock.isSubTypeOf( typeToMatchMock ) ) - .thenReturn( false ); - actualReturn = matcher.extract( typeToInspectMock ); - assertThat( actualReturn ).isNotNull(); - assertThat( actualReturn.isPresent() ).isFalse(); + assertThat( matcher ) + .hasToString( "hasRawSuperType(java.util.Collection) => java.lang.Integer" ); + + assertThat( matcher.extract( introspector.typeModel( EnumSet.class ) ) ) + .asInstanceOf( InstanceOfAssertFactories.OPTIONAL ) + .contains( introspector.typeModel( Integer.class ) ); + + assertThat( matcher.extract( introspector.typeModel( Iterable.class ) ) ) + .isEmpty(); } @Test @@ -192,7 +127,7 @@ void rawSuperType_resultIsTypeVariable() { } @Test - void rawSuperType_resultIsWildcard() { + void rawSuperType_resultIsWildcard() { assertThatThrownBy( () -> factory.createExtractingMatcher( String.class, new WildcardTypeCapture>() {}.getType() @@ -211,64 +146,33 @@ void rawSuperType_resultIsParameterized() { @Test void nonGenericArrayElement() { - PojoRawTypeModel typeToMatchMock = mock( PojoRawTypeModel.class ); - PojoRawTypeModel resultTypeMock = mock( PojoRawTypeModel.class ); - PojoTypeModel typeToInspectMock = mock( PojoTypeModel.class ); - PojoRawTypeModel typeToInspectRawTypeMock = mock( PojoRawTypeModel.class ); - - when( introspectorMock.typeModel( String[].class ) ) - .thenReturn( typeToMatchMock ); - when( introspectorMock.typeModel( Integer.class ) ) - .thenReturn( resultTypeMock ); ExtractingTypePatternMatcher matcher = factory.createExtractingMatcher( String[].class, Integer.class ); assertThat( matcher ).isNotNull(); - when( typeToMatchMock.name() ) - .thenReturn( "THE_TYPE_TO_MATCH" ); - when( resultTypeMock.name() ) - .thenReturn( "THE_RESULT_TYPE" ); - assertThat( matcher.toString() ) - .isEqualTo( "hasRawSuperType(THE_TYPE_TO_MATCH) => THE_RESULT_TYPE" ); - - Optional> actualReturn; - - when( typeToInspectMock.rawType() ) - .thenReturn( typeToInspectRawTypeMock ); - when( typeToInspectRawTypeMock.isSubTypeOf( typeToMatchMock ) ) - .thenReturn( true ); - actualReturn = matcher.extract( typeToInspectMock ); - assertThat( actualReturn ).isNotNull(); - assertThat( actualReturn.isPresent() ).isTrue(); - assertThat( actualReturn.get() ).isSameAs( resultTypeMock ); + assertThat( matcher ) + .hasToString( "hasRawSuperType([Ljava.lang.String;) => java.lang.Integer" ); + + assertThat( matcher.extract( introspector.typeModel( String[].class ) ) ) + .asInstanceOf( InstanceOfAssertFactories.OPTIONAL ) + .contains( introspector.typeModel( Integer.class ) ); } @Test - void genericArrayElement() { - PojoTypeModel typeToInspectMock = mock( PojoTypeModel.class ); - PojoTypeModel resultTypeMock = mock( PojoTypeModel.class ); - + void genericArrayElement() { ExtractingTypePatternMatcher matcher = factory.createExtractingMatcher( new TypeCapture() {}.getType(), new TypeCapture() {}.getType() ); assertThat( matcher ).isInstanceOf( ArrayElementTypeMatcher.class ); - assertThat( matcher.toString() ) - .isEqualTo( "T[] => T" ); - - Optional> actualReturn; - - when( typeToInspectMock.arrayElementType() ) - .thenReturn( (Optional) Optional.of( resultTypeMock ) ); - actualReturn = matcher.extract( typeToInspectMock ); - assertThat( actualReturn ).isNotNull(); - assertThat( actualReturn.isPresent() ).isTrue(); - assertThat( actualReturn.get() ).isSameAs( resultTypeMock ); - - when( typeToInspectMock.arrayElementType() ) - .thenReturn( Optional.empty() ); - actualReturn = matcher.extract( typeToInspectMock ); - assertThat( actualReturn ).isNotNull(); - assertThat( actualReturn.isPresent() ).isFalse(); + assertThat( matcher ) + .hasToString( "T[] => T" ); + + assertThat( matcher.extract( introspector.typeModel( new TypeCapture() {} ) ) ) + .asInstanceOf( InstanceOfAssertFactories.OPTIONAL ) + .contains( introspector.typeModel( new TypeCapture() {} ) ); + + assertThat( matcher.extract( introspector.typeModel( new TypeCapture() {} ) ) ) + .isEmpty(); } @Test @@ -308,32 +212,23 @@ void genericArrayElement_resultIsDifferentTypeArgument() { } @Test - void parameterizedType() { - PojoTypeModel typeToInspectMock = mock( PojoTypeModel.class ); - PojoTypeModel resultTypeMock = mock( PojoTypeModel.class ); - + void parameterizedType() { ExtractingTypePatternMatcher matcher = factory.createExtractingMatcher( new TypeCapture>() {}.getType(), new TypeCapture() {}.getType() ); assertThat( matcher ).isInstanceOf( ParameterizedTypeArgumentMatcher.class ); - assertThat( matcher.toString() ) - .isEqualTo( "java.util.Map => T" ); - - Optional> actualReturn; - - when( typeToInspectMock.typeArgument( Map.class, 1 ) ) - .thenReturn( (Optional) Optional.of( resultTypeMock ) ); - actualReturn = matcher.extract( typeToInspectMock ); - assertThat( actualReturn ).isNotNull(); - assertThat( actualReturn.isPresent() ).isTrue(); - assertThat( actualReturn.get() ).isSameAs( resultTypeMock ); - - when( typeToInspectMock.typeArgument( Map.class, 1 ) ) - .thenReturn( Optional.empty() ); - actualReturn = matcher.extract( typeToInspectMock ); - assertThat( actualReturn ).isNotNull(); - assertThat( actualReturn.isPresent() ).isFalse(); + assertThat( matcher ) + .hasToString( "java.util.Map => T" ); + + assertThat( matcher.extract( introspector.typeModel( new TypeCapture>() {} ) ) ) + .asInstanceOf( InstanceOfAssertFactories.OPTIONAL ) + .contains( introspector.typeModel( new TypeCapture() {} ) ); + + assertThat( matcher.extract( introspector.typeModel( new TypeCapture>() {} ) ) ) + .isEmpty(); + assertThat( matcher.extract( introspector.typeModel( new TypeCapture() {} ) ) ) + .isEmpty(); } @Test @@ -400,7 +295,7 @@ void parameterizedType_multipleTypeVariables() { } @Test - void parameterizedType_resultIsRawType() { + void parameterizedType_resultIsRawType() { assertThatThrownBy( () -> factory.createExtractingMatcher( new TypeCapture>() {}.getType(), Object.class diff --git a/mapper/pojo-base/src/test/java/org/hibernate/search/mapper/pojo/testsupport/TestIntrospector.java b/mapper/pojo-base/src/test/java/org/hibernate/search/mapper/pojo/testsupport/TestIntrospector.java new file mode 100644 index 00000000000..e7b03adb5a1 --- /dev/null +++ b/mapper/pojo-base/src/test/java/org/hibernate/search/mapper/pojo/testsupport/TestIntrospector.java @@ -0,0 +1,56 @@ +/* + * Hibernate Search, full-text search for your domain model + * + * 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.search.mapper.pojo.testsupport; + +import java.lang.reflect.Type; + +import org.hibernate.annotations.common.reflection.java.JavaReflectionManager; +import org.hibernate.search.mapper.pojo.model.hcann.spi.AbstractPojoHCAnnBootstrapIntrospector; +import org.hibernate.search.mapper.pojo.model.hcann.spi.PojoHCannOrmGenericContextHelper; +import org.hibernate.search.mapper.pojo.model.hcann.spi.PojoSimpleHCAnnRawTypeModel; +import org.hibernate.search.mapper.pojo.model.spi.GenericContextAwarePojoGenericTypeModel.RawTypeDeclaringContext; +import org.hibernate.search.mapper.pojo.model.spi.PojoRawTypeIdentifier; +import org.hibernate.search.mapper.pojo.model.spi.PojoRawTypeModel; +import org.hibernate.search.mapper.pojo.model.spi.PojoTypeModel; +import org.hibernate.search.util.common.AssertionFailure; +import org.hibernate.search.util.common.reflect.spi.ValueHandleFactory; +import org.hibernate.search.util.impl.test.reflect.TypeCapture; + +public class TestIntrospector extends AbstractPojoHCAnnBootstrapIntrospector { + private final PojoHCannOrmGenericContextHelper genericContextHelper = new PojoHCannOrmGenericContextHelper( this ); + + public TestIntrospector(ValueHandleFactory valueHandleFactory) { + super( new JavaReflectionManager(), valueHandleFactory ); + } + + @Override + public PojoRawTypeModel typeModel(Class clazz) { + return new PojoSimpleHCAnnRawTypeModel<>( this, PojoRawTypeIdentifier.of( clazz ), + new RawTypeDeclaringContext<>( genericContextHelper, clazz ) ); + } + + @Override + public PojoRawTypeModel typeModel(String name) { + throw new AssertionFailure( "This method is not supported" ); + } + + @SuppressWarnings("unchecked") + public PojoTypeModel typeModel(TypeCapture typeCapture) { + Type type = typeCapture.getType(); + if ( type instanceof Class ) { + return (PojoTypeModel) typeModel( (Class) type ); + } + else { + RawTypeDeclaringContext rootContext = new RawTypeDeclaringContext<>( genericContextHelper, TypeCapture.class ); + PojoTypeModel typeCaptureType = rootContext.memberTypeReference( typeCapture.getClass() ); + return (PojoTypeModel) typeCaptureType.typeArgument( TypeCapture.class, 0 ) + .orElseThrow( () -> new IllegalArgumentException( + typeCapture.getClass() + " doesn't extend or implement " + TypeCapture.class + + " directly with a type argument" ) ); + } + } +} diff --git a/mapper/pojo-standalone/src/main/java/org/hibernate/search/mapper/pojo/standalone/model/impl/StandalonePojoBootstrapIntrospector.java b/mapper/pojo-standalone/src/main/java/org/hibernate/search/mapper/pojo/standalone/model/impl/StandalonePojoBootstrapIntrospector.java index 53305006e44..c0291b128af 100644 --- a/mapper/pojo-standalone/src/main/java/org/hibernate/search/mapper/pojo/standalone/model/impl/StandalonePojoBootstrapIntrospector.java +++ b/mapper/pojo-standalone/src/main/java/org/hibernate/search/mapper/pojo/standalone/model/impl/StandalonePojoBootstrapIntrospector.java @@ -9,9 +9,7 @@ import java.lang.invoke.MethodHandles; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; -import java.lang.reflect.Field; import java.lang.reflect.Member; -import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Map; @@ -19,12 +17,12 @@ import org.hibernate.annotations.common.reflection.java.JavaReflectionManager; import org.hibernate.search.mapper.pojo.model.hcann.spi.AbstractPojoHCAnnBootstrapIntrospector; import org.hibernate.search.mapper.pojo.model.hcann.spi.PojoHCannOrmGenericContextHelper; +import org.hibernate.search.mapper.pojo.model.hcann.spi.PojoSimpleHCAnnRawTypeModel; import org.hibernate.search.mapper.pojo.model.spi.GenericContextAwarePojoGenericTypeModel.RawTypeDeclaringContext; import org.hibernate.search.mapper.pojo.model.spi.PojoBootstrapIntrospector; import org.hibernate.search.mapper.pojo.model.spi.PojoRawTypeIdentifier; import org.hibernate.search.mapper.pojo.model.spi.PojoRawTypeModel; import org.hibernate.search.mapper.pojo.standalone.logging.impl.Log; -import org.hibernate.search.util.common.AssertionFailure; import org.hibernate.search.util.common.impl.ReflectionHelper; import org.hibernate.search.util.common.logging.impl.LoggerFactory; import org.hibernate.search.util.common.reflect.spi.ValueCreateHandle; @@ -55,7 +53,7 @@ private StandalonePojoBootstrapIntrospector(ValueHandleFactory valueHandleFactor @Override @SuppressWarnings("unchecked") - public StandalonePojoRawTypeModel typeModel(Class clazz) { + public PojoSimpleHCAnnRawTypeModel typeModel(Class clazz) { if ( clazz.isPrimitive() ) { /* * We'll never manipulate the primitive type, as we're using generics everywhere, @@ -63,7 +61,7 @@ public StandalonePojoRawTypeModel typeModel(Class clazz) { */ clazz = (Class) ReflectionHelper.getPrimitiveWrapperType( clazz ); } - return (StandalonePojoRawTypeModel) typeModelCache.computeIfAbsent( clazz, this::createTypeModel ); + return (PojoSimpleHCAnnRawTypeModel) typeModelCache.computeIfAbsent( clazz, this::createTypeModel ); } @Override @@ -71,32 +69,22 @@ public PojoRawTypeModel typeModel(String name) { throw log.namedTypesNotSupported( name ); } + @Override + protected ValueReadHandle createValueReadHandle(Member member) throws IllegalAccessException { + setAccessible( member ); + return super.createValueReadHandle( member ); + } + @Override protected ValueCreateHandle createValueCreateHandle(Constructor constructor) throws IllegalAccessException { setAccessible( constructor ); return valueHandleFactory.createForConstructor( constructor ); } - ValueReadHandle createValueReadHandle(Member member) throws IllegalAccessException { - if ( member instanceof Method ) { - Method method = (Method) member; - setAccessible( method ); - return valueHandleFactory.createForMethod( method ); - } - else if ( member instanceof Field ) { - Field field = (Field) member; - setAccessible( field ); - return valueHandleFactory.createForField( field ); - } - else { - throw new AssertionFailure( "Unexpected type for a " + Member.class.getName() + ": " + member ); - } - } - private PojoRawTypeModel createTypeModel(Class clazz) { PojoRawTypeIdentifier typeIdentifier = PojoRawTypeIdentifier.of( clazz ); try { - return new StandalonePojoRawTypeModel<>( + return new PojoSimpleHCAnnRawTypeModel<>( this, typeIdentifier, new RawTypeDeclaringContext<>( genericContextHelper, clazz ) ); @@ -106,14 +94,15 @@ private PojoRawTypeModel createTypeModel(Class clazz) { } } - private static void setAccessible(AccessibleObject member) { + private static void setAccessible(Member member) { try { - // always set accessible to true as it bypasses the security model checks - // at execution time and is faster. - member.setAccessible( true ); + // always try to set accessible to true regardless of visibility + // as it's faster even for public fields: + // it bypasses the security model checks at execution time. + ( (AccessibleObject) member ).setAccessible( true ); } catch (SecurityException se) { - if ( !Modifier.isPublic( ( (Member) member ).getModifiers() ) ) { + if ( !Modifier.isPublic( member.getModifiers() ) ) { throw se; } } diff --git a/util/common/src/main/java/org/hibernate/search/util/common/reflect/impl/GenericTypeContext.java b/util/common/src/main/java/org/hibernate/search/util/common/reflect/impl/GenericTypeContext.java index a481386af56..4236fb50df7 100644 --- a/util/common/src/main/java/org/hibernate/search/util/common/reflect/impl/GenericTypeContext.java +++ b/util/common/src/main/java/org/hibernate/search/util/common/reflect/impl/GenericTypeContext.java @@ -118,20 +118,20 @@ public Class rawType() { public String name() { Class rawType = rawType(); - TypeVariable>[] typeParameters = rawType.getTypeParameters(); - if ( typeParameters.length == 0 ) { - return resolvedType.getTypeName(); - } StringBuilder builder = new StringBuilder( rawType.getTypeName() ); - builder.append( '<' ); - for ( int i = 0; i < typeParameters.length; i++ ) { - if ( i > 0 ) { - builder.append( ',' ); + + TypeVariable>[] typeParameters = rawType.getTypeParameters(); + if ( typeParameters.length > 0 ) { + builder.append( '<' ); + for ( int i = 0; i < typeParameters.length; i++ ) { + if ( i > 0 ) { + builder.append( ',' ); + } + builder.append( resolveTypeArgument( rawType, i ).orElse( typeParameters[i] ).getTypeName() ); } - builder.append( resolveTypeArgument( rawType, i ).orElse( typeParameters[i] ).getTypeName() ); + builder.append( '>' ); } - builder.append( '>' ); if ( resolvedType.getTypeName().equals( builder.toString() ) ) { return resolvedType.getTypeName(); diff --git a/util/internal/test/common/src/main/java/org/hibernate/search/util/impl/test/reflect/TypeCapture.java b/util/internal/test/common/src/main/java/org/hibernate/search/util/impl/test/reflect/TypeCapture.java index debb8adb891..77bcbf159eb 100644 --- a/util/internal/test/common/src/main/java/org/hibernate/search/util/impl/test/reflect/TypeCapture.java +++ b/util/internal/test/common/src/main/java/org/hibernate/search/util/impl/test/reflect/TypeCapture.java @@ -17,6 +17,11 @@ protected TypeCapture() { this.type = capture(); } + @Override + public String toString() { + return type.toString(); + } + Type capture() { return captureTypeArgument( TypeCapture.class, this ); }