Package org.springframework.roo.addon.gwt

Source Code of org.springframework.roo.addon.gwt.GwtTypeServiceImpl

package org.springframework.roo.addon.gwt;

import static org.springframework.roo.model.JavaType.LONG_OBJECT;
import static org.springframework.roo.model.JdkJavaType.BIG_DECIMAL;
import static org.springframework.roo.model.JdkJavaType.DATE;
import static org.springframework.roo.model.JdkJavaType.LIST;
import static org.springframework.roo.model.JdkJavaType.SET;
import static org.springframework.roo.model.JpaJavaType.EMBEDDABLE;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Logger;

import javax.xml.parsers.DocumentBuilder;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
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.springframework.roo.addon.gwt.scaffold.GwtScaffoldMetadata;
import org.springframework.roo.classpath.PhysicalTypeCategory;
import org.springframework.roo.classpath.PhysicalTypeIdentifier;
import org.springframework.roo.classpath.PhysicalTypeMetadata;
import org.springframework.roo.classpath.TypeLocationService;
import org.springframework.roo.classpath.customdata.CustomDataKeys;
import org.springframework.roo.classpath.details.AbstractIdentifiableAnnotatedJavaStructureBuilder;
import org.springframework.roo.classpath.details.BeanInfoUtils;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetailsBuilder;
import org.springframework.roo.classpath.details.ConstructorMetadata;
import org.springframework.roo.classpath.details.ConstructorMetadataBuilder;
import org.springframework.roo.classpath.details.FieldMetadataBuilder;
import org.springframework.roo.classpath.details.IdentifiableAnnotatedJavaStructure;
import org.springframework.roo.classpath.details.MemberHoldingTypeDetails;
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.itd.InvocableMemberBodyBuilder;
import org.springframework.roo.classpath.persistence.PersistenceMemberLocator;
import org.springframework.roo.classpath.scanner.MemberDetails;
import org.springframework.roo.classpath.scanner.MemberDetailsScanner;
import org.springframework.roo.file.monitor.event.FileDetails;
import org.springframework.roo.metadata.MetadataService;
import org.springframework.roo.model.DataType;
import org.springframework.roo.model.JavaPackage;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.model.RooJavaType;
import org.springframework.roo.process.manager.FileManager;
import org.springframework.roo.project.LogicalPath;
import org.springframework.roo.project.Path;
import org.springframework.roo.project.ProjectOperations;
import org.springframework.roo.support.logging.HandlerUtils;
import org.springframework.roo.support.util.FileUtils;
import org.springframework.roo.support.util.XmlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
* Provides a basic implementation of {@link GwtTypeService}.
*
* @author James Tyrrell
* @since 1.1.2
*/
@Component
@Service
public class GwtTypeServiceImpl implements GwtTypeService {

    private static final Logger LOGGER = HandlerUtils
            .getLogger(GwtTypeServiceImpl.class);
    private static final String PATH = "path";

    @Reference private FileManager fileManager;
    @Reference private GwtFileManager gwtFileManager;
    @Reference private MemberDetailsScanner memberDetailsScanner;
    @Reference private MetadataService metadataService;
    @Reference private PersistenceMemberLocator persistenceMemberLocator;
    @Reference private ProjectOperations projectOperations;
    @Reference private TypeLocationService typeLocationService;

    private final Set<String> warnings = new LinkedHashSet<String>();
    private final Timer warningTimer = new Timer();

    public void addSourcePath(final String sourcePath, final String moduleName) {
        final String gwtXmlPath = getGwtModuleXml(moduleName);
        Validate.notBlank(gwtXmlPath, "gwt.xml could not be found for module '"
                + moduleName + "'");
        final Document gwtXmlDoc = getGwtXmlDocument(gwtXmlPath);
        final Element gwtXmlRoot = gwtXmlDoc.getDocumentElement();
        final List<Element> sourceElements = XmlUtils.findElements(
                "/module/source", gwtXmlRoot);
        if (!anyExistingSourcePathsIncludePath(sourcePath, sourceElements)) {
            final Element firstSourceElement = sourceElements.get(0);
            final Element newSourceElement = gwtXmlDoc.createElement("source");
            newSourceElement.setAttribute(PATH, sourcePath);
            gwtXmlRoot.insertBefore(newSourceElement, firstSourceElement);
            fileManager.createOrUpdateTextFileIfRequired(gwtXmlPath,
                    XmlUtils.nodeToString(gwtXmlDoc),
                    "Added source paths to gwt.xml file", true);
        }
    }

    private boolean anyExistingSourcePathsIncludePath(final String sourcePath,
            final Iterable<Element> sourceElements) {
        for (final Element sourceElement : sourceElements) {
            if (sourcePath.startsWith(sourceElement.getAttribute(PATH))) {
                return true;
            }
        }
        return false;
    }

    public List<ClassOrInterfaceTypeDetails> buildType(final GwtType destType,
            final ClassOrInterfaceTypeDetails templateClass,
            final List<MemberHoldingTypeDetails> extendsTypes,
            final String moduleName) {
        try {
            // A type may consist of a concrete type which depend on
            final List<ClassOrInterfaceTypeDetails> types = new ArrayList<ClassOrInterfaceTypeDetails>();
            final ClassOrInterfaceTypeDetailsBuilder templateClassBuilder = new ClassOrInterfaceTypeDetailsBuilder(
                    templateClass);

            if (destType.isCreateAbstract()) {
                final ClassOrInterfaceTypeDetailsBuilder abstractClassBuilder = createAbstractBuilder(
                        templateClassBuilder, extendsTypes, moduleName);

                final ArrayList<FieldMetadataBuilder> fieldsToRemove = new ArrayList<FieldMetadataBuilder>();
                for (final JavaSymbolName fieldName : destType
                        .getWatchedFieldNames()) {
                    for (final FieldMetadataBuilder fieldBuilder : templateClassBuilder
                            .getDeclaredFields()) {
                        if (fieldBuilder.getFieldName().equals(fieldName)) {
                            final FieldMetadataBuilder abstractFieldBuilder = new FieldMetadataBuilder(
                                    abstractClassBuilder
                                            .getDeclaredByMetadataId(),
                                    fieldBuilder.build());
                            abstractClassBuilder
                                    .addField(convertModifier(abstractFieldBuilder));
                            fieldsToRemove.add(fieldBuilder);
                            break;
                        }
                    }
                }

                templateClassBuilder.getDeclaredFields().removeAll(
                        fieldsToRemove);

                final List<MethodMetadataBuilder> methodsToRemove = new ArrayList<MethodMetadataBuilder>();
                for (final JavaSymbolName methodName : destType
                        .getWatchedMethods().keySet()) {
                    for (final MethodMetadataBuilder methodBuilder : templateClassBuilder
                            .getDeclaredMethods()) {
                        final List<JavaType> params = new ArrayList<JavaType>();
                        for (final AnnotatedJavaType param : methodBuilder
                                .getParameterTypes()) {
                            params.add(new JavaType(param.getJavaType()
                                    .getFullyQualifiedTypeName()));
                        }
                        if (methodBuilder.getMethodName().equals(methodName)) {
                            if (destType.getWatchedMethods().get(methodName)
                                    .containsAll(params)) {
                                final MethodMetadataBuilder abstractMethodBuilder = new MethodMetadataBuilder(
                                        abstractClassBuilder
                                                .getDeclaredByMetadataId(),
                                        methodBuilder.build());
                                abstractClassBuilder
                                        .addMethod(convertModifier(abstractMethodBuilder));
                                methodsToRemove.add(methodBuilder);
                                break;
                            }
                        }
                    }
                }

                templateClassBuilder.removeAll(methodsToRemove);

                for (final JavaType innerTypeName : destType
                        .getWatchedInnerTypes()) {
                    for (final ClassOrInterfaceTypeDetailsBuilder innerTypeBuilder : templateClassBuilder
                            .getDeclaredInnerTypes()) {
                        if (innerTypeBuilder.getName().getSimpleTypeName()
                                .equals(innerTypeName.getSimpleTypeName())) {
                            final ClassOrInterfaceTypeDetailsBuilder builder = new ClassOrInterfaceTypeDetailsBuilder(
                                    abstractClassBuilder
                                            .getDeclaredByMetadataId(),
                                    innerTypeBuilder.build());
                            builder.setName(new JavaType(
                                    innerTypeBuilder.getName()
                                            .getSimpleTypeName() + "_Roo_Gwt",
                                    0, DataType.TYPE, null, innerTypeBuilder
                                            .getName().getParameters()));

                            templateClassBuilder.getDeclaredInnerTypes()
                                    .remove(innerTypeBuilder);
                            if (innerTypeBuilder.getPhysicalTypeCategory()
                                    .equals(PhysicalTypeCategory.INTERFACE)) {
                                final ClassOrInterfaceTypeDetailsBuilder interfaceInnerTypeBuilder = new ClassOrInterfaceTypeDetailsBuilder(
                                        innerTypeBuilder.build());
                                abstractClassBuilder.addInnerType(builder);
                                templateClassBuilder.getDeclaredInnerTypes()
                                        .remove(innerTypeBuilder);
                                interfaceInnerTypeBuilder
                                        .clearDeclaredMethods();
                                interfaceInnerTypeBuilder
                                        .getDeclaredInnerTypes().clear();
                                interfaceInnerTypeBuilder.getExtendsTypes()
                                        .clear();
                                interfaceInnerTypeBuilder
                                        .getExtendsTypes()
                                        .add(new JavaType(
                                                builder.getName()
                                                        .getSimpleTypeName(),
                                                0,
                                                DataType.TYPE,
                                                null,
                                                Collections
                                                        .singletonList(new JavaType(
                                                                "V",
                                                                0,
                                                                DataType.VARIABLE,
                                                                null,
                                                                new ArrayList<JavaType>()))));
                                templateClassBuilder.getDeclaredInnerTypes()
                                        .add(interfaceInnerTypeBuilder);
                            }
                            break;
                        }
                    }
                }

                abstractClassBuilder.setImplementsTypes(templateClass
                        .getImplementsTypes());
                templateClassBuilder.getImplementsTypes().clear();
                templateClassBuilder.getExtendsTypes().clear();
                templateClassBuilder.getExtendsTypes().add(
                        abstractClassBuilder.getName());
                types.add(abstractClassBuilder.build());
            }

            types.add(templateClassBuilder.build());

            return types;
        }
        catch (final Exception e) {
            throw new IllegalStateException(e);
        }
    }

    public void buildType(final GwtType type,
            final List<ClassOrInterfaceTypeDetails> templateTypeDetails,
            final String moduleName) {
        if (GwtType.LIST_PLACE_RENDERER.equals(type)) {
            final Map<JavaSymbolName, List<JavaType>> watchedMethods = new HashMap<JavaSymbolName, List<JavaType>>();
            watchedMethods.put(new JavaSymbolName("render"), Collections
                    .singletonList(new JavaType(projectOperations
                            .getTopLevelPackage(moduleName)
                            .getFullyQualifiedPackageName()
                            + ".client.scaffold.place.ProxyListPlace")));
            type.setWatchedMethods(watchedMethods);
        }
        else {
            type.resolveMethodsToWatch(type);
        }

        type.resolveWatchedFieldNames(type);
        final List<ClassOrInterfaceTypeDetails> typesToBeWritten = new ArrayList<ClassOrInterfaceTypeDetails>();
        for (final ClassOrInterfaceTypeDetails templateTypeDetail : templateTypeDetails) {
            typesToBeWritten.addAll(buildType(type, templateTypeDetail,
                    getExtendsTypes(templateTypeDetail), moduleName));
        }
        gwtFileManager.write(typesToBeWritten, type.isOverwriteConcrete());
    }

    private void checkPrimitive(final JavaType type) {
        if (type.isPrimitive() && !JavaType.VOID_PRIMITIVE.equals(type)) {
            final String to = type.getSimpleTypeName();
            final String from = to.toLowerCase();
            throw new IllegalStateException(
                    "GWT does not currently support primitive types in an entity. Please change any '"
                            + from
                            + "' entity property types to 'java.lang."
                            + to + "'.");
        }
    }

    private <T extends AbstractIdentifiableAnnotatedJavaStructureBuilder<? extends IdentifiableAnnotatedJavaStructure>> T convertModifier(
            final T builder) {
        if (Modifier.isPrivate(builder.getModifier())) {
            builder.setModifier(Modifier.PROTECTED);
        }
        return builder;
    }

    private ClassOrInterfaceTypeDetailsBuilder createAbstractBuilder(
            final ClassOrInterfaceTypeDetailsBuilder concreteClass,
            final List<MemberHoldingTypeDetails> extendsTypesDetails,
            final String moduleName) {
        final JavaType concreteType = concreteClass.getName();
        String abstractName = concreteType.getSimpleTypeName() + "_Roo_Gwt";
        abstractName = concreteType.getPackage().getFullyQualifiedPackageName()
                + '.' + abstractName;
        final JavaType abstractType = new JavaType(abstractName);
        final String abstractId = PhysicalTypeIdentifier.createIdentifier(
                abstractType,
                LogicalPath.getInstance(Path.SRC_MAIN_JAVA, moduleName));
        final ClassOrInterfaceTypeDetailsBuilder cidBuilder = new ClassOrInterfaceTypeDetailsBuilder(
                abstractId);
        cidBuilder.setPhysicalTypeCategory(PhysicalTypeCategory.CLASS);
        cidBuilder.setName(abstractType);
        cidBuilder.setModifier(Modifier.ABSTRACT | Modifier.PUBLIC);
        cidBuilder.getExtendsTypes().addAll(concreteClass.getExtendsTypes());
        cidBuilder.add(concreteClass.getRegisteredImports());

        for (final MemberHoldingTypeDetails extendsTypeDetails : extendsTypesDetails) {
            for (final ConstructorMetadata constructor : extendsTypeDetails
                    .getDeclaredConstructors()) {
                final ConstructorMetadataBuilder abstractConstructor = new ConstructorMetadataBuilder(
                        abstractId);
                abstractConstructor.setModifier(constructor.getModifier());

                final Map<JavaSymbolName, JavaType> typeMap = resolveTypes(
                        extendsTypeDetails.getName(), concreteClass
                                .getExtendsTypes().get(0));
                for (final AnnotatedJavaType type : constructor
                        .getParameterTypes()) {
                    JavaType newType = type.getJavaType();
                    if (type.getJavaType().getParameters().size() > 0) {
                        final ArrayList<JavaType> parameterTypes = new ArrayList<JavaType>();
                        for (final JavaType typeType : type.getJavaType()
                                .getParameters()) {
                            final JavaType typeParam = typeMap
                                    .get(new JavaSymbolName(typeType.toString()));
                            if (typeParam != null) {
                                parameterTypes.add(typeParam);
                            }
                        }
                        newType = new JavaType(type.getJavaType()
                                .getFullyQualifiedTypeName(), type
                                .getJavaType().getArray(), type.getJavaType()
                                .getDataType(),
                                type.getJavaType().getArgName(), parameterTypes);
                    }
                    abstractConstructor.getParameterTypes().add(
                            new AnnotatedJavaType(newType));
                }
                abstractConstructor.setParameterNames(constructor
                        .getParameterNames());

                final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
                bodyBuilder.newLine().indent().append("super(");

                int i = 0;
                for (final JavaSymbolName paramName : abstractConstructor
                        .getParameterNames()) {
                    bodyBuilder.append(" ").append(paramName.getSymbolName());
                    if (abstractConstructor.getParameterTypes().size() > i + 1) {
                        bodyBuilder.append(", ");
                    }
                    i++;
                }

                bodyBuilder.append(");");

                bodyBuilder.newLine().indentRemove();
                abstractConstructor.setBodyBuilder(bodyBuilder);
                cidBuilder.getDeclaredConstructors().add(abstractConstructor);
            }
        }
        return cidBuilder;
    }

    private void displayWarning(final String warning) {
        if (!warnings.contains(warning)) {
            warnings.add(warning);
            LOGGER.severe(warning);
            warningTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    warnings.clear();
                }
            }, 15000);
        }
    }

    public List<MemberHoldingTypeDetails> getExtendsTypes(
            final ClassOrInterfaceTypeDetails childType) {
        final List<MemberHoldingTypeDetails> extendsTypes = new ArrayList<MemberHoldingTypeDetails>();
        if (childType != null) {
            for (final JavaType javaType : childType.getExtendsTypes()) {
                final String superTypeId = typeLocationService
                        .getPhysicalTypeIdentifier(javaType);
                if (superTypeId == null
                        || metadataService.get(superTypeId) == null) {
                    continue;
                }
                final MemberHoldingTypeDetails superType = ((PhysicalTypeMetadata) metadataService
                        .get(superTypeId)).getMemberHoldingTypeDetails();
                extendsTypes.add(superType);
            }
        }
        return extendsTypes;
    }

    public String getGwtModuleXml(final String moduleName) {
        final LogicalPath logicalPath = LogicalPath.getInstance(
                Path.SRC_MAIN_JAVA, moduleName);
        final String gwtModuleXml = projectOperations.getPathResolver()
                .getRoot(logicalPath)
                + File.separatorChar
                + projectOperations.getTopLevelPackage(moduleName)
                        .getFullyQualifiedPackageName()
                        .replace('.', File.separatorChar)
                + File.separator
                + "*.gwt.xml";
        final Set<String> paths = new LinkedHashSet<String>();
        for (final FileDetails fileDetails : fileManager
                .findMatchingAntPath(gwtModuleXml)) {
            paths.add(fileDetails.getCanonicalPath());
        }
        if (paths.isEmpty()) {
            throw new IllegalStateException(
                    "Each module must have a gwt.xml file");
        }
        if (paths.size() > 1) {
            throw new IllegalStateException(
                    "Each module can only have only gwt.xml file: "
                            + paths.size());
        }
        return paths.iterator().next();
    }

    /**
     * Return the type arg for the client side method, given the domain method
     * return type. If domain method return type is List<Integer> or
     * Set<Integer>, returns the same. If domain method return type is
     * List<Employee>, return List<EmployeeProxy>
     *
     * @param returnType
     * @param projectMetadata
     * @param governorType
     * @return the GWT side leaf type as a JavaType
     */

    public JavaType getGwtSideLeafType(final JavaType returnType,
            final JavaType governorType, final boolean requestType,
            final boolean convertPrimitive) {
        if (returnType.isPrimitive() && convertPrimitive) {
            if (!requestType) {
                checkPrimitive(returnType);
            }
            return GwtUtils.convertPrimitiveType(returnType, requestType);
        }

        if (isTypeCommon(returnType)) {
            return returnType;
        }

        if (isCollectionType(returnType)) {
            final List<JavaType> args = returnType.getParameters();
            if (args != null && args.size() == 1) {
                final JavaType elementType = args.get(0);
                final JavaType convertedJavaType = getGwtSideLeafType(
                        elementType, governorType, requestType,
                        convertPrimitive);
                if (convertedJavaType == null) {
                    return null;
                }
                return new JavaType(returnType.getFullyQualifiedTypeName(), 0,
                        DataType.TYPE, null, Arrays.asList(convertedJavaType));
            }
            return returnType;
        }

        final ClassOrInterfaceTypeDetails ptmd = typeLocationService
                .getTypeDetails(returnType);
        if (isDomainObject(returnType, ptmd)) {
            if (isEmbeddable(ptmd)) {
                throw new IllegalStateException(
                        "GWT does not currently support embedding objects in entities, such as '"
                                + returnType.getSimpleTypeName() + "' in '"
                                + governorType.getSimpleTypeName() + "'.");
            }
            final ClassOrInterfaceTypeDetails typeDetails = typeLocationService
                    .getTypeDetails(returnType);
            if (typeDetails == null) {
                return null;
            }
            final ClassOrInterfaceTypeDetails proxy = lookupProxyFromEntity(typeDetails);
            if (proxy == null) {
                return null;
            }
            return proxy.getName();
        }
        return returnType;
    }

    public Document getGwtXmlDocument(final String gwtModuleCanonicalPath) {
        final DocumentBuilder builder = XmlUtils.getDocumentBuilder();
        builder.setEntityResolver(new EntityResolver() {
            public InputSource resolveEntity(final String publicId,
                    final String systemId) throws SAXException, IOException {
                if (systemId.endsWith("gwt-module.dtd")) {
                    return new InputSource(FileUtils.getInputStream(
                            GwtScaffoldMetadata.class,
                            "templates/gwt-module.dtd"));
                }
                // Use the default behaviour
                return null;
            }
        });

        InputStream inputStream = null;
        try {
            inputStream = fileManager.getInputStream(gwtModuleCanonicalPath);
            return builder.parse(inputStream);
        }
        catch (final Exception e) {
            throw new IllegalStateException(e);
        }
        finally {
            IOUtils.closeQuietly(inputStream);
        }
    }

    public List<MethodMetadata> getProxyMethods(
            final ClassOrInterfaceTypeDetails governorTypeDetails) {
        final List<MethodMetadata> proxyMethods = new ArrayList<MethodMetadata>();
        final MemberDetails memberDetails = memberDetailsScanner
                .getMemberDetails(GwtTypeServiceImpl.class.getName(),
                        governorTypeDetails);
        for (final MemberHoldingTypeDetails memberHoldingTypeDetails : memberDetails
                .getDetails()) {
            for (final MethodMetadata method : memberDetails.getMethods()) {
                if (!proxyMethods.contains(method)
                        && isPublicAccessor(method)
                        && isValidMethodReturnType(method,
                                memberHoldingTypeDetails)) {
                    if (method
                            .getCustomData()
                            .keySet()
                            .contains(CustomDataKeys.IDENTIFIER_ACCESSOR_METHOD)) {
                        proxyMethods.add(0, method);
                    }
                    else {
                        proxyMethods.add(method);
                    }
                }
            }
        }
        return proxyMethods;
    }

    public JavaType getServiceLocator(final String moduleName) {
        return new JavaType(projectOperations.getTopLevelPackage(moduleName)
                + ".server.locator.GwtServiceLocator");
    }

    public Collection<JavaPackage> getSourcePackages(final String moduleName) {
        final Document gwtXmlDoc = getGwtXmlDocument(getGwtModuleXml(moduleName));
        final Element gwtXmlRoot = gwtXmlDoc.getDocumentElement();
        final JavaPackage topLevelPackage = projectOperations
                .getTopLevelPackage(moduleName);
        final Collection<JavaPackage> sourcePackages = new HashSet<JavaPackage>();
        for (final Element sourcePathElement : XmlUtils.findElements(
                "/module/source", gwtXmlRoot)) {
            final String relativePackage = sourcePathElement.getAttribute(PATH)
                    .replace(GwtOperations.PATH_DELIMITER, ".");
            sourcePackages.add(new JavaPackage(topLevelPackage + "."
                    + relativePackage));
        }
        return sourcePackages;
    }

    private boolean isAllowableReturnType(final JavaType type) {
        return isCommonType(type) || isEntity(type) || isEnum(type);
    }

    private boolean isAllowableReturnType(final MethodMetadata method) {
        return isAllowableReturnType(method.getReturnType());
    }

    private boolean isCollectionType(final JavaType returnType) {
        return returnType.getFullyQualifiedTypeName().equals(
                LIST.getFullyQualifiedTypeName())
                || returnType.getFullyQualifiedTypeName().equals(
                        SET.getFullyQualifiedTypeName());
    }

    private boolean isCommonType(final JavaType type) {
        return isTypeCommon(type) || isCollectionType(type)
                && type.getParameters().size() == 1
                && isAllowableReturnType(type.getParameters().get(0));
    }

    public boolean isDomainObject(final JavaType type) {
        final ClassOrInterfaceTypeDetails ptmd = typeLocationService
                .getTypeDetails(type);
        return isDomainObject(type, ptmd);
    }

    private boolean isDomainObject(final JavaType returnType,
            final ClassOrInterfaceTypeDetails ptmd) {
        return !isEnum(ptmd) && isEntity(returnType)
                && !isRequestFactoryCompatible(returnType)
                && !isEmbeddable(ptmd);
    }

    private boolean isEmbeddable(final ClassOrInterfaceTypeDetails ptmd) {
        if (ptmd == null) {
            return false;
        }
        final AnnotationMetadata annotationMetadata = ptmd
                .getAnnotation(EMBEDDABLE);
        return annotationMetadata != null;
    }

    private boolean isEntity(final JavaType type) {
        return persistenceMemberLocator.getIdentifierFields(type).size() == 1;
    }

    private boolean isEnum(final ClassOrInterfaceTypeDetails ptmd) {
        return ptmd != null
                && ptmd.getPhysicalTypeCategory() == PhysicalTypeCategory.ENUMERATION;
    }

    private boolean isEnum(final JavaType type) {
        return isEnum(typeLocationService.getTypeDetails(type));
    }

    public boolean isMethodReturnTypeInSourcePath(final MethodMetadata method,
            final MemberHoldingTypeDetails memberHoldingTypeDetail,
            final Iterable<JavaPackage> sourcePackages) {
        final JavaType returnType = method.getReturnType();
        final boolean inSourcePath = isTypeInAnySourcePackage(returnType,
                sourcePackages);
        if (!inSourcePath
                && !isCommonType(returnType)
                && !JavaType.VOID_PRIMITIVE.getFullyQualifiedTypeName().equals(
                        returnType.getFullyQualifiedTypeName())) {
            displayWarning("The path to type "
                    + returnType.getFullyQualifiedTypeName()
                    + " which is used in type "
                    + memberHoldingTypeDetail.getName()
                    + " by the field '"
                    + method.getMethodName().getSymbolName()
                    + "' needs to be added to the module's gwt.xml file in order to be used in a Proxy.");
            return false;
        }
        return true;
    }

    private boolean isPrimitive(final JavaType type) {
        return type.isPrimitive() || isCollectionType(type)
                && type.getParameters().size() == 1
                && isPrimitive(type.getParameters().get(0));
    }

    private boolean isPublicAccessor(final MethodMetadata method) {
        return Modifier.isPublic(method.getModifier())
                && !method.getReturnType().equals(JavaType.VOID_PRIMITIVE)
                && method.getParameterTypes().isEmpty()
                && method.getMethodName().getSymbolName().startsWith("get");
    }

    private boolean isRequestFactoryCompatible(final JavaType type) {
        return isCommonType(type) || isCollectionType(type);
    }

    private boolean isTypeCommon(final JavaType type) {
        return JavaType.BOOLEAN_OBJECT.equals(type)
                || JavaType.CHAR_OBJECT.equals(type)
                || JavaType.BYTE_OBJECT.equals(type)
                || JavaType.SHORT_OBJECT.equals(type)
                || JavaType.INT_OBJECT.equals(type)
                || LONG_OBJECT.equals(type)
                || JavaType.FLOAT_OBJECT.equals(type)
                || JavaType.DOUBLE_OBJECT.equals(type)
                || JavaType.STRING.equals(type)
                || DATE.equals(type)
                || BIG_DECIMAL.equals(type)
                || type.isPrimitive()
                && !JavaType.VOID_PRIMITIVE.getFullyQualifiedTypeName().equals(
                        type.getFullyQualifiedTypeName());
    }

    private boolean isTypeInAnySourcePackage(final JavaType type,
            final Iterable<JavaPackage> sourcePackages) {
        for (final JavaPackage sourcePackage : sourcePackages) {
            if (type.getPackage().isWithin(sourcePackage)) {
                return true; // It's a project type
            }
            if (isCollectionType(type)
                    && type.getParameters().size() == 1
                    && type.getParameters().get(0).getPackage()
                            .isWithin(sourcePackage)) {
                return true; // It's a collection of a project type
            }
        }
        return false;
    }

    private boolean isValidMethodReturnType(final MethodMetadata method,
            final MemberHoldingTypeDetails memberHoldingTypeDetail) {
        final JavaType returnType = method.getReturnType();
        if (isPrimitive(returnType)) {
            displayWarning("The primitive field type, "
                    + method.getReturnType().getSimpleTypeName().toLowerCase()
                    + " of '"
                    + method.getMethodName().getSymbolName()
                    + "' in type "
                    + memberHoldingTypeDetail.getName().getSimpleTypeName()
                    + " is not currently support by GWT and will not be added to the scaffolded application.");
            return false;
        }

        final JavaSymbolName propertyName = new JavaSymbolName(
                StringUtils.uncapitalize(BeanInfoUtils
                        .getPropertyNameForJavaBeanMethod(method)
                        .getSymbolName()));
        if (!isAllowableReturnType(method)) {
            displayWarning("The field type "
                    + method.getReturnType().getFullyQualifiedTypeName()
                    + " of '"
                    + method.getMethodName().getSymbolName()
                    + "' in type "
                    + memberHoldingTypeDetail.getName().getSimpleTypeName()
                    + " is not currently support by GWT and will not be added to the scaffolded application.");
            return false;
        }
        if (propertyName.getSymbolName().equals("owner")) {
            displayWarning("'owner' is not allowed to be used as field name as it is currently reserved by GWT. Please rename the field 'owner' in type "
                    + memberHoldingTypeDetail.getName().getSimpleTypeName()
                    + ".");
            return false;
        }

        return true;
    }

    public ClassOrInterfaceTypeDetails lookupEntityFromLocator(
            final ClassOrInterfaceTypeDetails locator) {
        Validate.notNull(locator, "Locator is required");
        return lookupTargetFromX(locator, RooJavaType.ROO_GWT_LOCATOR);
    }

    public ClassOrInterfaceTypeDetails lookupEntityFromProxy(
            final ClassOrInterfaceTypeDetails proxy) {
        Validate.notNull(proxy, "Proxy is required");
        return lookupTargetFromX(proxy, RooJavaType.ROO_GWT_PROXY);
    }

    public ClassOrInterfaceTypeDetails lookupEntityFromRequest(
            final ClassOrInterfaceTypeDetails request) {
        Validate.notNull(request, "Request is required");
        return lookupTargetFromX(request, RooJavaType.ROO_GWT_REQUEST);
    }

    public ClassOrInterfaceTypeDetails lookupProxyFromEntity(
            final ClassOrInterfaceTypeDetails entity) {
        return lookupXFromEntity(entity, RooJavaType.ROO_GWT_PROXY);
    }

    public ClassOrInterfaceTypeDetails lookupProxyFromRequest(
            final ClassOrInterfaceTypeDetails request) {
        final AnnotationMetadata annotation = GwtUtils.getFirstAnnotation(
                request, RooJavaType.ROO_GWT_REQUEST);
        Validate.notNull(annotation, "Request '%s' isn't annotated with '%s'",
                request.getName(), RooJavaType.ROO_GWT_REQUEST);
        final AnnotationAttributeValue<?> attributeValue = annotation
                .getAttribute("value");
        final JavaType proxyType = new JavaType(
                GwtUtils.getStringValue(attributeValue));
        return lookupProxyFromEntity(typeLocationService
                .getTypeDetails(proxyType));
    }

    public ClassOrInterfaceTypeDetails lookupRequestFromEntity(
            final ClassOrInterfaceTypeDetails entity) {
        return lookupXFromEntity(entity, RooJavaType.ROO_GWT_REQUEST);
    }

    public ClassOrInterfaceTypeDetails lookupRequestFromProxy(
            final ClassOrInterfaceTypeDetails proxy) {
        final AnnotationMetadata annotation = GwtUtils.getFirstAnnotation(
                proxy, RooJavaType.ROO_GWT_PROXY);
        Validate.notNull(annotation, "Proxy '%s' isn't annotated with '%s'",
                proxy.getName(), RooJavaType.ROO_GWT_PROXY);
        final AnnotationAttributeValue<?> attributeValue = annotation
                .getAttribute("value");
        final JavaType serviceNameType = new JavaType(
                GwtUtils.getStringValue(attributeValue));
        return lookupRequestFromEntity(typeLocationService
                .getTypeDetails(serviceNameType));
    }

    public ClassOrInterfaceTypeDetails lookupUnmanagedRequestFromProxy(
            final ClassOrInterfaceTypeDetails proxy) {
        final AnnotationMetadata annotation = GwtUtils.getFirstAnnotation(
                proxy, RooJavaType.ROO_GWT_PROXY);
        Validate.notNull(annotation, "Proxy '%s' isn't annotated with '%s'",
                proxy.getName(), RooJavaType.ROO_GWT_PROXY);
        final AnnotationAttributeValue<?> attributeValue = annotation
                .getAttribute("value");
        final JavaType serviceNameType = new JavaType(
                GwtUtils.getStringValue(attributeValue));
        return lookupUnmanagedRequestFromEntity(typeLocationService
                .getTypeDetails(serviceNameType));
    }

    public ClassOrInterfaceTypeDetails lookupUnmanagedRequestFromEntity(
            final ClassOrInterfaceTypeDetails entity) {
        return lookupXFromEntity(entity, RooJavaType.ROO_GWT_UNMANAGED_REQUEST);
    }

    public ClassOrInterfaceTypeDetails lookupLocatorFromEntity(
            final ClassOrInterfaceTypeDetails entity) {
        return lookupXFromEntity(entity, RooJavaType.ROO_GWT_LOCATOR);
    }

    public ClassOrInterfaceTypeDetails lookupTargetFromX(
            final ClassOrInterfaceTypeDetails annotatedType,
            final JavaType... annotations) {
        final AnnotationMetadata annotation = GwtUtils.getFirstAnnotation(
                annotatedType, annotations);
        Validate.notNull(annotation,
                "Type '" + annotatedType.getName() + "' isn't annotated with '"
                        + StringUtils.join(Arrays.asList(annotations), ",")
                        + "'");
        final AnnotationAttributeValue<?> attributeValue = annotation
                .getAttribute("value");
        final JavaType targetType = new JavaType(
                GwtUtils.getStringValue(attributeValue));
        return typeLocationService.getTypeDetails(targetType);
    }

    public ClassOrInterfaceTypeDetails lookupTargetServiceFromRequest(
            final ClassOrInterfaceTypeDetails request) {
        Validate.notNull(request, "Request is required");
        return lookupTargetFromX(request, GwtUtils.REQUEST_ANNOTATIONS);
    }

    public ClassOrInterfaceTypeDetails lookupXFromEntity(
            final ClassOrInterfaceTypeDetails entity,
            final JavaType... annotations) {
        Validate.notNull(entity, "Entity not found");
        for (final ClassOrInterfaceTypeDetails cid : typeLocationService
                .findClassesOrInterfaceDetailsWithAnnotation(annotations)) {
            final AnnotationMetadata annotationMetadata = GwtUtils
                    .getFirstAnnotation(cid, annotations);
            if (annotationMetadata != null) {
                final AnnotationAttributeValue<?> attributeValue = annotationMetadata
                        .getAttribute("value");
                final String value = GwtUtils.getStringValue(attributeValue);
                if (entity.getName().getFullyQualifiedTypeName().equals(value)) {
                    return cid;
                }
            }
        }
        return null;
    }

    private Map<JavaSymbolName, JavaType> resolveTypes(final JavaType generic,
            final JavaType typed) {
        final Map<JavaSymbolName, JavaType> typeMap = new LinkedHashMap<JavaSymbolName, JavaType>();
        final boolean typeCountMatch = generic.getParameters().size() == typed
                .getParameters().size();
        Validate.isTrue(typeCountMatch, "Type count must match.");

        int i = 0;
        for (final JavaType genericParamType : generic.getParameters()) {
            typeMap.put(genericParamType.getArgName(), typed.getParameters()
                    .get(i));
            i++;
        }
        return typeMap;
    }
}
TOP

Related Classes of org.springframework.roo.addon.gwt.GwtTypeServiceImpl

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.