package org.springframework.roo.classpath.details;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.roo.model.Builder;
import org.springframework.roo.model.JavaType;
/**
* Abstract {@link Builder} to assist building {@link MemberHoldingTypeDetails}
* implementations.
*
* @author Ben Alex
* @since 1.1
* @param <T> the type of {@link MemberHoldingTypeDetails} being built
*/
public abstract class AbstractMemberHoldingTypeDetailsBuilder<T extends MemberHoldingTypeDetails>
extends AbstractIdentifiableAnnotatedJavaStructureBuilder<T> {
private final List<ConstructorMetadataBuilder> declaredConstructors =
new ArrayList<ConstructorMetadataBuilder>();
private final List<FieldMetadataBuilder> declaredFields = new ArrayList<FieldMetadataBuilder>();
private final List<InitializerMetadataBuilder> declaredInitializers =
new ArrayList<InitializerMetadataBuilder>();
private final List<ClassOrInterfaceTypeDetailsBuilder> declaredInnerTypes =
new ArrayList<ClassOrInterfaceTypeDetailsBuilder>();
private final List<MethodMetadataBuilder> declaredMethods =
new ArrayList<MethodMetadataBuilder>();
private final List<JavaType> extendsTypes = new ArrayList<JavaType>();
private final List<JavaType> implementsTypes = new ArrayList<JavaType>();
/**
* Constructor
*
* @param existing
*/
protected AbstractMemberHoldingTypeDetailsBuilder(final MemberHoldingTypeDetails existing) {
super(existing);
init(existing);
}
/**
* Constructor
*
* @param declaredbyMetadataId
*/
protected AbstractMemberHoldingTypeDetailsBuilder(final String declaredbyMetadataId) {
super(declaredbyMetadataId);
}
/**
* Constructor
*
* @param declaredbyMetadataId
* @param existing
*/
protected AbstractMemberHoldingTypeDetailsBuilder(final String declaredbyMetadataId,
final MemberHoldingTypeDetails existing) {
super(declaredbyMetadataId, existing);
init(existing);
}
public final boolean addConstructor(final ConstructorMetadata constructor) {
if (constructor == null) {
return false;
}
return addConstructor(new ConstructorMetadataBuilder(constructor));
}
public final boolean addConstructor(final ConstructorMetadataBuilder constructorBuilder) {
// If received constructor builder is null, return false
if (constructorBuilder == null) {
return false;
} else if (!getDeclaredByMetadataId().equals(constructorBuilder.getDeclaredByMetadataId())) {
// Generating new constructor using delcaredMetadataId
ConstructorMetadataBuilder updatedConstructorBuilder =
new ConstructorMetadataBuilder(getDeclaredByMetadataId(), constructorBuilder.build());
onAddConstructor(updatedConstructorBuilder);
return declaredConstructors.add(updatedConstructorBuilder);
}
onAddConstructor(constructorBuilder);
return declaredConstructors.add(constructorBuilder);
}
public final boolean addExtendsTypes(final JavaType extendsType) {
if (extendsType == null) {
return false;
}
onAddExtendsTypes(extendsType);
return extendsTypes.add(extendsType);
}
public final boolean addField(final FieldMetadata field) {
if (field == null) {
return false;
}
return addField(new FieldMetadataBuilder(field));
}
public final boolean addField(final FieldMetadataBuilder fieldBuilder) {
if (fieldBuilder == null
|| !getDeclaredByMetadataId().equals(fieldBuilder.getDeclaredByMetadataId())) {
return false;
}
onAddField(fieldBuilder);
return declaredFields.add(fieldBuilder);
}
public final boolean addImplementsType(final JavaType implementsType) {
if (implementsType == null) {
return false;
}
onAddImplementType(implementsType);
return implementsTypes.add(implementsType);
}
/**
* Adds the given imports to this builder if not already present
*
* @param imports the imports to add; can be <code>null</code>
* @since 1.2.0
*/
public abstract void addImports(Collection<ImportMetadata> imports);
public final boolean addInitializer(final InitializerMetadataBuilder initializer) {
if (initializer == null
|| !getDeclaredByMetadataId().equals(initializer.getDeclaredByMetadataId())) {
return false;
}
onAddInitializer(initializer);
return declaredInitializers.add(initializer);
}
public final boolean addInnerType(final ClassOrInterfaceTypeDetails innerType) {
if (innerType == null) {
return false;
}
return addInnerType(new ClassOrInterfaceTypeDetailsBuilder(innerType));
}
public final boolean addInnerType(final ClassOrInterfaceTypeDetailsBuilder innerType) {
/*
* There was originally a check to see if the declaredMIDs matched, but
* this doesn't really make much sense. We need to come up with a better
* model for inner types. I thought about adding an enclosingType
* attribute but this prototype just felt like a hack. In the short term
* I have just disabled the MID comparison as I think this is fairly
* reasonable until this is given some more time. JTT - 24/08/11
*/
if (innerType == null) {
return false;
}
onAddInnerType(innerType);
return declaredInnerTypes.add(innerType);
}
/**
* Adds the given method to this builder
*
* @param method the method to add; can be <code>null</code>
* @return <code>true</code> if the state of this builder changed
*/
public final boolean addMethod(final MethodMetadata method) {
if (method == null) {
return false;
}
return addMethod(new MethodMetadataBuilder(method));
}
/**
* Adds the given method to this builder
*
* @param methodBuilder the method builder to add; ignored if
* <code>null</code> or if its MID doesn't match this builder's
* MID
* @return true if the state of this builder changed
*/
public final boolean addMethod(final MethodMetadataBuilder methodBuilder) {
if (methodBuilder == null
|| !getDeclaredByMetadataId().equals(methodBuilder.getDeclaredByMetadataId())) {
return false;
}
onAddMethod(methodBuilder);
return declaredMethods.add(methodBuilder);
}
public final List<ConstructorMetadata> buildConstructors() {
final List<ConstructorMetadata> result = new ArrayList<ConstructorMetadata>();
for (final ConstructorMetadataBuilder builder : declaredConstructors) {
result.add(builder.build());
}
return result;
}
public final List<FieldMetadata> buildFields() {
final List<FieldMetadata> result = new ArrayList<FieldMetadata>();
for (final FieldMetadataBuilder builder : declaredFields) {
result.add(builder.build());
}
return result;
}
public final List<InitializerMetadata> buildInitializers() {
final List<InitializerMetadata> result = new ArrayList<InitializerMetadata>();
for (final InitializerMetadataBuilder builder : declaredInitializers) {
result.add(builder.build());
}
return result;
}
public final List<ClassOrInterfaceTypeDetails> buildInnerTypes() {
final List<ClassOrInterfaceTypeDetails> result = new ArrayList<ClassOrInterfaceTypeDetails>();
for (final ClassOrInterfaceTypeDetailsBuilder cidBuilder : declaredInnerTypes) {
result.add(cidBuilder.build());
}
return result;
}
public final List<MethodMetadata> buildMethods() {
final List<MethodMetadata> result = new ArrayList<MethodMetadata>();
for (final MethodMetadataBuilder builder : declaredMethods) {
result.add(builder.build());
}
return result;
}
/**
* Removes all declared methods from this builder
*/
public void clearDeclaredMethods() {
this.declaredMethods.clear();
}
public final List<ConstructorMetadataBuilder> getDeclaredConstructors() {
return declaredConstructors;
}
public final List<FieldMetadataBuilder> getDeclaredFields() {
return declaredFields;
}
public List<InitializerMetadataBuilder> getDeclaredInitializers() {
return declaredInitializers;
}
public List<ClassOrInterfaceTypeDetailsBuilder> getDeclaredInnerTypes() {
return declaredInnerTypes;
}
/**
* Returns the declared methods in this builder
*
* @return an unmodifiable copy of this list
*/
public final List<MethodMetadataBuilder> getDeclaredMethods() {
return Collections.unmodifiableList(declaredMethods);
}
/**
* Returns the types that the built instance will extend, if any. Does not
* return a copy, i.e. modifying the returned list will modify this builder!
* TODO improve encapsulation by returning a defensive copy and
* <em>updating callers accordingly</em>
*
* @return a non-<code>null</code> list
*/
public final List<JavaType> getExtendsTypes() {
return extendsTypes;
}
public final List<JavaType> getImplementsTypes() {
return implementsTypes;
}
private void init(final MemberHoldingTypeDetails existing) {
for (final ConstructorMetadata element : existing.getDeclaredConstructors()) {
declaredConstructors.add(new ConstructorMetadataBuilder(element));
}
for (final FieldMetadata element : existing.getDeclaredFields()) {
declaredFields.add(new FieldMetadataBuilder(element));
}
for (final MethodMetadata element : existing.getDeclaredMethods()) {
declaredMethods.add(new MethodMetadataBuilder(element));
}
for (final ClassOrInterfaceTypeDetails element : existing.getDeclaredInnerTypes()) {
declaredInnerTypes.add(new ClassOrInterfaceTypeDetailsBuilder(element));
}
for (final InitializerMetadata element : existing.getDeclaredInitializers()) {
declaredInitializers.add(new InitializerMetadataBuilder(element));
}
extendsTypes.addAll(existing.getExtendsTypes());
implementsTypes.addAll(existing.getImplementsTypes());
}
protected void onAddConstructor(final ConstructorMetadataBuilder constructorBuilder) {}
protected void onAddExtendsTypes(final JavaType extendsType) {}
protected void onAddField(final FieldMetadataBuilder fieldBuilder) {}
protected void onAddImplementType(final JavaType implementsType) {}
protected void onAddInitializer(final InitializerMetadataBuilder initializer) {}
protected void onAddInnerType(final ClassOrInterfaceTypeDetailsBuilder innerType) {}
/**
* Subclasses can perform their own actions upon a method builder being
* added. This implementation does nothing.
*
* @param methodBuilder the method being added; never <code>null</code>
*/
protected void onAddMethod(final MethodMetadataBuilder methodBuilder) {}
/**
* Removes the given methods from this builder
*
* @param methodsToRemove can be <code>null</code> for none
* @return true if this builder changed as a result
* @see List#removeAll(Collection)
*/
public boolean removeAll(final Collection<? extends MethodMetadataBuilder> methodsToRemove) {
if (methodsToRemove == null) {
return false;
}
return this.declaredMethods.removeAll(methodsToRemove);
}
/**
* Ensures that the type being built does not extend any of the given types
*
* @param superTypes the types to remove as supertypes
*/
public void removeExtendsTypes(final JavaType... superTypes) {
extendsTypes.removeAll(Arrays.asList(superTypes));
}
/**
* Sets the builders for the constructors that are to be declared
*
* @param declaredConstructors can be <code>null</code> for none
*/
public final void setDeclaredConstructors(
final Collection<? extends ConstructorMetadataBuilder> declaredConstructors) {
this.declaredConstructors.clear();
if (declaredConstructors != null) {
this.declaredConstructors.addAll(declaredConstructors);
}
}
/**
* Sets the builders for the fields to be declared by the type being built
*
* @param declaredFields the builders to set (can be <code>null</code> for
* none)
*/
public final void setDeclaredFields(
final Collection<? extends FieldMetadataBuilder> declaredFields) {
this.declaredFields.clear();
if (declaredFields != null) {
this.declaredFields.addAll(declaredFields);
}
}
/**
* Sets the builders for the initializers of the type being built
*
* @param declaredInitializers the builders to set; can be <code>null</code>
* for none
*/
public void setDeclaredInitializers(
final Collection<? extends InitializerMetadataBuilder> declaredInitializers) {
this.declaredInitializers.clear();
if (declaredInitializers != null) {
this.declaredInitializers.addAll(declaredInitializers);
}
}
/**
* Sets the builders for the inner types of the type being built
*
* @param declaredInnerTypes the builders to set; can be <code>null</code>
* for none
*/
public void setDeclaredInnerTypes(
final Collection<? extends ClassOrInterfaceTypeDetailsBuilder> declaredInnerTypes) {
this.declaredInnerTypes.clear();
if (declaredInnerTypes != null) {
this.declaredInnerTypes.addAll(declaredInnerTypes);
}
}
/**
* Sets the declared methods for this builder; equivalent to calling
* {@link #addMethod(MethodMetadataBuilder)} once for each item of the given
* {@link Iterable}.
*
* @param declaredMethods the methods to set; can be <code>null</code> for
* none, otherwise the {@link Iterable} is defensively copied
*/
public final void setDeclaredMethods(
final Iterable<? extends MethodMetadataBuilder> declaredMethods) {
this.declaredMethods.clear();
if (declaredMethods != null) {
for (final MethodMetadataBuilder methodBuilder : declaredMethods) {
addMethod(methodBuilder);
}
}
}
/**
* Sets the types that the built instance will extend
*
* @param extendsTypes can be <code>null</code> for none
*/
public final void setExtendsTypes(final Collection<? extends JavaType> extendsTypes) {
this.extendsTypes.clear();
if (extendsTypes != null) {
this.extendsTypes.addAll(extendsTypes);
}
}
/**
* Sets the types to be implemented by the type being built
*
* @param implementsTypes can be <code>null</code> for none
*/
public final void setImplementsTypes(final Collection<? extends JavaType> implementsTypes) {
this.implementsTypes.clear();
if (implementsTypes != null) {
this.implementsTypes.addAll(implementsTypes);
}
}
}