com.wesleyhome.dao.processor.method.IncludeQueryMethodGenerator.java Source code

Java tutorial

Introduction

Here is the source code for com.wesleyhome.dao.processor.method.IncludeQueryMethodGenerator.java

Source

/*
 * Copyright 2014 Justin Wesley
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.wesleyhome.dao.processor.method;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.lang.model.element.Element;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import javax.persistence.Tuple;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Root;
import org.apache.commons.lang3.StringUtils;
import com.sun.codemodel.JBlock;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JConditional;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JFieldRef;
import com.sun.codemodel.JFieldVar;
import com.sun.codemodel.JForEach;
import com.sun.codemodel.JInvocation;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JOp;
import com.sun.codemodel.JType;
import com.sun.codemodel.JVar;
import com.wesleyhome.dao.processor.EntityInformationMap;
import com.wesleyhome.dao.processor.model.EntityInfo;
import com.wesleyhome.dao.processor.model.MappingType;
import com.wesleyhome.dao.processor.model.QueryParameterInfo;

/**
 * The <code>IncludeQueryMethodGenerator</code> class is a
 *
 * @author
 * @since
 */
public class IncludeQueryMethodGenerator implements MethodGenerator {
    @Override
    public void createMethods(final JCodeModel model, final JClass entityClassRef,
            final JDefinedClass generatedClass, final JFieldVar entityManagerField, final EntityInfo entityInfo,
            final EntityInformationMap entityInfoMap, final QueryParameterInfo queryParameterInfo) {
        VariableElement fieldElement = queryParameterInfo.getFieldElement();
        TypeMirror fieldTypeMirror = fieldElement.asType();
        String fieldTypeString = fieldTypeMirror.toString();
        JClass fieldType = model.ref(fieldTypeString);
        if (generatedClass.isInterface() || queryParameterInfo != null && queryParameterInfo.isGenerate()) {
            createCollectionParameterMethod(model, entityClassRef, generatedClass, fieldElement, fieldType,
                    entityManagerField, entityInfoMap, queryParameterInfo.getMappingType());
        }
    }

    private void createCollectionParameterMethod(final JCodeModel model, final JClass entityJClass,
            final JDefinedClass generatedClass, final Element fieldElement, final JClass fieldType,
            final JFieldVar entityManagerField, final EntityInformationMap entityInfoMap,
            final MappingType mappingType) {
        JMethod method = getMethod(model, entityJClass, generatedClass, fieldElement, fieldType, mappingType);
        JClass parameterType = getParameterType(model, fieldType, entityInfoMap, mappingType);
        JVar param = method.param(model.ref(Collection.class).narrow(parameterType),
                fieldElement.getSimpleName().toString() + "s");
        if (!generatedClass.isInterface()) {
            createCollectionParameterMethodBody(method, param, model, entityJClass, fieldElement, fieldType,
                    entityManagerField, mappingType);
        }
        createVarargsParameterMethod(model, entityJClass, generatedClass, fieldElement, fieldType, method,
                entityInfoMap, mappingType);
    }

    private void createVarargsParameterMethod(final JCodeModel model, final JClass entityJClass,
            final JDefinedClass generatedClass, final Element fieldElement, final JClass fieldType,
            final JMethod collectionMethod, final EntityInformationMap entityInfoMap,
            final MappingType mappingType) {
        JMethod method = getMethod(model, entityJClass, generatedClass, fieldElement, fieldType, mappingType);
        JClass parameterType = getParameterType(model, fieldType, entityInfoMap, mappingType);
        JVar param = method.param(parameterType, fieldElement.getSimpleName().toString());
        JVar varParam = method.varParam(parameterType, "additional");
        if (!generatedClass.isInterface()) {
            createVarargsParameterMethodBody(method, param, varParam, model, fieldElement, collectionMethod);
        }
    }

    /**
     * @param method
     * @param param
     * @param model
     * @param entityJClass
     * @param fieldElement
     * @param fieldType
     * @param entityManagerField
     * @param mappingType
     */
    private void createCollectionParameterMethodBody(final JMethod method, final JVar param, final JCodeModel model,
            final JClass entityJClass, final Element fieldElement, final JClass fieldType,
            final JFieldVar entityManagerField, final MappingType mappingType) {
        method.annotate(Override.class);
        JBlock body = method.body();
        JClass valueClass;
        switch (mappingType) {
        case MANY_TO_ONE:
        case OBJECT:
        default:
            valueClass = model.ref(List.class).narrow(entityJClass);
            break;
        case ONE_TO_ONE:
        case UNIQUE_OBJECT:
            valueClass = entityJClass;
        }
        JClass map = model.ref(Map.class).narrow(fieldType, valueClass);
        JVar mapDeclaration = body.decl(map, "map",
                JExpr._new(model.ref(TreeMap.class).narrow(fieldType, valueClass)));
        JConditional emptyIfBlock = body._if(JOp.eq(param, JExpr._null()).cor(param.invoke("isEmpty")));
        emptyIfBlock._then()._return(mapDeclaration);
        JClass cbClass = model.ref(CriteriaBuilder.class);
        if (entityManagerField == null) {
            throw new IllegalStateException("What happened to the entity manager field?!?!?!");
        }
        JInvocation invoke = JExpr._this().invoke("getCriteriaBuilder");
        JVar criteriaBuilderVar = body.decl(cbClass, "cb", invoke);
        JInvocation tupleQueryInvoke = criteriaBuilderVar.invoke("createTupleQuery");
        JClass criteriaQueryRef = model.ref(CriteriaQuery.class);
        JClass cqNarrow = criteriaQueryRef.narrow(Tuple.class);
        JVar cqVar = body.decl(cqNarrow, "cq", tupleQueryInvoke);
        JClass rootRef = model.ref(Root.class);
        JClass rootNarrow = rootRef.narrow(entityJClass);
        JInvocation fromInvoke = cqVar.invoke("from");
        fromInvoke.arg(entityJClass.staticRef("class"));
        JVar rootVar = body.decl(rootNarrow, "root", fromInvoke);
        String entityClassName = entityJClass.fullName();
        String entityMetamodelClassName = entityClassName + "_";
        JClass metamodelClass = model.ref(entityMetamodelClassName);
        String fieldName = fieldElement.getSimpleName().toString();
        JFieldRef fieldMetamodelRef = metamodelClass.staticRef(fieldName);
        JClass pathClass = model.ref(Path.class);
        JClass fieldTypeRef = model.ref(fieldElement.asType().toString());
        JClass pathNarrow = pathClass.narrow(fieldTypeRef);
        JInvocation getInvoke = rootVar.invoke("get");
        getInvoke.arg(fieldMetamodelRef);
        JVar pathVar = body.decl(pathNarrow, fieldName + "Path", getInvoke);
        body.add(cqVar.invoke("multiselect").arg(pathVar).arg(rootVar));
        body.add(cqVar.invoke("where").arg(pathVar.invoke("in").arg(param)));
        JClass listTupleClass = model.ref(List.class).narrow(Tuple.class);
        JVar resultList = body.decl(listTupleClass, "resultList", JExpr._this().invoke("getResultList").arg(cqVar));
        JType tupleType = model.ref(Tuple.class);
        JForEach forEach = body.forEach(tupleType, "tuple", resultList);
        JBlock forEachBody = forEach.body();
        JVar var = forEach.var();
        JInvocation argInvocation = var.invoke("get").arg(pathVar);
        if (String.class.getName().equals(fieldType.binaryName())) {
            argInvocation = argInvocation.invoke("trim");
        } else {
            body.directStatement("// " + fieldType.binaryName());
        }
        JVar keyVar = forEachBody.decl(fieldType, fieldName, argInvocation);
        String varName = "value";
        switch (mappingType) {
        case MANY_TO_ONE:
        case OBJECT:
            varName = "list";
            break;
        case ONE_TO_ONE:
        case UNIQUE_OBJECT:
        default:
        }
        JVar valueVar = forEachBody.decl(entityJClass, StringUtils.uncapitalize(entityJClass.name()),
                var.invoke("get").arg(rootVar));
        switch (mappingType) {
        case MANY_TO_ONE:
        case OBJECT:
        default:
            JVar listVar = forEachBody.decl(valueClass, varName, mapDeclaration.invoke("get").arg(keyVar));
            JConditional ifCondition = forEachBody._if(JExpr._null().eq(listVar));
            JBlock ifBody = ifCondition._then();
            ifBody.assign(listVar, JExpr._new(model.ref(ArrayList.class).narrow(entityJClass)));
            ifBody.add(mapDeclaration.invoke("put").arg(keyVar).arg(listVar));
            forEachBody.invoke(listVar, "add").arg(valueVar);
            break;
        case ONE_TO_ONE:
        case UNIQUE_OBJECT:
            forEachBody.add(mapDeclaration.invoke("put").arg(keyVar).arg(valueVar));
            break;
        }
        body._return(mapDeclaration);
    }

    /**
     * @param method
     * @param param
     * @param varParam
     * @param model
     * @param fieldElement
     * @param collectionMethod
     */
    private void createVarargsParameterMethodBody(final JMethod method, final JVar param, final JVar varParam,
            final JCodeModel model, final Element fieldElement, final JMethod collectionMethod) {
        method.annotate(Override.class);
        JBlock body = method.body();
        JType type = param.type();
        JClass listClass = model.ref(List.class).narrow(type);
        JInvocation arrayList = JExpr._new(model.ref(ArrayList.class).narrow(type));
        JVar listVar = body.decl(listClass, fieldElement.getSimpleName().toString() + "s", arrayList);
        JInvocation addInvoke = listVar.invoke("add").arg(param);
        JInvocation addAllInvoke = listVar.invoke("addAll")
                .arg(model.ref(Arrays.class).staticInvoke("asList").arg(varParam));
        body.add(addInvoke);
        body.add(addAllInvoke);
        JInvocation methodInvoke = JExpr.invoke(collectionMethod).arg(listVar);
        body._return(methodInvoke);
    }

    /**
     * @param model
     * @param entityJClass
     * @param generatedClass
     * @param fieldElement
     * @param fieldType
     * @param mappingType
     * @return
     */
    private JMethod getMethod(final JCodeModel model, final JClass entityJClass, final JDefinedClass generatedClass,
            final Element fieldElement, final JClass fieldType, final MappingType mappingType) {
        JClass returnClass = getReturnClass(model, entityJClass, fieldType, mappingType);
        String entityName = entityJClass.name();
        String methodName = getMethodName(entityName, fieldElement);
        int queryMethodModifiers = generatedClass.isInterface() ? JMod.NONE : JMod.PUBLIC;
        return generatedClass.method(queryMethodModifiers, returnClass, methodName);
    }

    protected JClass getReturnClass(final JCodeModel model, final JClass entityJClass, final JClass fieldType,
            final MappingType mappingType) {
        switch (mappingType) {
        case MANY_TO_ONE:
        case OBJECT:
        default:
            JClass entityJClassList = model.ref(List.class).narrow(entityJClass);
            return model.ref(Map.class).narrow(fieldType, entityJClassList);
        case ONE_TO_ONE:
        case UNIQUE_OBJECT:
            return model.ref(Map.class).narrow(fieldType, entityJClass);
        }
    }

    /**
     * @param fieldElement
     * @param predicateString
     * @return
     */
    protected String getMethodName(final String entityName, final Element fieldElement) {
        return String.format("find%sBy%sThatInclude", entityName,
                StringUtils.capitalize(fieldElement.getSimpleName().toString()));
    }

    private JClass getParameterType(final JCodeModel model, final JClass fieldType,
            final EntityInformationMap entityInfoMap, final MappingType mappingType) {
        JClass parameterType = null;
        String fullName = fieldType.fullName();
        EntityInfo foreignKeyInfo = entityInfoMap.get(fullName);
        List<Element> foreignKeyIdElms = foreignKeyInfo == null ? null : foreignKeyInfo.getIdElements();
        if (foreignKeyIdElms == null || foreignKeyIdElms.size() > 1) {
            parameterType = fieldType;
        } else {
            switch (mappingType) {
            case UNIQUE_OBJECT:
            case OBJECT:
                parameterType = fieldType;
                break;
            case ONE_TO_ONE:
            case MANY_TO_ONE:
            default:
                Element element = foreignKeyIdElms.get(0);
                TypeMirror fieldTypeMirror = element.asType();
                String fieldTypeString = fieldTypeMirror.toString();
                parameterType = model.ref(fieldTypeString);
                break;
            }
        }
        return parameterType;
    }
}