Skip to content

Commit

Permalink
#73 - Add MutableAnnotationTarget#replace
Browse files Browse the repository at this point in the history
  • Loading branch information
sebersole committed Apr 25, 2024
1 parent b69d810 commit 9a088f8
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.hibernate.models.spi.AnnotationDescriptorRegistry;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.MutableAnnotationTarget;
import org.hibernate.models.spi.MutableAnnotationUsage;
import org.hibernate.models.spi.SourceModelBuildingContext;

/**
Expand Down Expand Up @@ -150,4 +151,20 @@ default <X extends Annotation> AnnotationUsage<X> getNamedAnnotationUsage(
attributeToMatch
);
}

@Override
default <S extends Annotation, P extends Annotation> MutableAnnotationUsage<P> replaceAnnotationUsage(
AnnotationDescriptor<S> repeatableType,
AnnotationDescriptor<P> containerType,
SourceModelBuildingContext buildingContext) {
assert repeatableType.isRepeatable();
assert repeatableType.getRepeatableContainer() == containerType;

final MutableAnnotationUsage<P> containerTypeUsage = containerType.createUsage( buildingContext );
// effectively overwrites any previous registrations
getUsageMap().put( containerType.getAnnotationType(), containerTypeUsage );
getUsageMap().put( repeatableType.getAnnotationType(), null );

return containerTypeUsage;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,42 @@ default <A extends Annotation> MutableAnnotationUsage<A> applyAnnotationUsage(
return usage;
}

/**
* Creates and replaces (if any) an existing usage of the given annotation.
* <p/>
* For repeatable annotations, use
* Applies a usage of the given {@code annotationType} to this target. Will return
* an existing usage, if one, or create a new usage.
*
* @apiNote Generally replacement is used with XML processing and, again generally,
* only for repeatable annotations using
* {@linkplain #replaceAnnotationUsage(AnnotationDescriptor, AnnotationDescriptor, SourceModelBuildingContext)}
*
* @see #replaceAnnotationUsage(AnnotationDescriptor, AnnotationDescriptor, SourceModelBuildingContext)
*/
default <A extends Annotation> MutableAnnotationUsage<A> replaceAnnotationUsage(
AnnotationDescriptor<A> annotationType,
SourceModelBuildingContext buildingContext) {
final MutableAnnotationUsage<A> usage = annotationType.createUsage( buildingContext );
// effectively overwrites any previous registration
addAnnotationUsage( usage );
return usage;
}

/**
* Creates and replaces (if any) an existing usage of the given annotation.
* <p/>
* For repeatable annotations, use
* Applies a usage of the given {@code annotationType} to this target. Will return
* an existing usage, if one, or create a new usage.
*
* @see #replaceAnnotationUsage(AnnotationDescriptor, SourceModelBuildingContext)
*/
<S extends Annotation, P extends Annotation> MutableAnnotationUsage<P> replaceAnnotationUsage(
AnnotationDescriptor<S> repeatableType,
AnnotationDescriptor<P> containerType,
SourceModelBuildingContext buildingContext);

@Override
MutableClassDetails asClassDetails();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/

package org.hibernate.models.annotations;

import java.util.ArrayList;
import java.util.List;

import org.hibernate.models.internal.SourceModelBuildingContextImpl;
import org.hibernate.models.orm.JpaAnnotations;
import org.hibernate.models.spi.AnnotationDescriptor;
import org.hibernate.models.spi.AnnotationDescriptorRegistry;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MutableAnnotationUsage;
import org.hibernate.models.spi.MutableClassDetails;

import org.junit.jupiter.api.Test;

import org.jboss.jandex.Index;

import jakarta.persistence.SecondaryTable;
import jakarta.persistence.SecondaryTables;

import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.models.SourceModelTestHelper.buildJandexIndex;
import static org.hibernate.models.SourceModelTestHelper.createBuildingContext;

/**
* @author Steve Ebersole
*/
public class AnnotationReplacementTests {
@Test
void testBasicReplacementWithJandex() {
basicReplacementChecks( buildJandexIndex( SimpleEntity.class ) );
}

@Test
void testBasicReplacementWithoutJandex() {
basicReplacementChecks( null );
}

void basicReplacementChecks(Index index) {
final SourceModelBuildingContextImpl buildingContext = createBuildingContext( index, SimpleEntity.class );

final MutableClassDetails classDetails = (MutableClassDetails) buildingContext.getClassDetailsRegistry().getClassDetails( SimpleEntity.class.getName() );
assertThat( classDetails.hasAnnotationUsage( SecondaryTable.class ) ).isTrue();
assertThat( classDetails.hasAnnotationUsage( SecondaryTables.class ) ).isFalse();

final MutableAnnotationUsage<SecondaryTables> replacement = classDetails.replaceAnnotationUsage(
JpaAnnotations.SECONDARY_TABLE,
JpaAnnotations.SECONDARY_TABLES,
buildingContext
);

assertThat( classDetails.hasAnnotationUsage( SecondaryTable.class ) ).isTrue();
assertThat( classDetails.hasAnnotationUsage( SecondaryTables.class ) ).isTrue();

List<MutableAnnotationUsage<SecondaryTable>> valueList = replacement.getList( "value" );
// because it is required
assertThat( valueList ).isNull();
valueList = new ArrayList<>();
replacement.setAttributeValue( "value", valueList );

final MutableAnnotationUsage<SecondaryTable> fromXml = JpaAnnotations.SECONDARY_TABLE.createUsage( buildingContext );
valueList.add( fromXml );
fromXml.setAttributeValue( "name", "from_xml" );

final AnnotationUsage<SecondaryTable> annotationUsage = classDetails.getAnnotationUsage( SecondaryTable.class );
assertThat( annotationUsage.getString( "name" ) ).isEqualTo( "from_xml" );

final AnnotationUsage<SecondaryTables> annotationUsage1 = classDetails.getAnnotationUsage( SecondaryTables.class );
assertThat( annotationUsage1.getList( "value" ) ).isSameAs( valueList );
}

}

0 comments on commit 9a088f8

Please sign in to comment.