Package org.springframework.roo.addon.gwt.request

Source Code of org.springframework.roo.addon.gwt.request.GwtRequestMetadataProviderImpl

package org.springframework.roo.addon.gwt.request;

import static java.lang.reflect.Modifier.ABSTRACT;
import static java.lang.reflect.Modifier.STATIC;
import static org.springframework.roo.addon.gwt.GwtJavaType.INSTANCE_REQUEST;
import static org.springframework.roo.addon.gwt.GwtJavaType.OLD_REQUEST_CONTEXT;
import static org.springframework.roo.addon.gwt.GwtJavaType.REQUEST;
import static org.springframework.roo.addon.gwt.GwtJavaType.REQUEST_CONTEXT;
import static org.springframework.roo.addon.gwt.GwtJavaType.SERVICE_NAME;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.COUNT_ALL_METHOD;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.FIND_ALL_METHOD;
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.PERSIST_METHOD;
import static org.springframework.roo.classpath.customdata.CustomDataKeys.REMOVE_METHOD;
import static org.springframework.roo.model.JavaType.INT_PRIMITIVE;
import static org.springframework.roo.model.JavaType.LONG_PRIMITIVE;
import static org.springframework.roo.model.JavaType.VOID_PRIMITIVE;
import static org.springframework.roo.model.RooJavaType.ROO_GWT_REQUEST;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang3.Validate;
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.gwt.GwtFileManager;
import org.springframework.roo.addon.gwt.GwtTypeService;
import org.springframework.roo.addon.gwt.GwtUtils;
import org.springframework.roo.classpath.PhysicalTypeIdentifier;
import org.springframework.roo.classpath.PhysicalTypeIdentifierNamingUtils;
import org.springframework.roo.classpath.TypeLocationService;
import org.springframework.roo.classpath.customdata.tagkeys.MethodMetadataCustomDataKey;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetailsBuilder;
import org.springframework.roo.classpath.details.FieldMetadata;
import org.springframework.roo.classpath.details.MethodMetadata;
import org.springframework.roo.classpath.details.MethodMetadataBuilder;
import org.springframework.roo.classpath.details.annotations.AnnotatedJavaType;
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.classpath.details.annotations.StringAttributeValue;
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.persistence.PersistenceMemberLocator;
import org.springframework.roo.classpath.scanner.MemberDetailsScanner;
import org.springframework.roo.metadata.AbstractHashCodeTrackingMetadataNotifier;
import org.springframework.roo.metadata.MetadataItem;
import org.springframework.roo.model.DataType;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.project.LogicalPath;
import org.springframework.roo.project.ProjectMetadata;
import org.springframework.roo.project.ProjectOperations;

@Component(immediate = true)
@Service
public class GwtRequestMetadataProviderImpl extends
        AbstractHashCodeTrackingMetadataNotifier implements
        GwtRequestMetadataProvider {

    private static final int LAYER_POSITION = LayerType.HIGHEST.getPosition();

    @Reference GwtFileManager gwtFileManager;
    @Reference GwtTypeService gwtTypeService;
    @Reference LayerService layerService;
    @Reference MemberDetailsScanner memberDetailsScanner;
    @Reference PersistenceMemberLocator persistenceMemberLocator;
    @Reference ProjectOperations projectOperations;
    @Reference TypeLocationService typeLocationService;

    protected void activate(final ComponentContext context) {
        metadataDependencyRegistry.registerDependency(
                PhysicalTypeIdentifier.getMetadataIdentiferType(),
                getProvidesType());
    }

    protected void deactivate(final ComponentContext context) {
        metadataDependencyRegistry.deregisterDependency(
                PhysicalTypeIdentifier.getMetadataIdentiferType(),
                getProvidesType());
    }

    public MetadataItem get(final String requestMetadataId) {
        final ProjectMetadata projectMetadata = projectOperations
                .getProjectMetadata(PhysicalTypeIdentifierNamingUtils
                        .getModule(requestMetadataId));
        if (projectMetadata == null) {
            return null;
        }

        final ClassOrInterfaceTypeDetails requestInterface = getGovernor(requestMetadataId);
        if (requestInterface == null) {
            return null;
        }

        final AnnotationMetadata gwtRequestAnnotation = requestInterface
                .getAnnotation(ROO_GWT_REQUEST);
        if (gwtRequestAnnotation == null) {
            return null;
        }

        final JavaType entityType = new JavaType((String) gwtRequestAnnotation
                .getAttribute("value").getValue());

        // Get the methods to be invoked and the type(s) that provide them
        // (should only be one such type, or null)
        final Map<MethodMetadata, FieldMetadata> requestMethods = getRequestMethodsAndInvokedTypes(
                entityType, requestMetadataId);
        if (requestMethods == null) {
            return null;
        }

        final JavaType invokedType = getInvokedType(requestMethods.values());
        final String requestTypeContents = writeRequestInterface(
                requestInterface, invokedType, requestMethods.keySet(),
                entityType, requestMetadataId);
        final GwtRequestMetadata gwtRequestMetadata = new GwtRequestMetadata(
                requestMetadataId, requestTypeContents);
        notifyIfRequired(gwtRequestMetadata);
        return gwtRequestMetadata;
    }

    private ClassOrInterfaceTypeDetails getGovernor(
            final String metadataIdentificationString) {
        final JavaType governorTypeName = GwtRequestMetadata
                .getJavaType(metadataIdentificationString);
        final LogicalPath governorTypePath = GwtRequestMetadata
                .getPath(metadataIdentificationString);
        final String physicalTypeId = PhysicalTypeIdentifier.createIdentifier(
                governorTypeName, governorTypePath);
        return typeLocationService.getTypeDetails(physicalTypeId);
    }

    /**
     * Returns the type on which the given request methods will be invoked
     *
     * @param invokedFields the autowired fields invoked by layer method calls
     *            (can include <code>null</code> elements for 'active record'
     *            calls)
     * @return <code>null</code> if active record is being used, otherwise a
     *         layer component type
     */
    private JavaType getInvokedType(
            final Collection<FieldMetadata> invokedFields) {
        final Collection<JavaType> distinctInvokedTypes = new HashSet<JavaType>();
        for (final FieldMetadata invokedField : invokedFields) {
            if (invokedField == null) {
                distinctInvokedTypes.add(null);
            }
            else {
                distinctInvokedTypes.add(invokedField.getFieldType());
            }
        }
        Validate.isTrue(distinctInvokedTypes.size() == 1,
                "Expected one invoked type but found: %s", distinctInvokedTypes);
        return distinctInvokedTypes.iterator().next();
    }

    public String getProvidesType() {
        return GwtRequestMetadata.getMetadataIdentifierType();
    }

    private MethodMetadataBuilder getRequestMethod(
            final ClassOrInterfaceTypeDetails request,
            final MethodMetadata method, final JavaType returnType) {
        final ClassOrInterfaceTypeDetails entity = gwtTypeService
                .lookupEntityFromRequest(request);
        if (entity == null) {
            return null;
        }
        final List<AnnotatedJavaType> parameterTypes = new ArrayList<AnnotatedJavaType>();
        for (final AnnotatedJavaType parameterType : method.getParameterTypes()) {
            parameterTypes.add(new AnnotatedJavaType(gwtTypeService
                    .getGwtSideLeafType(parameterType.getJavaType(),
                            entity.getType(), true, false)));
        }
        return new MethodMetadataBuilder(request.getDeclaredByMetadataId(),
                ABSTRACT, method.getMethodName(), returnType, parameterTypes,
                method.getParameterNames(), null);
    }

    private MethodMetadataBuilder getRequestMethod(
            final ClassOrInterfaceTypeDetails request,
            final MethodMetadata method, final JavaType entityType,
            final JavaType invokedType) {
        final ClassOrInterfaceTypeDetails proxy = gwtTypeService
                .lookupProxyFromRequest(request);
        if (proxy == null) {
            return null;
        }
        final JavaType methodReturnType = getRequestMethodReturnType(
                invokedType, method, proxy.getType());
        return getRequestMethod(request, method, methodReturnType);
    }

    private MethodMetadata getRequestMethod(final JavaType entity,
            final MethodMetadataCustomDataKey methodKey,
            final MemberTypeAdditions memberTypeAdditions,
            final String declaredByMetadataId) {
        final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
                declaredByMetadataId); // wrong MID, but doesn't matter here
        methodBuilder.setMethodName(new JavaSymbolName(memberTypeAdditions
                .getMethodName()));
        if (memberTypeAdditions.isStatic()) {
            // OK to overwrite any other modifiers
            methodBuilder.setModifier(STATIC);
        }
        /*
         * TODO make sure the active record instance methods have the correct
         * parameters
         *
         * expected: abstract
         * InstanceRequest<com.example.gwtbug.client.proxy.ThingProxy,
         * java.lang.Void> persist(); actual: abstract Request<java.lang.Void>
         * persist(ThingProxy proxy);
         */
        for (final MethodParameter methodParameter : memberTypeAdditions
                .getMethodParameters()) {
            methodBuilder.addParameter(methodParameter.getValue()
                    .getSymbolName(), methodParameter.getKey());
        }
        final JavaType returnType = getReturnType(methodKey, entity);
        final JavaType gwtType = gwtTypeService.getGwtSideLeafType(returnType,
                entity, true, true);
        methodBuilder.setReturnType(gwtType);
        return methodBuilder.build();
    }

    private JavaType getRequestMethodReturnType(final JavaType invokedType,
            final MethodMetadata method, final JavaType proxyType) {
        if (invokedType == null && !method.isStatic()) {
            // Calling an active record method that's non-static (i.e. target is
            // an entity instance)
            final List<JavaType> methodReturnTypeArgs = Arrays.asList(
                    proxyType, method.getReturnType());
            return new JavaType(INSTANCE_REQUEST.getFullyQualifiedTypeName(),
                    0, DataType.TYPE, null, methodReturnTypeArgs);
        }
        final List<JavaType> methodReturnTypeArgs = Collections
                .singletonList(method.getReturnType());
        return new JavaType(REQUEST.getFullyQualifiedTypeName(), 0,
                DataType.TYPE, null, methodReturnTypeArgs);
    }

    private Map<MethodMetadata, FieldMetadata> getRequestMethodsAndInvokedTypes(
            final JavaType entity, final String requestMetadataId) {
        final JavaType idType = persistenceMemberLocator
                .getIdentifierType(entity);
        if (idType == null) {
            return null;
        }
        final Map<MethodMetadata, FieldMetadata> requestMethods = new LinkedHashMap<MethodMetadata, FieldMetadata>();
        for (final Entry<MethodMetadataCustomDataKey, Collection<MethodParameter>> methodSignature : getRequestMethodSignatures(
                entity, idType).entrySet()) {
            final String methodId = methodSignature.getKey().name();
            final MemberTypeAdditions memberTypeAdditions = layerService
                    .getMemberTypeAdditions(requestMetadataId, methodId,
                            entity, idType, LAYER_POSITION,
                            methodSignature.getValue());
            Validate.notNull(memberTypeAdditions,
                    "No support for %s method for domain type %s", methodId,
                    entity);
            final MethodMetadata requestMethod = getRequestMethod(entity,
                    methodSignature.getKey(), memberTypeAdditions,
                    requestMetadataId);
            requestMethods.put(requestMethod,
                    memberTypeAdditions.getInvokedField());
        }
        return requestMethods;
    }

    private Map<MethodMetadataCustomDataKey, Collection<MethodParameter>> getRequestMethodSignatures(
            final JavaType domainType, final JavaType idType) {
        final Map<MethodMetadataCustomDataKey, Collection<MethodParameter>> signatures = new LinkedHashMap<MethodMetadataCustomDataKey, Collection<MethodParameter>>();
        final List<MethodParameter> noArgs = Arrays.asList();
        signatures.put(COUNT_ALL_METHOD, noArgs);
        signatures.put(FIND_ALL_METHOD, noArgs);
        signatures.put(FIND_ENTRIES_METHOD, Arrays.asList(new MethodParameter(
                INT_PRIMITIVE, "firstResult"), new MethodParameter(
                INT_PRIMITIVE, "maxResults")));
        signatures.put(FIND_METHOD,
                Arrays.asList(new MethodParameter(idType, "id")));
        final List<MethodParameter> proxyParameterAsList = Arrays
                .asList(new MethodParameter(domainType, "proxy"));
        signatures.put(PERSIST_METHOD, proxyParameterAsList);
        signatures.put(REMOVE_METHOD, proxyParameterAsList);
        return signatures;
    }

    private JavaType getReturnType(final MethodMetadataCustomDataKey methodKey,
            final JavaType entity) {
        if (COUNT_ALL_METHOD.equals(methodKey)) {
            return LONG_PRIMITIVE;
        }
        if (FIND_ALL_METHOD.equals(methodKey)
                || FIND_ENTRIES_METHOD.equals(methodKey)) {
            return JavaType.listOf(entity);
        }
        if (FIND_METHOD.equals(methodKey)) {
            return entity;
        }
        if (PERSIST_METHOD.equals(methodKey) || REMOVE_METHOD.equals(methodKey)) {
            return VOID_PRIMITIVE;
        }
        throw new IllegalStateException("Unexpected method key " + methodKey);
    }

    private AnnotationMetadata getServiceNameAnnotation(
            final ClassOrInterfaceTypeDetails request,
            final JavaType invokedType, final JavaType entityType,
            final String requestMetadataId) {
        final List<AnnotationAttributeValue<?>> serviceAttributeValues = new ArrayList<AnnotationAttributeValue<?>>();
        if (invokedType == null) {
            // Active record; specify the entity type as the invoked "service"
            final StringAttributeValue stringAttributeValue = new StringAttributeValue(
                    new JavaSymbolName("value"),
                    entityType.getFullyQualifiedTypeName());
            serviceAttributeValues.add(stringAttributeValue);
        }
        else {
            // Layer component, e.g. repository or service; specify its type as
            // the invoked "service"
            final StringAttributeValue stringAttributeValue = new StringAttributeValue(
                    new JavaSymbolName("value"),
                    invokedType.getFullyQualifiedTypeName());
            serviceAttributeValues.add(stringAttributeValue);

            // Specify the locator that GWT will use to find it
            final LogicalPath requestLogicalPath = PhysicalTypeIdentifier
                    .getPath(request.getDeclaredByMetadataId());
            final JavaType serviceLocator = gwtTypeService
                    .getServiceLocator(requestLogicalPath.getModule());
            final StringAttributeValue locatorAttributeValue = new StringAttributeValue(
                    new JavaSymbolName("locator"),
                    serviceLocator.getFullyQualifiedTypeName());
            serviceAttributeValues.add(locatorAttributeValue);
        }
        return new AnnotationMetadataBuilder(SERVICE_NAME,
                serviceAttributeValues).build();
    }

    /**
     * Creates or updates the entity-specific request interface with
     *
     * @param request
     * @param requestMethods the methods to declare in the interface, mapped to
     *            the injected field type on which they are invoked (required)
     * @param entityType
     * @param requestMetadataId
     * @return the Java source code for the request interface
     */
    private String writeRequestInterface(
            final ClassOrInterfaceTypeDetails request,
            final JavaType invokedType,
            final Iterable<MethodMetadata> requestMethods,
            final JavaType entityType, final String requestMetadataId) {
        final ClassOrInterfaceTypeDetailsBuilder typeDetailsBuilder = new ClassOrInterfaceTypeDetailsBuilder(
                request);

        // Service name annotation (@RooGwtRequest was already applied by
        // GwtOperationsImpl#createRequestInterface)
        typeDetailsBuilder.removeAnnotation(SERVICE_NAME);
        typeDetailsBuilder.addAnnotation(getServiceNameAnnotation(request,
                invokedType, entityType, requestMetadataId));

        // Super-interface
        typeDetailsBuilder.removeExtendsTypes(OLD_REQUEST_CONTEXT);
        if (!typeDetailsBuilder.getExtendsTypes().contains(REQUEST_CONTEXT)) {
            typeDetailsBuilder.addExtendsTypes(REQUEST_CONTEXT);
        }

        typeDetailsBuilder.clearDeclaredMethods();
        for (final MethodMetadata method : requestMethods) {
            typeDetailsBuilder.addMethod(getRequestMethod(request, method,
                    entityType, invokedType));
        }

        return gwtFileManager.write(typeDetailsBuilder.build(),
                GwtUtils.PROXY_REQUEST_WARNING);
    }
}
TOP

Related Classes of org.springframework.roo.addon.gwt.request.GwtRequestMetadataProviderImpl

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.