Package org.springframework.roo.addon.dod

Source Code of org.springframework.roo.addon.dod.DataOnDemandMetadataProviderImpl

package org.springframework.roo.addon.dod;

import static org.springframework.roo.classpath.customdata.CustomDataKeys.EMBEDDED_FIELD;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.EMBEDDED_ID_FIELD;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.FIND_ENTRIES_METHOD;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.FIND_METHOD;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.IDENTIFIER_ACCESSOR_METHOD;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.IDENTIFIER_FIELD;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.MANY_TO_MANY_FIELD;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.MANY_TO_ONE_FIELD;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.ONE_TO_MANY_FIELD;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.ONE_TO_ONE_FIELD;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.PERSISTENT_TYPE;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.TRANSIENT_FIELD;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.VERSION_FIELD;
import static org.springframework.roo.model.RooJavaType.ROO_DATA_ON_DEMAND;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

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.configurable.ConfigurableMetadataProvider;
import org.springframework.roo.classpath.PhysicalTypeIdentifier;
import org.springframework.roo.classpath.PhysicalTypeMetadata;
import org.springframework.roo.classpath.customdata.CustomDataKeys;
import org.springframework.roo.classpath.details.BeanInfoUtils;
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.details.MethodMetadata;
import org.springframework.roo.classpath.details.annotations.AnnotationAttributeValue;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadata;
import org.springframework.roo.classpath.itd.AbstractMemberDiscoveringItdMetadataProvider;
import org.springframework.roo.classpath.itd.ItdTypeDetailsProvidingMetadataItem;
import org.springframework.roo.classpath.layers.LayerService;
import org.springframework.roo.classpath.layers.LayerType;
import org.springframework.roo.classpath.layers.MemberTypeAdditions;
import org.springframework.roo.classpath.layers.MethodParameter;
import org.springframework.roo.classpath.scanner.MemberDetails;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.project.LogicalPath;
import org.springframework.roo.shell.NaturalOrderComparator;

/**
* Implementation of {@link DataOnDemandMetadataProvider}.
*
* @author Ben Alex
* @author Greg Turnquist
* @author Andrew Swan
* @since 1.0
*/
@Component(immediate = true)
@Service
public class DataOnDemandMetadataProviderImpl extends
        AbstractMemberDiscoveringItdMetadataProvider implements
        DataOnDemandMetadataProvider {

    private static final String FLUSH_METHOD = CustomDataKeys.FLUSH_METHOD
            .name();
    private static final String PERSIST_METHOD = CustomDataKeys.PERSIST_METHOD
            .name();

    @Reference private ConfigurableMetadataProvider configurableMetadataProvider;
    @Reference private LayerService layerService;
    private final Map<String, JavaType> dodMidToEntityMap = new LinkedHashMap<String, JavaType>();
    private final Map<JavaType, String> entityToDodMidMap = new LinkedHashMap<JavaType, String>();

    protected void activate(final ComponentContext context) {
        metadataDependencyRegistry.addNotificationListener(this);
        metadataDependencyRegistry.registerDependency(
                PhysicalTypeIdentifier.getMetadataIdentiferType(),
                getProvidesType());
        // DOD classes are @Configurable because they may need DI of other DOD
        // classes that provide M:1 relationships
        configurableMetadataProvider.addMetadataTrigger(ROO_DATA_ON_DEMAND);
        addMetadataTrigger(ROO_DATA_ON_DEMAND);
    }

    @Override
    protected String createLocalIdentifier(final JavaType javaType,
            final LogicalPath path) {
        return DataOnDemandMetadata.createIdentifier(javaType, path);
    }

    protected void deactivate(final ComponentContext context) {
        metadataDependencyRegistry.removeNotificationListener(this);
        metadataDependencyRegistry.deregisterDependency(
                PhysicalTypeIdentifier.getMetadataIdentiferType(),
                getProvidesType());
        configurableMetadataProvider.removeMetadataTrigger(ROO_DATA_ON_DEMAND);
        removeMetadataTrigger(ROO_DATA_ON_DEMAND);
    }

    private String getDataOnDemandMetadataId(final JavaType javaType,
            final Iterable<ClassOrInterfaceTypeDetails> dataOnDemandTypes) {
        for (final ClassOrInterfaceTypeDetails cid : dataOnDemandTypes) {
            final AnnotationMetadata dodAnnotation = cid
                    .getAnnotation(ROO_DATA_ON_DEMAND);
            final AnnotationAttributeValue<JavaType> entityAttribute = dodAnnotation
                    .getAttribute("entity");
            if (entityAttribute != null
                    && entityAttribute.getValue().equals(javaType)) {
                // Found the DoD type for the given field's type
                return DataOnDemandMetadata.createIdentifier(cid.getName(),
                        PhysicalTypeIdentifier.getPath(cid
                                .getDeclaredByMetadataId()));
            }
        }
        return null;
    }

    private List<EmbeddedHolder> getEmbeddedHolders(
            final MemberDetails memberDetails,
            final String metadataIdentificationString) {
        final List<EmbeddedHolder> embeddedHolders = new ArrayList<EmbeddedHolder>();

        final List<FieldMetadata> embeddedFields = MemberFindingUtils
                .getFieldsWithTag(memberDetails, EMBEDDED_FIELD);
        if (embeddedFields.isEmpty()) {
            return embeddedHolders;
        }

        for (final FieldMetadata embeddedField : embeddedFields) {
            final MemberDetails embeddedMemberDetails = getMemberDetails(embeddedField
                    .getFieldType());
            if (embeddedMemberDetails == null) {
                continue;
            }

            final List<FieldMetadata> fields = new ArrayList<FieldMetadata>();

            for (final FieldMetadata field : embeddedMemberDetails.getFields()) {
                if (!(Modifier.isStatic(field.getModifier())
                        || Modifier.isFinal(field.getModifier()) || Modifier
                            .isTransient(field.getModifier()))) {
                    metadataDependencyRegistry.registerDependency(
                            field.getDeclaredByMetadataId(),
                            metadataIdentificationString);
                    fields.add(field);
                }
            }
            embeddedHolders.add(new EmbeddedHolder(embeddedField, fields));
        }

        return embeddedHolders;
    }

    private EmbeddedIdHolder getEmbeddedIdHolder(
            final MemberDetails memberDetails,
            final String metadataIdentificationString) {
        final List<FieldMetadata> idFields = new ArrayList<FieldMetadata>();
        final List<FieldMetadata> fields = MemberFindingUtils.getFieldsWithTag(
                memberDetails, EMBEDDED_ID_FIELD);
        if (fields.isEmpty()) {
            return null;
        }
        final FieldMetadata embeddedIdField = fields.get(0);
        final MemberDetails identifierMemberDetails = getMemberDetails(embeddedIdField
                .getFieldType());
        if (identifierMemberDetails == null) {
            return null;
        }

        for (final FieldMetadata field : identifierMemberDetails.getFields()) {
            if (!(Modifier.isStatic(field.getModifier())
                    || Modifier.isFinal(field.getModifier()) || Modifier
                        .isTransient(field.getModifier()))) {
                metadataDependencyRegistry.registerDependency(
                        field.getDeclaredByMetadataId(),
                        metadataIdentificationString);
                idFields.add(field);
            }
        }

        return new EmbeddedIdHolder(embeddedIdField, idFields);
    }

    @Override
    protected String getGovernorPhysicalTypeIdentifier(
            final String metadataIdentificationString) {
        final JavaType javaType = DataOnDemandMetadata
                .getJavaType(metadataIdentificationString);
        final LogicalPath path = DataOnDemandMetadata
                .getPath(metadataIdentificationString);
        return PhysicalTypeIdentifier.createIdentifier(javaType, path);
    }

    public String getItdUniquenessFilenameSuffix() {
        return "DataOnDemand";
    }

    @Override
    protected String getLocalMidToRequest(final ItdTypeDetails itdTypeDetails) {
        // Determine the governor for this ITD, and whether any DOD metadata is
        // even hoping to hear about changes to that JavaType and its ITDs
        final JavaType governor = itdTypeDetails.getName();

        for (final JavaType type : itdTypeDetails.getGovernor()
                .getLayerEntities()) {
            final String localMidType = entityToDodMidMap.get(type);
            if (localMidType != null) {
                return localMidType;
            }
        }

        final String localMid = entityToDodMidMap.get(governor);
        if (localMid == null) {
            // No DOD is interested in this JavaType, so let's move on
            return null;
        }

        // We have some DOD metadata, so let's check if we care if any methods
        // match our requirements
        for (final MethodMetadata method : itdTypeDetails.getDeclaredMethods()) {
            if (BeanInfoUtils.isMutatorMethod(method)) {
                // A DOD cares about the JavaType, and an ITD offers a method
                // likely of interest, so let's formally trigger it to run.
                // Note that it will re-scan and discover this ITD, and register
                // a direct dependency on it for the future.
                return localMid;
            }
        }

        return null;
    }

    private Map<FieldMetadata, DataOnDemandMetadata> getLocatedFields(
            final MemberDetails memberDetails, final String dodMetadataId) {
        final Map<FieldMetadata, DataOnDemandMetadata> locatedFields = new LinkedHashMap<FieldMetadata, DataOnDemandMetadata>();
        final Iterable<ClassOrInterfaceTypeDetails> dataOnDemandTypes = typeLocationService
                .findClassesOrInterfaceDetailsWithAnnotation(ROO_DATA_ON_DEMAND);

        final List<MethodMetadata> mutatorMethods = memberDetails.getMethods();
        // To avoid unnecessary rewriting of the DoD ITD we sort the mutators by
        // method name to provide a consistent ordering
        Collections.sort(mutatorMethods,
                new NaturalOrderComparator<MethodMetadata>() {
                    @Override
                    protected String stringify(final MethodMetadata object) {
                        return object.getMethodName().getSymbolName();
                    }
                });

        for (final MethodMetadata method : mutatorMethods) {
            if (!BeanInfoUtils.isMutatorMethod(method)) {
                continue;
            }

            final FieldMetadata field = BeanInfoUtils
                    .getFieldForJavaBeanMethod(memberDetails, method);
            if (field == null) {
                continue;
            }

            // Track any changes to the mutator method (eg it goes away)
            metadataDependencyRegistry.registerDependency(
                    method.getDeclaredByMetadataId(), dodMetadataId);

            final Set<Object> fieldCustomDataKeys = field.getCustomData()
                    .keySet();

            // Never include id or version fields (they shouldn't normally have
            // a mutator anyway, but the user might have added one), or embedded
            // types
            if (fieldCustomDataKeys.contains(IDENTIFIER_FIELD)
                    || fieldCustomDataKeys.contains(EMBEDDED_ID_FIELD)
                    || fieldCustomDataKeys.contains(EMBEDDED_FIELD)
                    || fieldCustomDataKeys.contains(VERSION_FIELD)) {
                continue;
            }

            // Never include persistence transient fields
            if (fieldCustomDataKeys.contains(TRANSIENT_FIELD)) {
                continue;
            }

            // Never include any sort of collection; user has to make such
            // entities by hand
            if (field.getFieldType().isCommonCollectionType()
                    || fieldCustomDataKeys.contains(ONE_TO_MANY_FIELD)
                    || fieldCustomDataKeys.contains(MANY_TO_MANY_FIELD)) {
                continue;
            }

            // Look up collaborating metadata
            final DataOnDemandMetadata collaboratingMetadata = locateCollaboratingMetadata(
                    dodMetadataId, field, dataOnDemandTypes);
            locatedFields.put(field, collaboratingMetadata);
        }

        return locatedFields;
    }

    @Override
    protected ItdTypeDetailsProvidingMetadataItem getMetadata(
            final String dodMetadataId, final JavaType aspectName,
            final PhysicalTypeMetadata governorPhysicalTypeMetadata,
            final String itdFilename) {
        // We need to parse the annotation, which we expect to be present
        final DataOnDemandAnnotationValues annotationValues = new DataOnDemandAnnotationValues(
                governorPhysicalTypeMetadata);
        final JavaType entity = annotationValues.getEntity();
        if (!annotationValues.isAnnotationFound() || entity == null) {
            return null;
        }

        // Remember that this entity JavaType matches up with this DOD's
        // metadata identification string
        // Start by clearing the previous association
        final JavaType oldEntity = dodMidToEntityMap.get(dodMetadataId);
        if (oldEntity != null) {
            entityToDodMidMap.remove(oldEntity);
        }
        entityToDodMidMap.put(annotationValues.getEntity(), dodMetadataId);
        dodMidToEntityMap.put(dodMetadataId, annotationValues.getEntity());

        final JavaType identifierType = persistenceMemberLocator
                .getIdentifierType(entity);
        if (identifierType == null) {
            return null;
        }

        final MemberDetails memberDetails = getMemberDetails(entity);
        if (memberDetails == null) {
            return null;
        }

        final MemberHoldingTypeDetails persistenceMemberHoldingTypeDetails = MemberFindingUtils
                .getMostConcreteMemberHoldingTypeDetailsWithTag(memberDetails,
                        PERSISTENT_TYPE);
        if (persistenceMemberHoldingTypeDetails == null) {
            return null;
        }

        // We need to be informed if our dependent metadata changes
        metadataDependencyRegistry.registerDependency(
                persistenceMemberHoldingTypeDetails.getDeclaredByMetadataId(),
                dodMetadataId);

        // Get the additions to make for each required method
        final MethodParameter fromParameter = new MethodParameter(
                JavaType.INT_PRIMITIVE, "from");
        final MethodParameter toParameter = new MethodParameter(
                JavaType.INT_PRIMITIVE, "to");
        final MemberTypeAdditions findEntriesMethod = layerService
                .getMemberTypeAdditions(dodMetadataId,
                        FIND_ENTRIES_METHOD.name(), entity, identifierType,
                        LayerType.HIGHEST.getPosition(), fromParameter,
                        toParameter);
        final MemberTypeAdditions findMethodAdditions = layerService
                .getMemberTypeAdditions(dodMetadataId, FIND_METHOD.name(),
                        entity, identifierType,
                        LayerType.HIGHEST.getPosition(), new MethodParameter(
                                identifierType, "id"));
        final MethodParameter entityParameter = new MethodParameter(entity,
                "obj");
        final MemberTypeAdditions flushMethod = layerService
                .getMemberTypeAdditions(dodMetadataId, FLUSH_METHOD, entity,
                        identifierType, LayerType.HIGHEST.getPosition(),
                        entityParameter);
        final MethodMetadata identifierAccessor = memberDetails
                .getMostConcreteMethodWithTag(IDENTIFIER_ACCESSOR_METHOD);
        final MemberTypeAdditions persistMethodAdditions = layerService
                .getMemberTypeAdditions(dodMetadataId, PERSIST_METHOD, entity,
                        identifierType, LayerType.HIGHEST.getPosition(),
                        entityParameter);

        if (findEntriesMethod == null || findMethodAdditions == null
                || identifierAccessor == null || persistMethodAdditions == null) {
            return null;
        }

        // Identify all the fields we care about on the entity
        final Map<FieldMetadata, DataOnDemandMetadata> locatedFields = getLocatedFields(
                memberDetails, dodMetadataId);

        // Get the embedded identifier metadata holder - may be null if no
        // embedded identifier exists
        final EmbeddedIdHolder embeddedIdHolder = getEmbeddedIdHolder(
                memberDetails, dodMetadataId);

        // Get the list of embedded metadata holders - may be an empty list if
        // no embedded identifier exists
        final List<EmbeddedHolder> embeddedHolders = getEmbeddedHolders(
                memberDetails, dodMetadataId);

        return new DataOnDemandMetadata(dodMetadataId, aspectName,
                governorPhysicalTypeMetadata, annotationValues,
                identifierAccessor, findMethodAdditions, findEntriesMethod,
                persistMethodAdditions, flushMethod, locatedFields,
                identifierType, embeddedIdHolder, embeddedHolders);
    }

    public String getProvidesType() {
        return DataOnDemandMetadata.getMetadataIdentiferType();
    }

    /**
     * Returns the {@link DataOnDemandMetadata} for the entity that's the target
     * of the given reference field.
     *
     * @param dodMetadataId
     * @param field
     * @param dataOnDemandTypes
     * @return <code>null</code> if it's not an n:1 or 1:1 field, or the DoD
     *         metadata is simply not available
     */
    private DataOnDemandMetadata locateCollaboratingMetadata(
            final String dodMetadataId, final FieldMetadata field,
            final Iterable<ClassOrInterfaceTypeDetails> dataOnDemandTypes) {
        if (!(field.getCustomData().keySet().contains(MANY_TO_ONE_FIELD) || field
                .getCustomData().keySet().contains(ONE_TO_ONE_FIELD))) {
            return null;
        }

        final String otherDodMetadataId = getDataOnDemandMetadataId(
                field.getFieldType(), dataOnDemandTypes);

        if (otherDodMetadataId == null
                || otherDodMetadataId.equals(dodMetadataId)) {
            // No DoD for this field's type, or it's a self-reference
            return null;
        }

        // Make this DoD depend on the related entity (not its Dod, otherwise
        // we get a circular MD dependency)
        registerDependencyUponType(dodMetadataId, field.getFieldType());

        return (DataOnDemandMetadata) metadataService.get(otherDodMetadataId);
    }

    private void registerDependencyUponType(final String dodMetadataId,
            final JavaType type) {
        final String fieldPhysicalTypeId = typeLocationService
                .getPhysicalTypeIdentifier(type);
        metadataDependencyRegistry.registerDependency(fieldPhysicalTypeId,
                dodMetadataId);
    }
}
TOP

Related Classes of org.springframework.roo.addon.dod.DataOnDemandMetadataProviderImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.