package org.springframework.roo.classpath.details;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.commons.lang3.Validate;
import org.springframework.roo.classpath.PhysicalTypeIdentifier;
import org.springframework.roo.classpath.details.annotations.AnnotatedJavaType;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadata;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder;
import org.springframework.roo.model.ImportRegistrationResolver;
import org.springframework.roo.model.ImportRegistrationResolverImpl;
import org.springframework.roo.model.JavaType;
/**
* Assists in the building of an {@link ItdTypeDetails} instance.
* <p>
* All methods on this class (which does NOT include the constructor) accept
* null arguments, and will automatically ignore any attempt to add an
* {@link IdentifiableJavaStructure} that is not use the same
* declaredByMetadataId as when the instance was constructed.
* <p>
* In addition, any method on this class which accepts an
* {@link InvocableMemberMetadata} will verify a
* {@link InvocableMemberMetadata#getBody()} is provided. This therefore detects
* programming errors which result from requesting a member to be included in an
* ITD but without providing the actual executable body for that member.
*
* @author Ben Alex
* @author Stefan Schmidt
* @since 1.0
*/
public class ItdTypeDetailsBuilder extends
AbstractMemberHoldingTypeDetailsBuilder<ItdTypeDetails> {
private final JavaType aspect;
private final List<DeclaredFieldAnnotationDetails> fieldAnnotations = new ArrayList<DeclaredFieldAnnotationDetails>();
private final ClassOrInterfaceTypeDetails governor;
private final ImportRegistrationResolver importRegistrationResolver;
private final List<DeclaredMethodAnnotationDetails> methodAnnotations = new ArrayList<DeclaredMethodAnnotationDetails>();
private final boolean privilegedAspect;
/**
* Constructor based on an existing ITD
*
* @param existing (required)
*/
public ItdTypeDetailsBuilder(final ItdTypeDetails existing) {
super(existing.getDeclaredByMetadataId(), existing);
aspect = existing.getAspect();
governor = existing.getGovernor();
importRegistrationResolver = new ImportRegistrationResolverImpl(
aspect.getPackage());
privilegedAspect = existing.isPrivilegedAspect();
}
/**
* Constructor
*
* @param declaredByMetadataId
* @param governor (required)
* @param aspect (required)
* @param privilegedAspect
*/
public ItdTypeDetailsBuilder(final String declaredByMetadataId,
final ClassOrInterfaceTypeDetails governor, final JavaType aspect,
final boolean privilegedAspect) {
super(declaredByMetadataId);
Validate.notNull(governor,
"Name (to receive the introductions) required");
Validate.notNull(aspect, "Aspect required");
this.aspect = aspect;
this.governor = governor;
importRegistrationResolver = new ImportRegistrationResolverImpl(
aspect.getPackage());
this.privilegedAspect = privilegedAspect;
}
public void addFieldAnnotation(
final DeclaredFieldAnnotationDetails declaredFieldAnnotationDetails) {
if (declaredFieldAnnotationDetails == null) {
return;
}
final JavaType declaredBy = PhysicalTypeIdentifier
.getJavaType(declaredFieldAnnotationDetails.getField()
.getDeclaredByMetadataId());
final boolean hasAnnotation = MemberFindingUtils.getAnnotationOfType(
declaredFieldAnnotationDetails.getField().getAnnotations(),
declaredFieldAnnotationDetails.getFieldAnnotation()
.getAnnotationType()) != null;
if (!declaredFieldAnnotationDetails.isRemoveAnnotation()) {
Validate.isTrue(
!hasAnnotation,
"Field annotation '@"
+ declaredFieldAnnotationDetails
.getFieldAnnotation().getAnnotationType()
.getSimpleTypeName()
+ "' is already present on the target field '"
+ declaredBy.getFullyQualifiedTypeName()
+ "."
+ declaredFieldAnnotationDetails.getField()
.getFieldName().getSymbolName()
+ "' (ITD target '"
+ aspect.getFullyQualifiedTypeName() + "')");
}
else {
Validate.isTrue(
hasAnnotation,
"Field annotation '@"
+ declaredFieldAnnotationDetails
.getFieldAnnotation().getAnnotationType()
.getSimpleTypeName()
+ "' cannot be removed as it is not present on the target field '"
+ declaredBy.getFullyQualifiedTypeName()
+ "."
+ declaredFieldAnnotationDetails.getField()
.getFieldName().getSymbolName()
+ "' (ITD target '"
+ aspect.getFullyQualifiedTypeName() + "')");
}
fieldAnnotations.add(declaredFieldAnnotationDetails);
}
@Override
public void addImports(final Collection<ImportMetadata> imports) {
if (imports != null) {
for (final ImportMetadata anImport : imports) {
importRegistrationResolver.addImport(anImport.getImportType());
}
}
}
public void addMethodAnnotation(
final DeclaredMethodAnnotationDetails declaredMethodAnnotationDetails) {
if (declaredMethodAnnotationDetails == null) {
return;
}
final JavaType declaredBy = PhysicalTypeIdentifier
.getJavaType(declaredMethodAnnotationDetails
.getMethodMetadata().getDeclaredByMetadataId());
final boolean hasAnnotation = MemberFindingUtils.getAnnotationOfType(
declaredMethodAnnotationDetails.getMethodMetadata()
.getAnnotations(), declaredMethodAnnotationDetails
.getMethodAnnotation().getAnnotationType()) != null;
Validate.isTrue(
!hasAnnotation,
"Method annotation '@"
+ declaredMethodAnnotationDetails.getMethodAnnotation()
.getAnnotationType().getSimpleTypeName()
+ "' is already present on the target method '"
+ declaredBy.getFullyQualifiedTypeName()
+ "."
+ declaredMethodAnnotationDetails.getMethodMetadata()
.getMethodName().getSymbolName()
+ "()' (ITD target '"
+ aspect.getFullyQualifiedTypeName() + "')");
methodAnnotations.add(declaredMethodAnnotationDetails);
}
@Deprecated
// Should use addAnnotation() instead
public void addTypeAnnotation(final AnnotationMetadata annotationMetadata) {
addAnnotation(annotationMetadata);
}
public ItdTypeDetails build() {
return new DefaultItdTypeDetails(getCustomData().build(),
getDeclaredByMetadataId(), getModifier(), governor, aspect,
privilegedAspect,
importRegistrationResolver.getRegisteredImports(),
buildConstructors(), buildFields(), buildMethods(),
getExtendsTypes(), getImplementsTypes(), buildAnnotations(),
fieldAnnotations, methodAnnotations, buildInnerTypes());
}
public ImportRegistrationResolver getImportRegistrationResolver() {
return importRegistrationResolver;
}
@Override
protected void onAddAnnotation(final AnnotationMetadataBuilder md) {
Validate.isTrue(
governor.getAnnotation(md.getAnnotationType()) == null,
"Type annotation '" + md.getAnnotationType()
+ "' already defined in target type '"
+ governor.getName().getFullyQualifiedTypeName()
+ "' (ITD target '"
+ aspect.getFullyQualifiedTypeName() + "')");
Validate.isTrue(
build().getAnnotation(md.getAnnotationType()) == null,
"Type annotation '" + md.getAnnotationType()
+ "' already defined in ITD (ITD target '"
+ aspect.getFullyQualifiedTypeName() + "'");
}
@Override
protected void onAddConstructor(final ConstructorMetadataBuilder md) {
Validate.isTrue(
governor.getDeclaredConstructor(AnnotatedJavaType
.convertFromAnnotatedJavaTypes(md.getParameterTypes())) == null,
"Constructor with " + md.getParameterTypes().size()
+ " parameters already defined in target type '"
+ governor.getName().getFullyQualifiedTypeName()
+ "' (ITD target '"
+ aspect.getFullyQualifiedTypeName() + "')");
Validate.isTrue(
build().getDeclaredConstructor(
AnnotatedJavaType.convertFromAnnotatedJavaTypes(md
.getParameterTypes())) == null,
"Constructor with " + md.getParameterTypes().size()
+ " parameters already defined in ITD (ITD target '"
+ aspect.getFullyQualifiedTypeName() + "'");
Validate.notBlank(
md.getBody(),
"Constructor '"
+ md
+ "' failed to provide a body, despite being identified for ITD inclusion");
}
@Override
protected void onAddExtendsTypes(final JavaType type) {
Validate.isTrue(!governor.getExtendsTypes().contains(type), "Type '"
+ type
+ "' already declared in extends types list in target type '"
+ governor.getName().getFullyQualifiedTypeName()
+ "' (ITD target '" + aspect.getFullyQualifiedTypeName() + "')");
Validate.isTrue(
!getExtendsTypes().contains(type),
"Type '"
+ type
+ "' already declared in extends types list in ITD (ITD target '"
+ aspect.getFullyQualifiedTypeName() + "'");
}
@Override
protected void onAddField(final FieldMetadataBuilder md) {
Validate.isTrue(
governor.getDeclaredField(md.getFieldName()) == null,
"Field '" + md.getFieldName()
+ "' already defined in target type '"
+ governor.getName().getFullyQualifiedTypeName()
+ "' (ITD target '"
+ aspect.getFullyQualifiedTypeName() + "')");
Validate.isTrue(
build().getDeclaredField(md.getFieldName()) == null,
"Field '" + md.getFieldName()
+ "' already defined in ITD (ITD target '"
+ aspect.getFullyQualifiedTypeName() + ")'");
}
@Override
protected void onAddImplementType(final JavaType type) {
Validate.isTrue(
!governor.getImplementsTypes().contains(type),
"Type '"
+ type
+ "' already declared in implements types list in target type '"
+ governor.getName().getFullyQualifiedTypeName()
+ "' (ITD target '"
+ aspect.getFullyQualifiedTypeName() + "')");
Validate.isTrue(
!getImplementsTypes().contains(type),
"Type '"
+ type
+ "' already declared in implements types list in ITD (ITD target '"
+ aspect.getFullyQualifiedTypeName() + "'");
}
@Override
public void onAddInnerType(final ClassOrInterfaceTypeDetailsBuilder cid) {
if (cid == null) {
return;
}
Validate.isTrue(Modifier.isStatic(cid.getModifier()),
"Currently only static inner types are supported by AspectJ");
}
@Override
protected void onAddMethod(final MethodMetadataBuilder md) {
Validate.isTrue(
MemberFindingUtils.getDeclaredMethod(governor, md
.getMethodName(), AnnotatedJavaType
.convertFromAnnotatedJavaTypes(md.getParameterTypes())) == null,
"Method '" + md.getMethodName()
+ "' already defined in target type '"
+ governor.getName().getFullyQualifiedTypeName()
+ "' (ITD target '"
+ aspect.getFullyQualifiedTypeName() + "')");
Validate.isTrue(
MemberFindingUtils.getDeclaredMethod(build(), md
.getMethodName(), AnnotatedJavaType
.convertFromAnnotatedJavaTypes(md.getParameterTypes())) == null,
"Method '" + md.getMethodName()
+ "' already defined in ITD (ITD target '"
+ aspect.getFullyQualifiedTypeName() + "'");
if (!Modifier.isAbstract(md.getModifier())) {
Validate.notBlank(
md.getBody(),
"Method '"
+ md
+ "' failed to provide a body, despite being identified for ITD inclusion");
}
}
}