package org.springframework.roo.addon.javabean.addon;
import static org.springframework.roo.model.RooJavaType.ROO_EQUALS;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.osgi.service.component.ComponentContext;
import org.springframework.roo.classpath.PhysicalTypeIdentifier;
import org.springframework.roo.classpath.PhysicalTypeMetadata;
import org.springframework.roo.classpath.details.FieldMetadata;
import org.springframework.roo.classpath.details.ItdTypeDetails;
import org.springframework.roo.classpath.itd.AbstractMemberDiscoveringItdMetadataProvider;
import org.springframework.roo.classpath.itd.ItdTypeDetailsProvidingMetadataItem;
import org.springframework.roo.classpath.scanner.MemberDetails;
import org.springframework.roo.metadata.MetadataDependencyRegistry;
import org.springframework.roo.metadata.internal.MetadataDependencyRegistryTracker;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.model.RooJavaType;
import org.springframework.roo.project.LogicalPath;
import org.springframework.roo.support.util.CollectionUtils;
/**
* Implementation of {@link EqualsMetadataProvider}.
*
* @author Alan Stewart
* @author Enrique Ruiz at DISID Corporation S.L.
* @since 1.2.0
*/
@Component
@Service
public class EqualsMetadataProviderImpl extends AbstractMemberDiscoveringItdMetadataProvider
implements EqualsMetadataProvider {
protected MetadataDependencyRegistryTracker registryTracker = null;
/**
* This service is being activated so setup it:
* <ul>
* <li>Create and open the {@link MetadataDependencyRegistryTracker}</li>
* <li>Registers {@link RooJavaType#ROO_EQUALS} as additional JavaType
* that will trigger metadata registration.</li>
* </ul>
*/
@Override
protected void activate(final ComponentContext cContext) {
super.activate(cContext);
this.registryTracker =
new MetadataDependencyRegistryTracker(cContext.getBundleContext(), this,
PhysicalTypeIdentifier.getMetadataIdentiferType(), getProvidesType());
this.registryTracker.open();
addMetadataTrigger(ROO_EQUALS);
}
/**
* This service is being deactivated so unregister upstream-downstream
* dependencies, triggers, matchers and listeners.
*
* @param context
*/
protected void deactivate(final ComponentContext context) {
MetadataDependencyRegistry registry = this.registryTracker.getService();
registry.removeNotificationListener(this);
registry.deregisterDependency(PhysicalTypeIdentifier.getMetadataIdentiferType(),
getProvidesType());
this.registryTracker.close();
removeMetadataTrigger(ROO_EQUALS);
}
@Override
protected String createLocalIdentifier(final JavaType javaType, final LogicalPath path) {
return EqualsMetadata.createIdentifier(javaType, path);
}
@Override
protected String getGovernorPhysicalTypeIdentifier(final String metadataIdentificationString) {
final JavaType javaType = EqualsMetadata.getJavaType(metadataIdentificationString);
final LogicalPath path = EqualsMetadata.getPath(metadataIdentificationString);
return PhysicalTypeIdentifier.createIdentifier(javaType, path);
}
public String getItdUniquenessFilenameSuffix() {
return "Equals";
}
@Override
protected String getLocalMidToRequest(final ItdTypeDetails itdTypeDetails) {
return getLocalMid(itdTypeDetails);
}
@Override
protected ItdTypeDetailsProvidingMetadataItem getMetadata(
final String metadataIdentificationString, final JavaType aspectName,
final PhysicalTypeMetadata governorPhysicalTypeMetadata, final String itdFilename) {
final EqualsAnnotationValues annotationValues =
new EqualsAnnotationValues(governorPhysicalTypeMetadata);
if (!annotationValues.isAnnotationFound()) {
return null;
}
final MemberDetails memberDetails = getMemberDetails(governorPhysicalTypeMetadata);
if (memberDetails == null) {
return null;
}
final JavaType javaType = governorPhysicalTypeMetadata.getMemberHoldingTypeDetails().getName();
final List<FieldMetadata> equalityFields =
locateFields(javaType, annotationValues.getExcludeFields(), memberDetails,
metadataIdentificationString);
List<FieldMetadata> identifierFields =
getPersistenceMemberLocator().getIdentifierFields(governorPhysicalTypeMetadata.getType());
FieldMetadata identifierField = null;
if (!identifierFields.isEmpty()) {
identifierField = identifierFields.get(0);
}
return new EqualsMetadata(metadataIdentificationString, aspectName,
governorPhysicalTypeMetadata, annotationValues, equalityFields, identifierField);
}
public String getProvidesType() {
return EqualsMetadata.getMetadataIdentiferType();
}
/**
* Locates class fields needed to generate `equals` and `hashCode` methods.
* Also fills id field info if class is an entity.
*
* @param javaType the {@link JavaType} of the governor class.
* @param excludeFields the {@link String[]} with field names to exclude.
* @param memberDetails the {@link MemberDetails} of the class.
* @param metadataIdentificationString
* @return a {@link List<FieldMetadata>}
*/
private List<FieldMetadata> locateFields(final JavaType javaType, final String[] excludeFields,
final MemberDetails memberDetails, final String metadataIdentificationString) {
final SortedSet<FieldMetadata> locatedFields =
new TreeSet<FieldMetadata>(new Comparator<FieldMetadata>() {
public int compare(final FieldMetadata l, final FieldMetadata r) {
return l.getFieldName().compareTo(r.getFieldName());
}
});
final List<?> excludeFieldsList = CollectionUtils.arrayToList(excludeFields);
final FieldMetadata versionField = getPersistenceMemberLocator().getVersionField(javaType);
for (final FieldMetadata field : memberDetails.getFields()) {
if (excludeFieldsList.contains(field.getFieldName().getSymbolName())) {
continue;
}
if (Modifier.isStatic(field.getModifier()) || Modifier.isTransient(field.getModifier())
|| field.getFieldType().isCommonCollectionType() || field.getFieldType().isArray()) {
continue;
}
if (versionField != null && field.getFieldName().equals(versionField.getFieldName())) {
continue;
}
locatedFields.add(field);
getMetadataDependencyRegistry().registerDependency(field.getDeclaredByMetadataId(),
metadataIdentificationString);
}
return new ArrayList<FieldMetadata>(locatedFields);
}
}