Java tutorial
/* * 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; } }