Skip to content

Commit

Permalink
HSEARCH-4188 Preserve generic type arguments when casting type models…
Browse files Browse the repository at this point in the history
… during the mapping phase

Signed-off-by: Yoann Rodière <yoann@hibernate.org>
  • Loading branch information
yrodiere authored and fax4ever committed Mar 12, 2021
1 parent bd0fc62 commit 457f524
Show file tree
Hide file tree
Showing 15 changed files with 90 additions and 21 deletions.
Expand Up @@ -12,9 +12,12 @@

import org.hibernate.search.engine.mapper.model.spi.MappableTypeModel;
import org.hibernate.search.mapper.pojo.model.spi.AbstractPojoRawTypeModel;
import org.hibernate.search.mapper.pojo.model.spi.PojoGenericTypeModel;
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;

@SuppressWarnings("rawtypes")
public class HibernateOrmDynamicMapRawTypeModel
extends AbstractPojoRawTypeModel<Map, HibernateOrmBootstrapIntrospector> {

Expand Down Expand Up @@ -59,6 +62,20 @@ public Stream<Annotation> annotations() {
return Stream.empty();
}

@Override
@SuppressWarnings("unchecked")
public PojoTypeModel<? extends Map> cast(PojoGenericTypeModel<?> other) {
if ( other.rawType().isSubTypeOf( this ) ) {
// Redundant cast; no need to create a new type.
return (PojoTypeModel<? extends Map>) other;
}
else {
// There is no generic type information to retain for dynamic-map types; we can just return this.
// Also, calling other.castTo(...) would mean losing the type name, and we definitely don't want that.
return this;
}
}

@Override
protected Stream<String> declaredPropertyNames() {
return ormTypeMetadata.getPropertyNames().stream();
Expand Down
Expand Up @@ -29,7 +29,7 @@ BoundPojoModelPathCastedTypeNode<T, U> getModelPath() {
@Override
PojoImplicitReindexingResolverNode<T> doBuild(PojoImplicitReindexingResolverNode<? super U> nestedNode) {
return new PojoImplicitReindexingResolverCastedTypeNode<>(
getModelPath().getTypeModel().caster(), nestedNode
getModelPath().getTypeModel().rawType().caster(), nestedNode
);
}
}
Expand Up @@ -143,13 +143,13 @@ final void checkFrozen() {
}

@SuppressWarnings("unchecked") // We know builders have this exact type, by construction
private <U> PojoImplicitReindexingResolverCastedTypeNodeBuilder<V, U> getOrCreateCastedTypeNodeBuilder(
private <U> PojoImplicitReindexingResolverCastedTypeNodeBuilder<V, ? extends U> getOrCreateCastedTypeNodeBuilder(
PojoRawTypeModel<U> targetTypeModel) {
return (PojoImplicitReindexingResolverCastedTypeNodeBuilder<V, U>)
return (PojoImplicitReindexingResolverCastedTypeNodeBuilder<V, ? extends U>)
castedTypeNodeBuilders.computeIfAbsent( targetTypeModel, this::createCastedTypeNodeBuilder );
}

private <U> PojoImplicitReindexingResolverCastedTypeNodeBuilder<V, U> createCastedTypeNodeBuilder(
private <U> PojoImplicitReindexingResolverCastedTypeNodeBuilder<V, ? extends U> createCastedTypeNodeBuilder(
PojoRawTypeModel<U> targetTypeModel) {
checkNotFrozen();
return new PojoImplicitReindexingResolverCastedTypeNodeBuilder<>(
Expand Down
Expand Up @@ -103,7 +103,7 @@ public PojoIndexingDependencyCollectorTypeNode<V> type() {
);
}

public <U> PojoIndexingDependencyCollectorTypeNode<U> castedType(PojoRawTypeModel<U> typeModel) {
public <U> PojoIndexingDependencyCollectorTypeNode<? extends U> castedType(PojoRawTypeModel<U> typeModel) {
return new PojoIndexingDependencyCollectorTypeNode<>(
this,
modelPathFromLastEntityNode.castedType( typeModel ),
Expand Down
Expand Up @@ -24,10 +24,10 @@
*/
public class PojoImplicitReindexingResolverCastedTypeNode<T, U> extends PojoImplicitReindexingResolverNode<T> {

private final PojoCaster<U> caster;
private final PojoCaster<? super U> caster;
private final PojoImplicitReindexingResolverNode<? super U> nested;

public PojoImplicitReindexingResolverCastedTypeNode(PojoCaster<U> caster,
public PojoImplicitReindexingResolverCastedTypeNode(PojoCaster<? super U> caster,
PojoImplicitReindexingResolverNode<? super U> nested) {
this.caster = caster;
this.nested = nested;
Expand All @@ -50,7 +50,9 @@ public void appendTo(ToStringTreeBuilder builder) {
@Override
public void resolveEntitiesToReindex(PojoReindexingCollector collector,
T dirty, PojoImplicitReindexingResolverRootContext context) {
U castedDirty = caster.castOrNull( context.sessionContext().runtimeIntrospector().unproxy( dirty ) );
// The caster can only cast to the raw type, beyond that we have to use an unchecked cast.
@SuppressWarnings("unchecked")
U castedDirty = (U) caster.castOrNull( context.sessionContext().runtimeIntrospector().unproxy( dirty ) );
if ( castedDirty != null ) {
nested.resolveEntitiesToReindex( collector, castedDirty, context );
}
Expand Down
Expand Up @@ -18,7 +18,9 @@
import org.hibernate.search.engine.mapper.model.spi.MappableTypeModel;
import org.hibernate.search.mapper.pojo.model.spi.AbstractPojoRawTypeModel;
import org.hibernate.search.mapper.pojo.model.spi.GenericContextAwarePojoGenericTypeModel.RawTypeDeclaringContext;
import org.hibernate.search.mapper.pojo.model.spi.PojoGenericTypeModel;
import org.hibernate.search.mapper.pojo.model.spi.PojoRawTypeIdentifier;
import org.hibernate.search.mapper.pojo.model.spi.PojoTypeModel;

public abstract class AbstractPojoHCAnnRawTypeModel<T, I extends AbstractPojoHCAnnBootstrapIntrospector>
extends AbstractPojoRawTypeModel<T, I> {
Expand Down Expand Up @@ -52,6 +54,18 @@ public Stream<Annotation> annotations() {
return introspector.annotations( xClass );
}

@Override
@SuppressWarnings("unchecked")
public PojoTypeModel<? extends T> cast(PojoGenericTypeModel<?> other) {
if ( other.rawType().isSubTypeOf( this ) ) {
// Redundant cast; no need to create a new type.
return (PojoTypeModel<? extends T>) other;
}
else {
return other.castTo( typeIdentifier.javaClass() ).orElse( this );
}
}

@Override
protected final Stream<String> declaredPropertyNames() {
return Stream.concat(
Expand Down
Expand Up @@ -6,7 +6,7 @@
*/
package org.hibernate.search.mapper.pojo.model.path.impl;

import org.hibernate.search.mapper.pojo.model.spi.PojoRawTypeModel;
import org.hibernate.search.mapper.pojo.model.spi.PojoTypeModel;

/**
* @param <T> The type represented by the parent node, whose values are casted to {@link U}.
Expand All @@ -15,9 +15,9 @@
public class BoundPojoModelPathCastedTypeNode<T, U> extends BoundPojoModelPathTypeNode<U> {

private final BoundPojoModelPathValueNode<?, ?, T> parent;
private final PojoRawTypeModel<U> typeModel;
private final PojoTypeModel<U> typeModel;

BoundPojoModelPathCastedTypeNode(BoundPojoModelPathValueNode<?, ?, T> parent, PojoRawTypeModel<U> typeModel) {
BoundPojoModelPathCastedTypeNode(BoundPojoModelPathValueNode<?, ?, T> parent, PojoTypeModel<U> typeModel) {
this.parent = parent;
this.typeModel = typeModel;
}
Expand All @@ -28,7 +28,7 @@ public class BoundPojoModelPathCastedTypeNode<T, U> extends BoundPojoModelPathTy
}

@Override
public PojoRawTypeModel<U> getTypeModel() {
public PojoTypeModel<U> getTypeModel() {
return typeModel;
}

Expand Down
Expand Up @@ -66,8 +66,8 @@ public BoundPojoModelPathOriginalTypeNode<V> type() {
/**
* @return A child path node representing values represented by this node, casted to the given type.
*/
public <U> BoundPojoModelPathCastedTypeNode<V, U> castedType(PojoRawTypeModel<U> typeModel) {
return new BoundPojoModelPathCastedTypeNode<>( this, typeModel );
public <U> BoundPojoModelPathCastedTypeNode<V, ? extends U> castedType(PojoRawTypeModel<U> typeModel) {
return new BoundPojoModelPathCastedTypeNode<>( this, typeModel.cast( getTypeModel() ) );
}

public PojoGenericTypeModel<V> getTypeModel() {
Expand Down
Expand Up @@ -114,6 +114,12 @@ public PojoPropertyModel<?> property(String propertyName) {
return wrapProperty( super.property( propertyName ) );
}

@Override
public <U> Optional<PojoTypeModel<? extends U>> castTo(Class<U> target) {
return Optional.of( new GenericContextAwarePojoGenericTypeModel<U>( helper,
genericTypeContext.castTo( target ) ) );
}

@Override
public Optional<PojoGenericTypeModel<?>> typeArgument(Class<?> rawSuperType, int typeParameterIndex) {
return genericTypeContext.resolveTypeArgument( rawSuperType, typeParameterIndex )
Expand Down
Expand Up @@ -38,4 +38,13 @@ public interface PojoGenericTypeModel<T> extends PojoTypeModel<T> {
*/
Optional<? extends PojoGenericTypeModel<?>> arrayElementType();

/**
* @param target The type to cast to.
* @param <U> The type to cast to.
* @return A new type model, representing the current type cast to the given type,
* or {@link Optional#empty()} if casting is not supported.
* The type model will retain as much contextual type information as possible (type arguments, ...),
* so casting {@code List<Integer>} to {@code Collection} for example would return {@code Collection<Integer>}.
*/
<U> Optional<PojoTypeModel<? extends U>> castTo(Class<U> target);
}
Expand Up @@ -57,6 +57,16 @@ default PojoRawTypeModel<T> rawType() {

Collection<PojoPropertyModel<?>> declaredProperties();

/**
* @param other The type to cast to this type.
* @return A new type model, representing the given type cast to this type.
* If casting is not possible, returns {@code this}.
* If casting is possible, the returned type type model
* will retain as much contextual type information as possible (type arguments, ...),
* so casting {@code List<Integer>} to {@code Collection} for example would return {@code Collection<Integer>}.
*/
PojoTypeModel<? extends T> cast(PojoGenericTypeModel<?> other);

PojoCaster<T> caster();

}
Expand Up @@ -72,6 +72,12 @@ public String name() {
return builder.toString();
}

@Override
public <U> Optional<PojoTypeModel<? extends U>> castTo(Class<U> target) {
// Cannot cast synthetic types.
return Optional.empty();
}

@Override
public Optional<PojoGenericTypeModel<?>> arrayElementType() {
return Optional.ofNullable( arrayElementType );
Expand Down
Expand Up @@ -42,16 +42,19 @@ public BoundPojoModelPathCastedTypeNode<T, U> getModelPath() {
}

@Override
@SuppressWarnings("unchecked")
protected PojoIndexingDependencyCollectorTypeNode<U> toType(
PojoIndexingDependencyCollectorValueNode<?, T> valueDependencyCollector) {
return valueDependencyCollector.castedType( getModelPath().getTypeModel() );
// By construction, the casted type should be the same as U
return (PojoIndexingDependencyCollectorTypeNode<U>)
valueDependencyCollector.castedType( getModelPath().getTypeModel().rawType() );
}

@Override
protected PojoIndexingProcessor<T> doBuild(Collection<IndexObjectFieldReference> parentIndexObjectReferences,
PojoIndexingProcessor<? super U> nested) {
return new PojoIndexingProcessorCastedTypeNode<>(
getModelPath().getTypeModel().caster(),
getModelPath().getTypeModel().rawType().caster(),
parentIndexObjectReferences, nested
);
}
Expand Down
Expand Up @@ -120,8 +120,8 @@ public void indexedEmbedded(PojoRawTypeModel<?> definingTypeModel, String relati
);
}
else {
PojoRawTypeModel<?> castedType = mappingHelper.introspector().typeModel( targetType );
BoundPojoModelPathCastedTypeNode<V, ?> typeModelPath = modelPath.castedType( castedType );
PojoRawTypeModel<?> targetTypeModel = mappingHelper.introspector().typeModel( targetType );
BoundPojoModelPathCastedTypeNode<V, ?> typeModelPath = modelPath.castedType( targetTypeModel );
identityMappingCollector = new PojoIndexedEmbeddedIdentityMappingCollector<>(
typeModelPath.getTypeModel().rawType(), mappingHelper );
nestedProcessorBuilder = new PojoIndexingProcessorCastedTypeNodeBuilder<>(
Expand Down
Expand Up @@ -25,11 +25,11 @@
*/
public class PojoIndexingProcessorCastedTypeNode<T, U> extends PojoIndexingProcessor<T> {

private final PojoCaster<U> caster;
private final PojoCaster<? super U> caster;
private final Iterable<IndexObjectFieldReference> parentIndexObjectReferences;
private final PojoIndexingProcessor<? super U> nested;

public PojoIndexingProcessorCastedTypeNode(PojoCaster<U> caster,
public PojoIndexingProcessorCastedTypeNode(PojoCaster<? super U> caster,
Iterable<IndexObjectFieldReference> parentIndexObjectReferences,
PojoIndexingProcessor<? super U> nested) {
this.caster = caster;
Expand All @@ -55,7 +55,9 @@ public final void process(DocumentElement target, T source, PojoIndexingProcesso
if ( source == null ) {
return;
}
U castedSource = caster.cast( sessionContext.runtimeIntrospector().unproxy( source ) );
// The caster can only cast to the raw type, beyond that we have to use an unchecked cast.
@SuppressWarnings("unchecked")
U castedSource = (U) caster.cast( sessionContext.runtimeIntrospector().unproxy( source ) );
DocumentElement parentObject = target;
for ( IndexObjectFieldReference objectFieldReference : parentIndexObjectReferences ) {
parentObject = parentObject.addObject( objectFieldReference );
Expand Down

0 comments on commit 457f524

Please sign in to comment.