Package org.springframework.roo.addon.jpa.activerecord

Source Code of org.springframework.roo.addon.jpa.activerecord.JpaActiveRecordMetadata

package org.springframework.roo.addon.jpa.activerecord;

import static org.springframework.roo.model.JavaType.INT_PRIMITIVE;
import static org.springframework.roo.model.JavaType.STRING;
import static org.springframework.roo.model.JdkJavaType.LIST;
import static org.springframework.roo.model.JpaJavaType.ENTITY_MANAGER;
import static org.springframework.roo.model.JpaJavaType.PERSISTENCE_CONTEXT;
import static org.springframework.roo.model.SpringJavaType.PROPAGATION;
import static org.springframework.roo.model.SpringJavaType.TRANSACTIONAL;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.springframework.roo.classpath.PhysicalTypeIdentifierNamingUtils;
import org.springframework.roo.classpath.PhysicalTypeMetadata;
import org.springframework.roo.classpath.customdata.CustomDataKeys;
import org.springframework.roo.classpath.details.FieldMetadata;
import org.springframework.roo.classpath.details.FieldMetadataBuilder;
import org.springframework.roo.classpath.details.MemberFindingUtils;
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.AnnotationMetadataBuilder;
import org.springframework.roo.classpath.itd.AbstractItdTypeDetailsProvidingMetadataItem;
import org.springframework.roo.classpath.itd.InvocableMemberBodyBuilder;
import org.springframework.roo.metadata.MetadataIdentificationUtils;
import org.springframework.roo.model.DataType;
import org.springframework.roo.model.EnumDetails;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.project.LogicalPath;

/**
* Metadata for a type annotated with {@link RooJpaActiveRecord}.
*
* @author Ben Alex
* @author Stefan Schmidt
* @author Alan Stewart
* @since 1.0
*/
public class JpaActiveRecordMetadata extends
        AbstractItdTypeDetailsProvidingMetadataItem {

    private static final JavaType COUNT_RETURN_TYPE = JavaType.LONG_PRIMITIVE;
    private static final String ENTITY_MANAGER_METHOD_NAME = "entityManager";
    private static final String PROVIDES_TYPE_STRING = JpaActiveRecordMetadata.class
            .getName();
    private static final String PROVIDES_TYPE = MetadataIdentificationUtils
            .create(PROVIDES_TYPE_STRING);

    private JpaCrudAnnotationValues crudAnnotationValues;
    private MethodMetadata entityManagerMethod;
    private String entityName;
    private MethodMetadata findMethod;
    private FieldMetadata identifierField;
    private boolean isGaeEnabled;
    private JpaActiveRecordMetadata parent;
    private String plural;

    /**
     * Constructor
     *
     * @param metadataIdentificationString (required)
     * @param aspectName (required)
     * @param governorPhysicalTypeMetadata (required)
     * @param parent can be <code>null</code>
     * @param projectMetadata (required)
     * @param crudAnnotationValues the CRUD-related annotation values (required)
     * @param plural the plural form of the entity (required)
     * @param identifierField the entity's identifier field (required)
     * @param entityName the JPA entity name (required)
     */
    public JpaActiveRecordMetadata(final String metadataIdentificationString,
            final JavaType aspectName,
            final PhysicalTypeMetadata governorPhysicalTypeMetadata,
            final JpaActiveRecordMetadata parent,
            final JpaCrudAnnotationValues crudAnnotationValues,
            final String plural, final FieldMetadata identifierField,
            final String entityName, final boolean isGaeEnabled) {
        super(metadataIdentificationString, aspectName,
                governorPhysicalTypeMetadata);
        Validate.isTrue(
                isValid(metadataIdentificationString),
                "Metadata identification string '%s' does not appear to be a valid",
                metadataIdentificationString);
        Validate.notNull(crudAnnotationValues,
                "CRUD-related annotation values required");
        Validate.notNull(identifierField, "Identifier required for '%s'",
                metadataIdentificationString);
        Validate.notBlank(entityName, "Entity name required for '%s'",
                metadataIdentificationString);
        Validate.notBlank(plural, "Plural required for '%s'",
                metadataIdentificationString);

        if (!isValid()) {
            return;
        }

        this.crudAnnotationValues = crudAnnotationValues;
        this.entityName = entityName;
        this.identifierField = identifierField;
        this.isGaeEnabled = isGaeEnabled;
        this.parent = parent;
        this.plural = StringUtils.capitalize(plural);

        // Determine the entity's "entityManager" field, which is guaranteed to
        // be accessible to the ITD.
        builder.addField(getEntityManagerField());
       
        builder.addField(getFieldNames4OrderClauseFilter());

        // Add static methods
        setEntityManagerMethod();
        builder.addMethod(getCountMethod());
        builder.addMethod(getFindAllMethod());
        builder.addMethod(getFindAllSortedMethod());
        setFindMethod();
        builder.addMethod(getFindEntriesMethod());
        builder.addMethod(getFindEntriesSortedMethod());

        // Add helper methods
        builder.addMethod(getPersistMethod());
        builder.addMethod(getRemoveMethod());
        builder.addMethod(getFlushMethod());
        builder.addMethod(getClearMethod());
        builder.addMethod(getMergeMethod());

        builder.putCustomData(CustomDataKeys.DYNAMIC_FINDER_NAMES,
                getDynamicFinders());

        // Create a representation of the desired output ITD
        itdTypeDetails = builder.build();
    }


  public static String createIdentifier(final JavaType javaType,
            final LogicalPath path) {
        return PhysicalTypeIdentifierNamingUtils.createIdentifier(
                PROVIDES_TYPE_STRING, javaType, path);
    }

    public static JavaType getJavaType(final String metadataIdentificationString) {
        return PhysicalTypeIdentifierNamingUtils.getJavaType(
                PROVIDES_TYPE_STRING, metadataIdentificationString);
    }

    public static String getMetadataIdentifierType() {
        return PROVIDES_TYPE;
    }

    public static LogicalPath getPath(final String metadataIdentificationString) {
        return PhysicalTypeIdentifierNamingUtils.getPath(PROVIDES_TYPE_STRING,
                metadataIdentificationString);
    }

    public static boolean isValid(final String metadataIdentificationString) {
        return PhysicalTypeIdentifierNamingUtils.isValid(PROVIDES_TYPE_STRING,
                metadataIdentificationString);
    }

    /**
     * @return the dynamic, custom finders (never returns null, but may return
     *         an empty list)
     */
    public List<String> getDynamicFinders() {
        if (crudAnnotationValues.getFinders() == null) {
            return Collections.emptyList();
        }
        return Arrays.asList(crudAnnotationValues.getFinders());
    }

    /**
     * Locates the entity manager field that should be used.
     * <p>
     * If a parent is defined, it must provide the field unless a persistent
     * unit name is supplied on the child entity.
     * <p>
     * We generally expect the field to be named "entityManager" and be of type
     * javax.persistence.EntityManager. We also require it to be public or
     * protected, and annotated with @PersistenceContext. If there is an
     * existing field which doesn't meet these latter requirements, we add an
     * underscore prefix to the "entityManager" name and try again, until such
     * time as we come up with a unique name that either meets the requirements
     * or the name is not used and we will create it.
     *
     * @return the entity manager field (never returns null)
     */
    public FieldMetadata getEntityManagerField() {
        if (parent != null
                && StringUtils.isBlank(crudAnnotationValues
                        .getPersistenceUnit())) {
            // The parent is required to guarantee this is available
            return parent.getEntityManagerField();
        }

        // Need to locate it ourself
        int index = -1;
        while (true) {
            // Compute the required field name
            index++;
            final JavaSymbolName fieldSymbolName = new JavaSymbolName(
                    StringUtils.repeat("_", index) + "entityManager");
            final FieldMetadata candidate = governorTypeDetails
                    .getField(fieldSymbolName);
            if (candidate != null) {
                // Verify if candidate is suitable

                if (!Modifier.isPublic(candidate.getModifier())
                        && !Modifier.isProtected(candidate.getModifier())
                        && Modifier.TRANSIENT != candidate.getModifier()) {
                    // Candidate is not public and not protected and not simply
                    // a transient field (in which case subclasses
                    // will see the inherited field), so any subsequent
                    // subclasses won't be able to see it. Give up!
                    continue;
                }

                if (!candidate.getFieldType().equals(ENTITY_MANAGER)) {
                    // Candidate isn't an EntityManager, so give up
                    continue;
                }

                if (MemberFindingUtils.getAnnotationOfType(
                        candidate.getAnnotations(), PERSISTENCE_CONTEXT) == null) {
                    // Candidate doesn't have a PersistenceContext annotation,
                    // so give up
                    continue;
                }

                // If we got this far, we found a valid candidate
                return candidate;
            }

            // Candidate not found, so let's create one
            final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
            final AnnotationMetadataBuilder annotationBuilder = new AnnotationMetadataBuilder(
                    PERSISTENCE_CONTEXT);
            if (StringUtils.isNotBlank(crudAnnotationValues
                    .getPersistenceUnit())) {
                annotationBuilder.addStringAttribute("unitName",
                        crudAnnotationValues.getPersistenceUnit());
            }
            annotations.add(annotationBuilder);

            return new FieldMetadataBuilder(getId(), Modifier.TRANSIENT,
                    annotations, fieldSymbolName, ENTITY_MANAGER).build();
        }
    }
   
   
    /**
     * @return list of filedNames allowed for the "order by" clause (used to avoid JPQL injection)
     */
    public FieldMetadata getFieldNames4OrderClauseFilter() {    
     
      JavaSymbolName fieldName = new JavaSymbolName("fieldNames4OrderClauseFilter");
      JavaType fieldType = JavaType.listOf(JavaType.STRING);
     
        // Locate user-defined method
        final FieldMetadata userField = governorTypeDetails.getField(fieldName);
        if (userField != null) {
            return userField;
        }
       
      List<String> listOfFieldNames = new ArrayList<String>();    
      for (final FieldMetadata field : governorPhysicalTypeMetadata
                .getMemberHoldingTypeDetails().getDeclaredFields()) {
        listOfFieldNames.add(field.getFieldName().getSymbolName());
      }    
     
      String listOfFieldNamesAsStringExpr = "java.util.Arrays.asList(\""+ StringUtils.join(listOfFieldNames, "\", \"") + "\")";     
     
      return new FieldMetadataBuilder(getId(), Modifier.FINAL + Modifier.STATIC + Modifier.PUBLIC,
          fieldName, fieldType, listOfFieldNamesAsStringExpr).build();
  }
   
   
    /**
     * @return the static utility entityManager() method used by other methods
     *         to obtain entity manager and available as a utility for user code
     *         (never returns nulls)
     */
    public MethodMetadata getEntityManagerMethod() {
        return entityManagerMethod;
    }

    /**
     * Returns the JPA name of this entity.
     *
     * @return a non-<code>null</code> name (might be empty)
     */
    public String getEntityName() {
        return entityName;
    }

    /**
     * @return the find (by ID) method (may return null)
     */
    public MethodMetadata getFindMethod() {
        return findMethod;
    }

    /**
     * @return the pluralised name (never returns null or an empty string)
     */
    public String getPlural() {
        return plural;
    }

    @Override
    public String toString() {
        final ToStringBuilder builder = new ToStringBuilder(this);
        builder.append("identifier", getId());
        builder.append("valid", valid);
        builder.append("aspectName", aspectName);
        builder.append("destinationType", destination);
        builder.append("finders", crudAnnotationValues.getFinders());
        builder.append("governor", governorPhysicalTypeMetadata.getId());
        builder.append("itdTypeDetails", itdTypeDetails);
        return builder.toString();
    }

    private void addTransactionalAnnotation(
            final List<AnnotationMetadataBuilder> annotations) {
        addTransactionalAnnotation(annotations, false);
    }

    private void addTransactionalAnnotation(
            final List<AnnotationMetadataBuilder> annotations,
            final boolean isPersistMethod) {
        final AnnotationMetadataBuilder transactionalBuilder = new AnnotationMetadataBuilder(
                TRANSACTIONAL);
        if (StringUtils
                .isNotBlank(crudAnnotationValues.getTransactionManager())) {
            transactionalBuilder.addStringAttribute("value",
                    crudAnnotationValues.getTransactionManager());
        }
        if (isGaeEnabled && isPersistMethod) {
            transactionalBuilder.addEnumAttribute("propagation",
                    new EnumDetails(PROPAGATION, new JavaSymbolName(
                            "REQUIRES_NEW")));
        }
        annotations.add(transactionalBuilder);
    }

    /**
     * @return the clear method (never returns null)
     */
    private MethodMetadataBuilder getClearMethod() {
        if (parent != null) {
            final MethodMetadataBuilder found = parent.getClearMethod();
            if (found != null) {
                return found;
            }
        }
        if ("".equals(crudAnnotationValues.getClearMethod())) {
            return null;
        }
        return getDelegateMethod(
                new JavaSymbolName(crudAnnotationValues.getClearMethod()),
                "clear");
    }

    /**
     * Finds (creating if necessary) the method that counts entities of this
     * type
     *
     * @return the count method (never null)
     */
    private MethodMetadata getCountMethod() {
        // Method definition to find or build
        final JavaSymbolName methodName = new JavaSymbolName(
                crudAnnotationValues.getCountMethod() + plural);
        final JavaType[] parameterTypes = {};
        final List<JavaSymbolName> parameterNames = Collections
                .<JavaSymbolName> emptyList();

        // Locate user-defined method
        final MethodMetadata userMethod = getGovernorMethod(methodName,
                parameterTypes);
        if (userMethod != null) {
            Validate.isTrue(userMethod.getReturnType()
                    .equals(COUNT_RETURN_TYPE),
                    "Method '%s' on '%s' must return '%s'", methodName,
                    destination, COUNT_RETURN_TYPE
                            .getNameIncludingTypeParameters());
            return userMethod;
        }

        // Create method
        final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
        if (isGaeEnabled) {
            addTransactionalAnnotation(annotations);
        }

        final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
        if (isGaeEnabled) {
            bodyBuilder.appendFormalLine("return "
                    + getFindAllMethod().getMethodName() + "().size();");
        }
        else {
            bodyBuilder.appendFormalLine("return " + ENTITY_MANAGER_METHOD_NAME
                    + "().createQuery(\"SELECT COUNT(o) FROM " + entityName
                    + " o\", Long.class).getSingleResult();");
        }

        final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
                getId(), Modifier.PUBLIC | Modifier.STATIC, methodName,
                COUNT_RETURN_TYPE,
                AnnotatedJavaType.convertFromJavaTypes(parameterTypes),
                parameterNames, bodyBuilder);
        methodBuilder.setAnnotations(annotations);
        return methodBuilder.build();
    }

    private MethodMetadataBuilder getDelegateMethod(
            final JavaSymbolName methodName, final String methodDelegateName) {
        // Method definition to find or build
        final JavaType[] parameterTypes = {};

        // Locate user-defined method
        final MethodMetadata userMethod = getGovernorMethod(methodName,
                parameterTypes);
        if (userMethod != null) {
            return new MethodMetadataBuilder(userMethod);
        }

        // Create the method
        final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();

        final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();

        // Address non-injected entity manager field
        final MethodMetadata entityManagerMethod = getEntityManagerMethod();
        Validate.notNull(entityManagerMethod,
                "Entity manager method should not have returned null");

        // Use the getEntityManager() method to acquire an entity manager (the
        // method will throw an exception if it cannot be acquired)
        final String entityManagerFieldName = getEntityManagerField()
                .getFieldName().getSymbolName();
        bodyBuilder.appendFormalLine("if (this." + entityManagerFieldName
                + " == null) this." + entityManagerFieldName + " = "
                + entityManagerMethod.getMethodName().getSymbolName() + "();");

        JavaType returnType = JavaType.VOID_PRIMITIVE;
        if ("flush".equals(methodDelegateName)) {
            addTransactionalAnnotation(annotations);
            bodyBuilder.appendFormalLine("this." + entityManagerFieldName
                    + ".flush();");
        }
        else if ("clear".equals(methodDelegateName)) {
            addTransactionalAnnotation(annotations);
            bodyBuilder.appendFormalLine("this." + entityManagerFieldName
                    + ".clear();");
        }
        else if ("merge".equals(methodDelegateName)) {
            addTransactionalAnnotation(annotations);
            returnType = new JavaType(destination.getSimpleTypeName());
            bodyBuilder.appendFormalLine(destination.getSimpleTypeName()
                    + " merged = this." + entityManagerFieldName
                    + ".merge(this);");
            bodyBuilder.appendFormalLine("this." + entityManagerFieldName
                    + ".flush();");
            bodyBuilder.appendFormalLine("return merged;");
        }
        else if ("remove".equals(methodDelegateName)) {
            addTransactionalAnnotation(annotations);
            bodyBuilder.appendFormalLine("if (this." + entityManagerFieldName
                    + ".contains(this)) {");
            bodyBuilder.indent();
            bodyBuilder.appendFormalLine("this." + entityManagerFieldName
                    + ".remove(this);");
            bodyBuilder.indentRemove();
            bodyBuilder.appendFormalLine("} else {");
            bodyBuilder.indent();
            bodyBuilder.appendFormalLine(destination.getSimpleTypeName()
                    + " attached = " + destination.getSimpleTypeName() + "."
                    + getFindMethod().getMethodName().getSymbolName()
                    + "(this." + identifierField.getFieldName().getSymbolName()
                    + ");");
            bodyBuilder.appendFormalLine("this." + entityManagerFieldName
                    + ".remove(attached);");
            bodyBuilder.indentRemove();
            bodyBuilder.appendFormalLine("}");
        }
        else {
            // Persist
            addTransactionalAnnotation(annotations, true);
            bodyBuilder.appendFormalLine("this." + entityManagerFieldName + "."
                    + methodDelegateName + "(this);");
        }

        final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
                getId(), Modifier.PUBLIC, methodName, returnType,
                AnnotatedJavaType.convertFromJavaTypes(parameterTypes),
                new ArrayList<JavaSymbolName>(), bodyBuilder);
        methodBuilder.setAnnotations(annotations);
        return methodBuilder;
    }

    /**
     * @return the find all method (may return null)
     */
    private MethodMetadata getFindAllMethod() {
        if ("".equals(crudAnnotationValues.getFindAllMethod())) {
            return null;
        }

        // Method definition to find or build
        final JavaSymbolName methodName = new JavaSymbolName(
                crudAnnotationValues.getFindAllMethod() + plural);
        final JavaType[] parameterTypes = {};
        final List<JavaSymbolName> parameterNames = new ArrayList<JavaSymbolName>();
        final JavaType returnType = new JavaType(
                LIST.getFullyQualifiedTypeName(), 0, DataType.TYPE, null,
                Arrays.asList(destination));

        // Locate user-defined method
        final MethodMetadata userMethod = getGovernorMethod(methodName,
                parameterTypes);
        if (userMethod != null) {
            Validate.isTrue(userMethod.getReturnType().equals(returnType),
                    "Method '%s' on '%s' must return '%s'", methodName,
                    destination, returnType.getNameIncludingTypeParameters());
            return userMethod;
        }

        // Create method
        final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
        if (isGaeEnabled) {
            addTransactionalAnnotation(annotations);
        }
       
        final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
        bodyBuilder.appendFormalLine("return " + ENTITY_MANAGER_METHOD_NAME
                + "().createQuery(\"SELECT o FROM " + entityName + " o\", "
                + destination.getSimpleTypeName() + ".class).getResultList();");

        final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
                getId(), Modifier.PUBLIC | Modifier.STATIC, methodName,
                returnType,
                AnnotatedJavaType.convertFromJavaTypes(parameterTypes),
                parameterNames, bodyBuilder);
        methodBuilder.setAnnotations(annotations);
        return methodBuilder.build();
    }
   
    /**
     * getFindAllMethod method with sortFieldName and sortOrder parameters
     *
     * @return the find all method (may return null)
     */
    private MethodMetadata getFindAllSortedMethod() {
        if ("".equals(crudAnnotationValues.getFindAllSortedMethod())) {
            return null;
        }

        // Method definition to find or build
        final JavaSymbolName methodName = new JavaSymbolName(
                crudAnnotationValues.getFindAllSortedMethod() + plural);
        final JavaType[] parameterTypes = {STRING, STRING};
        final List<JavaSymbolName> parameterNames = Arrays.asList(
            new JavaSymbolName("sortFieldName"),
                new JavaSymbolName("sortOrder"));
        final JavaType returnType = new JavaType(
                LIST.getFullyQualifiedTypeName(), 0, DataType.TYPE, null,
                Arrays.asList(destination));

        // Locate user-defined method
        final MethodMetadata userMethod = getGovernorMethod(methodName,
                parameterTypes);
        if (userMethod != null) {
            Validate.isTrue(userMethod.getReturnType().equals(returnType),
                    "Method '%s' on '%s' must return '%s'", methodName,
                    destination, returnType.getNameIncludingTypeParameters());
            return userMethod;
        }

        // Create method
        final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
        if (isGaeEnabled) {
            addTransactionalAnnotation(annotations);
        }
       
        final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
        bodyBuilder.appendFormalLine("String jpaQuery = \"SELECT o FROM " + entityName + " o\";");
        bodyBuilder.appendFormalLine("if (fieldNames4OrderClauseFilter.contains(sortFieldName)) {");
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine("jpaQuery = jpaQuery + \" ORDER BY \" + sortFieldName;");
        bodyBuilder.appendFormalLine("if (\"ASC\".equalsIgnoreCase(sortOrder) || \"DESC\".equalsIgnoreCase(sortOrder)) {");
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine("jpaQuery = jpaQuery + \" \" + sortOrder;");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");
        bodyBuilder.appendFormalLine("return " + ENTITY_MANAGER_METHOD_NAME
            + "().createQuery(jpaQuery, " + destination.getSimpleTypeName() + ".class" + ").getResultList();");

        final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
                getId(), Modifier.PUBLIC | Modifier.STATIC, methodName,
                returnType,
                AnnotatedJavaType.convertFromJavaTypes(parameterTypes),
                parameterNames, bodyBuilder);
        methodBuilder.setAnnotations(annotations);
        return methodBuilder.build();
    }

    /**
     * @return the find entries method (may return null)
     */
    private MethodMetadata getFindEntriesMethod() {
        if ("".equals(crudAnnotationValues.getFindEntriesMethod())) {
            return null;
        }

        // Method definition to find or build
        final JavaSymbolName methodName = new JavaSymbolName(
                crudAnnotationValues.getFindEntriesMethod()
                        + destination.getSimpleTypeName() + "Entries");
        final JavaType[] parameterTypes = { INT_PRIMITIVE, INT_PRIMITIVE };
        final List<JavaSymbolName> parameterNames = Arrays.asList(
                new JavaSymbolName("firstResult"), new JavaSymbolName(
                        "maxResults"));
        final JavaType returnType = new JavaType(
                LIST.getFullyQualifiedTypeName(), 0, DataType.TYPE, null,
                Arrays.asList(destination));

        // Locate user-defined method
        final MethodMetadata userMethod = getGovernorMethod(methodName,
                parameterTypes);
        if (userMethod != null) {
            Validate.isTrue(userMethod.getReturnType().equals(returnType),
                    "Method '%s' on '%s' must return '%s'", methodName,
                    destination, returnType.getNameIncludingTypeParameters());
            return userMethod;
        }

        // Create method
        final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
        if (isGaeEnabled) {
            addTransactionalAnnotation(annotations);
        }

        final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
        bodyBuilder
                .appendFormalLine("return "
                        + ENTITY_MANAGER_METHOD_NAME
                        + "().createQuery(\"SELECT o FROM "
                        + entityName
                        + " o\", "
                        + destination.getSimpleTypeName()
                        + ".class).setFirstResult(firstResult).setMaxResults(maxResults).getResultList();");

        final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
                getId(), Modifier.PUBLIC | Modifier.STATIC, methodName,
                returnType,
                AnnotatedJavaType.convertFromJavaTypes(parameterTypes),
                parameterNames, bodyBuilder);
        methodBuilder.setAnnotations(annotations);
        return methodBuilder.build();
    }
   
    /**
     * getFindEntriesMethod method with sortFieldName and sortOrder parameters
     *
     * @return the find entries method (may return null)
     */
    private MethodMetadata getFindEntriesSortedMethod() {
        if ("".equals(crudAnnotationValues.getFindEntriesSortedMethod())) {
            return null;
        }

        // Method definition to find or build
        final JavaSymbolName methodName = new JavaSymbolName(
                crudAnnotationValues.getFindEntriesSortedMethod()
                        + destination.getSimpleTypeName() + "Entries");
        final JavaType[] parameterTypes = { INT_PRIMITIVE, INT_PRIMITIVE, JavaType.STRING, JavaType.STRING};
        final List<JavaSymbolName> parameterNames = Arrays.asList(
                new JavaSymbolName("firstResult"), new JavaSymbolName(
                        "maxResults"), new JavaSymbolName("sortFieldName"),
                        new JavaSymbolName("sortOrder"));
        final JavaType returnType = new JavaType(
                LIST.getFullyQualifiedTypeName(), 0, DataType.TYPE, null,
                Arrays.asList(destination));

        // Locate user-defined method
        final MethodMetadata userMethod = getGovernorMethod(methodName,
                parameterTypes);
        if (userMethod != null) {
            Validate.isTrue(userMethod.getReturnType().equals(returnType),
                    "Method '%s' on '%s' must return '%s'", methodName,
                    destination, returnType.getNameIncludingTypeParameters());
            return userMethod;
        }

        // Create method
        final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
        if (isGaeEnabled) {
            addTransactionalAnnotation(annotations);
        }

        final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();
        bodyBuilder.appendFormalLine("String jpaQuery = \"SELECT o FROM " + entityName + " o\";");
        bodyBuilder.appendFormalLine("if (fieldNames4OrderClauseFilter.contains(sortFieldName)) {");
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine("jpaQuery = jpaQuery + \" ORDER BY \" + sortFieldName;");
        bodyBuilder.appendFormalLine("if (\"ASC\".equalsIgnoreCase(sortOrder) || \"DESC\".equalsIgnoreCase(sortOrder)) {");
        bodyBuilder.indent();
        bodyBuilder.appendFormalLine("jpaQuery = jpaQuery + \" \" + sortOrder;");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");
        bodyBuilder.indentRemove();
        bodyBuilder.appendFormalLine("}");
        bodyBuilder.appendFormalLine("return " + ENTITY_MANAGER_METHOD_NAME
            + "().createQuery(jpaQuery, " + destination.getSimpleTypeName() + ".class).setFirstResult(firstResult).setMaxResults(maxResults).getResultList();");

        final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
                getId(), Modifier.PUBLIC | Modifier.STATIC, methodName,
                returnType,
                AnnotatedJavaType.convertFromJavaTypes(parameterTypes),
                parameterNames, bodyBuilder);
        methodBuilder.setAnnotations(annotations);
        return methodBuilder.build();
    }

    /**
     * @return the flush method (never returns null)
     */
    private MethodMetadataBuilder getFlushMethod() {
        if (parent != null) {
            final MethodMetadataBuilder found = parent.getFlushMethod();
            if (found != null) {
                return found;
            }
        }
        if ("".equals(crudAnnotationValues.getFlushMethod())) {
            return null;
        }
        return getDelegateMethod(
                new JavaSymbolName(crudAnnotationValues.getFlushMethod()),
                "flush");
    }

    /**
     * @return the merge method (may return null)
     */
    private MethodMetadataBuilder getMergeMethod() {
        if ("".equals(crudAnnotationValues.getMergeMethod())) {
            return null;
        }
        return getDelegateMethod(
                new JavaSymbolName(crudAnnotationValues.getMergeMethod()),
                "merge");
    }

    /**
     * @return the persist method (may return null)
     */
    private MethodMetadataBuilder getPersistMethod() {
        if (parent != null) {
            final MethodMetadataBuilder found = parent.getPersistMethod();
            if (found != null) {
                return found;
            }
        }
        if ("".equals(crudAnnotationValues.getPersistMethod())) {
            return null;
        }
        return getDelegateMethod(
                new JavaSymbolName(crudAnnotationValues.getPersistMethod()),
                "persist");
    }

    /**
     * @return the remove method (may return null)
     */
    private MethodMetadataBuilder getRemoveMethod() {
        if (parent != null) {
            final MethodMetadataBuilder found = parent.getRemoveMethod();
            if (found != null) {
                return found;
            }
        }
        if ("".equals(crudAnnotationValues.getRemoveMethod())) {
            return null;
        }
        return getDelegateMethod(
                new JavaSymbolName(crudAnnotationValues.getRemoveMethod()),
                "remove");
    }

    private void setEntityManagerMethod() {
        if (parent != null) {
            // The parent is required to guarantee this is available
            entityManagerMethod = parent.getEntityManagerMethod();
            return;
        }

        // Method definition to find or build
        final JavaSymbolName methodName = new JavaSymbolName(
                ENTITY_MANAGER_METHOD_NAME);
        final JavaType[] parameterTypes = {};
        final JavaType returnType = ENTITY_MANAGER;

        // Locate user-defined method
        final MethodMetadata userMethod = getGovernorMethod(methodName,
                parameterTypes);
        if (userMethod != null) {
            Validate.isTrue(userMethod.getReturnType().equals(returnType),
                    "Method '%s' on '%s' must return '%s'", methodName,
                    destination, returnType.getNameIncludingTypeParameters());
            entityManagerMethod = userMethod;
            return;
        }

        // Create method
        final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();

        if (Modifier.isAbstract(governorTypeDetails.getModifier())) {
            // Create an anonymous inner class that extends the abstract class
            // (no-arg constructor is available as this is a JPA entity)
            bodyBuilder.appendFormalLine(ENTITY_MANAGER
                    .getNameIncludingTypeParameters(false,
                            builder.getImportRegistrationResolver())
                    + " em = new " + destination.getSimpleTypeName() + "() {");
            // Handle any abstract methods in this class
            bodyBuilder.indent();
            for (final MethodMetadata method : governorTypeDetails.getMethods()) {
                if (Modifier.isAbstract(method.getModifier())) {
                    final StringBuilder params = new StringBuilder();
                    int i = -1;
                    final List<AnnotatedJavaType> types = method
                            .getParameterTypes();
                    for (final JavaSymbolName name : method.getParameterNames()) {
                        i++;
                        if (i > 0) {
                            params.append(", ");
                        }
                        final AnnotatedJavaType type = types.get(i);
                        params.append(type.toString()).append(" ").append(name);
                    }
                    final int newModifier = method.getModifier()
                            - Modifier.ABSTRACT;
                    bodyBuilder.appendFormalLine(Modifier.toString(newModifier)
                            + " "
                            + method.getReturnType()
                                    .getNameIncludingTypeParameters() + " "
                            + method.getMethodName().getSymbolName() + "("
                            + params.toString() + ") {");
                    bodyBuilder.indent();
                    bodyBuilder
                            .appendFormalLine("throw new UnsupportedOperationException();");
                    bodyBuilder.indentRemove();
                    bodyBuilder.appendFormalLine("}");
                }
            }
            bodyBuilder.indentRemove();
            bodyBuilder.appendFormalLine("}."
                    + getEntityManagerField().getFieldName().getSymbolName()
                    + ";");
        }
        else {
            // Instantiate using the no-argument constructor (we know this is
            // available as the entity must comply with the JPA no-arg
            // constructor requirement)
            bodyBuilder.appendFormalLine(ENTITY_MANAGER
                    .getNameIncludingTypeParameters(false,
                            builder.getImportRegistrationResolver())
                    + " em = new "
                    + destination.getSimpleTypeName()
                    + "()."
                    + getEntityManagerField().getFieldName().getSymbolName()
                    + ";");
        }

        bodyBuilder
                .appendFormalLine("if (em == null) throw new IllegalStateException(\"Entity manager has not been injected (is the Spring Aspects JAR configured as an AJC/AJDT aspects library?)\");");
        bodyBuilder.appendFormalLine("return em;");
        final int modifier = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL;

        final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
                getId(), modifier, methodName, returnType,
                AnnotatedJavaType.convertFromJavaTypes(parameterTypes),
                new ArrayList<JavaSymbolName>(), bodyBuilder);
        builder.addMethod(methodBuilder);
        entityManagerMethod = methodBuilder.build();
    }

    /**
     * @return the find (by ID) method (may return null)
     */
    private void setFindMethod() {
        if ("".equals(crudAnnotationValues.getFindMethod())) {
            return;
        }

        // Method definition to find or build
        final String idFieldName = identifierField.getFieldName()
                .getSymbolName();
        final JavaSymbolName methodName = new JavaSymbolName(
                crudAnnotationValues.getFindMethod()
                        + destination.getSimpleTypeName());
        final JavaType parameterType = identifierField.getFieldType();
        final List<JavaSymbolName> parameterNames = Arrays
                .asList(new JavaSymbolName(idFieldName));
        final JavaType returnType = destination;

        // Locate user-defined method
        final MethodMetadata userMethod = getGovernorMethod(methodName,
                parameterType);
        if (userMethod != null) {
            Validate.isTrue(
                    userMethod.getReturnType().equals(returnType),
                    "Method '" + methodName + "' on '" + returnType
                            + "' must return '"
                            + returnType.getNameIncludingTypeParameters() + "'");
            findMethod = userMethod;
            return;
        }

        // Create method
        final List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>();
        if (isGaeEnabled) {
            addTransactionalAnnotation(annotations);
        }

        final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder();

        if (JavaType.STRING.equals(identifierField.getFieldType())) {
            bodyBuilder.appendFormalLine("if (" + idFieldName + " == null || "
                    + idFieldName + ".length() == 0) return null;");
        }
        else if (!identifierField.getFieldType().isPrimitive()) {
            bodyBuilder.appendFormalLine("if (" + idFieldName
                    + " == null) return null;");
        }

        bodyBuilder.appendFormalLine("return " + ENTITY_MANAGER_METHOD_NAME
                + "().find(" + returnType.getSimpleTypeName() + ".class, "
                + idFieldName + ");");

        final MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
                getId(), Modifier.PUBLIC | Modifier.STATIC, methodName,
                returnType,
                AnnotatedJavaType.convertFromJavaTypes(parameterType),
                parameterNames, bodyBuilder);
        methodBuilder.setAnnotations(annotations);
        builder.addMethod(methodBuilder);
        findMethod = methodBuilder.build();
    }
   


}
TOP

Related Classes of org.springframework.roo.addon.jpa.activerecord.JpaActiveRecordMetadata

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.