package org.springframework.roo.addon.layers.service.addon; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.tuple.Pair; import org.springframework.roo.addon.jpa.addon.entity.JpaEntityMetadata; import org.springframework.roo.addon.jpa.addon.entity.JpaEntityMetadata.RelationInfo; import org.springframework.roo.addon.layers.repository.jpa.addon.RepositoryJpaMetadata; import org.springframework.roo.addon.layers.service.annotations.RooServiceImpl; import org.springframework.roo.classpath.PhysicalTypeIdentifierNamingUtils; import org.springframework.roo.classpath.PhysicalTypeMetadata; import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails; import org.springframework.roo.classpath.details.ConstructorMetadataBuilder; import org.springframework.roo.classpath.details.FieldMetadata; import org.springframework.roo.classpath.details.FieldMetadataBuilder; 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.AnnotationMetadata; 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.classpath.operations.Cardinality; import org.springframework.roo.metadata.MetadataIdentificationUtils; import org.springframework.roo.model.ImportRegistrationResolver; import org.springframework.roo.model.JavaSymbolName; import org.springframework.roo.model.JavaType; import org.springframework.roo.model.JdkJavaType; import org.springframework.roo.model.SpringJavaType; import org.springframework.roo.project.LogicalPath; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; /** * Metadata for {@link RooServiceImpl}. * * @author Juan Carlos García * @author Jose Manuel Vivó * @author Sergio Clares * @since 2.0 */ public class ServiceImplMetadata extends AbstractItdTypeDetailsProvidingMetadataItem { private static final String PROVIDES_TYPE_STRING = ServiceImplMetadata.class.getName(); private static final String PROVIDES_TYPE = MetadataIdentificationUtils .create(PROVIDES_TYPE_STRING); private static final JavaSymbolName GET_ENTITY_TYPE_METHOD_NAME = new JavaSymbolName( "getEntityType"); private static final JavaSymbolName GET_ID_TYPE_METHOD_NAME = new JavaSymbolName("getIdType"); private static final AnnotationMetadata LAZY_ANNOTATION = new AnnotationMetadataBuilder( SpringJavaType.LAZY).build(); private static final JavaSymbolName FIND_ONE_DETACHED = new JavaSymbolName("findOneDetached"); private ImportRegistrationResolver importResolver; private final JavaType repository; private final JavaType entity; private final Map<FieldMetadata, MethodMetadata> allCountByReferencedFieldMethods; private final Map<FieldMetadata, MethodMetadata> allFindAllByReferencedFieldMethods; private final MethodMetadata findAllIterableMethod; private final FieldMetadata repositoryFieldMetadata; private final Map<JavaType, FieldMetadata> requiredServiceFieldByEntity; private final ServiceMetadata serviceMetadata; private final JpaEntityMetadata entityMetadata; private final List<Pair<FieldMetadata, RelationInfo>> childRelationsInfo; private final JavaType entityIdentifierType; // Temporal arrays don't share private ArrayList<MethodMetadata> pendingTransactionalMethodToAdd; private ArrayList<MethodMetadata> pendingNonTransactionalMethodToAdd; 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 getMetadataIdentiferType() { 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); } /** * Constructor * * @param identifier * the identifier for this item of metadata (required) * @param aspectName * the Java type of the ITD (required) * @param governorPhysicalTypeMetadata * the governor, which is expected to contain a * {@link ClassOrInterfaceTypeDetails} (required) * @param serviceInterface * JavaType with interface that this service will implement * @param repositoryMetadata * @param entityMetadata * @param serviceMetadata * @param requiredServicesByEntity * @param childRelationsInfo */ public ServiceImplMetadata(final String identifier, final JavaType aspectName, final PhysicalTypeMetadata governorPhysicalTypeMetadata, final JavaType serviceInterface, final JavaType repository, RepositoryJpaMetadata repositoryMetadata, final JavaType entity, JpaEntityMetadata entityMetadata, ServiceMetadata serviceMetadata, Map<JavaType, ServiceMetadata> requiredServicesByEntity, List<Pair<FieldMetadata, RelationInfo>> childRelationsInfo) { super(identifier, aspectName, governorPhysicalTypeMetadata); this.importResolver = builder.getImportRegistrationResolver(); this.entity = entity; this.repository = repository; this.findAllIterableMethod = serviceMetadata.getCurrentFindAllIterableMethod(); this.allCountByReferencedFieldMethods = Collections.unmodifiableMap(serviceMetadata.getCountByReferenceFieldDefinedMethod()); this.allFindAllByReferencedFieldMethods = Collections.unmodifiableMap(serviceMetadata.getReferencedFieldsFindAllDefinedMethods()); this.serviceMetadata = serviceMetadata; this.entityMetadata = entityMetadata; this.childRelationsInfo = childRelationsInfo; this.entityIdentifierType = serviceMetadata.getIdType(); // All services should include @Service annotation AnnotationMetadataBuilder serviceAnnotation = new AnnotationMetadataBuilder(SpringJavaType.SERVICE); ensureGovernorIsAnnotated(serviceAnnotation); // All service related with repository should be generated with // @Transactional(readOnly = true) annotation AnnotationMetadataBuilder transactionalAnnotation = new AnnotationMetadataBuilder(SpringJavaType.TRANSACTIONAL); transactionalAnnotation.addBooleanAttribute("readOnly", true); ensureGovernorIsAnnotated(transactionalAnnotation); // Services should include repository field if there's // a repository related with managed entity this.repositoryFieldMetadata = getFieldFor(repository); ensureGovernorHasField(new FieldMetadataBuilder(repositoryFieldMetadata)); // Add fields for required services to Map<JavaType, FieldMetadata> requiredServiceFieldByEntityTemp = new TreeMap<JavaType, FieldMetadata>(); for (Entry<JavaType, ServiceMetadata> service : requiredServicesByEntity.entrySet()) { FieldMetadata field = getFieldFor(service.getValue().getDestination()); requiredServiceFieldByEntityTemp.put(service.getKey(), field); } for (FieldMetadata field : requiredServiceFieldByEntityTemp.values()) { ensureGovernorHasField(new FieldMetadataBuilder(field)); } this.requiredServiceFieldByEntity = Collections.unmodifiableMap(requiredServiceFieldByEntityTemp); // Add constructor ensureGovernorHasConstructor(getConstructor()); pendingTransactionalMethodToAdd = new ArrayList<MethodMetadata>(serviceMetadata.getTransactionalDefinedMethods()); pendingNonTransactionalMethodToAdd = new ArrayList<MethodMetadata>(serviceMetadata.getNotTransactionalDefinedMethods()); // Generating all addTo methods that should be implemented for (Entry<RelationInfo, MethodMetadata> entry : serviceMetadata.getAddToRelationMethods() .entrySet()) { ensureGovernorHasMethod( new MethodMetadataBuilder(getMethodAddTo(entry.getValue(), entry.getKey())), entry.getValue()); } // Generating all removeFrom methods that should be implemented for (Entry<RelationInfo, MethodMetadata> entry : serviceMetadata.getRemoveFromRelationMethods() .entrySet()) { ensureGovernorHasMethod( new MethodMetadataBuilder(getMethodRemoveFrom(entry.getValue(), entry.getKey())), entry.getValue()); } // Generating all setRelation methods that should be implemented for (Entry<RelationInfo, MethodMetadata> entry : serviceMetadata.getSetRelationMethods() .entrySet()) { ensureGovernorHasMethod( new MethodMetadataBuilder(getSetRelation(entry.getValue(), entry.getKey())), entry.getValue()); } // Generating transactional methods that should be implemented for (MethodMetadata method : pendingTransactionalMethodToAdd) { ensureGovernorHasMethod(new MethodMetadataBuilder(getMethod(method, true))); } // Generating not transactional methods that should be implemented for (MethodMetadata method : pendingNonTransactionalMethodToAdd) { ensureGovernorHasMethod(new MethodMetadataBuilder(getMethod(method))); } // ROO-3868: New entity visualization support ensureGovernorHasMethod(new MethodMetadataBuilder(getEntityTypeGetterMethod())); ensureGovernorHasMethod(new MethodMetadataBuilder(getIdentifierTypeGetterMethod())); // Build the ITD itdTypeDetails = builder.build(); } private MethodMetadata getMethodRemoveFrom(MethodMetadata methodToBeImplemented, RelationInfo relationInfo) { return getMethodAddRemoveRel(methodToBeImplemented, relationInfo, relationInfo.removeMethod); } private MethodMetadata getMethodAddRemoveRel(MethodMetadata methodToBeImplemented, RelationInfo relationInfo, MethodMetadata operationMethod) { InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); // Prepare constants final String operation = operationMethod.getMethodName().getSymbolName(); final JavaSymbolName param0 = methodToBeImplemented.getParameterNames().get(0); final JavaType childType = relationInfo.childType; final FieldMetadata childServideField = requiredServiceFieldByEntity.get(childType); final String parentFieldName = relationInfo.fieldName; final JavaSymbolName param1 = methodToBeImplemented.getParameterNames().get(1); final JavaType param1TypeWrapped = methodToBeImplemented.getParameterTypes().get(1).getJavaType().getParameters().get(0); final String saveMethod = serviceMetadata.getCurrentSaveMethod().getMethodName().getSymbolName(); String childListVariable; if (childType.equals(param1TypeWrapped)) { childListVariable = param1.getSymbolName(); } else { // List<{childType}> {parentFieldName} = // {childService}.findAll({param1}); bodyBuilder.appendFormalLine("%s<%s> %s = %s().findAll(%s);", getNameOfJavaType(JavaType.LIST), getNameOfJavaType(childType), parentFieldName, getAccessorMethod(childServideField).getMethodName(), param1); childListVariable = parentFieldName; } // {param0}.{operation}({childListVariable}); bodyBuilder.appendFormalLine("%s.%s(%s);", param0, operation, childListVariable); // return {repoField}.{saveMethod}({param0}); bodyBuilder.appendFormalLine("return %s().%s(%s);", getAccessorMethod(repositoryFieldMetadata) .getMethodName(), saveMethod, param0); return getMethod(methodToBeImplemented, true, bodyBuilder); } /** * Builds a method which returns the class of entity JavaType. * * @return MethodMetadataBuilder */ private MethodMetadata getEntityTypeGetterMethod() { List<JavaSymbolName> parameterNames = new ArrayList<JavaSymbolName>(); List<AnnotatedJavaType> parameterTypes = new ArrayList<AnnotatedJavaType>(); MethodMetadata existingMethod = getGovernorMethod(GET_ENTITY_TYPE_METHOD_NAME, AnnotatedJavaType.convertFromAnnotatedJavaTypes(parameterTypes)); if (existingMethod != null) { return existingMethod; } InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); // return ENTITY_TYPE.class; bodyBuilder.appendFormalLine("return %s.class;", this.entity.getNameIncludingTypeParameters(false, importResolver)); // Use the MethodMetadataBuilder for easy creation of MethodMetadata MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, GET_ENTITY_TYPE_METHOD_NAME, JavaType.wrapperOf(JavaType.CLASS, this.entity), parameterTypes, parameterNames, bodyBuilder); // Build and return a MethodMetadata instance return methodBuilder.build(); } /** * Builds a method which returns the class of the entity identifier JavaType. * * @return MethodMetadataBuilder */ private MethodMetadata getIdentifierTypeGetterMethod() { List<JavaSymbolName> parameterNames = new ArrayList<JavaSymbolName>(); List<AnnotatedJavaType> parameterTypes = new ArrayList<AnnotatedJavaType>(); MethodMetadata existingMethod = getGovernorMethod(GET_ID_TYPE_METHOD_NAME, AnnotatedJavaType.convertFromAnnotatedJavaTypes(parameterTypes)); if (existingMethod != null) { return existingMethod; } InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); // return IDENTIFIER_TYPE.class; bodyBuilder.appendFormalLine("return %s.class;", getNameOfJavaType(this.entityIdentifierType)); // Use the MethodMetadataBuilder for easy creation of MethodMetadata MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, GET_ID_TYPE_METHOD_NAME, JavaType.wrapperOf(JavaType.CLASS, this.serviceMetadata.getIdType()), parameterTypes, parameterNames, bodyBuilder); // Build and return a MethodMetadata instance return methodBuilder.build(); } private MethodMetadata getSetRelation(MethodMetadata methodToBeImplemented, RelationInfo relationInfo) { InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); // Prepare constants final JavaSymbolName param0 = methodToBeImplemented.getParameterNames().get(0); final JavaType childType = relationInfo.childType; final FieldMetadata childServideField = requiredServiceFieldByEntity.get(childType); final String childTypeNameJavaType = getNameOfJavaType(childType); final JavaSymbolName param1 = methodToBeImplemented.getParameterNames().get(1); final String saveMethod = serviceMetadata.getCurrentSaveMethod().getMethodName().getSymbolName(); String childListVariable = "items"; // List<{childType}> {parentFieldName} = // {childService}.findAll({param1}); bodyBuilder.appendFormalLine("%s<%s> %s = %s().findAll(%s);", getNameOfJavaType(JavaType.LIST), childTypeNameJavaType, childListVariable, getAccessorMethod(childServideField) .getMethodName(), param1); // Set<{childType}> currents = {param0}.get{rel.property}(); bodyBuilder.appendFormalLine("%s currents = %s.get%s();", getNameOfJavaType(relationInfo.fieldMetadata.getFieldType()), param0, relationInfo.fieldMetadata.getFieldName().getSymbolNameCapitalisedFirstLetter()); // Set<{childType}> toRemove = new // HashSet<{childType}>({parentFieldName}); bodyBuilder.appendFormalLine("%s<%s> toRemove = new %s<%s>();", getNameOfJavaType(JavaType.SET), childTypeNameJavaType, getNameOfJavaType(JdkJavaType.HASH_SET), childTypeNameJavaType); // for (Iterator<{childType}> iterator = current.iterator(); // iterator.hasNext();) { bodyBuilder.appendFormalLine( "for (%s<%s> iterator = currents.iterator(); iterator.hasNext();) {", getNameOfJavaType(JavaType.ITERATOR), childTypeNameJavaType); bodyBuilder.indent(); final String itemName = "next".concat(StringUtils.capitalize(childType.getSimpleTypeName())); // {childType} {itemName} = iterator.next(); bodyBuilder.appendFormalLine("%s %s = iterator.next();", childTypeNameJavaType, itemName); // if ({childListVariable}.contains({itemName})) { bodyBuilder.appendFormalLine("if (%s.contains(%s)) {", childListVariable, itemName); bodyBuilder.indent(); // {childListVariable}.remove({itemName}); bodyBuilder.appendFormalLine("%s.remove(%s);", childListVariable, itemName); bodyBuilder.indentRemove(); // } else { bodyBuilder.appendFormalLine("} else {"); bodyBuilder.indent(); // toRemove.add(product); bodyBuilder.appendFormalLine("toRemove.add(%s);", itemName); bodyBuilder.indentRemove(); // } bodyBuilder.appendFormalLine("}"); // } bodyBuilder.indentRemove(); bodyBuilder.appendFormalLine("}"); // {param0}.{removeFromMethod}(toRemove); bodyBuilder.appendFormalLine("%s.%s(toRemove);", param0, relationInfo.removeMethod.getMethodName()); // {param0}.{addToMethod}({childListVariable); bodyBuilder.appendFormalLine("%s.%s(%s);", param0, relationInfo.addMethod.getMethodName(), childListVariable); // // Force the version update of the parent side to know that the parent has changed bodyBuilder .appendFormalLine("// Force the version update of the parent side to know that the parent has changed"); // // because it has new books assigned bodyBuilder.appendFormalLine("// because it has new books assigned"); // {param0}.setVersion({param0}.getVersion() + 1); bodyBuilder.appendFormalLine("%s.setVersion(%s.getVersion() + 1);", param0, param0); // return {repoField}.{saveMethod}({param0}); bodyBuilder.appendFormalLine("return %s().%s(%s);", getAccessorMethod(repositoryFieldMetadata) .getMethodName(), saveMethod, param0); return getMethod(methodToBeImplemented, true, bodyBuilder); } private MethodMetadata getMethodAddTo(MethodMetadata methodToBeImplemented, RelationInfo relationInfo) { return getMethodAddRemoveRel(methodToBeImplemented, relationInfo, relationInfo.addMethod); } /** * Delegates on supper and remove original from * {@link #pendingNonTransactionalMethodToAdd} and * {@link #pendingNonTransactionalMethodToAdd} to avoid generate two times * the same method * * @param methodMetadata * @param original */ private void ensureGovernorHasMethod(final MethodMetadataBuilder methodMetadata, final MethodMetadata original) { ensureGovernorHasMethod(methodMetadata); removeMethodFromPendingList(pendingNonTransactionalMethodToAdd, original); removeMethodFromPendingList(pendingTransactionalMethodToAdd, original); } /** * Removes metadataToRemove from list * * Compares, method name, number params and params types to decide it method * match and should be removed from list. * * @param list * @param metadataToRemove */ private void removeMethodFromPendingList(List<MethodMetadata> list, MethodMetadata metadataToRemove) { final Iterator<MethodMetadata> iter = list.iterator(); MethodMetadata current; boolean matchTypes; while (iter.hasNext()) { current = iter.next(); matchTypes = true; // Check name if (!current.getMethodName().equals(metadataToRemove.getMethodName())) { continue; } // Check number of params if (current.getParameterTypes().size() != metadataToRemove.getParameterTypes().size()) { continue; } // Check params types for (int i = 0; i < current.getParameterTypes().size(); i++) { if (!current.getParameterTypes().get(i).equals(metadataToRemove.getParameterTypes().get(i))) { matchTypes = false; break; } } // all matches if (matchTypes) { // remove item iter.remove(); } } } /** * Method that generates Service implementation constructor. If exists a * repository, it will be included as constructor parameter * * @return */ private ConstructorMetadataBuilder getConstructor() { ConstructorMetadataBuilder constructorBuilder = new ConstructorMetadataBuilder(getId()); InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); // Append repository parameter if needed if (repositoryFieldMetadata != null) { addFieldToConstructor(bodyBuilder, constructorBuilder, repositoryFieldMetadata, false); } for (FieldMetadata requiredService : requiredServiceFieldByEntity.values()) { addFieldToConstructor(bodyBuilder, constructorBuilder, requiredService, true); } constructorBuilder.setBodyBuilder(bodyBuilder); constructorBuilder.setModifier(Modifier.PUBLIC); // Adding @Autowired annotation constructorBuilder.addAnnotation(new AnnotationMetadataBuilder(SpringJavaType.AUTOWIRED)); return constructorBuilder; } /** * Adds a field to constructor parameters and body * * @param bodyBuilder * @param constructorBuilder * @param field * @param lazy */ private void addFieldToConstructor(final InvocableMemberBodyBuilder bodyBuilder, final ConstructorMetadataBuilder constructorBuilder, final FieldMetadata field, final boolean lazy) { final String fieldName = field.getFieldName().getSymbolName(); final JavaType fieldType = field.getFieldType(); if (lazy) { importResolver.addImport(SpringJavaType.LAZY); constructorBuilder.addParameterName(field.getFieldName()); constructorBuilder.addParameterType(new AnnotatedJavaType(fieldType, LAZY_ANNOTATION)); } else { constructorBuilder.addParameter(fieldName, fieldType); } bodyBuilder.appendFormalLine(String.format("%s(" + fieldName + ");", getMutatorMethod(field) .getMethodName())); } /** * Method that generates implementation of provided method * * @return MethodMetadataBuilder */ private MethodMetadata getMethod(final MethodMetadata methodToBeImplemented) { return getMethod(methodToBeImplemented, false); } /** * Method that generates implementation of provided method * * @return MethodMetadataBuilder */ private MethodMetadata getMethod(final MethodMetadata methodToBeImplemented, boolean isTransactional) { // Check if is batch method boolean isBatch = false; for (AnnotatedJavaType parameters : methodToBeImplemented.getParameterTypes()) { JavaType type = parameters.getJavaType(); isBatch = !type.getParameters().isEmpty(); if (isBatch) { break; } } // Check what is the particular method boolean isDelete = methodToBeImplemented.getMethodName().getSymbolName().equals("delete"); boolean isSaveMethod = methodToBeImplemented.equals(this.serviceMetadata.getCurrentSaveMethod()); boolean isFindOneForUpdate = methodToBeImplemented.getMethodName().getSymbolName().equals("findOneForUpdate"); InvocableMemberBodyBuilder bodyBuilder; if (isDelete) { bodyBuilder = builDeleteMethodBody(methodToBeImplemented, isBatch); } else if (isSaveMethod) { bodyBuilder = builSaveMethodBody(methodToBeImplemented); } else if (isFindOneForUpdate) { bodyBuilder = buildFindOneForUpdateBody(methodToBeImplemented); } else { bodyBuilder = builMethodBody(methodToBeImplemented); } return getMethod(methodToBeImplemented, isTransactional, bodyBuilder); } /** * Method that generates implementation of provided method with specified * body * * @param methodToBeImplemented * @param isTransactional * @param bodyBuilder * @return */ private MethodMetadata getMethod(final MethodMetadata methodToBeImplemented, boolean isTransactional, InvocableMemberBodyBuilder bodyBuilder) { List<JavaSymbolName> parameterNames = new ArrayList<JavaSymbolName>(methodToBeImplemented.getParameterNames()); List<AnnotatedJavaType> parameterTypes = new ArrayList<AnnotatedJavaType>(methodToBeImplemented.getParameterTypes()); MethodMetadata existingMethod = getGovernorMethod(methodToBeImplemented.getMethodName(), AnnotatedJavaType.convertFromAnnotatedJavaTypes(parameterTypes)); if (existingMethod != null) { return existingMethod; } // Use the MethodMetadataBuilder for easy creation of MethodMetadata MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, methodToBeImplemented.getMethodName(), methodToBeImplemented.getReturnType(), parameterTypes, parameterNames, bodyBuilder); // Adding @Transactional if (isTransactional) { AnnotationMetadataBuilder transactionalAnnotation = new AnnotationMetadataBuilder(SpringJavaType.TRANSACTIONAL); methodBuilder.addAnnotation(transactionalAnnotation); } // Build and return a MethodMetadata instance return methodBuilder.build(); } /** * Build method body which delegates on repository * * @param methodToBeImplemented * @param isBatch * @param isDelete * @return */ private InvocableMemberBodyBuilder builMethodBody(final MethodMetadata methodToBeImplemented) { // Generate body InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); // Getting parameters String String parametersString = ""; for (JavaSymbolName parameterName : methodToBeImplemented.getParameterNames()) { parametersString = parametersString.concat(parameterName.getSymbolName()).concat(", "); } if (StringUtils.isNotBlank(parametersString)) { parametersString = parametersString.substring(0, parametersString.length() - 2); } bodyBuilder.appendFormalLine("%s %s().%s(%s);", methodToBeImplemented.getReturnType().equals(JavaType.VOID_PRIMITIVE) ? "" : "return", getAccessorMethod(repositoryFieldMetadata).getMethodName(), methodToBeImplemented .getMethodName().getSymbolName(), parametersString); return bodyBuilder; } /** * Build method body which delegates on repository * * @param methodToBeImplemented * @param isBatch * @param isDelete * @return */ private InvocableMemberBodyBuilder builDeleteMethodBody( final MethodMetadata methodToBeImplemented, boolean isBatch) { // Generate body final InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); final JavaSymbolName param0 = methodToBeImplemented.getParameterNames().get(0); final String entity = getNameOfJavaType(this.entity); if (isBatch) { // List<Entity> toDelete = repositoryField.FIND_ALL_METHOD(paramName); bodyBuilder.appendFormalLine("%s<%s> toDelete = %s().%s(%s);", getNameOfJavaType(JavaType.LIST), entity, getAccessorMethod(repositoryFieldMetadata) .getMethodName(), this.findAllIterableMethod.getMethodName(), param0); bodyBuilder.appendFormalLine("%s().deleteInBatch(toDelete);", getAccessorMethod(repositoryFieldMetadata).getMethodName(), methodToBeImplemented.getMethodName()); } else { // Clear relations as child part for (Pair<FieldMetadata, RelationInfo> item : childRelationsInfo) { final RelationInfo info = item.getRight(); final FieldMetadata field = item.getLeft(); final JavaType childType = field.getFieldType().getBaseType(); String mappedByCapitalized = StringUtils.capitalize(info.mappedBy); if (info.cardinality == Cardinality.ONE_TO_ONE) { // Skip } else if (info.cardinality == Cardinality.ONE_TO_MANY) { // Clear bidirectional many-to-one child relationship with Customer /* if (customerOrder.getCustomer() != null) { customerOrder.getCustomer().getOrders().remove(customerOrder); } */ bodyBuilder.appendFormalLine( "// Clear bidirectional many-to-one child relationship with %s", childType.getSimpleTypeName()); bodyBuilder.appendFormalLine("if (%s.get%s() != null) {", param0, mappedByCapitalized); bodyBuilder.indent(); bodyBuilder.appendFormalLine("%s.get%s().get%s().remove(%s);", param0, mappedByCapitalized, info.fieldMetadata.getFieldName() .getSymbolNameCapitalisedFirstLetter(), param0); bodyBuilder.indentRemove(); bodyBuilder.appendFormalLine("}"); bodyBuilder.appendFormalLine(""); } else { // MANY_TO_MANY String childTypeName = getNameOfJavaType(childType); /* // Clear bidirectional many-to-many child relationship with categories for (Category category : product.getCategories()) { category.getProducts().remove(product); } */ bodyBuilder.appendFormalLine( "// Clear bidirectional many-to-many child relationship with %s", childTypeName); bodyBuilder.appendFormalLine("for (%s item : %s.get%s()) {", childTypeName, param0, mappedByCapitalized); bodyBuilder.indent(); bodyBuilder.appendFormalLine("item.get%s().remove(%s);", info.fieldMetadata .getFieldName().getSymbolNameCapitalisedFirstLetter(), param0); bodyBuilder.indentRemove(); bodyBuilder.appendFormalLine("}"); bodyBuilder.appendFormalLine(""); } } // Clear relations as parent for (RelationInfo info : entityMetadata.getRelationInfos().values()) { if (info.cardinality == Cardinality.ONE_TO_ONE) { /* // Clear bidirectional one-to-one parent relationship with Address customer.removeFromAddress(); */ bodyBuilder.appendFormalLine( "// Clear bidirectional one-to-one parent relationship with %s", info.childType.getSimpleTypeName()); bodyBuilder.appendFormalLine("%s.%s();", param0, info.removeMethod.getMethodName()); bodyBuilder.appendFormalLine(""); } else if (info.cardinality == Cardinality.ONE_TO_MANY) { String childTypeName = getNameOfJavaType(info.childType); /* // Clear bidirectional one-to-many parent relationship with CustomerOrders for (CustomerOrder order : customer.getOrders()) { order.setCustomer(null); } */ bodyBuilder.appendFormalLine( "// Clear bidirectional one-to-many parent relationship with %s", childTypeName); bodyBuilder.appendFormalLine("for (%s item : %s.get%s()) {", childTypeName, param0, info.fieldMetadata.getFieldName().getSymbolNameCapitalisedFirstLetter()); bodyBuilder.indent(); bodyBuilder.appendFormalLine("item.set%s(null);", StringUtils.capitalize(info.mappedBy)); bodyBuilder.indentRemove(); bodyBuilder.appendFormalLine("}"); bodyBuilder.appendFormalLine(""); } else { String childTypeName = getNameOfJavaType(info.childType); // MANY_TO_MANY /* // Clear bidirectional many-to-many parent relationship with products for (Product product : category.getProducts()) { product.getCategories().remove(category); } */ bodyBuilder.appendFormalLine( "// Clear bidirectional many-to-many parent relationship with %s", childTypeName); bodyBuilder.appendFormalLine("for (%s item : %s.get%s()) {", childTypeName, param0, info.fieldMetadata.getFieldName().getSymbolNameCapitalisedFirstLetter()); bodyBuilder.indent(); bodyBuilder.appendFormalLine("item.get%s().remove(%s);", StringUtils.capitalize(info.mappedBy), param0); bodyBuilder.indentRemove(); bodyBuilder.appendFormalLine("}"); bodyBuilder.appendFormalLine(""); } } bodyBuilder.appendFormalLine("%s().delete(%s);", getAccessorMethod(repositoryFieldMetadata) .getMethodName(), param0); } return bodyBuilder; } /** * Build "findOneForUpdate" method body which delegates on repository * * @param methodToBeImplemented * @return */ private InvocableMemberBodyBuilder buildFindOneForUpdateBody(MethodMetadata methodToBeImplemented) { // Generate body InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); final JavaSymbolName param0 = methodToBeImplemented.getParameterNames().get(0); // return entityRepository.findOneDetached(id); bodyBuilder.appendFormalLine("%s %s().%s(%s);", methodToBeImplemented.getReturnType().equals(JavaType.VOID_PRIMITIVE) ? "" : "return", getAccessorMethod(repositoryFieldMetadata).getMethodName(), FIND_ONE_DETACHED.getSymbolName(), param0); return bodyBuilder; } /** * Build "save" method body which delegates on repository * * @param methodToBeImplemented * @param isBatch * @param isDelete * @return */ private InvocableMemberBodyBuilder builSaveMethodBody(final MethodMetadata methodToBeImplemented) { // Generate body InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); final JavaSymbolName param0 = methodToBeImplemented.getParameterNames().get(0); /* * // Ensure the relationships are maintained * entity.addToRelatedEntity(entity.getRelatedEntity()); */ boolean commentAdded = false; Map<String, RelationInfo> relationInfos = entityMetadata.getRelationInfos(); for (Entry<String, RelationInfo> entry : relationInfos.entrySet()) { RelationInfo info = entry.getValue(); if (info.cardinality == Cardinality.ONE_TO_ONE) { if (!commentAdded) { bodyBuilder.newLine(); bodyBuilder.appendFormalLine("// Ensure the relationships are maintained"); commentAdded = true; } bodyBuilder.appendFormalLine("%s.%s(%s.get%s());", param0, info.addMethod.getMethodName(), param0, StringUtils.capitalize(entry.getKey())); bodyBuilder.newLine(); } } bodyBuilder.appendFormalLine("%s %s().%s(%s);", methodToBeImplemented.getReturnType().equals(JavaType.VOID_PRIMITIVE) ? "" : "return", getAccessorMethod(repositoryFieldMetadata).getMethodName(), methodToBeImplemented .getMethodName().getSymbolName(), param0); return bodyBuilder; } /** * This method returns field to included on service for a Service or * Repository * * @param service * @return */ private FieldMetadata getFieldFor(JavaType type) { // Generating service field name final JavaSymbolName fieldName = new JavaSymbolName(StringUtils.uncapitalize(type.getSimpleTypeName())); FieldMetadata currentField = governorTypeDetails.getField(fieldName); if (currentField != null) { Validate.isTrue(currentField.getFieldType().equals(type), "Field %s already in %s but type not match: expected %s", currentField.getFieldName(), governorTypeDetails.getType(), type); return currentField; } return new FieldMetadataBuilder(getId(), Modifier.PRIVATE, new ArrayList<AnnotationMetadataBuilder>(), fieldName, type).build(); } public JavaType getEntity() { return entity; } public JavaType getRepository() { return repository; } @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("governor", governorPhysicalTypeMetadata.getId()); builder.append("itdTypeDetails", itdTypeDetails); return builder.toString(); } }