package org.springframework.roo.addon.web.mvc.controller.scaffold; import static org.springframework.roo.classpath.customdata.CustomDataKeys.PERSISTENT_TYPE; import static org.springframework.roo.model.RooJavaType.ROO_WEB_SCAFFOLD; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.SortedMap; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.Service; import org.osgi.service.component.ComponentContext; import org.springframework.roo.addon.web.mvc.controller.details.DateTimeFormatDetails; import org.springframework.roo.addon.web.mvc.controller.details.JavaTypeMetadataDetails; import org.springframework.roo.addon.web.mvc.controller.details.WebMetadataService; import org.springframework.roo.classpath.PhysicalTypeIdentifier; import org.springframework.roo.classpath.PhysicalTypeMetadata; import org.springframework.roo.classpath.customdata.tagkeys.MethodMetadataCustomDataKey; import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails; import org.springframework.roo.classpath.details.FieldMetadata; import org.springframework.roo.classpath.details.ItdTypeDetails; import org.springframework.roo.classpath.details.MemberFindingUtils; import org.springframework.roo.classpath.details.MemberHoldingTypeDetails; import org.springframework.roo.classpath.itd.AbstractMemberDiscoveringItdMetadataProvider; import org.springframework.roo.classpath.itd.ItdTypeDetailsProvidingMetadataItem; import org.springframework.roo.classpath.layers.MemberTypeAdditions; import org.springframework.roo.classpath.scanner.MemberDetails; import org.springframework.roo.model.JavaSymbolName; import org.springframework.roo.model.JavaType; import org.springframework.roo.project.LogicalPath; import org.springframework.roo.support.util.CollectionUtils; /** * Implementation of {@link WebScaffoldMetadataProvider}. * * @author Stefan Schmidt * @since 1.0 */ @Component(immediate = true) @Service public class WebScaffoldMetadataProviderImpl extends AbstractMemberDiscoveringItdMetadataProvider implements WebScaffoldMetadataProvider { @Reference private WebMetadataService webMetadataService; private final Map<JavaType, String> entityToWebScaffoldMidMap = new LinkedHashMap<JavaType, String>(); private final Map<String, JavaType> webScaffoldMidToEntityMap = new LinkedHashMap<String, JavaType>(); protected void activate(final ComponentContext context) { metadataDependencyRegistry.addNotificationListener(this); metadataDependencyRegistry.registerDependency( PhysicalTypeIdentifier.getMetadataIdentiferType(), getProvidesType()); addMetadataTrigger(ROO_WEB_SCAFFOLD); } @Override protected String createLocalIdentifier(final JavaType javaType, final LogicalPath path) { return WebScaffoldMetadata.createIdentifier(javaType, path); } protected void deactivate(final ComponentContext context) { metadataDependencyRegistry.removeNotificationListener(this); metadataDependencyRegistry.deregisterDependency( PhysicalTypeIdentifier.getMetadataIdentiferType(), getProvidesType()); removeMetadataTrigger(ROO_WEB_SCAFFOLD); } @Override protected String getGovernorPhysicalTypeIdentifier( final String metadataIdentificationString) { final JavaType javaType = WebScaffoldMetadata .getJavaType(metadataIdentificationString); final LogicalPath path = WebScaffoldMetadata .getPath(metadataIdentificationString); return PhysicalTypeIdentifier.createIdentifier(javaType, path); } public String getItdUniquenessFilenameSuffix() { return "Controller"; } @Override protected String getLocalMidToRequest(final ItdTypeDetails itdTypeDetails) { final JavaType governor = itdTypeDetails.getName(); // If the governor is a form backing object, refresh its local metadata final String localMid = entityToWebScaffoldMidMap.get(governor); if (localMid != null) { return localMid; } // If the governor is a layer component that manages a form backing // object, refresh that object's local metadata return getWebScaffoldMidIfLayerComponent(governor); } @Override protected ItdTypeDetailsProvidingMetadataItem getMetadata( final String metadataIdentificationString, final JavaType aspectName, final PhysicalTypeMetadata governorPhysicalType, final String itdFilename) { // We need to parse the annotation, which we expect to be present final WebScaffoldAnnotationValues annotationValues = new WebScaffoldAnnotationValues( governorPhysicalType); final JavaType formBackingType = annotationValues .getFormBackingObject(); if (!annotationValues.isAnnotationFound() || formBackingType == null) { return null; } final MemberDetails formBackingObjectMemberDetails = getMemberDetails(formBackingType); if (formBackingObjectMemberDetails == null) { return null; } final MemberHoldingTypeDetails formBackingMemberHoldingTypeDetails = MemberFindingUtils .getMostConcreteMemberHoldingTypeDetailsWithTag( formBackingObjectMemberDetails, PERSISTENT_TYPE); if (formBackingMemberHoldingTypeDetails == null) { return null; } final Map<MethodMetadataCustomDataKey, MemberTypeAdditions> crudAdditions = webMetadataService .getCrudAdditions(formBackingType, metadataIdentificationString); if (CollectionUtils.isEmpty(crudAdditions)) { return null; } // We need to be informed if our dependent metadata changes metadataDependencyRegistry.registerDependency( formBackingMemberHoldingTypeDetails.getDeclaredByMetadataId(), metadataIdentificationString); // Remember that this entity JavaType matches up with this metadata // identification string // Start by clearing any previous association final JavaType oldEntity = webScaffoldMidToEntityMap .get(metadataIdentificationString); if (oldEntity != null) { entityToWebScaffoldMidMap.remove(oldEntity); } entityToWebScaffoldMidMap.put(formBackingType, metadataIdentificationString); webScaffoldMidToEntityMap.put(metadataIdentificationString, formBackingType); final FieldMetadata idField = webMetadataService .getIdentifierField(formBackingType); final SortedMap<JavaType, JavaTypeMetadataDetails> relatedApplicationTypeMetadata = webMetadataService .getRelatedApplicationTypeMetadata(formBackingType, formBackingObjectMemberDetails, metadataIdentificationString); final List<JavaTypeMetadataDetails> dependentApplicationTypeMetadata = webMetadataService .getDependentApplicationTypeMetadata(formBackingType, formBackingObjectMemberDetails, metadataIdentificationString); final Map<JavaSymbolName, DateTimeFormatDetails> datePatterns = webMetadataService .getDatePatterns(formBackingType, formBackingObjectMemberDetails, metadataIdentificationString); final Collection<JavaType> editableFieldTypes = formBackingObjectMemberDetails .getPersistentFieldTypes(formBackingType, persistenceMemberLocator); return new WebScaffoldMetadata(metadataIdentificationString, aspectName, governorPhysicalType, annotationValues, idField, relatedApplicationTypeMetadata, dependentApplicationTypeMetadata, datePatterns, crudAdditions, editableFieldTypes); } public String getProvidesType() { return WebScaffoldMetadata.getMetadataIdentiferType(); } /** * If the given governor is a layer component (service, repository, etc.) * that manages an entity for which we maintain web scaffold metadata, * returns the ID of that metadata, otherwise returns <code>null</code>. * TODO doesn't handle the case where the governor is a component that * manages multiple entities, as it always returns the MID for the first * entity found (in annotation order) for which we provide web metadata. We * would need to enhance * {@link AbstractMemberDiscoveringItdMetadataProvider#getLocalMidToRequest} * to return a list of MIDs, rather than only one. * * @param governor the governor to check (required) * @return see above */ private String getWebScaffoldMidIfLayerComponent(final JavaType governor) { final ClassOrInterfaceTypeDetails governorTypeDetails = typeLocationService .getTypeDetails(governor); if (governorTypeDetails != null) { for (final JavaType type : governorTypeDetails.getLayerEntities()) { final String localMid = entityToWebScaffoldMidMap.get(type); if (localMid != null) { /* * The ITD's governor is a layer component that manages an * entity for which we maintain web scaffold metadata => * refresh that MD in case a layer has appeared or gone * away. */ return localMid; } } } return null; } }