package org.springframework.roo.classpath.details;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.Validate;
import org.springframework.roo.classpath.details.annotations.AnnotationAttributeValue;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadata;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder;
import org.springframework.roo.model.Builder;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
/**
* Assists in the creation of a {@link Builder} for types that eventually
* implement {@link IdentifiableAnnotatedJavaStructure}.
*
* @author Ben Alex
* @since 1.1
*/
public abstract class AbstractIdentifiableAnnotatedJavaStructureBuilder<T extends IdentifiableAnnotatedJavaStructure>
extends AbstractIdentifiableJavaStructureBuilder<T> {
private List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
protected AbstractIdentifiableAnnotatedJavaStructureBuilder(
final IdentifiableAnnotatedJavaStructure existing) {
super(existing);
init(existing);
}
protected AbstractIdentifiableAnnotatedJavaStructureBuilder(final String declaredbyMetadataId) {
super(declaredbyMetadataId);
}
protected AbstractIdentifiableAnnotatedJavaStructureBuilder(final String declaredbyMetadataId,
final IdentifiableAnnotatedJavaStructure existing) {
super(declaredbyMetadataId, existing);
init(existing);
}
public final boolean addAnnotation(final AnnotationMetadata annotationMetadata) {
if (annotationMetadata == null) {
return false;
}
return addAnnotation(new AnnotationMetadataBuilder(annotationMetadata));
}
public final boolean addAnnotation(final AnnotationMetadataBuilder annotationBuilder) {
if (annotationBuilder == null) {
return false;
}
onAddAnnotation(annotationBuilder);
return annotations.add(annotationBuilder);
}
public final List<AnnotationMetadata> buildAnnotations() {
final List<AnnotationMetadata> result = new ArrayList<AnnotationMetadata>();
for (final AnnotationMetadataBuilder annotationBuilder : annotations) {
result.add(annotationBuilder.build());
}
return result;
}
public final List<AnnotationMetadataBuilder> getAnnotations() {
return annotations;
}
/**
* Locates the specified type-level annotation.
*
* @param type to locate (can be <code>null</code>)
* @return the annotation, or null if not found
* @since 1.2.0
*/
public AnnotationMetadataBuilder getDeclaredTypeAnnotation(final JavaType type) {
for (final AnnotationMetadataBuilder annotationBuilder : getAnnotations()) {
if (annotationBuilder.getAnnotationType().equals(type)) {
return annotationBuilder;
}
}
return null;
}
private void init(final IdentifiableAnnotatedJavaStructure existing) {
for (final AnnotationMetadata element : existing.getAnnotations()) {
this.annotations.add(new AnnotationMetadataBuilder(element));
}
}
protected void onAddAnnotation(final AnnotationMetadataBuilder annotationMetadata) {}
public void removeAnnotation(final JavaType annotationType) {
for (final AnnotationMetadataBuilder annotationMetadataBuilder : annotations) {
if (annotationMetadataBuilder.getAnnotationType().equals(annotationType)) {
annotations.remove(annotationMetadataBuilder);
break;
}
}
}
public final void setAnnotations(final Collection<AnnotationMetadata> annotations) {
final List<AnnotationMetadataBuilder> annotationBuilders =
new ArrayList<AnnotationMetadataBuilder>();
for (final AnnotationMetadata annotationMetadata : annotations) {
annotationBuilders.add(new AnnotationMetadataBuilder(annotationMetadata));
}
setAnnotations(annotationBuilders);
}
public final void setAnnotations(final List<AnnotationMetadataBuilder> annotations) {
this.annotations = annotations;
}
public boolean updateTypeAnnotation(final AnnotationMetadata annotation) {
return updateTypeAnnotation(annotation, null);
}
public boolean updateTypeAnnotation(final AnnotationMetadata annotation,
final Set<JavaSymbolName> attributesToDeleteIfPresent) {
boolean hasChanged = false;
// We are going to build a replacement AnnotationMetadata.
// This variable tracks the new attribute values the replacement will
// hold.
final Map<JavaSymbolName, AnnotationAttributeValue<?>> replacementAttributeValues =
new LinkedHashMap<JavaSymbolName, AnnotationAttributeValue<?>>();
final AnnotationMetadataBuilder existingBuilder =
getDeclaredTypeAnnotation(annotation.getAnnotationType());
if (existingBuilder == null) {
// Not already present, so just go and add it
for (final JavaSymbolName incomingAttributeName : annotation.getAttributeNames()) {
// Do not copy incoming attributes which exist in the
// attributesToDeleteIfPresent Set
if (attributesToDeleteIfPresent == null
|| !attributesToDeleteIfPresent.contains(incomingAttributeName)) {
final AnnotationAttributeValue<?> incomingValue =
annotation.getAttribute(incomingAttributeName);
replacementAttributeValues.put(incomingAttributeName, incomingValue);
}
}
final AnnotationMetadataBuilder replacement =
new AnnotationMetadataBuilder(annotation.getAnnotationType(),
new ArrayList<AnnotationAttributeValue<?>>(replacementAttributeValues.values()));
addAnnotation(replacement);
return true;
}
final AnnotationMetadata existing = existingBuilder.build();
// Copy the existing attributes into the new attributes
for (final JavaSymbolName existingAttributeName : existing.getAttributeNames()) {
if (attributesToDeleteIfPresent != null
&& attributesToDeleteIfPresent.contains(existingAttributeName)) {
hasChanged = true;
} else {
final AnnotationAttributeValue<?> existingValue =
existing.getAttribute(existingAttributeName);
replacementAttributeValues.put(existingAttributeName, existingValue);
}
}
// Now we ensure every incoming attribute replaces the existing
for (final JavaSymbolName incomingAttributeName : annotation.getAttributeNames()) {
final AnnotationAttributeValue<?> incomingValue =
annotation.getAttribute(incomingAttributeName);
// Add this attribute to the end of the list if the attribute is not
// already present
if (replacementAttributeValues.keySet().contains(incomingAttributeName)) {
// There was already an attribute. Need to determine if this new
// attribute value is materially different
final AnnotationAttributeValue<?> existingValue =
replacementAttributeValues.get(incomingAttributeName);
Validate.notNull(existingValue, "Existing value should have been provided by earlier loop");
if (!existingValue.equals(incomingValue)) {
replacementAttributeValues.put(incomingAttributeName, incomingValue);
hasChanged = true;
}
} else if (attributesToDeleteIfPresent == null
|| !attributesToDeleteIfPresent.contains(incomingAttributeName)) {
// This is a new attribute that does not already exist, so add
// it to the end of the replacement attributes
replacementAttributeValues.put(incomingAttributeName, incomingValue);
hasChanged = true;
}
}
// Were there any material changes?
if (!hasChanged) {
return false;
}
// Make a new AnnotationMetadata representing the replacement
final AnnotationMetadataBuilder replacement =
new AnnotationMetadataBuilder(annotation.getAnnotationType(),
new ArrayList<AnnotationAttributeValue<?>>(replacementAttributeValues.values()));
annotations.remove(existingBuilder);
addAnnotation(replacement);
return true;
}
public boolean updateTypeAnnotation(final AnnotationMetadataBuilder annotationBuilder) {
return updateTypeAnnotation(annotationBuilder.build());
}
}