package org.springframework.roo.classpath.details; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; import org.springframework.roo.classpath.PhysicalTypeCategory; import org.springframework.roo.classpath.details.annotations.AnnotationMetadata; import org.springframework.roo.classpath.itd.AbstractItdMetadataProvider; import org.springframework.roo.classpath.itd.ItdSourceFileComposer; import org.springframework.roo.model.CustomData; import org.springframework.roo.model.CustomDataAccessor; import org.springframework.roo.model.JavaType; import org.springframework.roo.support.util.CollectionUtils; /** * Default representation of an {@link ItdTypeDetails}. * <p> * Provides a basic {@link #hashCode()} that is used for detecting significant * changes in {@link AbstractItdMetadataProvider} and avoiding downstream * notifications accordingly. * * @author Ben Alex * @author Stefan Schmidt * @author Juan Carlos GarcĂ­a * @since 1.0 */ public class DefaultItdTypeDetails extends AbstractMemberHoldingTypeDetails implements ItdTypeDetails { static final PhysicalTypeCategory PHYSICAL_TYPE_CATEGORY = PhysicalTypeCategory.ITD; private final JavaType aspect; private final List<ConstructorMetadata> declaredConstructors = new ArrayList<ConstructorMetadata>(); private final List<FieldMetadata> declaredFields = new ArrayList<FieldMetadata>(); private final List<MethodMetadata> declaredMethods = new ArrayList<MethodMetadata>(); private final List<JavaType> extendsTypes = new ArrayList<JavaType>(); private final List<DeclaredFieldAnnotationDetails> fieldAnnotations = new ArrayList<DeclaredFieldAnnotationDetails>(); private final ClassOrInterfaceTypeDetails governor; private final List<JavaType> implementsTypes = new ArrayList<JavaType>(); private final List<ClassOrInterfaceTypeDetails> innerTypes = new ArrayList<ClassOrInterfaceTypeDetails>(); private final List<DeclaredMethodAnnotationDetails> methodAnnotations = new ArrayList<DeclaredMethodAnnotationDetails>(); private final boolean privilegedAspect; private final Set<JavaType> declarePrecedence = new LinkedHashSet<JavaType>(); private final SortedMap<JavaType, Boolean> registeredImports = new TreeMap<JavaType, Boolean>(); private Integer hashCode; /** * Constructor (package protected to enforce the use of the corresponding * builder) * * @param customData * @param declaredByMetadataId * @param modifier * @param governor the type to receive the introductions (required) * @param aspect (required) * @param privilegedAspect * @param registeredImports can be <code>null</code> * @param declaredConstructors can be <code>null</code> * @param declaredFields can be <code>null</code> * @param declaredMethods can be <code>null</code> * @param extendsTypes can be <code>null</code> * @param implementsTypes can be <code>null</code> * @param typeAnnotations can be <code>null</code> * @param fieldAnnotations can be <code>null</code> * @param methodAnnotations can be <code>null</code> * @param innerTypes can be <code>null</code> * @param declarePrecedence can be <code>null</code> */ DefaultItdTypeDetails(final CustomData customData, final String declaredByMetadataId, final int modifier, final ClassOrInterfaceTypeDetails governor, final JavaType aspect, final boolean privilegedAspect, final SortedMap<JavaType, Boolean> registeredImports, final Collection<ConstructorMetadata> declaredConstructors, final Collection<FieldMetadata> declaredFields, final Collection<MethodMetadata> declaredMethods, final Collection<? extends JavaType> extendsTypes, final Collection<? extends JavaType> implementsTypes, final Collection<AnnotationMetadata> typeAnnotations, final Collection<? extends DeclaredFieldAnnotationDetails> fieldAnnotations, final Collection<? extends DeclaredMethodAnnotationDetails> methodAnnotations, final Collection<ClassOrInterfaceTypeDetails> innerTypes, final Collection<? extends JavaType> declarePrecedence) { super(customData, declaredByMetadataId, modifier, typeAnnotations); Validate.notNull(aspect, "Aspect required"); Validate.notNull(governor, "Governor (to receive the introductions) required"); this.aspect = aspect; this.governor = governor; this.privilegedAspect = privilegedAspect; this.registeredImports.putAll(registeredImports); CollectionUtils.populate(this.declaredConstructors, declaredConstructors); CollectionUtils.populate(this.declaredFields, declaredFields); CollectionUtils.populate(this.declaredMethods, declaredMethods); CollectionUtils.populate(this.extendsTypes, extendsTypes); CollectionUtils.populate(this.fieldAnnotations, fieldAnnotations); CollectionUtils.populate(this.implementsTypes, implementsTypes); CollectionUtils.populate(this.innerTypes, innerTypes); CollectionUtils.populate(this.methodAnnotations, methodAnnotations); CollectionUtils.populate(this.declarePrecedence, declarePrecedence); } public boolean extendsType(final JavaType type) { return extendsTypes.contains(type); } public JavaType getAspect() { return aspect; } public List<? extends ConstructorMetadata> getDeclaredConstructors() { return Collections.unmodifiableList(declaredConstructors); } public List<FieldMetadata> getDeclaredFields() { return Collections.unmodifiableList(declaredFields); } public List<InitializerMetadata> getDeclaredInitializers() { return Collections.emptyList(); } public List<ClassOrInterfaceTypeDetails> getDeclaredInnerTypes() { return Collections.emptyList(); } public List<MethodMetadata> getDeclaredMethods() { return Collections.unmodifiableList(declaredMethods); } public List<String> getDynamicFinderNames() { return Collections.emptyList(); } public List<JavaType> getExtendsTypes() { return Collections.unmodifiableList(extendsTypes); } public List<DeclaredFieldAnnotationDetails> getFieldAnnotations() { return Collections.unmodifiableList(fieldAnnotations); } public ClassOrInterfaceTypeDetails getGovernor() { return governor; } public List<JavaType> getImplementsTypes() { return Collections.unmodifiableList(implementsTypes); } public List<ClassOrInterfaceTypeDetails> getInnerTypes() { return Collections.unmodifiableList(innerTypes); } public List<DeclaredMethodAnnotationDetails> getMethodAnnotations() { return Collections.unmodifiableList(methodAnnotations); } public JavaType getName() { return getType(); } public PhysicalTypeCategory getPhysicalTypeCategory() { return PHYSICAL_TYPE_CATEGORY; } public SortedMap<JavaType, Boolean> getRegisteredImports() { return Collections.unmodifiableSortedMap(registeredImports); } public JavaType getType() { return governor.getType(); } @Override public int hashCode() { if (hashCode == null) { int hash = aspect.hashCode() * governor.getName().hashCode() * governor.getModifier() * governor.getCustomData().hashCode() * PHYSICAL_TYPE_CATEGORY.hashCode() * (privilegedAspect ? 2 : 3); hash *= includeCustomDataHash(declaredConstructors); hash *= includeCustomDataHash(declaredFields); hash *= includeCustomDataHash(declaredMethods); hash *= new ItdSourceFileComposer(this).getOutput().hashCode(); this.hashCode = hash; } return hashCode; } public boolean implementsAny(final JavaType... types) { for (final JavaType type : types) { if (implementsTypes.contains(type)) { return true; } } return false; } private int includeCustomDataHash(final Collection<? extends CustomDataAccessor> coll) { int result = 1; for (final CustomDataAccessor accessor : coll) { result *= accessor.getCustomData().hashCode(); } return result; } public boolean isPrivilegedAspect() { return privilegedAspect; } @Override public Set<JavaType> getDeclarePrecedence() { return Collections.unmodifiableSet(declarePrecedence); } @Override public String toString() { final ToStringBuilder builder = new ToStringBuilder(this); builder.append("declaredByMetadataId", getDeclaredByMetadataId()); builder.append("modifier", getModifier()); builder.append("name", governor); builder.append("aspect", aspect); builder.append("physicalTypeCategory", PHYSICAL_TYPE_CATEGORY); builder.append("privilegedAspect", privilegedAspect); builder.append("registeredImports", registeredImports); builder.append("declaredConstructors", declaredConstructors); builder.append("declaredFields", declaredFields); builder.append("declaredMethods", declaredMethods); builder.append("extendsTypes", extendsTypes); builder.append("fieldAnnotations", fieldAnnotations); builder.append("methodAnnotations", methodAnnotations); builder.append("typeAnnotations", getAnnotations()); builder.append("innerTypes", innerTypes); builder.append("customData", getCustomData()); return builder.toString(); } @Override public Set<ImportMetadata> getImports() { Set<ImportMetadata> imports = new HashSet<ImportMetadata>(); for (final Entry<JavaType, Boolean> entry : registeredImports.entrySet()) { JavaType registeredImport = entry.getKey(); boolean asStatic = entry.getValue(); imports.add(new ImportMetadataBuilder(getDeclaredByMetadataId(), Modifier.PUBLIC, registeredImport.getPackage(), registeredImport, asStatic, false).build()); } return imports; } }