diff --git a/cobigen-cli/cli-systemtest/src/test/java/com/devonfw/cobigen/cli/systemtest/AbstractCliTest.java b/cobigen-cli/cli-systemtest/src/test/java/com/devonfw/cobigen/cli/systemtest/AbstractCliTest.java index c6b69bb1d5..7dacb804b1 100644 --- a/cobigen-cli/cli-systemtest/src/test/java/com/devonfw/cobigen/cli/systemtest/AbstractCliTest.java +++ b/cobigen-cli/cli-systemtest/src/test/java/com/devonfw/cobigen/cli/systemtest/AbstractCliTest.java @@ -98,13 +98,13 @@ public static void determineDevTemplatesPath() throws URISyntaxException, IOExce } } - if (path.getFileName().toString().equals("templates-devon4j-utils")) { - if (Files.exists(path.resolve("pom.xml"))) { - try { - Files.delete(path.resolve("pom.xml")); - } catch (IOException e) { - throw new IOException("Error deleting file " + path.resolve("pom.xml"), e); - } + // Replace the pom.xml in the template sets. Needed so that the project in the temp directory is build + // properly + if (Files.exists(path.resolve("pom.xml"))) { + try { + Files.delete(path.resolve("pom.xml")); + } catch (IOException e) { + throw new IOException("Error deleting file " + path.resolve("pom.xml"), e); } try { Files.copy(utilsPom, path.resolve("pom.xml")); diff --git a/cobigen-templates/crud-angular-client-app/pom.xml b/cobigen-templates/crud-angular-client-app/pom.xml index c43279405c..e02d94378d 100644 --- a/cobigen-templates/crud-angular-client-app/pom.xml +++ b/cobigen-templates/crud-angular-client-app/pom.xml @@ -10,4 +10,11 @@ ${revision} + + + org.apache.commons + commons-lang3 + + + \ No newline at end of file diff --git a/cobigen-templates/crud-angular-client-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java b/cobigen-templates/crud-angular-client-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java new file mode 100644 index 0000000000..ced2f5fde2 --- /dev/null +++ b/cobigen-templates/crud-angular-client-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java @@ -0,0 +1,464 @@ +package com.devonfw.cobigen.templates.devon4j.utils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + +import org.apache.commons.lang3.ClassUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides type operations, mainly checks and casts for Java Primitives, to be used in the templates + * + */ +public class JavaUtil { + + /** + * Logger for this class + */ + private static final Logger LOG = LoggerFactory.getLogger(JavaUtil.class); + + /** + * The constructor. + */ + public JavaUtil() { + + // Empty for CobiGen to automatically instantiate it + } + + /** + * Returns the Object version of a Java primitive or the input if the input isn't a java primitive + * + * @param simpleType String + * @return the corresponding object wrapper type simple name of the input if the input is the name of a primitive java + * type. The input itself if not. (e.g. "int" results in "Integer") + * @throws ClassNotFoundException should not occur. + */ + public String boxJavaPrimitives(String simpleType) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return ClassUtils.primitiveToWrapper(ClassUtils.getClass(simpleType)).getSimpleName(); + } else { + return simpleType; + } + + } + + /** + * Returns the simple name of the type of a field in the pojoClass. If the type is a java primitive the name of the + * wrapper class is returned + * + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return String. The simple name of the field's type. The simple name of the wrapper class in case of java + * primitives + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String boxJavaPrimitives(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return ClassUtils.primitiveToWrapper(pojoClass.getDeclaredField(fieldName).getType()).getSimpleName(); + } else { + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + throw new IllegalAccessError("Could not find field " + fieldName + " in class " + pojoClass); + } else { + return field.getType().getSimpleName(); + } + } + } + + /** + * Checks if the given type is a Java primitive + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive + */ + public boolean equalsJavaPrimitive(String simpleType) { + + try { + return ClassUtils.getClass(simpleType).isPrimitive(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the given type is a Java primitive or wrapper + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive or wrapper + */ + public boolean equalsJavaPrimitiveOrWrapper(String simpleType) { + + try { + return ClassUtils.isPrimitiveOrWrapper(ClassUtils.getClass(simpleType)); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the type of the field in the pojo's class is a java primitive + * + * @param pojoClass the {@link Class} object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff the field is a java primitive + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitive(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return field.getType().isPrimitive(); + } + } + + /** + * Checks if the given type is a Java primitive or a Java primitive array + * + * @param simpleType the Type name to be checked + * @return true iff {@link #equalsJavaPrimitive(String)} is true or if simpleType is an array with a primitive + * component + */ + public boolean equalsJavaPrimitiveIncludingArrays(String simpleType) { + + Class klasse; + + try { + klasse = ClassUtils.getClass(simpleType).getComponentType(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + return equalsJavaPrimitive(simpleType) || (klasse != null && klasse.isPrimitive()); + } + + /** + * Checks if the given field in the pojo class is a java primitive or an array of java primitives + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff {@link #equalsJavaPrimitive(Class, String)} is true or the field is an array of primitives + * @throws NoSuchFieldException indicating something awfully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitiveIncludingArrays(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + return equalsJavaPrimitive(pojoClass, fieldName) || (pojoClass.getDeclaredField(fieldName).getType().isArray() + && pojoClass.getDeclaredField(fieldName).getType().getComponentType().isPrimitive()); + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param simpleType Java Type + * @param varName Variable name + * @return String either of the form '((Java Primitive Object Type)varName)' if simpleType is a primitive or the empty + * String otherwise + * @throws ClassNotFoundException should not occur + */ + public String castJavaPrimitives(String simpleType, String varName) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(simpleType), varName); + } else { + return ""; + } + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be casted + * @return if fieldName points to a primitive field then a casted statement (e.g. for an int field: + * '((Integer)field)') or an empty String otherwise + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String castJavaPrimitives(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(pojoClass, fieldName), fieldName); + } else { + return ""; + } + } + + /** + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return true if the field is an instance of java.utils.Collections + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean isCollection(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return Collection.class.isAssignableFrom(field.getType()); + } + + } + + /** + * Returns the Ext Type to a given java type + * + * @param simpleType any java type's simple name + * @return corresponding Ext type + */ + public String getExtType(String simpleType) { + + switch (simpleType) { + case "short": + case "Short": + case "int": + case "Integer": + case "long": + case "Long": + return "Integer"; + case "float": + case "Float": + case "double": + case "Double": + return "Number"; + case "boolean": + case "Boolean": + return "Boolean"; + case "char": + case "Character": + case "String": + return "String"; + case "Date": + return "Date"; + default: + return "Field"; + } + } + + /** + * returns the Angular5 type associated with a Java primitive + * + * @param simpleType :{@link String} the type to be parsed + * @return the corresponding Angular type or 'any' otherwise + */ + public String getAngularType(String simpleType) { + + switch (simpleType) { + case "boolean": + return "boolean"; + case "Boolean": + return "boolean"; + case "short": + return "number"; + case "Short": + return "number"; + case "int": + return "number"; + case "Integer": + return "number"; + case "long": + return "number"; + case "Long": + return "number"; + case "float": + return "number"; + case "Float": + return "number"; + case "double": + return "number"; + case "Double": + return "number"; + case "char": + return "string"; + case "Character": + return "string"; + case "String": + return "string"; + case "byte": + return "number"; + default: + return "any"; + } + } + + /** + * returns the class name of the return type of a specific method. + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName {@link String} the name of the method + * @return the class name of the return type of the specified method + * @throws SecurityException If no method of the given name can be found + * @throws NoSuchMethodException If no method of the given name can be found + */ + public String getReturnType(Class pojoClass, String methodName) throws NoSuchMethodException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + String s = "-"; + Method method = findMethod(pojoClass, methodName); + if (method != null && !method.getReturnType().equals(Void.TYPE)) { + s = method.getReturnType().toString(); + s = s.substring(s.lastIndexOf('.') + 1, s.length()); + } + return s; + } + + /** + * + * This methods returns the return type of the method in the given pojoClass which are annotated with the parameter + * annotatedClass + * + * @param pojoClass - The class in which to find if it has methods with annotatedClass + * @param annotatedClassName - The annotation which needs to be found + * @return Return type of the method annotated with the given annotation, else "null" + * @throws ClassNotFoundException if the annotated class name could not be found in the class path + */ + @SuppressWarnings("unchecked") + public String getReturnTypeOfMethodAnnotatedWith(Class pojoClass, String annotatedClassName) + throws ClassNotFoundException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + Method[] methods = pojoClass.getDeclaredMethods(); + for (Method method : methods) { + if (!method.getName().startsWith("get")) { + continue; + } + for (Annotation a : method.getAnnotations()) { + // better if (method.isAnnotationPresent(classObj)) {, but postponed as of different class + // loaders of a.getClass() and pojoClass.getClass() + if (a.getClass().getCanonicalName().equals(annotatedClassName)) { + return method.getReturnType().getSimpleName(); + } + } + } + return "null"; + } + + /** + * returns the HTTP request type corresponding to an annotation type + * + * @param annotations The annotation to get the type name of + * @return the HTTP request type name of the selected annotation + */ + public String getRequestType(Map annotations) { + + if (annotations.containsKey("javax_ws_rs_GET")) { + return "GET"; + } else if (annotations.containsKey("javax_ws_rs_PUT")) { + return "PUT"; + } else if (annotations.containsKey("javax_ws_rs_POST")) { + return "POST"; + } else if (annotations.containsKey("javax_ws_rs_DELETE")) { + return "DELETE"; + } else if (annotations.containsKey("javax_ws_rs_PATCH")) { + return "PATCH"; + } else { + return "-"; + } + } + + /** + * Helper method to find a class's specific method + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName The name of the method to be found + * @return The method object of the method to be found, null if it wasn't found + */ + private Method findMethod(Class pojoClass, String methodName) { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + for (Method m : pojoClass.getMethods()) { + if (m.getName().equals(methodName)) { + return m; + } + } + return null; + } + + /** + * Checks whether the class given by the full qualified name is an enum + * + * @param className full qualified class name + * @return true if the class is an enum, false otherwise + */ + public boolean isEnum(String className) { + + try { + return ClassUtils.getClass(className).isEnum(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return false; + } + } + + /** + * Returns the first enum value of an enum class + * + * @param className full qualified class name + * @return the first enum value name found in order + */ + public String getFirstEnumValue(String className) { + + try { + Class enumClass = ClassUtils.getClass(className); + Field[] declaredFields = enumClass.getDeclaredFields(); + if (declaredFields.length > 0) { + return declaredFields[0].getName(); + } else { + return null; + } + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return null; + } + } +} diff --git a/cobigen-templates/crud-ionic-client-app/pom.xml b/cobigen-templates/crud-ionic-client-app/pom.xml index e4ce241379..3a42d2a3e1 100644 --- a/cobigen-templates/crud-ionic-client-app/pom.xml +++ b/cobigen-templates/crud-ionic-client-app/pom.xml @@ -10,4 +10,11 @@ ${revision} + + + org.apache.commons + commons-lang3 + + + \ No newline at end of file diff --git a/cobigen-templates/crud-ionic-client-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java b/cobigen-templates/crud-ionic-client-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java new file mode 100644 index 0000000000..2d4025071d --- /dev/null +++ b/cobigen-templates/crud-ionic-client-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java @@ -0,0 +1,464 @@ +package com.devonfw.cobigen.templates.devon4j.utils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + +import org.apache.commons.lang3.ClassUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides type operations, mainly checks and casts for Java Primitives, to be used in the templates + * + */ +public class JavaUtil { + + /** + * Logger for this class + */ + private static final Logger LOG = LoggerFactory.getLogger(JavaUtil.class); + + /** + * The constructor. + */ + public JavaUtil() { + + // Empty for CobiGen to automatically instantiate it + } + + /** + * Returns the Object version of a Java primitive or the input if the input isn't a java primitive + * + * @param simpleType String + * @return the corresponding object wrapper type simple name of the input if the input is the name of a primitive java + * type. The input itself if not. (e.g. "int" results in "Integer") + * @throws ClassNotFoundException should not occur. + */ + public String boxJavaPrimitives(String simpleType) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return ClassUtils.primitiveToWrapper(ClassUtils.getClass(simpleType)).getSimpleName(); + } else { + return simpleType; + } + + } + + /** + * Returns the simple name of the type of a field in the pojoClass. If the type is a java primitive the name of the + * wrapper class is returned + * + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return String. The simple name of the field's type. The simple name of the wrapper class in case of java + * primitives + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String boxJavaPrimitives(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return ClassUtils.primitiveToWrapper(pojoClass.getDeclaredField(fieldName).getType()).getSimpleName(); + } else { + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + throw new IllegalAccessError("Could not find field " + fieldName + " in class " + pojoClass); + } else { + return field.getType().getSimpleName(); + } + } + } + + /** + * Checks if the given type is a Java primitive + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive + */ + public boolean equalsJavaPrimitive(String simpleType) { + + try { + return ClassUtils.getClass(simpleType).isPrimitive(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the given type is a Java primitive or wrapper + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive or wrapper + */ + public boolean equalsJavaPrimitiveOrWrapper(String simpleType) { + + try { + return ClassUtils.isPrimitiveOrWrapper(ClassUtils.getClass(simpleType)); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the type of the field in the pojo's class is a java primitive + * + * @param pojoClass the {@link Class} object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff the field is a java primitive + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitive(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return field.getType().isPrimitive(); + } + } + + /** + * Checks if the given type is a Java primitive or a Java primitive array + * + * @param simpleType the Type name to be checked + * @return true iff {@link #equalsJavaPrimitive(String)} is true or if simpleType is an array with a primitive + * component + */ + public boolean equalsJavaPrimitiveIncludingArrays(String simpleType) { + + Class klasse; + + try { + klasse = ClassUtils.getClass(simpleType).getComponentType(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + return equalsJavaPrimitive(simpleType) || (klasse != null && klasse.isPrimitive()); + } + + /** + * Checks if the given field in the pojo class is a java primitive or an array of java primitives + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff {@link #equalsJavaPrimitive(Class, String)} is true or the field is an array of primitives + * @throws NoSuchFieldException indicating something awfully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitiveIncludingArrays(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + return equalsJavaPrimitive(pojoClass, fieldName) || (pojoClass.getDeclaredField(fieldName).getType().isArray() + && pojoClass.getDeclaredField(fieldName).getType().getComponentType().isPrimitive()); + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param simpleType Java Type + * @param varName Variable name + * @return String either of the form '((Java Primitive Object Type)varName)' if simpleType is a primitive or the empty + * String otherwise + * @throws ClassNotFoundException should not occur + */ + public String castJavaPrimitives(String simpleType, String varName) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(simpleType), varName); + } else { + return ""; + } + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be casted + * @return if fieldName points to a primitive field then a casted statement (e.g. for an int field: + * '((Integer)field)') or an empty String otherwise + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String castJavaPrimitives(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(pojoClass, fieldName), fieldName); + } else { + return ""; + } + } + + /** + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return true if the field is an instance of java.utils.Collections + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean isCollection(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return Collection.class.isAssignableFrom(field.getType()); + } + + } + + /** + * Returns the Ext Type to a given java type + * + * @param simpleType any java type's simple name + * @return corresponding Ext type + */ + public String getExtType(String simpleType) { + + switch (simpleType) { + case "short": + case "Short": + case "int": + case "Integer": + case "long": + case "Long": + return "Integer"; + case "float": + case "Float": + case "double": + case "Double": + return "Number"; + case "boolean": + case "Boolean": + return "Boolean"; + case "char": + case "Character": + case "String": + return "String"; + case "Date": + return "Date"; + default: + return "Field"; + } + } + + /** + * returns the Angular5 type associated with a Java primitive + * + * @param simpleType :{@link String} the type to be parsed + * @return the corresponding Angular type or 'any' otherwise + */ + public String getAngularType(String simpleType) { + + switch (simpleType) { + case "boolean": + return "boolean"; + case "Boolean": + return "boolean"; + case "short": + return "number"; + case "Short": + return "number"; + case "int": + return "number"; + case "Integer": + return "number"; + case "long": + return "number"; + case "Long": + return "number"; + case "float": + return "number"; + case "Float": + return "number"; + case "double": + return "number"; + case "Double": + return "number"; + case "char": + return "string"; + case "Character": + return "string"; + case "String": + return "string"; + case "byte": + return "number"; + default: + return "any"; + } + } + + /** + * returns the class name of the return type of a specific method. + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName {@link String} the name of the method + * @return the class name of the return type of the specified method + * @throws SecurityException If no method of the given name can be found + * @throws NoSuchMethodException If no method of the given name can be found + */ + public String getReturnType(Class pojoClass, String methodName) throws NoSuchMethodException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + String s = "-"; + Method method = findMethod(pojoClass, methodName); + if (method != null && !method.getReturnType().equals(Void.TYPE)) { + s = method.getReturnType().toString(); + s = s.substring(s.lastIndexOf('.') + 1, s.length()); + } + return s; + } + + /** + * + * This methods returns the return type of the method in the given pojoClass which are annotated with the parameter + * annotatedClass + * + * @param pojoClass - The class in which to find if it has methods with annotatedClass + * @param annotatedClassName - The annotation which needs to be found + * @return Return type of the method annotated with the given annotation, else "null" + * @throws ClassNotFoundException if the annotated class name could not be found in the class path + */ + @SuppressWarnings("unchecked") + public String getReturnTypeOfMethodAnnotatedWith(Class pojoClass, String annotatedClassName) + throws ClassNotFoundException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + Method[] methods = pojoClass.getDeclaredMethods(); + for (Method method : methods) { + if (!method.getName().startsWith("get")) { + continue; + } + for (Annotation a : method.getAnnotations()) { + // better if (method.isAnnotationPresent(classObj)) {, but postponed as of different class + // loaders of a.getClass() and pojoClass.getClass() + if (a.getClass().getCanonicalName().equals(annotatedClassName)) { + return method.getReturnType().getSimpleName(); + } + } + } + return "null"; + } + + /** + * returns the HTTP request type corresponding to an annotation type + * + * @param annotations The annotation to get the type name of + * @return the HTTP request type name of the selected annotation + */ + public String getRequestType(Map annotations) { + + if (annotations.containsKey("javax_ws_rs_GET")) { + return "GET"; + } else if (annotations.containsKey("javax_ws_rs_PUT")) { + return "PUT"; + } else if (annotations.containsKey("javax_ws_rs_POST")) { + return "POST"; + } else if (annotations.containsKey("javax_ws_rs_DELETE")) { + return "DELETE"; + } else if (annotations.containsKey("javax_ws_rs_PATCH")) { + return "PATCH"; + } else { + return "-"; + } + } + + /** + * Helper method to find a class's specific method + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName The name of the method to be found + * @return The method object of the method to be found, null if it wasn't found + */ + private Method findMethod(Class pojoClass, String methodName) { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + for (Method m : pojoClass.getMethods()) { + if (m.getName().equals(methodName)) { + return m; + } + } + return null; + } + + /** + * Checks whether the class given by the full qualified name is an enum + * + * @param className full qualified class name + * @return true if the class is an enum, false otherwise + */ + public boolean isEnum(String className) { + + try { + return ClassUtils.getClass(className).isEnum(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return false; + } + } + + /** + * Returns the first enum value of an enum class + * + * @param className full qualified class name + * @return the first enum value name found in order + */ + public String getFirstEnumValue(String className) { + + try { + Class enumClass = ClassUtils.getClass(className); + Field[] declaredFields = enumClass.getDeclaredFields(); + if (declaredFields.length > 0) { + return declaredFields[0].getName(); + } else { + return null; + } + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return null; + } + } +} diff --git a/cobigen-templates/crud-java-ea-uml/pom.xml b/cobigen-templates/crud-java-ea-uml/pom.xml index 285f86ef71..69799afa8e 100644 --- a/cobigen-templates/crud-java-ea-uml/pom.xml +++ b/cobigen-templates/crud-java-ea-uml/pom.xml @@ -10,4 +10,10 @@ ${revision} + + + org.apache.commons + commons-lang3 + + \ No newline at end of file diff --git a/cobigen-templates/crud-java-ea-uml/src/main/java/com/devonfw/cobigen/templates/devon4j/constants/Field.java b/cobigen-templates/crud-java-ea-uml/src/main/java/com/devonfw/cobigen/templates/devon4j/constants/Field.java new file mode 100644 index 0000000000..5376daaea9 --- /dev/null +++ b/cobigen-templates/crud-java-ea-uml/src/main/java/com/devonfw/cobigen/templates/devon4j/constants/Field.java @@ -0,0 +1,48 @@ +package com.devonfw.cobigen.templates.devon4j.constants; + +/** + * Contains the used keys for the pojo field mapping + */ +public enum Field { + + /** + * Name of the field + */ + NAME("name"), + /** + * Type of the field + */ + TYPE("type"), + /** + * Canonical Type of the field + */ + CANONICAL_TYPE("canonicalType"), + /** + * The Javadoc of the field + */ + JAVA_DOC("javaDoc"), + /** + * Annotations + */ + ANNOTATIONS("annotations"); + + /** + * key value + */ + private String value; + + /** + * @param value of the key + */ + Field(String value) { + + this.value = value; + } + + @Override + public String toString() { + + return this.value; + } + +} diff --git a/cobigen-templates/crud-java-ea-uml/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/DevonfwUtil.java b/cobigen-templates/crud-java-ea-uml/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/DevonfwUtil.java new file mode 100644 index 0000000000..36e39b5e06 --- /dev/null +++ b/cobigen-templates/crud-java-ea-uml/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/DevonfwUtil.java @@ -0,0 +1,450 @@ +package com.devonfw.cobigen.templates.devon4j.utils; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.w3c.dom.Attr; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.NodeList; + +import com.devonfw.cobigen.templates.devon4j.constants.Field; +import com.sun.org.apache.xerces.internal.dom.DeferredElementNSImpl; + +/** + * A class for shared devon4j specific functions in the templates + * + */ +@SuppressWarnings("restriction") +public class DevonfwUtil { + + /** + * Check whether the given 'canonicalType' is a devon4j Entity, which is declared in the given 'component' + * + * @param canonicalType the type name + * @param component the component name + * @return true iff the canonicalType is a devon Entity + */ + public boolean isEntityInComponent(String canonicalType, String component) { + + return canonicalType.matches(String.format(".+%1$s\\.dataaccess\\.api\\.[A-Za-z0-9]+Entity(<.*)?", component)); + } + + /** + * Check whether the given 'canonicalType' is declared in the given 'component' + * + * @param canonicalType the type name + * @param component the component name + * @return true iff the canonicalType is inside the given component + */ + public boolean isTypeInComponent(String canonicalType, String component) { + + return canonicalType.matches(String.format("%1$s.[A-Za-z0-9]+(<.*)?", component)); + } + + /** + * Determines the ID getter for a given 'field' dependent on whether the getter should access the ID via an object + * reference or a direct ID getter + * + * @param field the field + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} + '()' with + * capitalize=true + */ + public String resolveIdGetter(Map field, boolean byObjectReference, String component) { + + // If field comes from an UML file + if (field.getClass().toGenericString().contains("freemarker.ext.beans.HashAdapter")) { + DeferredElementNSImpl umlNode = (DeferredElementNSImpl) field; + return resolveIdGetter(umlNode, byObjectReference, component); + } + return "get" + resolveIdVariableNameOrSetterGetterSuffix(field, byObjectReference, true, component) + "()"; + } + + /** + * Determines the ID getter for a given 'field' dependent on whether the getter should access the ID via an object + * reference or a direct ID getter + * + * This method is used when the field parameter comes from an UML file. The name and type of the attributes must be + * pre-processed for later inserting them inside the HashMap. + * + * @param field the field + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} + '()' with + * capitalize=true + */ + public String resolveIdGetter(DeferredElementNSImpl field, boolean byObjectReference, String component) { + + HashMap nodeHash = new HashMap<>(); + + // Putting the name of the attribute to the hash + nodeHash.put(Field.NAME.toString(), field.getAttribute("name")); + + // Putting the type of the attribute to the hash + NodeList childs = field.getChildNodes(); + for (int i = 0; i < childs.getLength(); i++) { + // Retrieve "type" tag + if (childs.item(i).getNodeName().equals("type")) { + NamedNodeMap attrs = childs.item(i).getAttributes(); + for (int j = 0; j < attrs.getLength(); j++) { + Attr attribute = (Attr) attrs.item(j); + // Try to find the attribute that contains the type + if (attribute.getName().equals("xmi:idref")) { + nodeHash.put(Field.TYPE.toString(), attribute.getName().replace("EAJava_", "")); + } + } + } + } + return "get" + resolveIdVariableNameOrSetterGetterSuffix(nodeHash, byObjectReference, true, component) + "()"; + + } + + /** + * Determines the ID getter for a given 'field' dependent on whether the getter should access the ID via an object + * reference or a direct ID getter + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} + '()' with + * capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdGetter(Class pojoClass, Map fieldMap, boolean byObjectReference, + String component) throws NoSuchFieldException, SecurityException { + + return "get" + resolveIdVariableNameOrSetterGetterSuffix(pojoClass, fieldMap, byObjectReference, true, component) + + "()"; + } + + /** + * same as {@link #resolveIdGetter(Map, boolean, String)} but with byObjectReference=false and component="" + * + * @param field the field + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} + '()' with + * capitalize=true + */ + public String resolveIdGetter(Map field) { + + return this.resolveIdGetter(field, false, ""); + } + + /** + * same as {@link #resolveIdGetter(Class,Map,boolean,String)} but with byObjectReference=false and component="" + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} + '()' with + * capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdGetter(Class pojoClass, Map fieldMap) + throws NoSuchFieldException, SecurityException { + + return resolveIdGetter(pojoClass, fieldMap, false, ""); + } + + /** + * Determines the ID setter for a given 'field' dependent on whether the setter should access the ID via an object + * reference or a direct ID setter. In contrast to resolveIdGetter, this function does not generate the function + * parenthesis to enable parameter declaration. + * + * @param field the field + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'set' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} with + * capitalize=true + */ + public String resolveIdSetter(Map field, boolean byObjectReference, String component) { + + return "set" + resolveIdVariableNameOrSetterGetterSuffix(field, byObjectReference, true, component); + } + + /** + * same as {@link #resolveIdSetter(Map, boolean, String)} but with byObjectReference=false and component="" + * + * @param field the field + * @return 'set' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} with + * capitalize=true + */ + public String resolveIdSetter(Map field) { + + return this.resolveIdSetter(field, false, ""); + } + + /** + * Determines the ID setter for a given 'field' dependent on whether the setter should access the ID via an object + * reference or a direct ID setter. In contrast to resolveIdGetter, this function does not generate the function + * parenthesis to enable parameter declaration. + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'set'+ {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} with capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdSetter(Class pojoClass, Map fieldMap, boolean byObjectReference, + String component) throws NoSuchFieldException, SecurityException { + + return "set" + resolveIdVariableNameOrSetterGetterSuffix(pojoClass, fieldMap, byObjectReference, true, component); + } + + /** + * same as {@link #resolveIdSetter(Class,Map,boolean,String)} but with byObjectReference=false and component="" + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @return 'set' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} with capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdSetter(Class pojoClass, Map fieldMap) + throws NoSuchFieldException, SecurityException { + + return resolveIdSetter(pojoClass, fieldMap, false, ""); + } + + /** + * Determines the variable name for the id value of the 'field' + * + * @param field the field + * @return {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)}) with + * byObjectReference=false, capitalize=false and component="" + */ + public String resolveIdVariableName(Map field) { + + // the component is passed down as an empty string since byObjectReference is false and therefore the + // component is + // never touched + return resolveIdVariableNameOrSetterGetterSuffix(field, false, false, ""); + } + + /** + * Determines the variable name for the id value of the specified field in the pojo + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @return {@link #resolveIdVariableNameOrSetterGetterSuffix(Class, Map, boolean, boolean, String)}) with + * byObjectReference=false, capitalize=false and component="" + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdVariableName(Class pojoClass, Map fieldMap) + throws NoSuchFieldException, SecurityException { + + // the component is passed down as an empty string since byObjectReference is false and therefore the + // component is + // never touched + return resolveIdVariableNameOrSetterGetterSuffix(pojoClass, fieldMap, false, false, ""); + } + + /** + * Determines the ID setter/getter suffix for a given 'field' dependent on whether the setter/getter should access the + * ID via an object reference or a direct ID setter/getter + * + * @param field the field + * @param byObjectReference boolean + * @param capitalize if the field name should be capitalized + * @param component the devon4j component. Only needed if $byObjectReference is true + * @return idVariable name or getter/setter suffix + */ + public String resolveIdVariableNameOrSetterGetterSuffix(Map field, boolean byObjectReference, + boolean capitalize, String component) { + + String fieldName = (String) field.get(Field.NAME.toString()); + if (capitalize) { + fieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); + } + String suffix = ""; + + String fieldType = (String) field.get(Field.TYPE.toString()); + String fieldCType = (String) field.get(Field.CANONICAL_TYPE.toString()); + if (fieldType.contains("Entity")) { + if (fieldCType.startsWith("java.util.List") || fieldCType.startsWith("java.util.Set")) { + suffix = "Ids"; + if (fieldName.endsWith("s")) { + // Assume trailing 's' as indicator for a plural + fieldName = fieldName.substring(0, fieldName.length() - 1); + } + } else { + suffix = "Id"; + } + if (byObjectReference && isTypeInComponent(fieldCType, component)) { + // direct references for Entities in same component, so get id of the object reference + suffix = "().getId"; + } + } + + return fieldName + suffix; + + } + + /** + * Determines the ID setter/getter suffix for a given 'field' dependent on whether the setter/getter should access the + * ID via an object reference or a direct ID setter/getter + * + * @param pojoClass the {@link Class} object of the pojo + * @param fieldMap the field mapping + * @param byObjectReference boolean + * @param capitalize if the field name should be capitalized + * @param component the devon4j component. Only needed if byObjectReference is true + * @return idVariable name or getter/setter suffix + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdVariableNameOrSetterGetterSuffix(Class pojoClass, Map fieldMap, + boolean byObjectReference, boolean capitalize, String component) throws NoSuchFieldException, SecurityException { + + String resultName = (String) fieldMap.get(Field.NAME.toString()); + if (capitalize) { + resultName = resultName.substring(0, 1).toUpperCase() + resultName.substring(1); + } + String suffix = ""; + String fieldType = (String) fieldMap.get(Field.TYPE.toString()); + String fieldName = (String) fieldMap.get(Field.NAME.toString()); + if (fieldType.contains("Entity")) { + if (Collection.class.isAssignableFrom(pojoClass.getDeclaredField(fieldName).getType())) { + suffix = "Ids"; + if (resultName.endsWith("s")) { + // Assume trailing 's' as indicator for a plural + resultName = resultName.substring(0, resultName.length() - 1); + } + } else { + suffix = "Id"; + } + if (byObjectReference + && isEntityInComponent(pojoClass.getDeclaredField(fieldName).getType().getName(), component)) { + // direct references for Entities in same component, so get id of the object reference + suffix = "().getId"; + } + } + + return resultName + suffix; + + } + + /** + * Returns the argument type of the list or set from a field. If the string contains "Entity" it will remove that + * part. For example, if we have a List <SampleEntity> it will return "Sample" + * + * @param field the field + * @param pojoClass the object class of the Entity that contains the field + * @return fieldType argument of the list + * @throws SecurityException if field type could not accessed + * @throws NoSuchFieldException if field could not be found + */ + public String getListArgumentType(Map field, Class pojoClass) + throws NoSuchFieldException, SecurityException { + + JavaUtil javaUtil = new JavaUtil(); + + String fieldType = (String) field.get(Field.TYPE.toString()); + String fieldName = (String) field.get(Field.NAME.toString()); + + if (fieldType.contains("Entity")) { + if (javaUtil.isCollection(pojoClass, fieldName)) { + + fieldType = fieldType.replace("Entity", ""); + // Regex: Extracts the argument type of the list 'List' => type + String regex = "(?<=\\<).+?(?=\\>)"; + Pattern pattern = Pattern.compile(regex); + Matcher regexMatcher = pattern.matcher(fieldType); + + if (regexMatcher.find()) { + fieldType = regexMatcher.group(0); + } + } + } + return fieldType; + + } + + /** + * Converts all occurrences of devon4j Entity types in the given 'field' simple type (possibly generic) to Longs + * + * @param field the field + * @return the field type as String. If field type contains 'Entity' the result is Long + */ + public String getSimpleEntityTypeAsLongReference(Map field) { + + String fieldType = (String) field.get(Field.TYPE.toString()); + if (fieldType.endsWith("Entity")) { + fieldType = fieldType.replaceAll("[^<>]+Entity", "Long"); + } + return fieldType; + } + + /** + * If the string last character is an 's', then it gets removed + * + * @param targetClassName string to remove plural + * @return string without 's' + */ + public String removePlural(String targetClassName) { + + if (targetClassName.charAt(targetClassName.length() - 1) == 's') { + targetClassName = targetClassName.substring(0, targetClassName.length() - 1); + } + return targetClassName; + } + + /** + * Checks whether the operation with the given ID corresponds to any standard CRUD method name. + * + * @param operationId operation ID interpreted as method name + * @param entityName entity name to check standard CRUD methods for + * @return true if the operation ID maps any standard CRUD method name, false otherwise + */ + public boolean isCrudOperation(String operationId, String entityName) { + + if (operationId == null) { + return false; + } + String opIdLowerCase = operationId.toLowerCase(); + String entityNameLowerCase = entityName.toLowerCase(); + if (opIdLowerCase.contains(entityNameLowerCase)) { + return opIdLowerCase.equals("find" + entityNameLowerCase) + || opIdLowerCase.equals("find" + entityNameLowerCase + "Etos") + || opIdLowerCase.equals("delete" + entityNameLowerCase) || opIdLowerCase.equals("save" + entityNameLowerCase); + } else { + return false; + } + } + + /** + * Converts the given media type to the spring Java enum value + * + * @param mediaType to be converted + * @return the spring enum value representing the given media type + */ + public String getSpringMediaType(String mediaType) { + + switch (mediaType) { + case "application/xml": + return "APPLICATION_XML_VALUE"; + case "application / x-www-form-urlencoded": + return "APPLICATION_FORM_URLENCODED_VALUE"; + case "multipart/form-data": + return "MULTIPART_FORM_DATA_VALUE"; + case "text/plain": + return "TEXT_PLAIN_VALUE"; + case "text/html": + return "TEXT_HTML_VALUE"; + case "application/pdf": + return "APPLICATION_PDF_VALUE"; + case "image/png": + return "IMAGE_PNG_VALUE"; + default: + return "APPLICATION_JSON_VALUE"; + } + } +} \ No newline at end of file diff --git a/cobigen-templates/crud-java-ea-uml/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java b/cobigen-templates/crud-java-ea-uml/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java new file mode 100644 index 0000000000..ced2f5fde2 --- /dev/null +++ b/cobigen-templates/crud-java-ea-uml/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java @@ -0,0 +1,464 @@ +package com.devonfw.cobigen.templates.devon4j.utils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + +import org.apache.commons.lang3.ClassUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides type operations, mainly checks and casts for Java Primitives, to be used in the templates + * + */ +public class JavaUtil { + + /** + * Logger for this class + */ + private static final Logger LOG = LoggerFactory.getLogger(JavaUtil.class); + + /** + * The constructor. + */ + public JavaUtil() { + + // Empty for CobiGen to automatically instantiate it + } + + /** + * Returns the Object version of a Java primitive or the input if the input isn't a java primitive + * + * @param simpleType String + * @return the corresponding object wrapper type simple name of the input if the input is the name of a primitive java + * type. The input itself if not. (e.g. "int" results in "Integer") + * @throws ClassNotFoundException should not occur. + */ + public String boxJavaPrimitives(String simpleType) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return ClassUtils.primitiveToWrapper(ClassUtils.getClass(simpleType)).getSimpleName(); + } else { + return simpleType; + } + + } + + /** + * Returns the simple name of the type of a field in the pojoClass. If the type is a java primitive the name of the + * wrapper class is returned + * + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return String. The simple name of the field's type. The simple name of the wrapper class in case of java + * primitives + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String boxJavaPrimitives(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return ClassUtils.primitiveToWrapper(pojoClass.getDeclaredField(fieldName).getType()).getSimpleName(); + } else { + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + throw new IllegalAccessError("Could not find field " + fieldName + " in class " + pojoClass); + } else { + return field.getType().getSimpleName(); + } + } + } + + /** + * Checks if the given type is a Java primitive + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive + */ + public boolean equalsJavaPrimitive(String simpleType) { + + try { + return ClassUtils.getClass(simpleType).isPrimitive(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the given type is a Java primitive or wrapper + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive or wrapper + */ + public boolean equalsJavaPrimitiveOrWrapper(String simpleType) { + + try { + return ClassUtils.isPrimitiveOrWrapper(ClassUtils.getClass(simpleType)); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the type of the field in the pojo's class is a java primitive + * + * @param pojoClass the {@link Class} object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff the field is a java primitive + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitive(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return field.getType().isPrimitive(); + } + } + + /** + * Checks if the given type is a Java primitive or a Java primitive array + * + * @param simpleType the Type name to be checked + * @return true iff {@link #equalsJavaPrimitive(String)} is true or if simpleType is an array with a primitive + * component + */ + public boolean equalsJavaPrimitiveIncludingArrays(String simpleType) { + + Class klasse; + + try { + klasse = ClassUtils.getClass(simpleType).getComponentType(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + return equalsJavaPrimitive(simpleType) || (klasse != null && klasse.isPrimitive()); + } + + /** + * Checks if the given field in the pojo class is a java primitive or an array of java primitives + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff {@link #equalsJavaPrimitive(Class, String)} is true or the field is an array of primitives + * @throws NoSuchFieldException indicating something awfully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitiveIncludingArrays(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + return equalsJavaPrimitive(pojoClass, fieldName) || (pojoClass.getDeclaredField(fieldName).getType().isArray() + && pojoClass.getDeclaredField(fieldName).getType().getComponentType().isPrimitive()); + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param simpleType Java Type + * @param varName Variable name + * @return String either of the form '((Java Primitive Object Type)varName)' if simpleType is a primitive or the empty + * String otherwise + * @throws ClassNotFoundException should not occur + */ + public String castJavaPrimitives(String simpleType, String varName) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(simpleType), varName); + } else { + return ""; + } + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be casted + * @return if fieldName points to a primitive field then a casted statement (e.g. for an int field: + * '((Integer)field)') or an empty String otherwise + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String castJavaPrimitives(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(pojoClass, fieldName), fieldName); + } else { + return ""; + } + } + + /** + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return true if the field is an instance of java.utils.Collections + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean isCollection(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return Collection.class.isAssignableFrom(field.getType()); + } + + } + + /** + * Returns the Ext Type to a given java type + * + * @param simpleType any java type's simple name + * @return corresponding Ext type + */ + public String getExtType(String simpleType) { + + switch (simpleType) { + case "short": + case "Short": + case "int": + case "Integer": + case "long": + case "Long": + return "Integer"; + case "float": + case "Float": + case "double": + case "Double": + return "Number"; + case "boolean": + case "Boolean": + return "Boolean"; + case "char": + case "Character": + case "String": + return "String"; + case "Date": + return "Date"; + default: + return "Field"; + } + } + + /** + * returns the Angular5 type associated with a Java primitive + * + * @param simpleType :{@link String} the type to be parsed + * @return the corresponding Angular type or 'any' otherwise + */ + public String getAngularType(String simpleType) { + + switch (simpleType) { + case "boolean": + return "boolean"; + case "Boolean": + return "boolean"; + case "short": + return "number"; + case "Short": + return "number"; + case "int": + return "number"; + case "Integer": + return "number"; + case "long": + return "number"; + case "Long": + return "number"; + case "float": + return "number"; + case "Float": + return "number"; + case "double": + return "number"; + case "Double": + return "number"; + case "char": + return "string"; + case "Character": + return "string"; + case "String": + return "string"; + case "byte": + return "number"; + default: + return "any"; + } + } + + /** + * returns the class name of the return type of a specific method. + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName {@link String} the name of the method + * @return the class name of the return type of the specified method + * @throws SecurityException If no method of the given name can be found + * @throws NoSuchMethodException If no method of the given name can be found + */ + public String getReturnType(Class pojoClass, String methodName) throws NoSuchMethodException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + String s = "-"; + Method method = findMethod(pojoClass, methodName); + if (method != null && !method.getReturnType().equals(Void.TYPE)) { + s = method.getReturnType().toString(); + s = s.substring(s.lastIndexOf('.') + 1, s.length()); + } + return s; + } + + /** + * + * This methods returns the return type of the method in the given pojoClass which are annotated with the parameter + * annotatedClass + * + * @param pojoClass - The class in which to find if it has methods with annotatedClass + * @param annotatedClassName - The annotation which needs to be found + * @return Return type of the method annotated with the given annotation, else "null" + * @throws ClassNotFoundException if the annotated class name could not be found in the class path + */ + @SuppressWarnings("unchecked") + public String getReturnTypeOfMethodAnnotatedWith(Class pojoClass, String annotatedClassName) + throws ClassNotFoundException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + Method[] methods = pojoClass.getDeclaredMethods(); + for (Method method : methods) { + if (!method.getName().startsWith("get")) { + continue; + } + for (Annotation a : method.getAnnotations()) { + // better if (method.isAnnotationPresent(classObj)) {, but postponed as of different class + // loaders of a.getClass() and pojoClass.getClass() + if (a.getClass().getCanonicalName().equals(annotatedClassName)) { + return method.getReturnType().getSimpleName(); + } + } + } + return "null"; + } + + /** + * returns the HTTP request type corresponding to an annotation type + * + * @param annotations The annotation to get the type name of + * @return the HTTP request type name of the selected annotation + */ + public String getRequestType(Map annotations) { + + if (annotations.containsKey("javax_ws_rs_GET")) { + return "GET"; + } else if (annotations.containsKey("javax_ws_rs_PUT")) { + return "PUT"; + } else if (annotations.containsKey("javax_ws_rs_POST")) { + return "POST"; + } else if (annotations.containsKey("javax_ws_rs_DELETE")) { + return "DELETE"; + } else if (annotations.containsKey("javax_ws_rs_PATCH")) { + return "PATCH"; + } else { + return "-"; + } + } + + /** + * Helper method to find a class's specific method + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName The name of the method to be found + * @return The method object of the method to be found, null if it wasn't found + */ + private Method findMethod(Class pojoClass, String methodName) { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + for (Method m : pojoClass.getMethods()) { + if (m.getName().equals(methodName)) { + return m; + } + } + return null; + } + + /** + * Checks whether the class given by the full qualified name is an enum + * + * @param className full qualified class name + * @return true if the class is an enum, false otherwise + */ + public boolean isEnum(String className) { + + try { + return ClassUtils.getClass(className).isEnum(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return false; + } + } + + /** + * Returns the first enum value of an enum class + * + * @param className full qualified class name + * @return the first enum value name found in order + */ + public String getFirstEnumValue(String className) { + + try { + Class enumClass = ClassUtils.getClass(className); + Field[] declaredFields = enumClass.getDeclaredFields(); + if (declaredFields.length > 0) { + return declaredFields[0].getName(); + } else { + return null; + } + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return null; + } + } +} diff --git a/cobigen-templates/crud-java-ea-uml/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/uml/Connector.java b/cobigen-templates/crud-java-ea-uml/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/uml/Connector.java new file mode 100644 index 0000000000..fd1a9c838d --- /dev/null +++ b/cobigen-templates/crud-java-ea-uml/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/uml/Connector.java @@ -0,0 +1,104 @@ +package com.devonfw.cobigen.templates.devon4j.utils.uml; + +/** + * Connector is one association between classes in a class diagram. This class is used for storing that data, for later + * developing templates. + */ +public class Connector { + + private String counterpartName = ""; + + private String counterpartMultiplicity = ""; + + private String className; + + private String multiplicity; + + final Boolean ISSOURCE; + + final Boolean ISTARGET; + + /** + * @param className The name of the connector + * @param multiplicity The multiplicity of the target of this connector + * @param isSource True if the connector is the source, false if it is the target + */ + public Connector(String className, String multiplicity, boolean isSource) { + + this.className = className; + this.multiplicity = multiplicity; + this.ISSOURCE = isSource; + this.ISTARGET = !isSource; + } + + /** + * @return className name of the class + */ + public String getClassName() { + + return this.className; + } + + /** + * @param className name of the class + */ + public void setClassName(String className) { + + this.className = className; + } + + /** + * @return multiplicity + */ + public String getMultiplicity() { + + return this.multiplicity; + } + + /** + * @param multiplicity multiplicity of the connection + */ + public void setMultiplicity(String multiplicity) { + + this.multiplicity = multiplicity; + } + + /** + * @return counterpartName + */ + public String getCounterpartName() { + + return this.counterpartName; + } + + /** + * @param counterpartName Name of the counter part entity + */ + public void setCounterpartName(String counterpartName) { + + this.counterpartName = counterpartName; + } + + /** + * @return counterpartMultiplicity + */ + public String getCounterpartMultiplicity() { + + return this.counterpartMultiplicity; + } + + /** + * @param counterpartMuliplicity multiplicity of the counter part entity + */ + public void setCounterpartMultiplicity(String counterpartMuliplicity) { + + this.counterpartMultiplicity = counterpartMuliplicity; + } + + @Override + public String toString() { + + return this.ISSOURCE + " " + this.className + " " + this.multiplicity + " --> " + this.counterpartName + " " + + this.counterpartMultiplicity + " " + this.ISTARGET; + } +} diff --git a/cobigen-templates/crud-java-ea-uml/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/uml/UmlUtil.java b/cobigen-templates/crud-java-ea-uml/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/uml/UmlUtil.java new file mode 100644 index 0000000000..0d8f745615 --- /dev/null +++ b/cobigen-templates/crud-java-ea-uml/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/uml/UmlUtil.java @@ -0,0 +1,343 @@ +package com.devonfw.cobigen.templates.devon4j.utils.uml; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.apache.commons.lang3.text.WordUtils; +import org.w3c.dom.Attr; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import com.devonfw.cobigen.templates.devon4j.utils.DevonfwUtil; + +/** + * + */ +public class UmlUtil { + + /** + * List of connectors + */ + private List connectors = new ArrayList<>(); + + /** + * For generating the variables and methods (Getters and Setters) of all the connected classes to this class + * + * @param isImpl Boolean: Is implementation tag needed + * @param isOverride Boolean: Is override tag needed + * @return String: Contains all the generated text + */ + public String generateConnectorsVariablesMethodsText(boolean isImpl, boolean isOverride) { + + String textContent = generateText(isImpl, isOverride); + + this.connectors = new ArrayList(); + + return textContent; + } + + /** + * Stores connector's source and target in HashMaps for further generation + * + * @param source source object + * @param target target object + * @param className name of the class + */ + public void resolveConnectorsContent(Object source, Object target, String className) { + + Node sourceNode = (Node) source; + Node targetNode = (Node) target; + + HashMap sourceHash = new HashMap<>(); + NodeList childs = sourceNode.getChildNodes(); + for (int i = 0; i < childs.getLength(); i++) { + sourceHash.put(childs.item(i).getNodeName(), childs.item(i)); + } + + HashMap targetHash = new HashMap<>(); + childs = targetNode.getChildNodes(); + for (int i = 0; i < childs.getLength(); i++) { + targetHash.put(childs.item(i).getNodeName(), childs.item(i)); + } + + setConnectorsContent(sourceHash, targetHash, className); + } + + /** + * Sets to the Connectors class the information retrieved from source and target tags. Only sets the classes that are + * connected to our class + * + * @param sourceHash Source hash + * @param targetHash Target hash + * @param className name of the class + */ + public void setConnectorsContent(HashMap sourceHash, HashMap targetHash, String className) { + + Connector sourceConnector = null; + Connector targetConnector = null; + boolean isTarget = false; + boolean isSource = false; + + String sourceName = getClassName(sourceHash); + String sourceMultiplicity = getMultiplicity(sourceHash); + if (sourceName.equals(className)) { + isSource = true; + } + + String targetName = getClassName(targetHash); + String targetMultiplicity = getMultiplicity(targetHash); + if (sourceName.equals(className)) { + isTarget = true; + } + + if (isSource) { + sourceConnector = getConnector(sourceHash, true, targetMultiplicity, targetName); + this.connectors.add(sourceConnector); + } else if (isTarget) { + targetConnector = getConnector(targetHash, false, sourceMultiplicity, sourceName); + this.connectors.add(targetConnector); + } + } + + /** + * Creates a Connector. The connector class is contains the information retrieved to the classes that are connected to + * our class + * + * @param nodeHash contains the node + * @param isSource true if I am source + * @param counterpartMultiplicity multiplicity of the counter part + * @param counterpartName Name of the counter part + * @return A newly created Connector + */ + private Connector getConnector(HashMap nodeHash, boolean isSource, String counterpartMultiplicity, + String counterpartName) { + + Connector connector = new Connector(getClassName(nodeHash), getMultiplicity(nodeHash), isSource); + connector.setCounterpartMultiplicity(counterpartMultiplicity); + connector.setCounterpartName(counterpartName); + + return connector; + } + + /** + * Extracts the multiplicity of a Connector from a Node + * + * @param nodeHash The node to get the multiplicity of + * @return The multiplicity of the connector + */ + private String getMultiplicity(HashMap nodeHash) { + + if (nodeHash.containsKey("type")) { + Node node = (Node) nodeHash.get("type"); + NamedNodeMap attrs = node.getAttributes(); + for (int j = 0; j < attrs.getLength(); j++) { + Attr attribute = (Attr) attrs.item(j); + if (attribute.getName().equals("multiplicity")) { + return attribute.getValue(); + } + } + } + return "1"; + } + + /** + * Extracts the name of a Connector from a Node + * + * @param nodeHash The node to get the name of + * @return The name of the connector + */ + private String getClassName(HashMap nodeHash) { + + if (nodeHash.containsKey("model")) { + Node node = (Node) nodeHash.get("model"); + NamedNodeMap attrs = node.getAttributes(); + for (int j = 0; j < attrs.getLength(); j++) { + Attr attribute = (Attr) attrs.item(j); + if (attribute.getName().equals("name")) { + return attribute.getValue(); + } + } + } + return "ErrorClassName"; + } + + /** + * @param isImpl true if this is called from an Implementation template + * @param isOverride true if this is called from an Entity template + * @return Generated text + */ + public String generateText(boolean isImpl, boolean isOverride) { + + String content = ""; + if (isImpl) { + for (Connector connector : this.connectors) { + String connectedClassName = connector.getCounterpartName(); + String multiplicity = connector.getCounterpartMultiplicity(); + if (multiplicity == null || multiplicity.equals("1")) { + content += "\n\n\tprivate " + connectedClassName + "Entity " + connectedClassName.toLowerCase() + ";"; + } else if (multiplicity != null && multiplicity.equals("*")) { + content += "\n\n\tprivate List<" + connectedClassName + "Entity> " + + new DevonfwUtil().removePlural(connectedClassName.toLowerCase()) + "s;"; + } + } + } + + for (Connector connector : this.connectors) { + String connectedClassName = connector.getCounterpartName(); + String multiplicity = connector.getCounterpartMultiplicity(); + if (multiplicity == null || multiplicity.equals("1")) { + + content += "\n\n\t"; + if (isOverride) { + content += "@Override\n\t"; + } + if (isImpl) { + content += getRelationshipAnnotations(connector) + "\n\t"; + content += "public " + connectedClassName + "Entity get" + connectedClassName + "()"; + } else { + content += "public Long get" + connectedClassName + "Id()"; + } + if (isImpl) { + content += "{" + "\n\t\treturn this." + connectedClassName.toLowerCase() + ";" + "\n\t}"; + } else { + content += ";"; + } + + content += "\n\n\t"; + if (isOverride) { + content += "@Override\n\t"; + } + if (isImpl) { + content += "public void set" + connectedClassName + "(" + connectedClassName + "Entity " + + connectedClassName.toLowerCase() + ")"; + } else { + content += "public void set" + connectedClassName + "Id(Long " + connectedClassName.toLowerCase() + "Id)"; + } + if (isImpl) { + content += "{" + "\n\t\tthis." + connectedClassName.toLowerCase() + " = " + connectedClassName.toLowerCase() + + ";" + "\n\t}"; + } else { + content += ";"; + } + // Now w generate the get and set IDs methods for the implementation + if (isImpl) { + // getter + content += "\n\n\t"; + content += "@Override\n\t"; + content += "public Long get" + connectedClassName + "Id()"; + content += "{" + "\n\t\tif(this." + connectedClassName.toLowerCase() + " == null){"; + content += "\n\t\treturn null;\n\t}"; + content += "\n\t\treturn this." + connectedClassName.toLowerCase() + ".getId();" + "\n\t}"; + // setter + content += "\n\n\t"; + content += "@Override\n\t"; + content += "public void set" + connectedClassName + "Id(Long " + connectedClassName.toLowerCase() + "Id)"; + content += "{" + "\n\t\tif(" + connectedClassName.toLowerCase() + "Id == null){"; + content += "\n\t\tthis." + connectedClassName.toLowerCase() + " = null;\n\t}"; + content += "else {\n\t"; + content += connectedClassName + "Entity " + connectedClassName.toLowerCase() + "Entity = new " + + connectedClassName + "Entity();\n\n\t"; + content += connectedClassName.toLowerCase() + ".setId(" + connectedClassName.toLowerCase() + "Id);\n\n\t"; + content += "this." + connectedClassName.toLowerCase() + " " + "= " + connectedClassName.toLowerCase() + + "Entity;\n\n\t}"; + content += "\n\n\t}"; + } + + } else if (multiplicity != null && multiplicity.equals("*") && isImpl) { + + content += "\n\n\t"; + if (isOverride) { + content += "@Override\n\t"; + } + content += getRelationshipAnnotations(connector) + "\n\t"; + content += "public List<" + connectedClassName + "Entity> get" + + new DevonfwUtil().removePlural(connectedClassName) + "s()"; + content += "{" + "\n\t\treturn this." + new DevonfwUtil().removePlural(connectedClassName.toLowerCase()) + "s;" + + "\n\t}"; + content += "\n\n\t"; + if (isOverride) { + content += "@Override\n\t"; + } + content += "public void set" + new DevonfwUtil().removePlural(connectedClassName) + "s(List<" + connectedClassName + + "Entity> " + new DevonfwUtil().removePlural(connectedClassName.toLowerCase()) + "s)"; + content += "{" + "\n\t\tthis." + new DevonfwUtil().removePlural(connectedClassName.toLowerCase()) + "s = " + + new DevonfwUtil().removePlural(connectedClassName.toLowerCase()) + "s;" + "\n\t}"; + } + } + return content; + } + + /** + * Generates the annotations of all the connected classes + * + * @param source The source connector that is used to generate relationship annotations + * @return relationship string with all the annotations for the connected classes + */ + private String getRelationshipAnnotations(Connector source) { + + String relationship = ""; + if (source.ISSOURCE) { + if (source.getMultiplicity() == null || source.getMultiplicity().equals("1")) { + if (source.getCounterpartMultiplicity() == null || source.getCounterpartMultiplicity().equals("1")) { + relationship = "@OneToOne()" + "\n\t@JoinColumn(name = \"" + source.getCounterpartName() + "Id\")"; + } else if (source.getCounterpartMultiplicity().equals("*")) { + relationship = "@OneToMany(fetch = FetchType.LAZY)\n\t@JoinColumn(name = \"" + + WordUtils.capitalize(source.getCounterpartName()) + "id\")"; + } + } else if (source.getMultiplicity().equals("*")) { + if (source.getCounterpartMultiplicity().equals("*")) { + relationship += "@ManyToMany()"; + relationship += "\n\t@JoinTable(name = \"" + WordUtils.capitalize(source.getCounterpartName()) + + WordUtils.capitalize(source.getClassName()) + "\", joinColumns = @JoinColumn(name = \"" + + source.getClassName() + "Id\"), inverseJoinColumns = @JoinColumn(name = \"" + + source.getCounterpartName() + "Id\"))"; + } else if (source.getCounterpartMultiplicity().equals("1")) { + relationship += "@ManyToOne(fetch = FetchType.LAZY)\n\t@JoinColumn(name = \"" + source.getCounterpartName() + + "Id\")"; + } + } + } else if (source.ISTARGET) { + if (source.getCounterpartMultiplicity() == null || source.getCounterpartMultiplicity().equals("1")) { + if (source.getMultiplicity() == null || source.getMultiplicity().equals("1")) { + relationship = "@OneToOne()" + "\n\t"// + + "@JoinColumn(name = \"" + source.getCounterpartName() + "Id\")"; + } else if (source.getMultiplicity().equals("*")) { + relationship += "@ManyToOne(fetch = FetchType.LAZY)\n\t"// + + "@JoinColumn(name = \"" + source.getCounterpartName() + "Id\")"; + } + } else if (source.getCounterpartMultiplicity().equals("*")) { + if (source.getMultiplicity().equals("*")) { + relationship += "@ManyToMany(mappedBy = \"" + + new DevonfwUtil().removePlural(source.getClassName()).toLowerCase() + "s\")"; + } else if (source.getMultiplicity().equals("1")) { + relationship = "@OneToMany(fetch = FetchType.LAZY, mappedBy = \"" + source.getCounterpartName().toLowerCase() + + "\")"; + } + } + } + return relationship; + } + + /** + * Returns connectors + * + * @return connectors + */ + public List getConnectors() { + + return this.connectors; + } + + /** + * Sets a new connector list + * + * @param connectors The new list of connectors + */ + public void setConnectors(List connectors) { + + this.connectors = connectors; + } + +} diff --git a/cobigen-templates/crud-java-server-app-complex/pom.xml b/cobigen-templates/crud-java-server-app-complex/pom.xml index 82a06f6b0c..7876d0c5f7 100644 --- a/cobigen-templates/crud-java-server-app-complex/pom.xml +++ b/cobigen-templates/crud-java-server-app-complex/pom.xml @@ -10,4 +10,10 @@ ${revision} + + + org.apache.commons + commons-lang3 + + diff --git a/cobigen-templates/crud-java-server-app-complex/src/main/java/com/devonfw/cobigen/templates/devon4j/constants/Field.java b/cobigen-templates/crud-java-server-app-complex/src/main/java/com/devonfw/cobigen/templates/devon4j/constants/Field.java new file mode 100644 index 0000000000..5376daaea9 --- /dev/null +++ b/cobigen-templates/crud-java-server-app-complex/src/main/java/com/devonfw/cobigen/templates/devon4j/constants/Field.java @@ -0,0 +1,48 @@ +package com.devonfw.cobigen.templates.devon4j.constants; + +/** + * Contains the used keys for the pojo field mapping + */ +public enum Field { + + /** + * Name of the field + */ + NAME("name"), + /** + * Type of the field + */ + TYPE("type"), + /** + * Canonical Type of the field + */ + CANONICAL_TYPE("canonicalType"), + /** + * The Javadoc of the field + */ + JAVA_DOC("javaDoc"), + /** + * Annotations + */ + ANNOTATIONS("annotations"); + + /** + * key value + */ + private String value; + + /** + * @param value of the key + */ + Field(String value) { + + this.value = value; + } + + @Override + public String toString() { + + return this.value; + } + +} diff --git a/cobigen-templates/crud-java-server-app-complex/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/DevonfwUtil.java b/cobigen-templates/crud-java-server-app-complex/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/DevonfwUtil.java new file mode 100644 index 0000000000..36e39b5e06 --- /dev/null +++ b/cobigen-templates/crud-java-server-app-complex/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/DevonfwUtil.java @@ -0,0 +1,450 @@ +package com.devonfw.cobigen.templates.devon4j.utils; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.w3c.dom.Attr; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.NodeList; + +import com.devonfw.cobigen.templates.devon4j.constants.Field; +import com.sun.org.apache.xerces.internal.dom.DeferredElementNSImpl; + +/** + * A class for shared devon4j specific functions in the templates + * + */ +@SuppressWarnings("restriction") +public class DevonfwUtil { + + /** + * Check whether the given 'canonicalType' is a devon4j Entity, which is declared in the given 'component' + * + * @param canonicalType the type name + * @param component the component name + * @return true iff the canonicalType is a devon Entity + */ + public boolean isEntityInComponent(String canonicalType, String component) { + + return canonicalType.matches(String.format(".+%1$s\\.dataaccess\\.api\\.[A-Za-z0-9]+Entity(<.*)?", component)); + } + + /** + * Check whether the given 'canonicalType' is declared in the given 'component' + * + * @param canonicalType the type name + * @param component the component name + * @return true iff the canonicalType is inside the given component + */ + public boolean isTypeInComponent(String canonicalType, String component) { + + return canonicalType.matches(String.format("%1$s.[A-Za-z0-9]+(<.*)?", component)); + } + + /** + * Determines the ID getter for a given 'field' dependent on whether the getter should access the ID via an object + * reference or a direct ID getter + * + * @param field the field + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} + '()' with + * capitalize=true + */ + public String resolveIdGetter(Map field, boolean byObjectReference, String component) { + + // If field comes from an UML file + if (field.getClass().toGenericString().contains("freemarker.ext.beans.HashAdapter")) { + DeferredElementNSImpl umlNode = (DeferredElementNSImpl) field; + return resolveIdGetter(umlNode, byObjectReference, component); + } + return "get" + resolveIdVariableNameOrSetterGetterSuffix(field, byObjectReference, true, component) + "()"; + } + + /** + * Determines the ID getter for a given 'field' dependent on whether the getter should access the ID via an object + * reference or a direct ID getter + * + * This method is used when the field parameter comes from an UML file. The name and type of the attributes must be + * pre-processed for later inserting them inside the HashMap. + * + * @param field the field + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} + '()' with + * capitalize=true + */ + public String resolveIdGetter(DeferredElementNSImpl field, boolean byObjectReference, String component) { + + HashMap nodeHash = new HashMap<>(); + + // Putting the name of the attribute to the hash + nodeHash.put(Field.NAME.toString(), field.getAttribute("name")); + + // Putting the type of the attribute to the hash + NodeList childs = field.getChildNodes(); + for (int i = 0; i < childs.getLength(); i++) { + // Retrieve "type" tag + if (childs.item(i).getNodeName().equals("type")) { + NamedNodeMap attrs = childs.item(i).getAttributes(); + for (int j = 0; j < attrs.getLength(); j++) { + Attr attribute = (Attr) attrs.item(j); + // Try to find the attribute that contains the type + if (attribute.getName().equals("xmi:idref")) { + nodeHash.put(Field.TYPE.toString(), attribute.getName().replace("EAJava_", "")); + } + } + } + } + return "get" + resolveIdVariableNameOrSetterGetterSuffix(nodeHash, byObjectReference, true, component) + "()"; + + } + + /** + * Determines the ID getter for a given 'field' dependent on whether the getter should access the ID via an object + * reference or a direct ID getter + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} + '()' with + * capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdGetter(Class pojoClass, Map fieldMap, boolean byObjectReference, + String component) throws NoSuchFieldException, SecurityException { + + return "get" + resolveIdVariableNameOrSetterGetterSuffix(pojoClass, fieldMap, byObjectReference, true, component) + + "()"; + } + + /** + * same as {@link #resolveIdGetter(Map, boolean, String)} but with byObjectReference=false and component="" + * + * @param field the field + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} + '()' with + * capitalize=true + */ + public String resolveIdGetter(Map field) { + + return this.resolveIdGetter(field, false, ""); + } + + /** + * same as {@link #resolveIdGetter(Class,Map,boolean,String)} but with byObjectReference=false and component="" + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} + '()' with + * capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdGetter(Class pojoClass, Map fieldMap) + throws NoSuchFieldException, SecurityException { + + return resolveIdGetter(pojoClass, fieldMap, false, ""); + } + + /** + * Determines the ID setter for a given 'field' dependent on whether the setter should access the ID via an object + * reference or a direct ID setter. In contrast to resolveIdGetter, this function does not generate the function + * parenthesis to enable parameter declaration. + * + * @param field the field + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'set' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} with + * capitalize=true + */ + public String resolveIdSetter(Map field, boolean byObjectReference, String component) { + + return "set" + resolveIdVariableNameOrSetterGetterSuffix(field, byObjectReference, true, component); + } + + /** + * same as {@link #resolveIdSetter(Map, boolean, String)} but with byObjectReference=false and component="" + * + * @param field the field + * @return 'set' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} with + * capitalize=true + */ + public String resolveIdSetter(Map field) { + + return this.resolveIdSetter(field, false, ""); + } + + /** + * Determines the ID setter for a given 'field' dependent on whether the setter should access the ID via an object + * reference or a direct ID setter. In contrast to resolveIdGetter, this function does not generate the function + * parenthesis to enable parameter declaration. + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'set'+ {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} with capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdSetter(Class pojoClass, Map fieldMap, boolean byObjectReference, + String component) throws NoSuchFieldException, SecurityException { + + return "set" + resolveIdVariableNameOrSetterGetterSuffix(pojoClass, fieldMap, byObjectReference, true, component); + } + + /** + * same as {@link #resolveIdSetter(Class,Map,boolean,String)} but with byObjectReference=false and component="" + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @return 'set' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} with capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdSetter(Class pojoClass, Map fieldMap) + throws NoSuchFieldException, SecurityException { + + return resolveIdSetter(pojoClass, fieldMap, false, ""); + } + + /** + * Determines the variable name for the id value of the 'field' + * + * @param field the field + * @return {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)}) with + * byObjectReference=false, capitalize=false and component="" + */ + public String resolveIdVariableName(Map field) { + + // the component is passed down as an empty string since byObjectReference is false and therefore the + // component is + // never touched + return resolveIdVariableNameOrSetterGetterSuffix(field, false, false, ""); + } + + /** + * Determines the variable name for the id value of the specified field in the pojo + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @return {@link #resolveIdVariableNameOrSetterGetterSuffix(Class, Map, boolean, boolean, String)}) with + * byObjectReference=false, capitalize=false and component="" + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdVariableName(Class pojoClass, Map fieldMap) + throws NoSuchFieldException, SecurityException { + + // the component is passed down as an empty string since byObjectReference is false and therefore the + // component is + // never touched + return resolveIdVariableNameOrSetterGetterSuffix(pojoClass, fieldMap, false, false, ""); + } + + /** + * Determines the ID setter/getter suffix for a given 'field' dependent on whether the setter/getter should access the + * ID via an object reference or a direct ID setter/getter + * + * @param field the field + * @param byObjectReference boolean + * @param capitalize if the field name should be capitalized + * @param component the devon4j component. Only needed if $byObjectReference is true + * @return idVariable name or getter/setter suffix + */ + public String resolveIdVariableNameOrSetterGetterSuffix(Map field, boolean byObjectReference, + boolean capitalize, String component) { + + String fieldName = (String) field.get(Field.NAME.toString()); + if (capitalize) { + fieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); + } + String suffix = ""; + + String fieldType = (String) field.get(Field.TYPE.toString()); + String fieldCType = (String) field.get(Field.CANONICAL_TYPE.toString()); + if (fieldType.contains("Entity")) { + if (fieldCType.startsWith("java.util.List") || fieldCType.startsWith("java.util.Set")) { + suffix = "Ids"; + if (fieldName.endsWith("s")) { + // Assume trailing 's' as indicator for a plural + fieldName = fieldName.substring(0, fieldName.length() - 1); + } + } else { + suffix = "Id"; + } + if (byObjectReference && isTypeInComponent(fieldCType, component)) { + // direct references for Entities in same component, so get id of the object reference + suffix = "().getId"; + } + } + + return fieldName + suffix; + + } + + /** + * Determines the ID setter/getter suffix for a given 'field' dependent on whether the setter/getter should access the + * ID via an object reference or a direct ID setter/getter + * + * @param pojoClass the {@link Class} object of the pojo + * @param fieldMap the field mapping + * @param byObjectReference boolean + * @param capitalize if the field name should be capitalized + * @param component the devon4j component. Only needed if byObjectReference is true + * @return idVariable name or getter/setter suffix + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdVariableNameOrSetterGetterSuffix(Class pojoClass, Map fieldMap, + boolean byObjectReference, boolean capitalize, String component) throws NoSuchFieldException, SecurityException { + + String resultName = (String) fieldMap.get(Field.NAME.toString()); + if (capitalize) { + resultName = resultName.substring(0, 1).toUpperCase() + resultName.substring(1); + } + String suffix = ""; + String fieldType = (String) fieldMap.get(Field.TYPE.toString()); + String fieldName = (String) fieldMap.get(Field.NAME.toString()); + if (fieldType.contains("Entity")) { + if (Collection.class.isAssignableFrom(pojoClass.getDeclaredField(fieldName).getType())) { + suffix = "Ids"; + if (resultName.endsWith("s")) { + // Assume trailing 's' as indicator for a plural + resultName = resultName.substring(0, resultName.length() - 1); + } + } else { + suffix = "Id"; + } + if (byObjectReference + && isEntityInComponent(pojoClass.getDeclaredField(fieldName).getType().getName(), component)) { + // direct references for Entities in same component, so get id of the object reference + suffix = "().getId"; + } + } + + return resultName + suffix; + + } + + /** + * Returns the argument type of the list or set from a field. If the string contains "Entity" it will remove that + * part. For example, if we have a List <SampleEntity> it will return "Sample" + * + * @param field the field + * @param pojoClass the object class of the Entity that contains the field + * @return fieldType argument of the list + * @throws SecurityException if field type could not accessed + * @throws NoSuchFieldException if field could not be found + */ + public String getListArgumentType(Map field, Class pojoClass) + throws NoSuchFieldException, SecurityException { + + JavaUtil javaUtil = new JavaUtil(); + + String fieldType = (String) field.get(Field.TYPE.toString()); + String fieldName = (String) field.get(Field.NAME.toString()); + + if (fieldType.contains("Entity")) { + if (javaUtil.isCollection(pojoClass, fieldName)) { + + fieldType = fieldType.replace("Entity", ""); + // Regex: Extracts the argument type of the list 'List' => type + String regex = "(?<=\\<).+?(?=\\>)"; + Pattern pattern = Pattern.compile(regex); + Matcher regexMatcher = pattern.matcher(fieldType); + + if (regexMatcher.find()) { + fieldType = regexMatcher.group(0); + } + } + } + return fieldType; + + } + + /** + * Converts all occurrences of devon4j Entity types in the given 'field' simple type (possibly generic) to Longs + * + * @param field the field + * @return the field type as String. If field type contains 'Entity' the result is Long + */ + public String getSimpleEntityTypeAsLongReference(Map field) { + + String fieldType = (String) field.get(Field.TYPE.toString()); + if (fieldType.endsWith("Entity")) { + fieldType = fieldType.replaceAll("[^<>]+Entity", "Long"); + } + return fieldType; + } + + /** + * If the string last character is an 's', then it gets removed + * + * @param targetClassName string to remove plural + * @return string without 's' + */ + public String removePlural(String targetClassName) { + + if (targetClassName.charAt(targetClassName.length() - 1) == 's') { + targetClassName = targetClassName.substring(0, targetClassName.length() - 1); + } + return targetClassName; + } + + /** + * Checks whether the operation with the given ID corresponds to any standard CRUD method name. + * + * @param operationId operation ID interpreted as method name + * @param entityName entity name to check standard CRUD methods for + * @return true if the operation ID maps any standard CRUD method name, false otherwise + */ + public boolean isCrudOperation(String operationId, String entityName) { + + if (operationId == null) { + return false; + } + String opIdLowerCase = operationId.toLowerCase(); + String entityNameLowerCase = entityName.toLowerCase(); + if (opIdLowerCase.contains(entityNameLowerCase)) { + return opIdLowerCase.equals("find" + entityNameLowerCase) + || opIdLowerCase.equals("find" + entityNameLowerCase + "Etos") + || opIdLowerCase.equals("delete" + entityNameLowerCase) || opIdLowerCase.equals("save" + entityNameLowerCase); + } else { + return false; + } + } + + /** + * Converts the given media type to the spring Java enum value + * + * @param mediaType to be converted + * @return the spring enum value representing the given media type + */ + public String getSpringMediaType(String mediaType) { + + switch (mediaType) { + case "application/xml": + return "APPLICATION_XML_VALUE"; + case "application / x-www-form-urlencoded": + return "APPLICATION_FORM_URLENCODED_VALUE"; + case "multipart/form-data": + return "MULTIPART_FORM_DATA_VALUE"; + case "text/plain": + return "TEXT_PLAIN_VALUE"; + case "text/html": + return "TEXT_HTML_VALUE"; + case "application/pdf": + return "APPLICATION_PDF_VALUE"; + case "image/png": + return "IMAGE_PNG_VALUE"; + default: + return "APPLICATION_JSON_VALUE"; + } + } +} \ No newline at end of file diff --git a/cobigen-templates/crud-java-server-app-complex/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java b/cobigen-templates/crud-java-server-app-complex/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java new file mode 100644 index 0000000000..ced2f5fde2 --- /dev/null +++ b/cobigen-templates/crud-java-server-app-complex/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java @@ -0,0 +1,464 @@ +package com.devonfw.cobigen.templates.devon4j.utils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + +import org.apache.commons.lang3.ClassUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides type operations, mainly checks and casts for Java Primitives, to be used in the templates + * + */ +public class JavaUtil { + + /** + * Logger for this class + */ + private static final Logger LOG = LoggerFactory.getLogger(JavaUtil.class); + + /** + * The constructor. + */ + public JavaUtil() { + + // Empty for CobiGen to automatically instantiate it + } + + /** + * Returns the Object version of a Java primitive or the input if the input isn't a java primitive + * + * @param simpleType String + * @return the corresponding object wrapper type simple name of the input if the input is the name of a primitive java + * type. The input itself if not. (e.g. "int" results in "Integer") + * @throws ClassNotFoundException should not occur. + */ + public String boxJavaPrimitives(String simpleType) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return ClassUtils.primitiveToWrapper(ClassUtils.getClass(simpleType)).getSimpleName(); + } else { + return simpleType; + } + + } + + /** + * Returns the simple name of the type of a field in the pojoClass. If the type is a java primitive the name of the + * wrapper class is returned + * + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return String. The simple name of the field's type. The simple name of the wrapper class in case of java + * primitives + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String boxJavaPrimitives(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return ClassUtils.primitiveToWrapper(pojoClass.getDeclaredField(fieldName).getType()).getSimpleName(); + } else { + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + throw new IllegalAccessError("Could not find field " + fieldName + " in class " + pojoClass); + } else { + return field.getType().getSimpleName(); + } + } + } + + /** + * Checks if the given type is a Java primitive + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive + */ + public boolean equalsJavaPrimitive(String simpleType) { + + try { + return ClassUtils.getClass(simpleType).isPrimitive(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the given type is a Java primitive or wrapper + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive or wrapper + */ + public boolean equalsJavaPrimitiveOrWrapper(String simpleType) { + + try { + return ClassUtils.isPrimitiveOrWrapper(ClassUtils.getClass(simpleType)); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the type of the field in the pojo's class is a java primitive + * + * @param pojoClass the {@link Class} object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff the field is a java primitive + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitive(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return field.getType().isPrimitive(); + } + } + + /** + * Checks if the given type is a Java primitive or a Java primitive array + * + * @param simpleType the Type name to be checked + * @return true iff {@link #equalsJavaPrimitive(String)} is true or if simpleType is an array with a primitive + * component + */ + public boolean equalsJavaPrimitiveIncludingArrays(String simpleType) { + + Class klasse; + + try { + klasse = ClassUtils.getClass(simpleType).getComponentType(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + return equalsJavaPrimitive(simpleType) || (klasse != null && klasse.isPrimitive()); + } + + /** + * Checks if the given field in the pojo class is a java primitive or an array of java primitives + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff {@link #equalsJavaPrimitive(Class, String)} is true or the field is an array of primitives + * @throws NoSuchFieldException indicating something awfully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitiveIncludingArrays(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + return equalsJavaPrimitive(pojoClass, fieldName) || (pojoClass.getDeclaredField(fieldName).getType().isArray() + && pojoClass.getDeclaredField(fieldName).getType().getComponentType().isPrimitive()); + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param simpleType Java Type + * @param varName Variable name + * @return String either of the form '((Java Primitive Object Type)varName)' if simpleType is a primitive or the empty + * String otherwise + * @throws ClassNotFoundException should not occur + */ + public String castJavaPrimitives(String simpleType, String varName) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(simpleType), varName); + } else { + return ""; + } + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be casted + * @return if fieldName points to a primitive field then a casted statement (e.g. for an int field: + * '((Integer)field)') or an empty String otherwise + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String castJavaPrimitives(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(pojoClass, fieldName), fieldName); + } else { + return ""; + } + } + + /** + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return true if the field is an instance of java.utils.Collections + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean isCollection(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return Collection.class.isAssignableFrom(field.getType()); + } + + } + + /** + * Returns the Ext Type to a given java type + * + * @param simpleType any java type's simple name + * @return corresponding Ext type + */ + public String getExtType(String simpleType) { + + switch (simpleType) { + case "short": + case "Short": + case "int": + case "Integer": + case "long": + case "Long": + return "Integer"; + case "float": + case "Float": + case "double": + case "Double": + return "Number"; + case "boolean": + case "Boolean": + return "Boolean"; + case "char": + case "Character": + case "String": + return "String"; + case "Date": + return "Date"; + default: + return "Field"; + } + } + + /** + * returns the Angular5 type associated with a Java primitive + * + * @param simpleType :{@link String} the type to be parsed + * @return the corresponding Angular type or 'any' otherwise + */ + public String getAngularType(String simpleType) { + + switch (simpleType) { + case "boolean": + return "boolean"; + case "Boolean": + return "boolean"; + case "short": + return "number"; + case "Short": + return "number"; + case "int": + return "number"; + case "Integer": + return "number"; + case "long": + return "number"; + case "Long": + return "number"; + case "float": + return "number"; + case "Float": + return "number"; + case "double": + return "number"; + case "Double": + return "number"; + case "char": + return "string"; + case "Character": + return "string"; + case "String": + return "string"; + case "byte": + return "number"; + default: + return "any"; + } + } + + /** + * returns the class name of the return type of a specific method. + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName {@link String} the name of the method + * @return the class name of the return type of the specified method + * @throws SecurityException If no method of the given name can be found + * @throws NoSuchMethodException If no method of the given name can be found + */ + public String getReturnType(Class pojoClass, String methodName) throws NoSuchMethodException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + String s = "-"; + Method method = findMethod(pojoClass, methodName); + if (method != null && !method.getReturnType().equals(Void.TYPE)) { + s = method.getReturnType().toString(); + s = s.substring(s.lastIndexOf('.') + 1, s.length()); + } + return s; + } + + /** + * + * This methods returns the return type of the method in the given pojoClass which are annotated with the parameter + * annotatedClass + * + * @param pojoClass - The class in which to find if it has methods with annotatedClass + * @param annotatedClassName - The annotation which needs to be found + * @return Return type of the method annotated with the given annotation, else "null" + * @throws ClassNotFoundException if the annotated class name could not be found in the class path + */ + @SuppressWarnings("unchecked") + public String getReturnTypeOfMethodAnnotatedWith(Class pojoClass, String annotatedClassName) + throws ClassNotFoundException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + Method[] methods = pojoClass.getDeclaredMethods(); + for (Method method : methods) { + if (!method.getName().startsWith("get")) { + continue; + } + for (Annotation a : method.getAnnotations()) { + // better if (method.isAnnotationPresent(classObj)) {, but postponed as of different class + // loaders of a.getClass() and pojoClass.getClass() + if (a.getClass().getCanonicalName().equals(annotatedClassName)) { + return method.getReturnType().getSimpleName(); + } + } + } + return "null"; + } + + /** + * returns the HTTP request type corresponding to an annotation type + * + * @param annotations The annotation to get the type name of + * @return the HTTP request type name of the selected annotation + */ + public String getRequestType(Map annotations) { + + if (annotations.containsKey("javax_ws_rs_GET")) { + return "GET"; + } else if (annotations.containsKey("javax_ws_rs_PUT")) { + return "PUT"; + } else if (annotations.containsKey("javax_ws_rs_POST")) { + return "POST"; + } else if (annotations.containsKey("javax_ws_rs_DELETE")) { + return "DELETE"; + } else if (annotations.containsKey("javax_ws_rs_PATCH")) { + return "PATCH"; + } else { + return "-"; + } + } + + /** + * Helper method to find a class's specific method + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName The name of the method to be found + * @return The method object of the method to be found, null if it wasn't found + */ + private Method findMethod(Class pojoClass, String methodName) { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + for (Method m : pojoClass.getMethods()) { + if (m.getName().equals(methodName)) { + return m; + } + } + return null; + } + + /** + * Checks whether the class given by the full qualified name is an enum + * + * @param className full qualified class name + * @return true if the class is an enum, false otherwise + */ + public boolean isEnum(String className) { + + try { + return ClassUtils.getClass(className).isEnum(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return false; + } + } + + /** + * Returns the first enum value of an enum class + * + * @param className full qualified class name + * @return the first enum value name found in order + */ + public String getFirstEnumValue(String className) { + + try { + Class enumClass = ClassUtils.getClass(className); + Field[] declaredFields = enumClass.getDeclaredFields(); + if (declaredFields.length > 0) { + return declaredFields[0].getName(); + } else { + return null; + } + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return null; + } + } +} diff --git a/cobigen-templates/crud-java-server-app/pom.xml b/cobigen-templates/crud-java-server-app/pom.xml index a96bb42a5f..ba577e68e5 100644 --- a/cobigen-templates/crud-java-server-app/pom.xml +++ b/cobigen-templates/crud-java-server-app/pom.xml @@ -10,4 +10,10 @@ ${revision} + + + org.apache.commons + commons-lang3 + + \ No newline at end of file diff --git a/cobigen-templates/crud-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/constants/Field.java b/cobigen-templates/crud-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/constants/Field.java new file mode 100644 index 0000000000..5376daaea9 --- /dev/null +++ b/cobigen-templates/crud-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/constants/Field.java @@ -0,0 +1,48 @@ +package com.devonfw.cobigen.templates.devon4j.constants; + +/** + * Contains the used keys for the pojo field mapping + */ +public enum Field { + + /** + * Name of the field + */ + NAME("name"), + /** + * Type of the field + */ + TYPE("type"), + /** + * Canonical Type of the field + */ + CANONICAL_TYPE("canonicalType"), + /** + * The Javadoc of the field + */ + JAVA_DOC("javaDoc"), + /** + * Annotations + */ + ANNOTATIONS("annotations"); + + /** + * key value + */ + private String value; + + /** + * @param value of the key + */ + Field(String value) { + + this.value = value; + } + + @Override + public String toString() { + + return this.value; + } + +} diff --git a/cobigen-templates/crud-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/DevonfwUtil.java b/cobigen-templates/crud-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/DevonfwUtil.java new file mode 100644 index 0000000000..36e39b5e06 --- /dev/null +++ b/cobigen-templates/crud-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/DevonfwUtil.java @@ -0,0 +1,450 @@ +package com.devonfw.cobigen.templates.devon4j.utils; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.w3c.dom.Attr; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.NodeList; + +import com.devonfw.cobigen.templates.devon4j.constants.Field; +import com.sun.org.apache.xerces.internal.dom.DeferredElementNSImpl; + +/** + * A class for shared devon4j specific functions in the templates + * + */ +@SuppressWarnings("restriction") +public class DevonfwUtil { + + /** + * Check whether the given 'canonicalType' is a devon4j Entity, which is declared in the given 'component' + * + * @param canonicalType the type name + * @param component the component name + * @return true iff the canonicalType is a devon Entity + */ + public boolean isEntityInComponent(String canonicalType, String component) { + + return canonicalType.matches(String.format(".+%1$s\\.dataaccess\\.api\\.[A-Za-z0-9]+Entity(<.*)?", component)); + } + + /** + * Check whether the given 'canonicalType' is declared in the given 'component' + * + * @param canonicalType the type name + * @param component the component name + * @return true iff the canonicalType is inside the given component + */ + public boolean isTypeInComponent(String canonicalType, String component) { + + return canonicalType.matches(String.format("%1$s.[A-Za-z0-9]+(<.*)?", component)); + } + + /** + * Determines the ID getter for a given 'field' dependent on whether the getter should access the ID via an object + * reference or a direct ID getter + * + * @param field the field + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} + '()' with + * capitalize=true + */ + public String resolveIdGetter(Map field, boolean byObjectReference, String component) { + + // If field comes from an UML file + if (field.getClass().toGenericString().contains("freemarker.ext.beans.HashAdapter")) { + DeferredElementNSImpl umlNode = (DeferredElementNSImpl) field; + return resolveIdGetter(umlNode, byObjectReference, component); + } + return "get" + resolveIdVariableNameOrSetterGetterSuffix(field, byObjectReference, true, component) + "()"; + } + + /** + * Determines the ID getter for a given 'field' dependent on whether the getter should access the ID via an object + * reference or a direct ID getter + * + * This method is used when the field parameter comes from an UML file. The name and type of the attributes must be + * pre-processed for later inserting them inside the HashMap. + * + * @param field the field + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} + '()' with + * capitalize=true + */ + public String resolveIdGetter(DeferredElementNSImpl field, boolean byObjectReference, String component) { + + HashMap nodeHash = new HashMap<>(); + + // Putting the name of the attribute to the hash + nodeHash.put(Field.NAME.toString(), field.getAttribute("name")); + + // Putting the type of the attribute to the hash + NodeList childs = field.getChildNodes(); + for (int i = 0; i < childs.getLength(); i++) { + // Retrieve "type" tag + if (childs.item(i).getNodeName().equals("type")) { + NamedNodeMap attrs = childs.item(i).getAttributes(); + for (int j = 0; j < attrs.getLength(); j++) { + Attr attribute = (Attr) attrs.item(j); + // Try to find the attribute that contains the type + if (attribute.getName().equals("xmi:idref")) { + nodeHash.put(Field.TYPE.toString(), attribute.getName().replace("EAJava_", "")); + } + } + } + } + return "get" + resolveIdVariableNameOrSetterGetterSuffix(nodeHash, byObjectReference, true, component) + "()"; + + } + + /** + * Determines the ID getter for a given 'field' dependent on whether the getter should access the ID via an object + * reference or a direct ID getter + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} + '()' with + * capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdGetter(Class pojoClass, Map fieldMap, boolean byObjectReference, + String component) throws NoSuchFieldException, SecurityException { + + return "get" + resolveIdVariableNameOrSetterGetterSuffix(pojoClass, fieldMap, byObjectReference, true, component) + + "()"; + } + + /** + * same as {@link #resolveIdGetter(Map, boolean, String)} but with byObjectReference=false and component="" + * + * @param field the field + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} + '()' with + * capitalize=true + */ + public String resolveIdGetter(Map field) { + + return this.resolveIdGetter(field, false, ""); + } + + /** + * same as {@link #resolveIdGetter(Class,Map,boolean,String)} but with byObjectReference=false and component="" + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} + '()' with + * capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdGetter(Class pojoClass, Map fieldMap) + throws NoSuchFieldException, SecurityException { + + return resolveIdGetter(pojoClass, fieldMap, false, ""); + } + + /** + * Determines the ID setter for a given 'field' dependent on whether the setter should access the ID via an object + * reference or a direct ID setter. In contrast to resolveIdGetter, this function does not generate the function + * parenthesis to enable parameter declaration. + * + * @param field the field + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'set' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} with + * capitalize=true + */ + public String resolveIdSetter(Map field, boolean byObjectReference, String component) { + + return "set" + resolveIdVariableNameOrSetterGetterSuffix(field, byObjectReference, true, component); + } + + /** + * same as {@link #resolveIdSetter(Map, boolean, String)} but with byObjectReference=false and component="" + * + * @param field the field + * @return 'set' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} with + * capitalize=true + */ + public String resolveIdSetter(Map field) { + + return this.resolveIdSetter(field, false, ""); + } + + /** + * Determines the ID setter for a given 'field' dependent on whether the setter should access the ID via an object + * reference or a direct ID setter. In contrast to resolveIdGetter, this function does not generate the function + * parenthesis to enable parameter declaration. + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'set'+ {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} with capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdSetter(Class pojoClass, Map fieldMap, boolean byObjectReference, + String component) throws NoSuchFieldException, SecurityException { + + return "set" + resolveIdVariableNameOrSetterGetterSuffix(pojoClass, fieldMap, byObjectReference, true, component); + } + + /** + * same as {@link #resolveIdSetter(Class,Map,boolean,String)} but with byObjectReference=false and component="" + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @return 'set' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} with capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdSetter(Class pojoClass, Map fieldMap) + throws NoSuchFieldException, SecurityException { + + return resolveIdSetter(pojoClass, fieldMap, false, ""); + } + + /** + * Determines the variable name for the id value of the 'field' + * + * @param field the field + * @return {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)}) with + * byObjectReference=false, capitalize=false and component="" + */ + public String resolveIdVariableName(Map field) { + + // the component is passed down as an empty string since byObjectReference is false and therefore the + // component is + // never touched + return resolveIdVariableNameOrSetterGetterSuffix(field, false, false, ""); + } + + /** + * Determines the variable name for the id value of the specified field in the pojo + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @return {@link #resolveIdVariableNameOrSetterGetterSuffix(Class, Map, boolean, boolean, String)}) with + * byObjectReference=false, capitalize=false and component="" + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdVariableName(Class pojoClass, Map fieldMap) + throws NoSuchFieldException, SecurityException { + + // the component is passed down as an empty string since byObjectReference is false and therefore the + // component is + // never touched + return resolveIdVariableNameOrSetterGetterSuffix(pojoClass, fieldMap, false, false, ""); + } + + /** + * Determines the ID setter/getter suffix for a given 'field' dependent on whether the setter/getter should access the + * ID via an object reference or a direct ID setter/getter + * + * @param field the field + * @param byObjectReference boolean + * @param capitalize if the field name should be capitalized + * @param component the devon4j component. Only needed if $byObjectReference is true + * @return idVariable name or getter/setter suffix + */ + public String resolveIdVariableNameOrSetterGetterSuffix(Map field, boolean byObjectReference, + boolean capitalize, String component) { + + String fieldName = (String) field.get(Field.NAME.toString()); + if (capitalize) { + fieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); + } + String suffix = ""; + + String fieldType = (String) field.get(Field.TYPE.toString()); + String fieldCType = (String) field.get(Field.CANONICAL_TYPE.toString()); + if (fieldType.contains("Entity")) { + if (fieldCType.startsWith("java.util.List") || fieldCType.startsWith("java.util.Set")) { + suffix = "Ids"; + if (fieldName.endsWith("s")) { + // Assume trailing 's' as indicator for a plural + fieldName = fieldName.substring(0, fieldName.length() - 1); + } + } else { + suffix = "Id"; + } + if (byObjectReference && isTypeInComponent(fieldCType, component)) { + // direct references for Entities in same component, so get id of the object reference + suffix = "().getId"; + } + } + + return fieldName + suffix; + + } + + /** + * Determines the ID setter/getter suffix for a given 'field' dependent on whether the setter/getter should access the + * ID via an object reference or a direct ID setter/getter + * + * @param pojoClass the {@link Class} object of the pojo + * @param fieldMap the field mapping + * @param byObjectReference boolean + * @param capitalize if the field name should be capitalized + * @param component the devon4j component. Only needed if byObjectReference is true + * @return idVariable name or getter/setter suffix + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdVariableNameOrSetterGetterSuffix(Class pojoClass, Map fieldMap, + boolean byObjectReference, boolean capitalize, String component) throws NoSuchFieldException, SecurityException { + + String resultName = (String) fieldMap.get(Field.NAME.toString()); + if (capitalize) { + resultName = resultName.substring(0, 1).toUpperCase() + resultName.substring(1); + } + String suffix = ""; + String fieldType = (String) fieldMap.get(Field.TYPE.toString()); + String fieldName = (String) fieldMap.get(Field.NAME.toString()); + if (fieldType.contains("Entity")) { + if (Collection.class.isAssignableFrom(pojoClass.getDeclaredField(fieldName).getType())) { + suffix = "Ids"; + if (resultName.endsWith("s")) { + // Assume trailing 's' as indicator for a plural + resultName = resultName.substring(0, resultName.length() - 1); + } + } else { + suffix = "Id"; + } + if (byObjectReference + && isEntityInComponent(pojoClass.getDeclaredField(fieldName).getType().getName(), component)) { + // direct references for Entities in same component, so get id of the object reference + suffix = "().getId"; + } + } + + return resultName + suffix; + + } + + /** + * Returns the argument type of the list or set from a field. If the string contains "Entity" it will remove that + * part. For example, if we have a List <SampleEntity> it will return "Sample" + * + * @param field the field + * @param pojoClass the object class of the Entity that contains the field + * @return fieldType argument of the list + * @throws SecurityException if field type could not accessed + * @throws NoSuchFieldException if field could not be found + */ + public String getListArgumentType(Map field, Class pojoClass) + throws NoSuchFieldException, SecurityException { + + JavaUtil javaUtil = new JavaUtil(); + + String fieldType = (String) field.get(Field.TYPE.toString()); + String fieldName = (String) field.get(Field.NAME.toString()); + + if (fieldType.contains("Entity")) { + if (javaUtil.isCollection(pojoClass, fieldName)) { + + fieldType = fieldType.replace("Entity", ""); + // Regex: Extracts the argument type of the list 'List' => type + String regex = "(?<=\\<).+?(?=\\>)"; + Pattern pattern = Pattern.compile(regex); + Matcher regexMatcher = pattern.matcher(fieldType); + + if (regexMatcher.find()) { + fieldType = regexMatcher.group(0); + } + } + } + return fieldType; + + } + + /** + * Converts all occurrences of devon4j Entity types in the given 'field' simple type (possibly generic) to Longs + * + * @param field the field + * @return the field type as String. If field type contains 'Entity' the result is Long + */ + public String getSimpleEntityTypeAsLongReference(Map field) { + + String fieldType = (String) field.get(Field.TYPE.toString()); + if (fieldType.endsWith("Entity")) { + fieldType = fieldType.replaceAll("[^<>]+Entity", "Long"); + } + return fieldType; + } + + /** + * If the string last character is an 's', then it gets removed + * + * @param targetClassName string to remove plural + * @return string without 's' + */ + public String removePlural(String targetClassName) { + + if (targetClassName.charAt(targetClassName.length() - 1) == 's') { + targetClassName = targetClassName.substring(0, targetClassName.length() - 1); + } + return targetClassName; + } + + /** + * Checks whether the operation with the given ID corresponds to any standard CRUD method name. + * + * @param operationId operation ID interpreted as method name + * @param entityName entity name to check standard CRUD methods for + * @return true if the operation ID maps any standard CRUD method name, false otherwise + */ + public boolean isCrudOperation(String operationId, String entityName) { + + if (operationId == null) { + return false; + } + String opIdLowerCase = operationId.toLowerCase(); + String entityNameLowerCase = entityName.toLowerCase(); + if (opIdLowerCase.contains(entityNameLowerCase)) { + return opIdLowerCase.equals("find" + entityNameLowerCase) + || opIdLowerCase.equals("find" + entityNameLowerCase + "Etos") + || opIdLowerCase.equals("delete" + entityNameLowerCase) || opIdLowerCase.equals("save" + entityNameLowerCase); + } else { + return false; + } + } + + /** + * Converts the given media type to the spring Java enum value + * + * @param mediaType to be converted + * @return the spring enum value representing the given media type + */ + public String getSpringMediaType(String mediaType) { + + switch (mediaType) { + case "application/xml": + return "APPLICATION_XML_VALUE"; + case "application / x-www-form-urlencoded": + return "APPLICATION_FORM_URLENCODED_VALUE"; + case "multipart/form-data": + return "MULTIPART_FORM_DATA_VALUE"; + case "text/plain": + return "TEXT_PLAIN_VALUE"; + case "text/html": + return "TEXT_HTML_VALUE"; + case "application/pdf": + return "APPLICATION_PDF_VALUE"; + case "image/png": + return "IMAGE_PNG_VALUE"; + default: + return "APPLICATION_JSON_VALUE"; + } + } +} \ No newline at end of file diff --git a/cobigen-templates/crud-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java b/cobigen-templates/crud-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java new file mode 100644 index 0000000000..ced2f5fde2 --- /dev/null +++ b/cobigen-templates/crud-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java @@ -0,0 +1,464 @@ +package com.devonfw.cobigen.templates.devon4j.utils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + +import org.apache.commons.lang3.ClassUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides type operations, mainly checks and casts for Java Primitives, to be used in the templates + * + */ +public class JavaUtil { + + /** + * Logger for this class + */ + private static final Logger LOG = LoggerFactory.getLogger(JavaUtil.class); + + /** + * The constructor. + */ + public JavaUtil() { + + // Empty for CobiGen to automatically instantiate it + } + + /** + * Returns the Object version of a Java primitive or the input if the input isn't a java primitive + * + * @param simpleType String + * @return the corresponding object wrapper type simple name of the input if the input is the name of a primitive java + * type. The input itself if not. (e.g. "int" results in "Integer") + * @throws ClassNotFoundException should not occur. + */ + public String boxJavaPrimitives(String simpleType) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return ClassUtils.primitiveToWrapper(ClassUtils.getClass(simpleType)).getSimpleName(); + } else { + return simpleType; + } + + } + + /** + * Returns the simple name of the type of a field in the pojoClass. If the type is a java primitive the name of the + * wrapper class is returned + * + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return String. The simple name of the field's type. The simple name of the wrapper class in case of java + * primitives + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String boxJavaPrimitives(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return ClassUtils.primitiveToWrapper(pojoClass.getDeclaredField(fieldName).getType()).getSimpleName(); + } else { + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + throw new IllegalAccessError("Could not find field " + fieldName + " in class " + pojoClass); + } else { + return field.getType().getSimpleName(); + } + } + } + + /** + * Checks if the given type is a Java primitive + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive + */ + public boolean equalsJavaPrimitive(String simpleType) { + + try { + return ClassUtils.getClass(simpleType).isPrimitive(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the given type is a Java primitive or wrapper + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive or wrapper + */ + public boolean equalsJavaPrimitiveOrWrapper(String simpleType) { + + try { + return ClassUtils.isPrimitiveOrWrapper(ClassUtils.getClass(simpleType)); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the type of the field in the pojo's class is a java primitive + * + * @param pojoClass the {@link Class} object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff the field is a java primitive + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitive(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return field.getType().isPrimitive(); + } + } + + /** + * Checks if the given type is a Java primitive or a Java primitive array + * + * @param simpleType the Type name to be checked + * @return true iff {@link #equalsJavaPrimitive(String)} is true or if simpleType is an array with a primitive + * component + */ + public boolean equalsJavaPrimitiveIncludingArrays(String simpleType) { + + Class klasse; + + try { + klasse = ClassUtils.getClass(simpleType).getComponentType(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + return equalsJavaPrimitive(simpleType) || (klasse != null && klasse.isPrimitive()); + } + + /** + * Checks if the given field in the pojo class is a java primitive or an array of java primitives + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff {@link #equalsJavaPrimitive(Class, String)} is true or the field is an array of primitives + * @throws NoSuchFieldException indicating something awfully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitiveIncludingArrays(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + return equalsJavaPrimitive(pojoClass, fieldName) || (pojoClass.getDeclaredField(fieldName).getType().isArray() + && pojoClass.getDeclaredField(fieldName).getType().getComponentType().isPrimitive()); + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param simpleType Java Type + * @param varName Variable name + * @return String either of the form '((Java Primitive Object Type)varName)' if simpleType is a primitive or the empty + * String otherwise + * @throws ClassNotFoundException should not occur + */ + public String castJavaPrimitives(String simpleType, String varName) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(simpleType), varName); + } else { + return ""; + } + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be casted + * @return if fieldName points to a primitive field then a casted statement (e.g. for an int field: + * '((Integer)field)') or an empty String otherwise + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String castJavaPrimitives(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(pojoClass, fieldName), fieldName); + } else { + return ""; + } + } + + /** + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return true if the field is an instance of java.utils.Collections + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean isCollection(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return Collection.class.isAssignableFrom(field.getType()); + } + + } + + /** + * Returns the Ext Type to a given java type + * + * @param simpleType any java type's simple name + * @return corresponding Ext type + */ + public String getExtType(String simpleType) { + + switch (simpleType) { + case "short": + case "Short": + case "int": + case "Integer": + case "long": + case "Long": + return "Integer"; + case "float": + case "Float": + case "double": + case "Double": + return "Number"; + case "boolean": + case "Boolean": + return "Boolean"; + case "char": + case "Character": + case "String": + return "String"; + case "Date": + return "Date"; + default: + return "Field"; + } + } + + /** + * returns the Angular5 type associated with a Java primitive + * + * @param simpleType :{@link String} the type to be parsed + * @return the corresponding Angular type or 'any' otherwise + */ + public String getAngularType(String simpleType) { + + switch (simpleType) { + case "boolean": + return "boolean"; + case "Boolean": + return "boolean"; + case "short": + return "number"; + case "Short": + return "number"; + case "int": + return "number"; + case "Integer": + return "number"; + case "long": + return "number"; + case "Long": + return "number"; + case "float": + return "number"; + case "Float": + return "number"; + case "double": + return "number"; + case "Double": + return "number"; + case "char": + return "string"; + case "Character": + return "string"; + case "String": + return "string"; + case "byte": + return "number"; + default: + return "any"; + } + } + + /** + * returns the class name of the return type of a specific method. + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName {@link String} the name of the method + * @return the class name of the return type of the specified method + * @throws SecurityException If no method of the given name can be found + * @throws NoSuchMethodException If no method of the given name can be found + */ + public String getReturnType(Class pojoClass, String methodName) throws NoSuchMethodException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + String s = "-"; + Method method = findMethod(pojoClass, methodName); + if (method != null && !method.getReturnType().equals(Void.TYPE)) { + s = method.getReturnType().toString(); + s = s.substring(s.lastIndexOf('.') + 1, s.length()); + } + return s; + } + + /** + * + * This methods returns the return type of the method in the given pojoClass which are annotated with the parameter + * annotatedClass + * + * @param pojoClass - The class in which to find if it has methods with annotatedClass + * @param annotatedClassName - The annotation which needs to be found + * @return Return type of the method annotated with the given annotation, else "null" + * @throws ClassNotFoundException if the annotated class name could not be found in the class path + */ + @SuppressWarnings("unchecked") + public String getReturnTypeOfMethodAnnotatedWith(Class pojoClass, String annotatedClassName) + throws ClassNotFoundException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + Method[] methods = pojoClass.getDeclaredMethods(); + for (Method method : methods) { + if (!method.getName().startsWith("get")) { + continue; + } + for (Annotation a : method.getAnnotations()) { + // better if (method.isAnnotationPresent(classObj)) {, but postponed as of different class + // loaders of a.getClass() and pojoClass.getClass() + if (a.getClass().getCanonicalName().equals(annotatedClassName)) { + return method.getReturnType().getSimpleName(); + } + } + } + return "null"; + } + + /** + * returns the HTTP request type corresponding to an annotation type + * + * @param annotations The annotation to get the type name of + * @return the HTTP request type name of the selected annotation + */ + public String getRequestType(Map annotations) { + + if (annotations.containsKey("javax_ws_rs_GET")) { + return "GET"; + } else if (annotations.containsKey("javax_ws_rs_PUT")) { + return "PUT"; + } else if (annotations.containsKey("javax_ws_rs_POST")) { + return "POST"; + } else if (annotations.containsKey("javax_ws_rs_DELETE")) { + return "DELETE"; + } else if (annotations.containsKey("javax_ws_rs_PATCH")) { + return "PATCH"; + } else { + return "-"; + } + } + + /** + * Helper method to find a class's specific method + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName The name of the method to be found + * @return The method object of the method to be found, null if it wasn't found + */ + private Method findMethod(Class pojoClass, String methodName) { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + for (Method m : pojoClass.getMethods()) { + if (m.getName().equals(methodName)) { + return m; + } + } + return null; + } + + /** + * Checks whether the class given by the full qualified name is an enum + * + * @param className full qualified class name + * @return true if the class is an enum, false otherwise + */ + public boolean isEnum(String className) { + + try { + return ClassUtils.getClass(className).isEnum(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return false; + } + } + + /** + * Returns the first enum value of an enum class + * + * @param className full qualified class name + * @return the first enum value name found in order + */ + public String getFirstEnumValue(String className) { + + try { + Class enumClass = ClassUtils.getClass(className); + Field[] declaredFields = enumClass.getDeclaredFields(); + if (declaredFields.length > 0) { + return declaredFields[0].getName(); + } else { + return null; + } + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return null; + } + } +} diff --git a/cobigen-templates/crud-ngrx-client-app/pom.xml b/cobigen-templates/crud-ngrx-client-app/pom.xml index 7268b65f2f..b5287e0cd5 100644 --- a/cobigen-templates/crud-ngrx-client-app/pom.xml +++ b/cobigen-templates/crud-ngrx-client-app/pom.xml @@ -10,4 +10,10 @@ ${revision} + + + org.apache.commons + commons-lang3 + + \ No newline at end of file diff --git a/cobigen-templates/crud-ngrx-client-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java b/cobigen-templates/crud-ngrx-client-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java new file mode 100644 index 0000000000..2d4025071d --- /dev/null +++ b/cobigen-templates/crud-ngrx-client-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java @@ -0,0 +1,464 @@ +package com.devonfw.cobigen.templates.devon4j.utils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + +import org.apache.commons.lang3.ClassUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides type operations, mainly checks and casts for Java Primitives, to be used in the templates + * + */ +public class JavaUtil { + + /** + * Logger for this class + */ + private static final Logger LOG = LoggerFactory.getLogger(JavaUtil.class); + + /** + * The constructor. + */ + public JavaUtil() { + + // Empty for CobiGen to automatically instantiate it + } + + /** + * Returns the Object version of a Java primitive or the input if the input isn't a java primitive + * + * @param simpleType String + * @return the corresponding object wrapper type simple name of the input if the input is the name of a primitive java + * type. The input itself if not. (e.g. "int" results in "Integer") + * @throws ClassNotFoundException should not occur. + */ + public String boxJavaPrimitives(String simpleType) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return ClassUtils.primitiveToWrapper(ClassUtils.getClass(simpleType)).getSimpleName(); + } else { + return simpleType; + } + + } + + /** + * Returns the simple name of the type of a field in the pojoClass. If the type is a java primitive the name of the + * wrapper class is returned + * + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return String. The simple name of the field's type. The simple name of the wrapper class in case of java + * primitives + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String boxJavaPrimitives(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return ClassUtils.primitiveToWrapper(pojoClass.getDeclaredField(fieldName).getType()).getSimpleName(); + } else { + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + throw new IllegalAccessError("Could not find field " + fieldName + " in class " + pojoClass); + } else { + return field.getType().getSimpleName(); + } + } + } + + /** + * Checks if the given type is a Java primitive + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive + */ + public boolean equalsJavaPrimitive(String simpleType) { + + try { + return ClassUtils.getClass(simpleType).isPrimitive(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the given type is a Java primitive or wrapper + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive or wrapper + */ + public boolean equalsJavaPrimitiveOrWrapper(String simpleType) { + + try { + return ClassUtils.isPrimitiveOrWrapper(ClassUtils.getClass(simpleType)); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the type of the field in the pojo's class is a java primitive + * + * @param pojoClass the {@link Class} object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff the field is a java primitive + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitive(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return field.getType().isPrimitive(); + } + } + + /** + * Checks if the given type is a Java primitive or a Java primitive array + * + * @param simpleType the Type name to be checked + * @return true iff {@link #equalsJavaPrimitive(String)} is true or if simpleType is an array with a primitive + * component + */ + public boolean equalsJavaPrimitiveIncludingArrays(String simpleType) { + + Class klasse; + + try { + klasse = ClassUtils.getClass(simpleType).getComponentType(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + return equalsJavaPrimitive(simpleType) || (klasse != null && klasse.isPrimitive()); + } + + /** + * Checks if the given field in the pojo class is a java primitive or an array of java primitives + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff {@link #equalsJavaPrimitive(Class, String)} is true or the field is an array of primitives + * @throws NoSuchFieldException indicating something awfully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitiveIncludingArrays(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + return equalsJavaPrimitive(pojoClass, fieldName) || (pojoClass.getDeclaredField(fieldName).getType().isArray() + && pojoClass.getDeclaredField(fieldName).getType().getComponentType().isPrimitive()); + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param simpleType Java Type + * @param varName Variable name + * @return String either of the form '((Java Primitive Object Type)varName)' if simpleType is a primitive or the empty + * String otherwise + * @throws ClassNotFoundException should not occur + */ + public String castJavaPrimitives(String simpleType, String varName) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(simpleType), varName); + } else { + return ""; + } + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be casted + * @return if fieldName points to a primitive field then a casted statement (e.g. for an int field: + * '((Integer)field)') or an empty String otherwise + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String castJavaPrimitives(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(pojoClass, fieldName), fieldName); + } else { + return ""; + } + } + + /** + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return true if the field is an instance of java.utils.Collections + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean isCollection(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return Collection.class.isAssignableFrom(field.getType()); + } + + } + + /** + * Returns the Ext Type to a given java type + * + * @param simpleType any java type's simple name + * @return corresponding Ext type + */ + public String getExtType(String simpleType) { + + switch (simpleType) { + case "short": + case "Short": + case "int": + case "Integer": + case "long": + case "Long": + return "Integer"; + case "float": + case "Float": + case "double": + case "Double": + return "Number"; + case "boolean": + case "Boolean": + return "Boolean"; + case "char": + case "Character": + case "String": + return "String"; + case "Date": + return "Date"; + default: + return "Field"; + } + } + + /** + * returns the Angular5 type associated with a Java primitive + * + * @param simpleType :{@link String} the type to be parsed + * @return the corresponding Angular type or 'any' otherwise + */ + public String getAngularType(String simpleType) { + + switch (simpleType) { + case "boolean": + return "boolean"; + case "Boolean": + return "boolean"; + case "short": + return "number"; + case "Short": + return "number"; + case "int": + return "number"; + case "Integer": + return "number"; + case "long": + return "number"; + case "Long": + return "number"; + case "float": + return "number"; + case "Float": + return "number"; + case "double": + return "number"; + case "Double": + return "number"; + case "char": + return "string"; + case "Character": + return "string"; + case "String": + return "string"; + case "byte": + return "number"; + default: + return "any"; + } + } + + /** + * returns the class name of the return type of a specific method. + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName {@link String} the name of the method + * @return the class name of the return type of the specified method + * @throws SecurityException If no method of the given name can be found + * @throws NoSuchMethodException If no method of the given name can be found + */ + public String getReturnType(Class pojoClass, String methodName) throws NoSuchMethodException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + String s = "-"; + Method method = findMethod(pojoClass, methodName); + if (method != null && !method.getReturnType().equals(Void.TYPE)) { + s = method.getReturnType().toString(); + s = s.substring(s.lastIndexOf('.') + 1, s.length()); + } + return s; + } + + /** + * + * This methods returns the return type of the method in the given pojoClass which are annotated with the parameter + * annotatedClass + * + * @param pojoClass - The class in which to find if it has methods with annotatedClass + * @param annotatedClassName - The annotation which needs to be found + * @return Return type of the method annotated with the given annotation, else "null" + * @throws ClassNotFoundException if the annotated class name could not be found in the class path + */ + @SuppressWarnings("unchecked") + public String getReturnTypeOfMethodAnnotatedWith(Class pojoClass, String annotatedClassName) + throws ClassNotFoundException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + Method[] methods = pojoClass.getDeclaredMethods(); + for (Method method : methods) { + if (!method.getName().startsWith("get")) { + continue; + } + for (Annotation a : method.getAnnotations()) { + // better if (method.isAnnotationPresent(classObj)) {, but postponed as of different class + // loaders of a.getClass() and pojoClass.getClass() + if (a.getClass().getCanonicalName().equals(annotatedClassName)) { + return method.getReturnType().getSimpleName(); + } + } + } + return "null"; + } + + /** + * returns the HTTP request type corresponding to an annotation type + * + * @param annotations The annotation to get the type name of + * @return the HTTP request type name of the selected annotation + */ + public String getRequestType(Map annotations) { + + if (annotations.containsKey("javax_ws_rs_GET")) { + return "GET"; + } else if (annotations.containsKey("javax_ws_rs_PUT")) { + return "PUT"; + } else if (annotations.containsKey("javax_ws_rs_POST")) { + return "POST"; + } else if (annotations.containsKey("javax_ws_rs_DELETE")) { + return "DELETE"; + } else if (annotations.containsKey("javax_ws_rs_PATCH")) { + return "PATCH"; + } else { + return "-"; + } + } + + /** + * Helper method to find a class's specific method + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName The name of the method to be found + * @return The method object of the method to be found, null if it wasn't found + */ + private Method findMethod(Class pojoClass, String methodName) { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + for (Method m : pojoClass.getMethods()) { + if (m.getName().equals(methodName)) { + return m; + } + } + return null; + } + + /** + * Checks whether the class given by the full qualified name is an enum + * + * @param className full qualified class name + * @return true if the class is an enum, false otherwise + */ + public boolean isEnum(String className) { + + try { + return ClassUtils.getClass(className).isEnum(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return false; + } + } + + /** + * Returns the first enum value of an enum class + * + * @param className full qualified class name + * @return the first enum value name found in order + */ + public String getFirstEnumValue(String className) { + + try { + Class enumClass = ClassUtils.getClass(className); + Field[] declaredFields = enumClass.getDeclaredFields(); + if (declaredFields.length > 0) { + return declaredFields[0].getName(); + } else { + return null; + } + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return null; + } + } +} diff --git a/cobigen-templates/crud-openapi-angular-client-app/pom.xml b/cobigen-templates/crud-openapi-angular-client-app/pom.xml index 0b768bd5c8..f9f6d93770 100644 --- a/cobigen-templates/crud-openapi-angular-client-app/pom.xml +++ b/cobigen-templates/crud-openapi-angular-client-app/pom.xml @@ -10,4 +10,10 @@ ${revision} + + + org.apache.commons + commons-lang3 + + \ No newline at end of file diff --git a/cobigen-templates/crud-openapi-angular-client-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java b/cobigen-templates/crud-openapi-angular-client-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java new file mode 100644 index 0000000000..ced2f5fde2 --- /dev/null +++ b/cobigen-templates/crud-openapi-angular-client-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java @@ -0,0 +1,464 @@ +package com.devonfw.cobigen.templates.devon4j.utils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + +import org.apache.commons.lang3.ClassUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides type operations, mainly checks and casts for Java Primitives, to be used in the templates + * + */ +public class JavaUtil { + + /** + * Logger for this class + */ + private static final Logger LOG = LoggerFactory.getLogger(JavaUtil.class); + + /** + * The constructor. + */ + public JavaUtil() { + + // Empty for CobiGen to automatically instantiate it + } + + /** + * Returns the Object version of a Java primitive or the input if the input isn't a java primitive + * + * @param simpleType String + * @return the corresponding object wrapper type simple name of the input if the input is the name of a primitive java + * type. The input itself if not. (e.g. "int" results in "Integer") + * @throws ClassNotFoundException should not occur. + */ + public String boxJavaPrimitives(String simpleType) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return ClassUtils.primitiveToWrapper(ClassUtils.getClass(simpleType)).getSimpleName(); + } else { + return simpleType; + } + + } + + /** + * Returns the simple name of the type of a field in the pojoClass. If the type is a java primitive the name of the + * wrapper class is returned + * + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return String. The simple name of the field's type. The simple name of the wrapper class in case of java + * primitives + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String boxJavaPrimitives(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return ClassUtils.primitiveToWrapper(pojoClass.getDeclaredField(fieldName).getType()).getSimpleName(); + } else { + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + throw new IllegalAccessError("Could not find field " + fieldName + " in class " + pojoClass); + } else { + return field.getType().getSimpleName(); + } + } + } + + /** + * Checks if the given type is a Java primitive + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive + */ + public boolean equalsJavaPrimitive(String simpleType) { + + try { + return ClassUtils.getClass(simpleType).isPrimitive(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the given type is a Java primitive or wrapper + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive or wrapper + */ + public boolean equalsJavaPrimitiveOrWrapper(String simpleType) { + + try { + return ClassUtils.isPrimitiveOrWrapper(ClassUtils.getClass(simpleType)); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the type of the field in the pojo's class is a java primitive + * + * @param pojoClass the {@link Class} object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff the field is a java primitive + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitive(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return field.getType().isPrimitive(); + } + } + + /** + * Checks if the given type is a Java primitive or a Java primitive array + * + * @param simpleType the Type name to be checked + * @return true iff {@link #equalsJavaPrimitive(String)} is true or if simpleType is an array with a primitive + * component + */ + public boolean equalsJavaPrimitiveIncludingArrays(String simpleType) { + + Class klasse; + + try { + klasse = ClassUtils.getClass(simpleType).getComponentType(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + return equalsJavaPrimitive(simpleType) || (klasse != null && klasse.isPrimitive()); + } + + /** + * Checks if the given field in the pojo class is a java primitive or an array of java primitives + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff {@link #equalsJavaPrimitive(Class, String)} is true or the field is an array of primitives + * @throws NoSuchFieldException indicating something awfully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitiveIncludingArrays(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + return equalsJavaPrimitive(pojoClass, fieldName) || (pojoClass.getDeclaredField(fieldName).getType().isArray() + && pojoClass.getDeclaredField(fieldName).getType().getComponentType().isPrimitive()); + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param simpleType Java Type + * @param varName Variable name + * @return String either of the form '((Java Primitive Object Type)varName)' if simpleType is a primitive or the empty + * String otherwise + * @throws ClassNotFoundException should not occur + */ + public String castJavaPrimitives(String simpleType, String varName) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(simpleType), varName); + } else { + return ""; + } + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be casted + * @return if fieldName points to a primitive field then a casted statement (e.g. for an int field: + * '((Integer)field)') or an empty String otherwise + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String castJavaPrimitives(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(pojoClass, fieldName), fieldName); + } else { + return ""; + } + } + + /** + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return true if the field is an instance of java.utils.Collections + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean isCollection(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return Collection.class.isAssignableFrom(field.getType()); + } + + } + + /** + * Returns the Ext Type to a given java type + * + * @param simpleType any java type's simple name + * @return corresponding Ext type + */ + public String getExtType(String simpleType) { + + switch (simpleType) { + case "short": + case "Short": + case "int": + case "Integer": + case "long": + case "Long": + return "Integer"; + case "float": + case "Float": + case "double": + case "Double": + return "Number"; + case "boolean": + case "Boolean": + return "Boolean"; + case "char": + case "Character": + case "String": + return "String"; + case "Date": + return "Date"; + default: + return "Field"; + } + } + + /** + * returns the Angular5 type associated with a Java primitive + * + * @param simpleType :{@link String} the type to be parsed + * @return the corresponding Angular type or 'any' otherwise + */ + public String getAngularType(String simpleType) { + + switch (simpleType) { + case "boolean": + return "boolean"; + case "Boolean": + return "boolean"; + case "short": + return "number"; + case "Short": + return "number"; + case "int": + return "number"; + case "Integer": + return "number"; + case "long": + return "number"; + case "Long": + return "number"; + case "float": + return "number"; + case "Float": + return "number"; + case "double": + return "number"; + case "Double": + return "number"; + case "char": + return "string"; + case "Character": + return "string"; + case "String": + return "string"; + case "byte": + return "number"; + default: + return "any"; + } + } + + /** + * returns the class name of the return type of a specific method. + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName {@link String} the name of the method + * @return the class name of the return type of the specified method + * @throws SecurityException If no method of the given name can be found + * @throws NoSuchMethodException If no method of the given name can be found + */ + public String getReturnType(Class pojoClass, String methodName) throws NoSuchMethodException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + String s = "-"; + Method method = findMethod(pojoClass, methodName); + if (method != null && !method.getReturnType().equals(Void.TYPE)) { + s = method.getReturnType().toString(); + s = s.substring(s.lastIndexOf('.') + 1, s.length()); + } + return s; + } + + /** + * + * This methods returns the return type of the method in the given pojoClass which are annotated with the parameter + * annotatedClass + * + * @param pojoClass - The class in which to find if it has methods with annotatedClass + * @param annotatedClassName - The annotation which needs to be found + * @return Return type of the method annotated with the given annotation, else "null" + * @throws ClassNotFoundException if the annotated class name could not be found in the class path + */ + @SuppressWarnings("unchecked") + public String getReturnTypeOfMethodAnnotatedWith(Class pojoClass, String annotatedClassName) + throws ClassNotFoundException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + Method[] methods = pojoClass.getDeclaredMethods(); + for (Method method : methods) { + if (!method.getName().startsWith("get")) { + continue; + } + for (Annotation a : method.getAnnotations()) { + // better if (method.isAnnotationPresent(classObj)) {, but postponed as of different class + // loaders of a.getClass() and pojoClass.getClass() + if (a.getClass().getCanonicalName().equals(annotatedClassName)) { + return method.getReturnType().getSimpleName(); + } + } + } + return "null"; + } + + /** + * returns the HTTP request type corresponding to an annotation type + * + * @param annotations The annotation to get the type name of + * @return the HTTP request type name of the selected annotation + */ + public String getRequestType(Map annotations) { + + if (annotations.containsKey("javax_ws_rs_GET")) { + return "GET"; + } else if (annotations.containsKey("javax_ws_rs_PUT")) { + return "PUT"; + } else if (annotations.containsKey("javax_ws_rs_POST")) { + return "POST"; + } else if (annotations.containsKey("javax_ws_rs_DELETE")) { + return "DELETE"; + } else if (annotations.containsKey("javax_ws_rs_PATCH")) { + return "PATCH"; + } else { + return "-"; + } + } + + /** + * Helper method to find a class's specific method + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName The name of the method to be found + * @return The method object of the method to be found, null if it wasn't found + */ + private Method findMethod(Class pojoClass, String methodName) { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + for (Method m : pojoClass.getMethods()) { + if (m.getName().equals(methodName)) { + return m; + } + } + return null; + } + + /** + * Checks whether the class given by the full qualified name is an enum + * + * @param className full qualified class name + * @return true if the class is an enum, false otherwise + */ + public boolean isEnum(String className) { + + try { + return ClassUtils.getClass(className).isEnum(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return false; + } + } + + /** + * Returns the first enum value of an enum class + * + * @param className full qualified class name + * @return the first enum value name found in order + */ + public String getFirstEnumValue(String className) { + + try { + Class enumClass = ClassUtils.getClass(className); + Field[] declaredFields = enumClass.getDeclaredFields(); + if (declaredFields.length > 0) { + return declaredFields[0].getName(); + } else { + return null; + } + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return null; + } + } +} diff --git a/cobigen-templates/crud-openapi-angular-client-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/OpenApiUtil.java b/cobigen-templates/crud-openapi-angular-client-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/OpenApiUtil.java new file mode 100644 index 0000000000..a93288f45a --- /dev/null +++ b/cobigen-templates/crud-openapi-angular-client-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/OpenApiUtil.java @@ -0,0 +1,255 @@ +package com.devonfw.cobigen.templates.devon4j.utils; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.Instant; +import java.time.LocalDate; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; + +/** Utils on OpenAPI template model */ +public class OpenApiUtil { + + /** + * Prints javax validation constraints from an OpenAPI model + * + * @param constraints OpenAPI model constraints + * @return the list of supported annotations in Java syntax + */ + public String printJavaConstraints(Map constraints) { + + String consts = ""; + if (constraints.get("maximum") != null) { + consts = consts + "@Max(" + constraints.get("maximum") + ")"; + } + if (constraints.get("minimum") != null) { + consts = consts + "@Min(" + constraints.get("minimum") + ")"; + } + if (constraints.get("maxLength") != null) { + consts = consts + "@Size(max=" + constraints.get("maxLength"); + if (constraints.get("minLength") != null) { + consts = consts + ", min=" + constraints.get("minLength"); + } + consts = consts + ")"; + } else { + if (constraints.get("minLength") != null) { + consts = consts + "@Size(min=" + constraints.get("minLength"); + if (constraints.get("maxLength") != null) { + consts = consts + ", max=" + constraints.get("maxLength"); + } + consts = consts + ")"; + } + } + return consts; + } + + /** + * Prints the return type of a response based on the OpenAPI model + * + * @param response the OpenAPI model of a response + * @return simple type of the response return type in Java syntax + */ + public String printJavaServiceResponseReturnType(Map response) { + + String returnType = toJavaType(response, false); + if ((boolean) response.get("isVoid")) { + return "void"; + } + if ((boolean) response.get("isArray")) { + if ((boolean) response.get("isEntity")) { + if (returnType.contains("Entity")) { + return "List<" + returnType + ">"; + } else { + return "List<" + returnType + "Entity>"; + } + } else { + return "List<" + returnType + ">"; + } + } else if ((boolean) response.get("isPaginated")) { + if ((boolean) response.get("isEntity")) { + if (returnType.contains("Entity")) { + return "Page<" + returnType + ">"; + } else { + return "Page<" + returnType + "Entity>"; + } + } else { + return "Page<" + returnType + ">"; + } + } else { + return returnType; + } + } + + /** + * Returns the Java type corresponding to the OpenAPI type definition. If the type could not be matched, Object will + * be returned. + * + * @param parameter OpenAPI model of a parameter + * @param simpleType if a Java simple type should be returned if possible + * @return the Java type + */ + public String toJavaType(Map parameter, boolean simpleType) { + + String typeConverted = null; + String format = (String) parameter.get("format"); + String type = (String) parameter.get("type"); + boolean isCollection = false; + if (parameter.get("isCollection") != null) { + isCollection = (boolean) parameter.get("isCollection"); + } + + boolean isEntity = (boolean) parameter.get("isEntity"); + + if (type != null) { + switch (type.toLowerCase()) { + case "integer": + if (format != null) { + switch (format) { + case "int32": + typeConverted = simpleType ? "int" : "Integer"; + break; + case "int64": + typeConverted = simpleType ? "long" : "Long"; + break; + default: + typeConverted = BigInteger.class.getSimpleName(); + break; + } + } else { + typeConverted = BigInteger.class.getSimpleName(); + } + break; + case "number": + if (format != null) { + switch (format) { + case "float": + typeConverted = simpleType ? "float" : "Float"; + break; + case "double": + typeConverted = simpleType ? "double" : "Double"; + break; + default: + typeConverted = BigDecimal.class.getSimpleName(); + break; + } + } else { + typeConverted = BigDecimal.class.getSimpleName(); + } + break; + case "string": + if (format != null) { + switch (format) { + case "date": + typeConverted = LocalDate.class.getSimpleName(); + break; + case "date-time": + typeConverted = Instant.class.getSimpleName(); + break; + case "binary": + typeConverted = simpleType ? "float" : "Float"; + break; + case "email": + case "password": + typeConverted = String.class.getSimpleName(); + break; + default: + typeConverted = "String"; + break; + } + } else { + typeConverted = "String"; + } + break; + case "boolean": + typeConverted = simpleType ? "boolean" : "Boolean"; + break; + default: + typeConverted = "void"; + break; + } + } else { + typeConverted = "void"; + } + + if (isCollection) { + if (isEntity) { + return "List<" + parameter.get("type") + ">"; + } else { + return "List<" + typeConverted + ">"; + } + } else if (isEntity) { + return (String) parameter.get("type"); + } + return typeConverted; + } + + /** + * Returns the TypeScript type corresponding to the OpenAPI type definition. If the type could not be matched, the + * same value will be returned. + * + * @param parameter OpenAPI model of a parameter + * @return the Java type + */ + // getOaspTypeFromOpenAPI + public String toTypeScriptType(Map parameter) { + + String typeConverted = null; + String type = (String) parameter.get("type"); + boolean isCollection = false; + if (parameter.get("isCollection") != null) { + isCollection = (boolean) parameter.get("isCollection"); + } + + boolean isEntity = (boolean) parameter.get("isEntity"); + + if (type != null) { + switch (type.toLowerCase()) { + case "integer": + typeConverted = "number"; + break; + default: + typeConverted = type; + break; + } + } else { + typeConverted = "undefined"; + } + + if (isCollection) { + if (isEntity) { + return parameter.get("type") + "[]"; + } else { + return typeConverted + "[]"; + } + } else if (isEntity) { + return (String) parameter.get("type"); + } + return typeConverted; + } + + /** + * Prints the service operation name based on the operationId or generates one based on the servicePath while printing + * a comment how to get better service names. + * + * @param operation operation Model of the OpenAPI model + * @param servicePath service path of the + * @return the service method name + */ + public String printServiceOperationName(Map operation, String servicePath) { + + String operationId = (String) operation.get("operationId"); + if (StringUtils.isEmpty(operationId)) { + String[] split = servicePath.split("/"); + String lastSegment; + if (!split[split.length - 1].isEmpty()) { + lastSegment = split[split.length - 1]; + } else { + lastSegment = split[split.length - 2]; + } + return ((String) operation.get("type")) + lastSegment; + } else { + return operationId; + } + } +} diff --git a/cobigen-templates/crud-openapi-ionic-client-app/pom.xml b/cobigen-templates/crud-openapi-ionic-client-app/pom.xml index 2b7cfa971a..fbfd00fd3f 100644 --- a/cobigen-templates/crud-openapi-ionic-client-app/pom.xml +++ b/cobigen-templates/crud-openapi-ionic-client-app/pom.xml @@ -10,4 +10,10 @@ ${revision} + + + org.apache.commons + commons-lang3 + + \ No newline at end of file diff --git a/cobigen-templates/crud-openapi-ionic-client-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java b/cobigen-templates/crud-openapi-ionic-client-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java new file mode 100644 index 0000000000..ced2f5fde2 --- /dev/null +++ b/cobigen-templates/crud-openapi-ionic-client-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java @@ -0,0 +1,464 @@ +package com.devonfw.cobigen.templates.devon4j.utils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + +import org.apache.commons.lang3.ClassUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides type operations, mainly checks and casts for Java Primitives, to be used in the templates + * + */ +public class JavaUtil { + + /** + * Logger for this class + */ + private static final Logger LOG = LoggerFactory.getLogger(JavaUtil.class); + + /** + * The constructor. + */ + public JavaUtil() { + + // Empty for CobiGen to automatically instantiate it + } + + /** + * Returns the Object version of a Java primitive or the input if the input isn't a java primitive + * + * @param simpleType String + * @return the corresponding object wrapper type simple name of the input if the input is the name of a primitive java + * type. The input itself if not. (e.g. "int" results in "Integer") + * @throws ClassNotFoundException should not occur. + */ + public String boxJavaPrimitives(String simpleType) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return ClassUtils.primitiveToWrapper(ClassUtils.getClass(simpleType)).getSimpleName(); + } else { + return simpleType; + } + + } + + /** + * Returns the simple name of the type of a field in the pojoClass. If the type is a java primitive the name of the + * wrapper class is returned + * + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return String. The simple name of the field's type. The simple name of the wrapper class in case of java + * primitives + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String boxJavaPrimitives(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return ClassUtils.primitiveToWrapper(pojoClass.getDeclaredField(fieldName).getType()).getSimpleName(); + } else { + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + throw new IllegalAccessError("Could not find field " + fieldName + " in class " + pojoClass); + } else { + return field.getType().getSimpleName(); + } + } + } + + /** + * Checks if the given type is a Java primitive + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive + */ + public boolean equalsJavaPrimitive(String simpleType) { + + try { + return ClassUtils.getClass(simpleType).isPrimitive(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the given type is a Java primitive or wrapper + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive or wrapper + */ + public boolean equalsJavaPrimitiveOrWrapper(String simpleType) { + + try { + return ClassUtils.isPrimitiveOrWrapper(ClassUtils.getClass(simpleType)); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the type of the field in the pojo's class is a java primitive + * + * @param pojoClass the {@link Class} object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff the field is a java primitive + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitive(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return field.getType().isPrimitive(); + } + } + + /** + * Checks if the given type is a Java primitive or a Java primitive array + * + * @param simpleType the Type name to be checked + * @return true iff {@link #equalsJavaPrimitive(String)} is true or if simpleType is an array with a primitive + * component + */ + public boolean equalsJavaPrimitiveIncludingArrays(String simpleType) { + + Class klasse; + + try { + klasse = ClassUtils.getClass(simpleType).getComponentType(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + return equalsJavaPrimitive(simpleType) || (klasse != null && klasse.isPrimitive()); + } + + /** + * Checks if the given field in the pojo class is a java primitive or an array of java primitives + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff {@link #equalsJavaPrimitive(Class, String)} is true or the field is an array of primitives + * @throws NoSuchFieldException indicating something awfully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitiveIncludingArrays(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + return equalsJavaPrimitive(pojoClass, fieldName) || (pojoClass.getDeclaredField(fieldName).getType().isArray() + && pojoClass.getDeclaredField(fieldName).getType().getComponentType().isPrimitive()); + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param simpleType Java Type + * @param varName Variable name + * @return String either of the form '((Java Primitive Object Type)varName)' if simpleType is a primitive or the empty + * String otherwise + * @throws ClassNotFoundException should not occur + */ + public String castJavaPrimitives(String simpleType, String varName) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(simpleType), varName); + } else { + return ""; + } + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be casted + * @return if fieldName points to a primitive field then a casted statement (e.g. for an int field: + * '((Integer)field)') or an empty String otherwise + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String castJavaPrimitives(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(pojoClass, fieldName), fieldName); + } else { + return ""; + } + } + + /** + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return true if the field is an instance of java.utils.Collections + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean isCollection(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return Collection.class.isAssignableFrom(field.getType()); + } + + } + + /** + * Returns the Ext Type to a given java type + * + * @param simpleType any java type's simple name + * @return corresponding Ext type + */ + public String getExtType(String simpleType) { + + switch (simpleType) { + case "short": + case "Short": + case "int": + case "Integer": + case "long": + case "Long": + return "Integer"; + case "float": + case "Float": + case "double": + case "Double": + return "Number"; + case "boolean": + case "Boolean": + return "Boolean"; + case "char": + case "Character": + case "String": + return "String"; + case "Date": + return "Date"; + default: + return "Field"; + } + } + + /** + * returns the Angular5 type associated with a Java primitive + * + * @param simpleType :{@link String} the type to be parsed + * @return the corresponding Angular type or 'any' otherwise + */ + public String getAngularType(String simpleType) { + + switch (simpleType) { + case "boolean": + return "boolean"; + case "Boolean": + return "boolean"; + case "short": + return "number"; + case "Short": + return "number"; + case "int": + return "number"; + case "Integer": + return "number"; + case "long": + return "number"; + case "Long": + return "number"; + case "float": + return "number"; + case "Float": + return "number"; + case "double": + return "number"; + case "Double": + return "number"; + case "char": + return "string"; + case "Character": + return "string"; + case "String": + return "string"; + case "byte": + return "number"; + default: + return "any"; + } + } + + /** + * returns the class name of the return type of a specific method. + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName {@link String} the name of the method + * @return the class name of the return type of the specified method + * @throws SecurityException If no method of the given name can be found + * @throws NoSuchMethodException If no method of the given name can be found + */ + public String getReturnType(Class pojoClass, String methodName) throws NoSuchMethodException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + String s = "-"; + Method method = findMethod(pojoClass, methodName); + if (method != null && !method.getReturnType().equals(Void.TYPE)) { + s = method.getReturnType().toString(); + s = s.substring(s.lastIndexOf('.') + 1, s.length()); + } + return s; + } + + /** + * + * This methods returns the return type of the method in the given pojoClass which are annotated with the parameter + * annotatedClass + * + * @param pojoClass - The class in which to find if it has methods with annotatedClass + * @param annotatedClassName - The annotation which needs to be found + * @return Return type of the method annotated with the given annotation, else "null" + * @throws ClassNotFoundException if the annotated class name could not be found in the class path + */ + @SuppressWarnings("unchecked") + public String getReturnTypeOfMethodAnnotatedWith(Class pojoClass, String annotatedClassName) + throws ClassNotFoundException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + Method[] methods = pojoClass.getDeclaredMethods(); + for (Method method : methods) { + if (!method.getName().startsWith("get")) { + continue; + } + for (Annotation a : method.getAnnotations()) { + // better if (method.isAnnotationPresent(classObj)) {, but postponed as of different class + // loaders of a.getClass() and pojoClass.getClass() + if (a.getClass().getCanonicalName().equals(annotatedClassName)) { + return method.getReturnType().getSimpleName(); + } + } + } + return "null"; + } + + /** + * returns the HTTP request type corresponding to an annotation type + * + * @param annotations The annotation to get the type name of + * @return the HTTP request type name of the selected annotation + */ + public String getRequestType(Map annotations) { + + if (annotations.containsKey("javax_ws_rs_GET")) { + return "GET"; + } else if (annotations.containsKey("javax_ws_rs_PUT")) { + return "PUT"; + } else if (annotations.containsKey("javax_ws_rs_POST")) { + return "POST"; + } else if (annotations.containsKey("javax_ws_rs_DELETE")) { + return "DELETE"; + } else if (annotations.containsKey("javax_ws_rs_PATCH")) { + return "PATCH"; + } else { + return "-"; + } + } + + /** + * Helper method to find a class's specific method + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName The name of the method to be found + * @return The method object of the method to be found, null if it wasn't found + */ + private Method findMethod(Class pojoClass, String methodName) { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + for (Method m : pojoClass.getMethods()) { + if (m.getName().equals(methodName)) { + return m; + } + } + return null; + } + + /** + * Checks whether the class given by the full qualified name is an enum + * + * @param className full qualified class name + * @return true if the class is an enum, false otherwise + */ + public boolean isEnum(String className) { + + try { + return ClassUtils.getClass(className).isEnum(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return false; + } + } + + /** + * Returns the first enum value of an enum class + * + * @param className full qualified class name + * @return the first enum value name found in order + */ + public String getFirstEnumValue(String className) { + + try { + Class enumClass = ClassUtils.getClass(className); + Field[] declaredFields = enumClass.getDeclaredFields(); + if (declaredFields.length > 0) { + return declaredFields[0].getName(); + } else { + return null; + } + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return null; + } + } +} diff --git a/cobigen-templates/crud-openapi-java-server-app/pom.xml b/cobigen-templates/crud-openapi-java-server-app/pom.xml index 66c044a54b..9d845790e1 100644 --- a/cobigen-templates/crud-openapi-java-server-app/pom.xml +++ b/cobigen-templates/crud-openapi-java-server-app/pom.xml @@ -10,4 +10,10 @@ ${revision} + + + org.apache.commons + commons-lang3 + + \ No newline at end of file diff --git a/cobigen-templates/crud-openapi-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/constants/Field.java b/cobigen-templates/crud-openapi-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/constants/Field.java new file mode 100644 index 0000000000..5376daaea9 --- /dev/null +++ b/cobigen-templates/crud-openapi-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/constants/Field.java @@ -0,0 +1,48 @@ +package com.devonfw.cobigen.templates.devon4j.constants; + +/** + * Contains the used keys for the pojo field mapping + */ +public enum Field { + + /** + * Name of the field + */ + NAME("name"), + /** + * Type of the field + */ + TYPE("type"), + /** + * Canonical Type of the field + */ + CANONICAL_TYPE("canonicalType"), + /** + * The Javadoc of the field + */ + JAVA_DOC("javaDoc"), + /** + * Annotations + */ + ANNOTATIONS("annotations"); + + /** + * key value + */ + private String value; + + /** + * @param value of the key + */ + Field(String value) { + + this.value = value; + } + + @Override + public String toString() { + + return this.value; + } + +} diff --git a/cobigen-templates/crud-openapi-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/DevonfwUtil.java b/cobigen-templates/crud-openapi-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/DevonfwUtil.java new file mode 100644 index 0000000000..36e39b5e06 --- /dev/null +++ b/cobigen-templates/crud-openapi-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/DevonfwUtil.java @@ -0,0 +1,450 @@ +package com.devonfw.cobigen.templates.devon4j.utils; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.w3c.dom.Attr; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.NodeList; + +import com.devonfw.cobigen.templates.devon4j.constants.Field; +import com.sun.org.apache.xerces.internal.dom.DeferredElementNSImpl; + +/** + * A class for shared devon4j specific functions in the templates + * + */ +@SuppressWarnings("restriction") +public class DevonfwUtil { + + /** + * Check whether the given 'canonicalType' is a devon4j Entity, which is declared in the given 'component' + * + * @param canonicalType the type name + * @param component the component name + * @return true iff the canonicalType is a devon Entity + */ + public boolean isEntityInComponent(String canonicalType, String component) { + + return canonicalType.matches(String.format(".+%1$s\\.dataaccess\\.api\\.[A-Za-z0-9]+Entity(<.*)?", component)); + } + + /** + * Check whether the given 'canonicalType' is declared in the given 'component' + * + * @param canonicalType the type name + * @param component the component name + * @return true iff the canonicalType is inside the given component + */ + public boolean isTypeInComponent(String canonicalType, String component) { + + return canonicalType.matches(String.format("%1$s.[A-Za-z0-9]+(<.*)?", component)); + } + + /** + * Determines the ID getter for a given 'field' dependent on whether the getter should access the ID via an object + * reference or a direct ID getter + * + * @param field the field + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} + '()' with + * capitalize=true + */ + public String resolveIdGetter(Map field, boolean byObjectReference, String component) { + + // If field comes from an UML file + if (field.getClass().toGenericString().contains("freemarker.ext.beans.HashAdapter")) { + DeferredElementNSImpl umlNode = (DeferredElementNSImpl) field; + return resolveIdGetter(umlNode, byObjectReference, component); + } + return "get" + resolveIdVariableNameOrSetterGetterSuffix(field, byObjectReference, true, component) + "()"; + } + + /** + * Determines the ID getter for a given 'field' dependent on whether the getter should access the ID via an object + * reference or a direct ID getter + * + * This method is used when the field parameter comes from an UML file. The name and type of the attributes must be + * pre-processed for later inserting them inside the HashMap. + * + * @param field the field + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} + '()' with + * capitalize=true + */ + public String resolveIdGetter(DeferredElementNSImpl field, boolean byObjectReference, String component) { + + HashMap nodeHash = new HashMap<>(); + + // Putting the name of the attribute to the hash + nodeHash.put(Field.NAME.toString(), field.getAttribute("name")); + + // Putting the type of the attribute to the hash + NodeList childs = field.getChildNodes(); + for (int i = 0; i < childs.getLength(); i++) { + // Retrieve "type" tag + if (childs.item(i).getNodeName().equals("type")) { + NamedNodeMap attrs = childs.item(i).getAttributes(); + for (int j = 0; j < attrs.getLength(); j++) { + Attr attribute = (Attr) attrs.item(j); + // Try to find the attribute that contains the type + if (attribute.getName().equals("xmi:idref")) { + nodeHash.put(Field.TYPE.toString(), attribute.getName().replace("EAJava_", "")); + } + } + } + } + return "get" + resolveIdVariableNameOrSetterGetterSuffix(nodeHash, byObjectReference, true, component) + "()"; + + } + + /** + * Determines the ID getter for a given 'field' dependent on whether the getter should access the ID via an object + * reference or a direct ID getter + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} + '()' with + * capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdGetter(Class pojoClass, Map fieldMap, boolean byObjectReference, + String component) throws NoSuchFieldException, SecurityException { + + return "get" + resolveIdVariableNameOrSetterGetterSuffix(pojoClass, fieldMap, byObjectReference, true, component) + + "()"; + } + + /** + * same as {@link #resolveIdGetter(Map, boolean, String)} but with byObjectReference=false and component="" + * + * @param field the field + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} + '()' with + * capitalize=true + */ + public String resolveIdGetter(Map field) { + + return this.resolveIdGetter(field, false, ""); + } + + /** + * same as {@link #resolveIdGetter(Class,Map,boolean,String)} but with byObjectReference=false and component="" + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} + '()' with + * capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdGetter(Class pojoClass, Map fieldMap) + throws NoSuchFieldException, SecurityException { + + return resolveIdGetter(pojoClass, fieldMap, false, ""); + } + + /** + * Determines the ID setter for a given 'field' dependent on whether the setter should access the ID via an object + * reference or a direct ID setter. In contrast to resolveIdGetter, this function does not generate the function + * parenthesis to enable parameter declaration. + * + * @param field the field + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'set' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} with + * capitalize=true + */ + public String resolveIdSetter(Map field, boolean byObjectReference, String component) { + + return "set" + resolveIdVariableNameOrSetterGetterSuffix(field, byObjectReference, true, component); + } + + /** + * same as {@link #resolveIdSetter(Map, boolean, String)} but with byObjectReference=false and component="" + * + * @param field the field + * @return 'set' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} with + * capitalize=true + */ + public String resolveIdSetter(Map field) { + + return this.resolveIdSetter(field, false, ""); + } + + /** + * Determines the ID setter for a given 'field' dependent on whether the setter should access the ID via an object + * reference or a direct ID setter. In contrast to resolveIdGetter, this function does not generate the function + * parenthesis to enable parameter declaration. + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'set'+ {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} with capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdSetter(Class pojoClass, Map fieldMap, boolean byObjectReference, + String component) throws NoSuchFieldException, SecurityException { + + return "set" + resolveIdVariableNameOrSetterGetterSuffix(pojoClass, fieldMap, byObjectReference, true, component); + } + + /** + * same as {@link #resolveIdSetter(Class,Map,boolean,String)} but with byObjectReference=false and component="" + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @return 'set' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} with capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdSetter(Class pojoClass, Map fieldMap) + throws NoSuchFieldException, SecurityException { + + return resolveIdSetter(pojoClass, fieldMap, false, ""); + } + + /** + * Determines the variable name for the id value of the 'field' + * + * @param field the field + * @return {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)}) with + * byObjectReference=false, capitalize=false and component="" + */ + public String resolveIdVariableName(Map field) { + + // the component is passed down as an empty string since byObjectReference is false and therefore the + // component is + // never touched + return resolveIdVariableNameOrSetterGetterSuffix(field, false, false, ""); + } + + /** + * Determines the variable name for the id value of the specified field in the pojo + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @return {@link #resolveIdVariableNameOrSetterGetterSuffix(Class, Map, boolean, boolean, String)}) with + * byObjectReference=false, capitalize=false and component="" + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdVariableName(Class pojoClass, Map fieldMap) + throws NoSuchFieldException, SecurityException { + + // the component is passed down as an empty string since byObjectReference is false and therefore the + // component is + // never touched + return resolveIdVariableNameOrSetterGetterSuffix(pojoClass, fieldMap, false, false, ""); + } + + /** + * Determines the ID setter/getter suffix for a given 'field' dependent on whether the setter/getter should access the + * ID via an object reference or a direct ID setter/getter + * + * @param field the field + * @param byObjectReference boolean + * @param capitalize if the field name should be capitalized + * @param component the devon4j component. Only needed if $byObjectReference is true + * @return idVariable name or getter/setter suffix + */ + public String resolveIdVariableNameOrSetterGetterSuffix(Map field, boolean byObjectReference, + boolean capitalize, String component) { + + String fieldName = (String) field.get(Field.NAME.toString()); + if (capitalize) { + fieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); + } + String suffix = ""; + + String fieldType = (String) field.get(Field.TYPE.toString()); + String fieldCType = (String) field.get(Field.CANONICAL_TYPE.toString()); + if (fieldType.contains("Entity")) { + if (fieldCType.startsWith("java.util.List") || fieldCType.startsWith("java.util.Set")) { + suffix = "Ids"; + if (fieldName.endsWith("s")) { + // Assume trailing 's' as indicator for a plural + fieldName = fieldName.substring(0, fieldName.length() - 1); + } + } else { + suffix = "Id"; + } + if (byObjectReference && isTypeInComponent(fieldCType, component)) { + // direct references for Entities in same component, so get id of the object reference + suffix = "().getId"; + } + } + + return fieldName + suffix; + + } + + /** + * Determines the ID setter/getter suffix for a given 'field' dependent on whether the setter/getter should access the + * ID via an object reference or a direct ID setter/getter + * + * @param pojoClass the {@link Class} object of the pojo + * @param fieldMap the field mapping + * @param byObjectReference boolean + * @param capitalize if the field name should be capitalized + * @param component the devon4j component. Only needed if byObjectReference is true + * @return idVariable name or getter/setter suffix + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdVariableNameOrSetterGetterSuffix(Class pojoClass, Map fieldMap, + boolean byObjectReference, boolean capitalize, String component) throws NoSuchFieldException, SecurityException { + + String resultName = (String) fieldMap.get(Field.NAME.toString()); + if (capitalize) { + resultName = resultName.substring(0, 1).toUpperCase() + resultName.substring(1); + } + String suffix = ""; + String fieldType = (String) fieldMap.get(Field.TYPE.toString()); + String fieldName = (String) fieldMap.get(Field.NAME.toString()); + if (fieldType.contains("Entity")) { + if (Collection.class.isAssignableFrom(pojoClass.getDeclaredField(fieldName).getType())) { + suffix = "Ids"; + if (resultName.endsWith("s")) { + // Assume trailing 's' as indicator for a plural + resultName = resultName.substring(0, resultName.length() - 1); + } + } else { + suffix = "Id"; + } + if (byObjectReference + && isEntityInComponent(pojoClass.getDeclaredField(fieldName).getType().getName(), component)) { + // direct references for Entities in same component, so get id of the object reference + suffix = "().getId"; + } + } + + return resultName + suffix; + + } + + /** + * Returns the argument type of the list or set from a field. If the string contains "Entity" it will remove that + * part. For example, if we have a List <SampleEntity> it will return "Sample" + * + * @param field the field + * @param pojoClass the object class of the Entity that contains the field + * @return fieldType argument of the list + * @throws SecurityException if field type could not accessed + * @throws NoSuchFieldException if field could not be found + */ + public String getListArgumentType(Map field, Class pojoClass) + throws NoSuchFieldException, SecurityException { + + JavaUtil javaUtil = new JavaUtil(); + + String fieldType = (String) field.get(Field.TYPE.toString()); + String fieldName = (String) field.get(Field.NAME.toString()); + + if (fieldType.contains("Entity")) { + if (javaUtil.isCollection(pojoClass, fieldName)) { + + fieldType = fieldType.replace("Entity", ""); + // Regex: Extracts the argument type of the list 'List' => type + String regex = "(?<=\\<).+?(?=\\>)"; + Pattern pattern = Pattern.compile(regex); + Matcher regexMatcher = pattern.matcher(fieldType); + + if (regexMatcher.find()) { + fieldType = regexMatcher.group(0); + } + } + } + return fieldType; + + } + + /** + * Converts all occurrences of devon4j Entity types in the given 'field' simple type (possibly generic) to Longs + * + * @param field the field + * @return the field type as String. If field type contains 'Entity' the result is Long + */ + public String getSimpleEntityTypeAsLongReference(Map field) { + + String fieldType = (String) field.get(Field.TYPE.toString()); + if (fieldType.endsWith("Entity")) { + fieldType = fieldType.replaceAll("[^<>]+Entity", "Long"); + } + return fieldType; + } + + /** + * If the string last character is an 's', then it gets removed + * + * @param targetClassName string to remove plural + * @return string without 's' + */ + public String removePlural(String targetClassName) { + + if (targetClassName.charAt(targetClassName.length() - 1) == 's') { + targetClassName = targetClassName.substring(0, targetClassName.length() - 1); + } + return targetClassName; + } + + /** + * Checks whether the operation with the given ID corresponds to any standard CRUD method name. + * + * @param operationId operation ID interpreted as method name + * @param entityName entity name to check standard CRUD methods for + * @return true if the operation ID maps any standard CRUD method name, false otherwise + */ + public boolean isCrudOperation(String operationId, String entityName) { + + if (operationId == null) { + return false; + } + String opIdLowerCase = operationId.toLowerCase(); + String entityNameLowerCase = entityName.toLowerCase(); + if (opIdLowerCase.contains(entityNameLowerCase)) { + return opIdLowerCase.equals("find" + entityNameLowerCase) + || opIdLowerCase.equals("find" + entityNameLowerCase + "Etos") + || opIdLowerCase.equals("delete" + entityNameLowerCase) || opIdLowerCase.equals("save" + entityNameLowerCase); + } else { + return false; + } + } + + /** + * Converts the given media type to the spring Java enum value + * + * @param mediaType to be converted + * @return the spring enum value representing the given media type + */ + public String getSpringMediaType(String mediaType) { + + switch (mediaType) { + case "application/xml": + return "APPLICATION_XML_VALUE"; + case "application / x-www-form-urlencoded": + return "APPLICATION_FORM_URLENCODED_VALUE"; + case "multipart/form-data": + return "MULTIPART_FORM_DATA_VALUE"; + case "text/plain": + return "TEXT_PLAIN_VALUE"; + case "text/html": + return "TEXT_HTML_VALUE"; + case "application/pdf": + return "APPLICATION_PDF_VALUE"; + case "image/png": + return "IMAGE_PNG_VALUE"; + default: + return "APPLICATION_JSON_VALUE"; + } + } +} \ No newline at end of file diff --git a/cobigen-templates/crud-openapi-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java b/cobigen-templates/crud-openapi-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java new file mode 100644 index 0000000000..ced2f5fde2 --- /dev/null +++ b/cobigen-templates/crud-openapi-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java @@ -0,0 +1,464 @@ +package com.devonfw.cobigen.templates.devon4j.utils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + +import org.apache.commons.lang3.ClassUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides type operations, mainly checks and casts for Java Primitives, to be used in the templates + * + */ +public class JavaUtil { + + /** + * Logger for this class + */ + private static final Logger LOG = LoggerFactory.getLogger(JavaUtil.class); + + /** + * The constructor. + */ + public JavaUtil() { + + // Empty for CobiGen to automatically instantiate it + } + + /** + * Returns the Object version of a Java primitive or the input if the input isn't a java primitive + * + * @param simpleType String + * @return the corresponding object wrapper type simple name of the input if the input is the name of a primitive java + * type. The input itself if not. (e.g. "int" results in "Integer") + * @throws ClassNotFoundException should not occur. + */ + public String boxJavaPrimitives(String simpleType) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return ClassUtils.primitiveToWrapper(ClassUtils.getClass(simpleType)).getSimpleName(); + } else { + return simpleType; + } + + } + + /** + * Returns the simple name of the type of a field in the pojoClass. If the type is a java primitive the name of the + * wrapper class is returned + * + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return String. The simple name of the field's type. The simple name of the wrapper class in case of java + * primitives + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String boxJavaPrimitives(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return ClassUtils.primitiveToWrapper(pojoClass.getDeclaredField(fieldName).getType()).getSimpleName(); + } else { + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + throw new IllegalAccessError("Could not find field " + fieldName + " in class " + pojoClass); + } else { + return field.getType().getSimpleName(); + } + } + } + + /** + * Checks if the given type is a Java primitive + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive + */ + public boolean equalsJavaPrimitive(String simpleType) { + + try { + return ClassUtils.getClass(simpleType).isPrimitive(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the given type is a Java primitive or wrapper + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive or wrapper + */ + public boolean equalsJavaPrimitiveOrWrapper(String simpleType) { + + try { + return ClassUtils.isPrimitiveOrWrapper(ClassUtils.getClass(simpleType)); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the type of the field in the pojo's class is a java primitive + * + * @param pojoClass the {@link Class} object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff the field is a java primitive + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitive(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return field.getType().isPrimitive(); + } + } + + /** + * Checks if the given type is a Java primitive or a Java primitive array + * + * @param simpleType the Type name to be checked + * @return true iff {@link #equalsJavaPrimitive(String)} is true or if simpleType is an array with a primitive + * component + */ + public boolean equalsJavaPrimitiveIncludingArrays(String simpleType) { + + Class klasse; + + try { + klasse = ClassUtils.getClass(simpleType).getComponentType(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + return equalsJavaPrimitive(simpleType) || (klasse != null && klasse.isPrimitive()); + } + + /** + * Checks if the given field in the pojo class is a java primitive or an array of java primitives + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff {@link #equalsJavaPrimitive(Class, String)} is true or the field is an array of primitives + * @throws NoSuchFieldException indicating something awfully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitiveIncludingArrays(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + return equalsJavaPrimitive(pojoClass, fieldName) || (pojoClass.getDeclaredField(fieldName).getType().isArray() + && pojoClass.getDeclaredField(fieldName).getType().getComponentType().isPrimitive()); + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param simpleType Java Type + * @param varName Variable name + * @return String either of the form '((Java Primitive Object Type)varName)' if simpleType is a primitive or the empty + * String otherwise + * @throws ClassNotFoundException should not occur + */ + public String castJavaPrimitives(String simpleType, String varName) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(simpleType), varName); + } else { + return ""; + } + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be casted + * @return if fieldName points to a primitive field then a casted statement (e.g. for an int field: + * '((Integer)field)') or an empty String otherwise + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String castJavaPrimitives(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(pojoClass, fieldName), fieldName); + } else { + return ""; + } + } + + /** + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return true if the field is an instance of java.utils.Collections + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean isCollection(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return Collection.class.isAssignableFrom(field.getType()); + } + + } + + /** + * Returns the Ext Type to a given java type + * + * @param simpleType any java type's simple name + * @return corresponding Ext type + */ + public String getExtType(String simpleType) { + + switch (simpleType) { + case "short": + case "Short": + case "int": + case "Integer": + case "long": + case "Long": + return "Integer"; + case "float": + case "Float": + case "double": + case "Double": + return "Number"; + case "boolean": + case "Boolean": + return "Boolean"; + case "char": + case "Character": + case "String": + return "String"; + case "Date": + return "Date"; + default: + return "Field"; + } + } + + /** + * returns the Angular5 type associated with a Java primitive + * + * @param simpleType :{@link String} the type to be parsed + * @return the corresponding Angular type or 'any' otherwise + */ + public String getAngularType(String simpleType) { + + switch (simpleType) { + case "boolean": + return "boolean"; + case "Boolean": + return "boolean"; + case "short": + return "number"; + case "Short": + return "number"; + case "int": + return "number"; + case "Integer": + return "number"; + case "long": + return "number"; + case "Long": + return "number"; + case "float": + return "number"; + case "Float": + return "number"; + case "double": + return "number"; + case "Double": + return "number"; + case "char": + return "string"; + case "Character": + return "string"; + case "String": + return "string"; + case "byte": + return "number"; + default: + return "any"; + } + } + + /** + * returns the class name of the return type of a specific method. + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName {@link String} the name of the method + * @return the class name of the return type of the specified method + * @throws SecurityException If no method of the given name can be found + * @throws NoSuchMethodException If no method of the given name can be found + */ + public String getReturnType(Class pojoClass, String methodName) throws NoSuchMethodException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + String s = "-"; + Method method = findMethod(pojoClass, methodName); + if (method != null && !method.getReturnType().equals(Void.TYPE)) { + s = method.getReturnType().toString(); + s = s.substring(s.lastIndexOf('.') + 1, s.length()); + } + return s; + } + + /** + * + * This methods returns the return type of the method in the given pojoClass which are annotated with the parameter + * annotatedClass + * + * @param pojoClass - The class in which to find if it has methods with annotatedClass + * @param annotatedClassName - The annotation which needs to be found + * @return Return type of the method annotated with the given annotation, else "null" + * @throws ClassNotFoundException if the annotated class name could not be found in the class path + */ + @SuppressWarnings("unchecked") + public String getReturnTypeOfMethodAnnotatedWith(Class pojoClass, String annotatedClassName) + throws ClassNotFoundException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + Method[] methods = pojoClass.getDeclaredMethods(); + for (Method method : methods) { + if (!method.getName().startsWith("get")) { + continue; + } + for (Annotation a : method.getAnnotations()) { + // better if (method.isAnnotationPresent(classObj)) {, but postponed as of different class + // loaders of a.getClass() and pojoClass.getClass() + if (a.getClass().getCanonicalName().equals(annotatedClassName)) { + return method.getReturnType().getSimpleName(); + } + } + } + return "null"; + } + + /** + * returns the HTTP request type corresponding to an annotation type + * + * @param annotations The annotation to get the type name of + * @return the HTTP request type name of the selected annotation + */ + public String getRequestType(Map annotations) { + + if (annotations.containsKey("javax_ws_rs_GET")) { + return "GET"; + } else if (annotations.containsKey("javax_ws_rs_PUT")) { + return "PUT"; + } else if (annotations.containsKey("javax_ws_rs_POST")) { + return "POST"; + } else if (annotations.containsKey("javax_ws_rs_DELETE")) { + return "DELETE"; + } else if (annotations.containsKey("javax_ws_rs_PATCH")) { + return "PATCH"; + } else { + return "-"; + } + } + + /** + * Helper method to find a class's specific method + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName The name of the method to be found + * @return The method object of the method to be found, null if it wasn't found + */ + private Method findMethod(Class pojoClass, String methodName) { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + for (Method m : pojoClass.getMethods()) { + if (m.getName().equals(methodName)) { + return m; + } + } + return null; + } + + /** + * Checks whether the class given by the full qualified name is an enum + * + * @param className full qualified class name + * @return true if the class is an enum, false otherwise + */ + public boolean isEnum(String className) { + + try { + return ClassUtils.getClass(className).isEnum(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return false; + } + } + + /** + * Returns the first enum value of an enum class + * + * @param className full qualified class name + * @return the first enum value name found in order + */ + public String getFirstEnumValue(String className) { + + try { + Class enumClass = ClassUtils.getClass(className); + Field[] declaredFields = enumClass.getDeclaredFields(); + if (declaredFields.length > 0) { + return declaredFields[0].getName(); + } else { + return null; + } + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return null; + } + } +} diff --git a/cobigen-templates/crud-openapi-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/OpenApiUtil.java b/cobigen-templates/crud-openapi-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/OpenApiUtil.java new file mode 100644 index 0000000000..a93288f45a --- /dev/null +++ b/cobigen-templates/crud-openapi-java-server-app/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/OpenApiUtil.java @@ -0,0 +1,255 @@ +package com.devonfw.cobigen.templates.devon4j.utils; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.Instant; +import java.time.LocalDate; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; + +/** Utils on OpenAPI template model */ +public class OpenApiUtil { + + /** + * Prints javax validation constraints from an OpenAPI model + * + * @param constraints OpenAPI model constraints + * @return the list of supported annotations in Java syntax + */ + public String printJavaConstraints(Map constraints) { + + String consts = ""; + if (constraints.get("maximum") != null) { + consts = consts + "@Max(" + constraints.get("maximum") + ")"; + } + if (constraints.get("minimum") != null) { + consts = consts + "@Min(" + constraints.get("minimum") + ")"; + } + if (constraints.get("maxLength") != null) { + consts = consts + "@Size(max=" + constraints.get("maxLength"); + if (constraints.get("minLength") != null) { + consts = consts + ", min=" + constraints.get("minLength"); + } + consts = consts + ")"; + } else { + if (constraints.get("minLength") != null) { + consts = consts + "@Size(min=" + constraints.get("minLength"); + if (constraints.get("maxLength") != null) { + consts = consts + ", max=" + constraints.get("maxLength"); + } + consts = consts + ")"; + } + } + return consts; + } + + /** + * Prints the return type of a response based on the OpenAPI model + * + * @param response the OpenAPI model of a response + * @return simple type of the response return type in Java syntax + */ + public String printJavaServiceResponseReturnType(Map response) { + + String returnType = toJavaType(response, false); + if ((boolean) response.get("isVoid")) { + return "void"; + } + if ((boolean) response.get("isArray")) { + if ((boolean) response.get("isEntity")) { + if (returnType.contains("Entity")) { + return "List<" + returnType + ">"; + } else { + return "List<" + returnType + "Entity>"; + } + } else { + return "List<" + returnType + ">"; + } + } else if ((boolean) response.get("isPaginated")) { + if ((boolean) response.get("isEntity")) { + if (returnType.contains("Entity")) { + return "Page<" + returnType + ">"; + } else { + return "Page<" + returnType + "Entity>"; + } + } else { + return "Page<" + returnType + ">"; + } + } else { + return returnType; + } + } + + /** + * Returns the Java type corresponding to the OpenAPI type definition. If the type could not be matched, Object will + * be returned. + * + * @param parameter OpenAPI model of a parameter + * @param simpleType if a Java simple type should be returned if possible + * @return the Java type + */ + public String toJavaType(Map parameter, boolean simpleType) { + + String typeConverted = null; + String format = (String) parameter.get("format"); + String type = (String) parameter.get("type"); + boolean isCollection = false; + if (parameter.get("isCollection") != null) { + isCollection = (boolean) parameter.get("isCollection"); + } + + boolean isEntity = (boolean) parameter.get("isEntity"); + + if (type != null) { + switch (type.toLowerCase()) { + case "integer": + if (format != null) { + switch (format) { + case "int32": + typeConverted = simpleType ? "int" : "Integer"; + break; + case "int64": + typeConverted = simpleType ? "long" : "Long"; + break; + default: + typeConverted = BigInteger.class.getSimpleName(); + break; + } + } else { + typeConverted = BigInteger.class.getSimpleName(); + } + break; + case "number": + if (format != null) { + switch (format) { + case "float": + typeConverted = simpleType ? "float" : "Float"; + break; + case "double": + typeConverted = simpleType ? "double" : "Double"; + break; + default: + typeConverted = BigDecimal.class.getSimpleName(); + break; + } + } else { + typeConverted = BigDecimal.class.getSimpleName(); + } + break; + case "string": + if (format != null) { + switch (format) { + case "date": + typeConverted = LocalDate.class.getSimpleName(); + break; + case "date-time": + typeConverted = Instant.class.getSimpleName(); + break; + case "binary": + typeConverted = simpleType ? "float" : "Float"; + break; + case "email": + case "password": + typeConverted = String.class.getSimpleName(); + break; + default: + typeConverted = "String"; + break; + } + } else { + typeConverted = "String"; + } + break; + case "boolean": + typeConverted = simpleType ? "boolean" : "Boolean"; + break; + default: + typeConverted = "void"; + break; + } + } else { + typeConverted = "void"; + } + + if (isCollection) { + if (isEntity) { + return "List<" + parameter.get("type") + ">"; + } else { + return "List<" + typeConverted + ">"; + } + } else if (isEntity) { + return (String) parameter.get("type"); + } + return typeConverted; + } + + /** + * Returns the TypeScript type corresponding to the OpenAPI type definition. If the type could not be matched, the + * same value will be returned. + * + * @param parameter OpenAPI model of a parameter + * @return the Java type + */ + // getOaspTypeFromOpenAPI + public String toTypeScriptType(Map parameter) { + + String typeConverted = null; + String type = (String) parameter.get("type"); + boolean isCollection = false; + if (parameter.get("isCollection") != null) { + isCollection = (boolean) parameter.get("isCollection"); + } + + boolean isEntity = (boolean) parameter.get("isEntity"); + + if (type != null) { + switch (type.toLowerCase()) { + case "integer": + typeConverted = "number"; + break; + default: + typeConverted = type; + break; + } + } else { + typeConverted = "undefined"; + } + + if (isCollection) { + if (isEntity) { + return parameter.get("type") + "[]"; + } else { + return typeConverted + "[]"; + } + } else if (isEntity) { + return (String) parameter.get("type"); + } + return typeConverted; + } + + /** + * Prints the service operation name based on the operationId or generates one based on the servicePath while printing + * a comment how to get better service names. + * + * @param operation operation Model of the OpenAPI model + * @param servicePath service path of the + * @return the service method name + */ + public String printServiceOperationName(Map operation, String servicePath) { + + String operationId = (String) operation.get("operationId"); + if (StringUtils.isEmpty(operationId)) { + String[] split = servicePath.split("/"); + String lastSegment; + if (!split[split.length - 1].isEmpty()) { + lastSegment = split[split.length - 1]; + } else { + lastSegment = split[split.length - 2]; + } + return ((String) operation.get("type")) + lastSegment; + } else { + return operationId; + } + } +} diff --git a/cobigen-templates/crud-openapi-net/pom.xml b/cobigen-templates/crud-openapi-net/pom.xml index 72c9d20582..ddd28d9c64 100644 --- a/cobigen-templates/crud-openapi-net/pom.xml +++ b/cobigen-templates/crud-openapi-net/pom.xml @@ -10,4 +10,10 @@ ${revision} + + + org.apache.commons + commons-lang3 + + \ No newline at end of file diff --git a/cobigen-templates/crud-openapi-net/src/main/java/com/devonfw/cobigen/templates/devon4j/constants/Field.java b/cobigen-templates/crud-openapi-net/src/main/java/com/devonfw/cobigen/templates/devon4j/constants/Field.java new file mode 100644 index 0000000000..5376daaea9 --- /dev/null +++ b/cobigen-templates/crud-openapi-net/src/main/java/com/devonfw/cobigen/templates/devon4j/constants/Field.java @@ -0,0 +1,48 @@ +package com.devonfw.cobigen.templates.devon4j.constants; + +/** + * Contains the used keys for the pojo field mapping + */ +public enum Field { + + /** + * Name of the field + */ + NAME("name"), + /** + * Type of the field + */ + TYPE("type"), + /** + * Canonical Type of the field + */ + CANONICAL_TYPE("canonicalType"), + /** + * The Javadoc of the field + */ + JAVA_DOC("javaDoc"), + /** + * Annotations + */ + ANNOTATIONS("annotations"); + + /** + * key value + */ + private String value; + + /** + * @param value of the key + */ + Field(String value) { + + this.value = value; + } + + @Override + public String toString() { + + return this.value; + } + +} diff --git a/cobigen-templates/crud-openapi-net/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/DevonfwUtil.java b/cobigen-templates/crud-openapi-net/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/DevonfwUtil.java new file mode 100644 index 0000000000..36e39b5e06 --- /dev/null +++ b/cobigen-templates/crud-openapi-net/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/DevonfwUtil.java @@ -0,0 +1,450 @@ +package com.devonfw.cobigen.templates.devon4j.utils; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.w3c.dom.Attr; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.NodeList; + +import com.devonfw.cobigen.templates.devon4j.constants.Field; +import com.sun.org.apache.xerces.internal.dom.DeferredElementNSImpl; + +/** + * A class for shared devon4j specific functions in the templates + * + */ +@SuppressWarnings("restriction") +public class DevonfwUtil { + + /** + * Check whether the given 'canonicalType' is a devon4j Entity, which is declared in the given 'component' + * + * @param canonicalType the type name + * @param component the component name + * @return true iff the canonicalType is a devon Entity + */ + public boolean isEntityInComponent(String canonicalType, String component) { + + return canonicalType.matches(String.format(".+%1$s\\.dataaccess\\.api\\.[A-Za-z0-9]+Entity(<.*)?", component)); + } + + /** + * Check whether the given 'canonicalType' is declared in the given 'component' + * + * @param canonicalType the type name + * @param component the component name + * @return true iff the canonicalType is inside the given component + */ + public boolean isTypeInComponent(String canonicalType, String component) { + + return canonicalType.matches(String.format("%1$s.[A-Za-z0-9]+(<.*)?", component)); + } + + /** + * Determines the ID getter for a given 'field' dependent on whether the getter should access the ID via an object + * reference or a direct ID getter + * + * @param field the field + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} + '()' with + * capitalize=true + */ + public String resolveIdGetter(Map field, boolean byObjectReference, String component) { + + // If field comes from an UML file + if (field.getClass().toGenericString().contains("freemarker.ext.beans.HashAdapter")) { + DeferredElementNSImpl umlNode = (DeferredElementNSImpl) field; + return resolveIdGetter(umlNode, byObjectReference, component); + } + return "get" + resolveIdVariableNameOrSetterGetterSuffix(field, byObjectReference, true, component) + "()"; + } + + /** + * Determines the ID getter for a given 'field' dependent on whether the getter should access the ID via an object + * reference or a direct ID getter + * + * This method is used when the field parameter comes from an UML file. The name and type of the attributes must be + * pre-processed for later inserting them inside the HashMap. + * + * @param field the field + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} + '()' with + * capitalize=true + */ + public String resolveIdGetter(DeferredElementNSImpl field, boolean byObjectReference, String component) { + + HashMap nodeHash = new HashMap<>(); + + // Putting the name of the attribute to the hash + nodeHash.put(Field.NAME.toString(), field.getAttribute("name")); + + // Putting the type of the attribute to the hash + NodeList childs = field.getChildNodes(); + for (int i = 0; i < childs.getLength(); i++) { + // Retrieve "type" tag + if (childs.item(i).getNodeName().equals("type")) { + NamedNodeMap attrs = childs.item(i).getAttributes(); + for (int j = 0; j < attrs.getLength(); j++) { + Attr attribute = (Attr) attrs.item(j); + // Try to find the attribute that contains the type + if (attribute.getName().equals("xmi:idref")) { + nodeHash.put(Field.TYPE.toString(), attribute.getName().replace("EAJava_", "")); + } + } + } + } + return "get" + resolveIdVariableNameOrSetterGetterSuffix(nodeHash, byObjectReference, true, component) + "()"; + + } + + /** + * Determines the ID getter for a given 'field' dependent on whether the getter should access the ID via an object + * reference or a direct ID getter + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} + '()' with + * capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdGetter(Class pojoClass, Map fieldMap, boolean byObjectReference, + String component) throws NoSuchFieldException, SecurityException { + + return "get" + resolveIdVariableNameOrSetterGetterSuffix(pojoClass, fieldMap, byObjectReference, true, component) + + "()"; + } + + /** + * same as {@link #resolveIdGetter(Map, boolean, String)} but with byObjectReference=false and component="" + * + * @param field the field + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} + '()' with + * capitalize=true + */ + public String resolveIdGetter(Map field) { + + return this.resolveIdGetter(field, false, ""); + } + + /** + * same as {@link #resolveIdGetter(Class,Map,boolean,String)} but with byObjectReference=false and component="" + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @return 'get' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} + '()' with + * capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdGetter(Class pojoClass, Map fieldMap) + throws NoSuchFieldException, SecurityException { + + return resolveIdGetter(pojoClass, fieldMap, false, ""); + } + + /** + * Determines the ID setter for a given 'field' dependent on whether the setter should access the ID via an object + * reference or a direct ID setter. In contrast to resolveIdGetter, this function does not generate the function + * parenthesis to enable parameter declaration. + * + * @param field the field + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'set' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} with + * capitalize=true + */ + public String resolveIdSetter(Map field, boolean byObjectReference, String component) { + + return "set" + resolveIdVariableNameOrSetterGetterSuffix(field, byObjectReference, true, component); + } + + /** + * same as {@link #resolveIdSetter(Map, boolean, String)} but with byObjectReference=false and component="" + * + * @param field the field + * @return 'set' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)} with + * capitalize=true + */ + public String resolveIdSetter(Map field) { + + return this.resolveIdSetter(field, false, ""); + } + + /** + * Determines the ID setter for a given 'field' dependent on whether the setter should access the ID via an object + * reference or a direct ID setter. In contrast to resolveIdGetter, this function does not generate the function + * parenthesis to enable parameter declaration. + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @param byObjectReference boolean + * @param component the devon4j component name + * @return 'set'+ {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} with capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdSetter(Class pojoClass, Map fieldMap, boolean byObjectReference, + String component) throws NoSuchFieldException, SecurityException { + + return "set" + resolveIdVariableNameOrSetterGetterSuffix(pojoClass, fieldMap, byObjectReference, true, component); + } + + /** + * same as {@link #resolveIdSetter(Class,Map,boolean,String)} but with byObjectReference=false and component="" + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @return 'set' + {@link #resolveIdVariableNameOrSetterGetterSuffix(Map,boolean,boolean,String)} with capitalize=true + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdSetter(Class pojoClass, Map fieldMap) + throws NoSuchFieldException, SecurityException { + + return resolveIdSetter(pojoClass, fieldMap, false, ""); + } + + /** + * Determines the variable name for the id value of the 'field' + * + * @param field the field + * @return {@link #resolveIdVariableNameOrSetterGetterSuffix(Map, boolean, boolean, String)}) with + * byObjectReference=false, capitalize=false and component="" + */ + public String resolveIdVariableName(Map field) { + + // the component is passed down as an empty string since byObjectReference is false and therefore the + // component is + // never touched + return resolveIdVariableNameOrSetterGetterSuffix(field, false, false, ""); + } + + /** + * Determines the variable name for the id value of the specified field in the pojo + * + * @param pojoClass the class object of the pojo + * @param fieldMap the field mapping + * @return {@link #resolveIdVariableNameOrSetterGetterSuffix(Class, Map, boolean, boolean, String)}) with + * byObjectReference=false, capitalize=false and component="" + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdVariableName(Class pojoClass, Map fieldMap) + throws NoSuchFieldException, SecurityException { + + // the component is passed down as an empty string since byObjectReference is false and therefore the + // component is + // never touched + return resolveIdVariableNameOrSetterGetterSuffix(pojoClass, fieldMap, false, false, ""); + } + + /** + * Determines the ID setter/getter suffix for a given 'field' dependent on whether the setter/getter should access the + * ID via an object reference or a direct ID setter/getter + * + * @param field the field + * @param byObjectReference boolean + * @param capitalize if the field name should be capitalized + * @param component the devon4j component. Only needed if $byObjectReference is true + * @return idVariable name or getter/setter suffix + */ + public String resolveIdVariableNameOrSetterGetterSuffix(Map field, boolean byObjectReference, + boolean capitalize, String component) { + + String fieldName = (String) field.get(Field.NAME.toString()); + if (capitalize) { + fieldName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); + } + String suffix = ""; + + String fieldType = (String) field.get(Field.TYPE.toString()); + String fieldCType = (String) field.get(Field.CANONICAL_TYPE.toString()); + if (fieldType.contains("Entity")) { + if (fieldCType.startsWith("java.util.List") || fieldCType.startsWith("java.util.Set")) { + suffix = "Ids"; + if (fieldName.endsWith("s")) { + // Assume trailing 's' as indicator for a plural + fieldName = fieldName.substring(0, fieldName.length() - 1); + } + } else { + suffix = "Id"; + } + if (byObjectReference && isTypeInComponent(fieldCType, component)) { + // direct references for Entities in same component, so get id of the object reference + suffix = "().getId"; + } + } + + return fieldName + suffix; + + } + + /** + * Determines the ID setter/getter suffix for a given 'field' dependent on whether the setter/getter should access the + * ID via an object reference or a direct ID setter/getter + * + * @param pojoClass the {@link Class} object of the pojo + * @param fieldMap the field mapping + * @param byObjectReference boolean + * @param capitalize if the field name should be capitalized + * @param component the devon4j component. Only needed if byObjectReference is true + * @return idVariable name or getter/setter suffix + * @throws NoSuchFieldException indicating a severe problem in the used model + * @throws SecurityException if the field cannot be accessed for any reason + */ + public String resolveIdVariableNameOrSetterGetterSuffix(Class pojoClass, Map fieldMap, + boolean byObjectReference, boolean capitalize, String component) throws NoSuchFieldException, SecurityException { + + String resultName = (String) fieldMap.get(Field.NAME.toString()); + if (capitalize) { + resultName = resultName.substring(0, 1).toUpperCase() + resultName.substring(1); + } + String suffix = ""; + String fieldType = (String) fieldMap.get(Field.TYPE.toString()); + String fieldName = (String) fieldMap.get(Field.NAME.toString()); + if (fieldType.contains("Entity")) { + if (Collection.class.isAssignableFrom(pojoClass.getDeclaredField(fieldName).getType())) { + suffix = "Ids"; + if (resultName.endsWith("s")) { + // Assume trailing 's' as indicator for a plural + resultName = resultName.substring(0, resultName.length() - 1); + } + } else { + suffix = "Id"; + } + if (byObjectReference + && isEntityInComponent(pojoClass.getDeclaredField(fieldName).getType().getName(), component)) { + // direct references for Entities in same component, so get id of the object reference + suffix = "().getId"; + } + } + + return resultName + suffix; + + } + + /** + * Returns the argument type of the list or set from a field. If the string contains "Entity" it will remove that + * part. For example, if we have a List <SampleEntity> it will return "Sample" + * + * @param field the field + * @param pojoClass the object class of the Entity that contains the field + * @return fieldType argument of the list + * @throws SecurityException if field type could not accessed + * @throws NoSuchFieldException if field could not be found + */ + public String getListArgumentType(Map field, Class pojoClass) + throws NoSuchFieldException, SecurityException { + + JavaUtil javaUtil = new JavaUtil(); + + String fieldType = (String) field.get(Field.TYPE.toString()); + String fieldName = (String) field.get(Field.NAME.toString()); + + if (fieldType.contains("Entity")) { + if (javaUtil.isCollection(pojoClass, fieldName)) { + + fieldType = fieldType.replace("Entity", ""); + // Regex: Extracts the argument type of the list 'List' => type + String regex = "(?<=\\<).+?(?=\\>)"; + Pattern pattern = Pattern.compile(regex); + Matcher regexMatcher = pattern.matcher(fieldType); + + if (regexMatcher.find()) { + fieldType = regexMatcher.group(0); + } + } + } + return fieldType; + + } + + /** + * Converts all occurrences of devon4j Entity types in the given 'field' simple type (possibly generic) to Longs + * + * @param field the field + * @return the field type as String. If field type contains 'Entity' the result is Long + */ + public String getSimpleEntityTypeAsLongReference(Map field) { + + String fieldType = (String) field.get(Field.TYPE.toString()); + if (fieldType.endsWith("Entity")) { + fieldType = fieldType.replaceAll("[^<>]+Entity", "Long"); + } + return fieldType; + } + + /** + * If the string last character is an 's', then it gets removed + * + * @param targetClassName string to remove plural + * @return string without 's' + */ + public String removePlural(String targetClassName) { + + if (targetClassName.charAt(targetClassName.length() - 1) == 's') { + targetClassName = targetClassName.substring(0, targetClassName.length() - 1); + } + return targetClassName; + } + + /** + * Checks whether the operation with the given ID corresponds to any standard CRUD method name. + * + * @param operationId operation ID interpreted as method name + * @param entityName entity name to check standard CRUD methods for + * @return true if the operation ID maps any standard CRUD method name, false otherwise + */ + public boolean isCrudOperation(String operationId, String entityName) { + + if (operationId == null) { + return false; + } + String opIdLowerCase = operationId.toLowerCase(); + String entityNameLowerCase = entityName.toLowerCase(); + if (opIdLowerCase.contains(entityNameLowerCase)) { + return opIdLowerCase.equals("find" + entityNameLowerCase) + || opIdLowerCase.equals("find" + entityNameLowerCase + "Etos") + || opIdLowerCase.equals("delete" + entityNameLowerCase) || opIdLowerCase.equals("save" + entityNameLowerCase); + } else { + return false; + } + } + + /** + * Converts the given media type to the spring Java enum value + * + * @param mediaType to be converted + * @return the spring enum value representing the given media type + */ + public String getSpringMediaType(String mediaType) { + + switch (mediaType) { + case "application/xml": + return "APPLICATION_XML_VALUE"; + case "application / x-www-form-urlencoded": + return "APPLICATION_FORM_URLENCODED_VALUE"; + case "multipart/form-data": + return "MULTIPART_FORM_DATA_VALUE"; + case "text/plain": + return "TEXT_PLAIN_VALUE"; + case "text/html": + return "TEXT_HTML_VALUE"; + case "application/pdf": + return "APPLICATION_PDF_VALUE"; + case "image/png": + return "IMAGE_PNG_VALUE"; + default: + return "APPLICATION_JSON_VALUE"; + } + } +} \ No newline at end of file diff --git a/cobigen-templates/crud-openapi-net/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java b/cobigen-templates/crud-openapi-net/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java new file mode 100644 index 0000000000..ced2f5fde2 --- /dev/null +++ b/cobigen-templates/crud-openapi-net/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java @@ -0,0 +1,464 @@ +package com.devonfw.cobigen.templates.devon4j.utils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + +import org.apache.commons.lang3.ClassUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides type operations, mainly checks and casts for Java Primitives, to be used in the templates + * + */ +public class JavaUtil { + + /** + * Logger for this class + */ + private static final Logger LOG = LoggerFactory.getLogger(JavaUtil.class); + + /** + * The constructor. + */ + public JavaUtil() { + + // Empty for CobiGen to automatically instantiate it + } + + /** + * Returns the Object version of a Java primitive or the input if the input isn't a java primitive + * + * @param simpleType String + * @return the corresponding object wrapper type simple name of the input if the input is the name of a primitive java + * type. The input itself if not. (e.g. "int" results in "Integer") + * @throws ClassNotFoundException should not occur. + */ + public String boxJavaPrimitives(String simpleType) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return ClassUtils.primitiveToWrapper(ClassUtils.getClass(simpleType)).getSimpleName(); + } else { + return simpleType; + } + + } + + /** + * Returns the simple name of the type of a field in the pojoClass. If the type is a java primitive the name of the + * wrapper class is returned + * + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return String. The simple name of the field's type. The simple name of the wrapper class in case of java + * primitives + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String boxJavaPrimitives(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return ClassUtils.primitiveToWrapper(pojoClass.getDeclaredField(fieldName).getType()).getSimpleName(); + } else { + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + throw new IllegalAccessError("Could not find field " + fieldName + " in class " + pojoClass); + } else { + return field.getType().getSimpleName(); + } + } + } + + /** + * Checks if the given type is a Java primitive + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive + */ + public boolean equalsJavaPrimitive(String simpleType) { + + try { + return ClassUtils.getClass(simpleType).isPrimitive(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the given type is a Java primitive or wrapper + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive or wrapper + */ + public boolean equalsJavaPrimitiveOrWrapper(String simpleType) { + + try { + return ClassUtils.isPrimitiveOrWrapper(ClassUtils.getClass(simpleType)); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the type of the field in the pojo's class is a java primitive + * + * @param pojoClass the {@link Class} object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff the field is a java primitive + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitive(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return field.getType().isPrimitive(); + } + } + + /** + * Checks if the given type is a Java primitive or a Java primitive array + * + * @param simpleType the Type name to be checked + * @return true iff {@link #equalsJavaPrimitive(String)} is true or if simpleType is an array with a primitive + * component + */ + public boolean equalsJavaPrimitiveIncludingArrays(String simpleType) { + + Class klasse; + + try { + klasse = ClassUtils.getClass(simpleType).getComponentType(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + return equalsJavaPrimitive(simpleType) || (klasse != null && klasse.isPrimitive()); + } + + /** + * Checks if the given field in the pojo class is a java primitive or an array of java primitives + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff {@link #equalsJavaPrimitive(Class, String)} is true or the field is an array of primitives + * @throws NoSuchFieldException indicating something awfully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitiveIncludingArrays(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + return equalsJavaPrimitive(pojoClass, fieldName) || (pojoClass.getDeclaredField(fieldName).getType().isArray() + && pojoClass.getDeclaredField(fieldName).getType().getComponentType().isPrimitive()); + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param simpleType Java Type + * @param varName Variable name + * @return String either of the form '((Java Primitive Object Type)varName)' if simpleType is a primitive or the empty + * String otherwise + * @throws ClassNotFoundException should not occur + */ + public String castJavaPrimitives(String simpleType, String varName) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(simpleType), varName); + } else { + return ""; + } + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be casted + * @return if fieldName points to a primitive field then a casted statement (e.g. for an int field: + * '((Integer)field)') or an empty String otherwise + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String castJavaPrimitives(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(pojoClass, fieldName), fieldName); + } else { + return ""; + } + } + + /** + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return true if the field is an instance of java.utils.Collections + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean isCollection(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return Collection.class.isAssignableFrom(field.getType()); + } + + } + + /** + * Returns the Ext Type to a given java type + * + * @param simpleType any java type's simple name + * @return corresponding Ext type + */ + public String getExtType(String simpleType) { + + switch (simpleType) { + case "short": + case "Short": + case "int": + case "Integer": + case "long": + case "Long": + return "Integer"; + case "float": + case "Float": + case "double": + case "Double": + return "Number"; + case "boolean": + case "Boolean": + return "Boolean"; + case "char": + case "Character": + case "String": + return "String"; + case "Date": + return "Date"; + default: + return "Field"; + } + } + + /** + * returns the Angular5 type associated with a Java primitive + * + * @param simpleType :{@link String} the type to be parsed + * @return the corresponding Angular type or 'any' otherwise + */ + public String getAngularType(String simpleType) { + + switch (simpleType) { + case "boolean": + return "boolean"; + case "Boolean": + return "boolean"; + case "short": + return "number"; + case "Short": + return "number"; + case "int": + return "number"; + case "Integer": + return "number"; + case "long": + return "number"; + case "Long": + return "number"; + case "float": + return "number"; + case "Float": + return "number"; + case "double": + return "number"; + case "Double": + return "number"; + case "char": + return "string"; + case "Character": + return "string"; + case "String": + return "string"; + case "byte": + return "number"; + default: + return "any"; + } + } + + /** + * returns the class name of the return type of a specific method. + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName {@link String} the name of the method + * @return the class name of the return type of the specified method + * @throws SecurityException If no method of the given name can be found + * @throws NoSuchMethodException If no method of the given name can be found + */ + public String getReturnType(Class pojoClass, String methodName) throws NoSuchMethodException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + String s = "-"; + Method method = findMethod(pojoClass, methodName); + if (method != null && !method.getReturnType().equals(Void.TYPE)) { + s = method.getReturnType().toString(); + s = s.substring(s.lastIndexOf('.') + 1, s.length()); + } + return s; + } + + /** + * + * This methods returns the return type of the method in the given pojoClass which are annotated with the parameter + * annotatedClass + * + * @param pojoClass - The class in which to find if it has methods with annotatedClass + * @param annotatedClassName - The annotation which needs to be found + * @return Return type of the method annotated with the given annotation, else "null" + * @throws ClassNotFoundException if the annotated class name could not be found in the class path + */ + @SuppressWarnings("unchecked") + public String getReturnTypeOfMethodAnnotatedWith(Class pojoClass, String annotatedClassName) + throws ClassNotFoundException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + Method[] methods = pojoClass.getDeclaredMethods(); + for (Method method : methods) { + if (!method.getName().startsWith("get")) { + continue; + } + for (Annotation a : method.getAnnotations()) { + // better if (method.isAnnotationPresent(classObj)) {, but postponed as of different class + // loaders of a.getClass() and pojoClass.getClass() + if (a.getClass().getCanonicalName().equals(annotatedClassName)) { + return method.getReturnType().getSimpleName(); + } + } + } + return "null"; + } + + /** + * returns the HTTP request type corresponding to an annotation type + * + * @param annotations The annotation to get the type name of + * @return the HTTP request type name of the selected annotation + */ + public String getRequestType(Map annotations) { + + if (annotations.containsKey("javax_ws_rs_GET")) { + return "GET"; + } else if (annotations.containsKey("javax_ws_rs_PUT")) { + return "PUT"; + } else if (annotations.containsKey("javax_ws_rs_POST")) { + return "POST"; + } else if (annotations.containsKey("javax_ws_rs_DELETE")) { + return "DELETE"; + } else if (annotations.containsKey("javax_ws_rs_PATCH")) { + return "PATCH"; + } else { + return "-"; + } + } + + /** + * Helper method to find a class's specific method + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName The name of the method to be found + * @return The method object of the method to be found, null if it wasn't found + */ + private Method findMethod(Class pojoClass, String methodName) { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + for (Method m : pojoClass.getMethods()) { + if (m.getName().equals(methodName)) { + return m; + } + } + return null; + } + + /** + * Checks whether the class given by the full qualified name is an enum + * + * @param className full qualified class name + * @return true if the class is an enum, false otherwise + */ + public boolean isEnum(String className) { + + try { + return ClassUtils.getClass(className).isEnum(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return false; + } + } + + /** + * Returns the first enum value of an enum class + * + * @param className full qualified class name + * @return the first enum value name found in order + */ + public String getFirstEnumValue(String className) { + + try { + Class enumClass = ClassUtils.getClass(className); + Field[] declaredFields = enumClass.getDeclaredFields(); + if (declaredFields.length > 0) { + return declaredFields[0].getName(); + } else { + return null; + } + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return null; + } + } +} diff --git a/cobigen-templates/crud-openapi-net/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/OpenApiUtil.java b/cobigen-templates/crud-openapi-net/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/OpenApiUtil.java new file mode 100644 index 0000000000..a93288f45a --- /dev/null +++ b/cobigen-templates/crud-openapi-net/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/OpenApiUtil.java @@ -0,0 +1,255 @@ +package com.devonfw.cobigen.templates.devon4j.utils; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.Instant; +import java.time.LocalDate; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; + +/** Utils on OpenAPI template model */ +public class OpenApiUtil { + + /** + * Prints javax validation constraints from an OpenAPI model + * + * @param constraints OpenAPI model constraints + * @return the list of supported annotations in Java syntax + */ + public String printJavaConstraints(Map constraints) { + + String consts = ""; + if (constraints.get("maximum") != null) { + consts = consts + "@Max(" + constraints.get("maximum") + ")"; + } + if (constraints.get("minimum") != null) { + consts = consts + "@Min(" + constraints.get("minimum") + ")"; + } + if (constraints.get("maxLength") != null) { + consts = consts + "@Size(max=" + constraints.get("maxLength"); + if (constraints.get("minLength") != null) { + consts = consts + ", min=" + constraints.get("minLength"); + } + consts = consts + ")"; + } else { + if (constraints.get("minLength") != null) { + consts = consts + "@Size(min=" + constraints.get("minLength"); + if (constraints.get("maxLength") != null) { + consts = consts + ", max=" + constraints.get("maxLength"); + } + consts = consts + ")"; + } + } + return consts; + } + + /** + * Prints the return type of a response based on the OpenAPI model + * + * @param response the OpenAPI model of a response + * @return simple type of the response return type in Java syntax + */ + public String printJavaServiceResponseReturnType(Map response) { + + String returnType = toJavaType(response, false); + if ((boolean) response.get("isVoid")) { + return "void"; + } + if ((boolean) response.get("isArray")) { + if ((boolean) response.get("isEntity")) { + if (returnType.contains("Entity")) { + return "List<" + returnType + ">"; + } else { + return "List<" + returnType + "Entity>"; + } + } else { + return "List<" + returnType + ">"; + } + } else if ((boolean) response.get("isPaginated")) { + if ((boolean) response.get("isEntity")) { + if (returnType.contains("Entity")) { + return "Page<" + returnType + ">"; + } else { + return "Page<" + returnType + "Entity>"; + } + } else { + return "Page<" + returnType + ">"; + } + } else { + return returnType; + } + } + + /** + * Returns the Java type corresponding to the OpenAPI type definition. If the type could not be matched, Object will + * be returned. + * + * @param parameter OpenAPI model of a parameter + * @param simpleType if a Java simple type should be returned if possible + * @return the Java type + */ + public String toJavaType(Map parameter, boolean simpleType) { + + String typeConverted = null; + String format = (String) parameter.get("format"); + String type = (String) parameter.get("type"); + boolean isCollection = false; + if (parameter.get("isCollection") != null) { + isCollection = (boolean) parameter.get("isCollection"); + } + + boolean isEntity = (boolean) parameter.get("isEntity"); + + if (type != null) { + switch (type.toLowerCase()) { + case "integer": + if (format != null) { + switch (format) { + case "int32": + typeConverted = simpleType ? "int" : "Integer"; + break; + case "int64": + typeConverted = simpleType ? "long" : "Long"; + break; + default: + typeConverted = BigInteger.class.getSimpleName(); + break; + } + } else { + typeConverted = BigInteger.class.getSimpleName(); + } + break; + case "number": + if (format != null) { + switch (format) { + case "float": + typeConverted = simpleType ? "float" : "Float"; + break; + case "double": + typeConverted = simpleType ? "double" : "Double"; + break; + default: + typeConverted = BigDecimal.class.getSimpleName(); + break; + } + } else { + typeConverted = BigDecimal.class.getSimpleName(); + } + break; + case "string": + if (format != null) { + switch (format) { + case "date": + typeConverted = LocalDate.class.getSimpleName(); + break; + case "date-time": + typeConverted = Instant.class.getSimpleName(); + break; + case "binary": + typeConverted = simpleType ? "float" : "Float"; + break; + case "email": + case "password": + typeConverted = String.class.getSimpleName(); + break; + default: + typeConverted = "String"; + break; + } + } else { + typeConverted = "String"; + } + break; + case "boolean": + typeConverted = simpleType ? "boolean" : "Boolean"; + break; + default: + typeConverted = "void"; + break; + } + } else { + typeConverted = "void"; + } + + if (isCollection) { + if (isEntity) { + return "List<" + parameter.get("type") + ">"; + } else { + return "List<" + typeConverted + ">"; + } + } else if (isEntity) { + return (String) parameter.get("type"); + } + return typeConverted; + } + + /** + * Returns the TypeScript type corresponding to the OpenAPI type definition. If the type could not be matched, the + * same value will be returned. + * + * @param parameter OpenAPI model of a parameter + * @return the Java type + */ + // getOaspTypeFromOpenAPI + public String toTypeScriptType(Map parameter) { + + String typeConverted = null; + String type = (String) parameter.get("type"); + boolean isCollection = false; + if (parameter.get("isCollection") != null) { + isCollection = (boolean) parameter.get("isCollection"); + } + + boolean isEntity = (boolean) parameter.get("isEntity"); + + if (type != null) { + switch (type.toLowerCase()) { + case "integer": + typeConverted = "number"; + break; + default: + typeConverted = type; + break; + } + } else { + typeConverted = "undefined"; + } + + if (isCollection) { + if (isEntity) { + return parameter.get("type") + "[]"; + } else { + return typeConverted + "[]"; + } + } else if (isEntity) { + return (String) parameter.get("type"); + } + return typeConverted; + } + + /** + * Prints the service operation name based on the operationId or generates one based on the servicePath while printing + * a comment how to get better service names. + * + * @param operation operation Model of the OpenAPI model + * @param servicePath service path of the + * @return the service method name + */ + public String printServiceOperationName(Map operation, String servicePath) { + + String operationId = (String) operation.get("operationId"); + if (StringUtils.isEmpty(operationId)) { + String[] split = servicePath.split("/"); + String lastSegment; + if (!split[split.length - 1].isEmpty()) { + lastSegment = split[split.length - 1]; + } else { + lastSegment = split[split.length - 2]; + } + return ((String) operation.get("type")) + lastSegment; + } else { + return operationId; + } + } +} diff --git a/cobigen-templates/kafka-documentation/pom.xml b/cobigen-templates/kafka-documentation/pom.xml index 9e494624e3..fc496c637a 100644 --- a/cobigen-templates/kafka-documentation/pom.xml +++ b/cobigen-templates/kafka-documentation/pom.xml @@ -10,4 +10,15 @@ ${revision} + + + javax.ws.rs + javax.ws.rs-api + 2.0 + + + com.fasterxml.jackson.core + jackson-databind + + \ No newline at end of file diff --git a/cobigen-templates/kafka-documentation/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/documentation/JavaDocumentationUtil.java b/cobigen-templates/kafka-documentation/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/documentation/JavaDocumentationUtil.java new file mode 100644 index 0000000000..8832368645 --- /dev/null +++ b/cobigen-templates/kafka-documentation/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/documentation/JavaDocumentationUtil.java @@ -0,0 +1,346 @@ +package com.devonfw.cobigen.templates.devon4j.utils.documentation; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +/** + * + */ +public class JavaDocumentationUtil { + + /** Full qualified name of spring RequestMapping annotation */ + private final String requestMapping = "org_springframework_web_bind_annotation_RequestMapping"; + + /** Full qualified name of javax Path annotation */ + private final String javaxPath = "javax_ws_rs_Path"; + + /** + * Creates a list of parameters of a specific method as an asciidoc string, including its name, type, description, + * constraints + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName {@link String} the name of the method to get the parameter info of + * @param javaDoc the javadoc of the method, taken from the javaplugin model + * @return A list of the parameters info in asciidoc code + * @throws Exception When errors occur in invoking an annotations method + */ + public String getParams(Class pojoClass, String methodName, Map javaDoc) throws Exception { + + String result = ""; + Method m = findMethod(pojoClass, methodName); + if (m.getParameterCount() < 1) { + return "!-!-!-!-"; + } + if (m.getParameterCount() == 1) { + if (!m.getParameters()[0].getType().isPrimitive()) { + return "!-!-!-!-"; + } + } + for (Parameter param : m.getParameters()) { + // Add the name of the parameter as path or query parameter + boolean isPath = param.isAnnotationPresent(javax.ws.rs.PathParam.class); + boolean isQuery = param.isAnnotationPresent(javax.ws.rs.QueryParam.class); + if (isPath || isQuery) { + result += "!"; + if (isPath) { + result += "{" + param.getAnnotation(javax.ws.rs.PathParam.class).value() + "}" + System.lineSeparator(); + } else if (isQuery) { + result += "?" + param.getAnnotation(javax.ws.rs.QueryParam.class).value() + System.lineSeparator(); + } + + // Add the type + String type = param.getType().getSimpleName(); + result += "!" + type + System.lineSeparator(); + + // Add the constraints + result += "!"; + int counter = 0; + for (Annotation anno : param.getAnnotations()) { + String annoName = anno.annotationType().getName(); + Pattern p = Pattern.compile("javax\\.validation\\.constraints\\.([^\\.]*)"); + Matcher match = p.matcher(annoName); + if (match.find()) { + counter++; + String shortName = annoName.substring(annoName.lastIndexOf('.') + 1); + Object value; + Method method; + try { + method = anno.getClass().getMethod("value"); + value = method.invoke(anno); + result += shortName + " = " + value + " +" + System.lineSeparator(); + } catch (NoSuchMethodException e) { + result += shortName + " +" + System.lineSeparator(); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException + | SecurityException e) { + throw new Exception(e.getMessage()); + } + } + } + if (counter == 0) { + result += "-" + System.lineSeparator(); + } + + // Add Javadoc + Map params = (Map) javaDoc.get("params"); + result += "!" + getJavaDocWithoutLink(params.get(param.getName())) + " +" + System.lineSeparator(); + } + } + return result; + } + + /** + * returns the javadoc of an element, stripped of any links to other sources + * + * @param doc the javadoc to be changed + * @return the input string stripped of all links + */ + public String getJavaDocWithoutLink(String doc) { + + Pattern p = Pattern.compile("(\\{@link ([^\\}]*)\\})"); + Matcher m = p.matcher(doc); + while (m.find()) { + doc = doc.replace(m.group(1), m.group(2)); + } + return doc; + } + + /** + * Create a response in JSON format, iterating through non-primitive types to get their data as well + * + * @param pojoClass The input class + * @param methodName The name of the operation to get the response of + * @return A JSON representation of the response object + * @throws Exception When Jackson fails + */ + public String getJSONResponseBody(Class pojoClass, String methodName) throws Exception { + + Class responseType = findMethod(pojoClass, methodName).getReturnType(); + if (hasBody(pojoClass, methodName, true)) { + return getJSON(responseType); + } + return "-"; + } + + /** + * Create a request in JSON format, iterating through non-primitive types to get their data as well + * + * @param pojoClass The input class + * @param methodName The name of the operation to get the request of + * @return A JSON representation of the request object + * @throws Exception When Jackson fails + */ + public String getJSONRequestBody(Class pojoClass, String methodName) throws Exception { + + Method m = findMethod(pojoClass, methodName); + if (hasBody(pojoClass, methodName, false)) { + Parameter param = m.getParameters()[0]; + Class requestType = param.getType(); + return getJSON(requestType); + } + return "-"; + } + + /** + * Using Jackson, creates a JSON string for Asciidoc + * + * @param clazz The class to create the JSON string of + * @return A JSON representation of the given class + * @throws Exception When Jackson fails + */ + public String getJSON(Class clazz) throws Exception { + + ObjectMapper mapper = new ObjectMapper(); + mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY); + mapper.enable(SerializationFeature.INDENT_OUTPUT); + try { + Object obj = clazz.newInstance(); + return "...." + System.lineSeparator() + mapper.writeValueAsString(obj) + System.lineSeparator() + "...."; + } catch (InstantiationException | IllegalAccessException | JsonProcessingException e) { + throw new Exception(e.getMessage()); + } + } + + /** + * Checks if a request/response has a body + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName The name of the operation to be checked + * @param isResponse true if the response of the operation should be checked, false if the request should be checked + * @return true if the response/request has a body, false if not + * @throws SecurityException If no method of the given name can be found + */ + public boolean hasBody(Class pojoClass, String methodName, boolean isResponse) throws SecurityException { + + Method m = findMethod(pojoClass, methodName); + if (isResponse) { + Class returnType = m.getReturnType(); + if (!returnType.isPrimitive() && !returnType.equals(Void.TYPE)) { + return true; + } + } else { + int nr = 0; + int position = 0; + for (Parameter param : m.getParameters()) { + if (!param.isAnnotationPresent(javax.ws.rs.PathParam.class) + && !param.isAnnotationPresent(javax.ws.rs.QueryParam.class)) { + nr++; + position = Integer.parseInt(param.getName().replace("arg", "")); + } + } + if (nr == 1) { + Parameter param = m.getParameters()[position]; + Class requestType = param.getType(); + if (!requestType.isPrimitive() && !requestType.equals(Void.TYPE) + && !param.isAnnotationPresent(javax.ws.rs.PathParam.class) + && !param.isAnnotationPresent(javax.ws.rs.QueryParam.class)) { + return true; + } + } + } + return false; + } + + /** + * returns the HTTP request type corresponding to an annotation type + * + * @param annotations The annotation to get the type name of + * @return the HTTP request type name of the selected annotation + */ + public String getRequestType(Map annotations) { + + String type = ""; + if (annotations.containsKey(this.requestMapping)) { + Map method = (Map) annotations.get(this.requestMapping); + String rm = (String) method.get("method"); + type = rm.toLowerCase(); + } + if (annotations.containsKey("javax_ws_rs_GET") || type.equals("requestmethod.get")) { + return "GET"; + } else if (annotations.containsKey("javax_ws_rs_PUT") || type.equals("requestmethod.put")) { + return "PUT"; + } else if (annotations.containsKey("javax_ws_rs_POST") || type.equals("requestmethod.post")) { + return "POST"; + } else if (annotations.containsKey("javax_ws_rs_DELETE") || type.equals("requestmethod.delete")) { + return "DELETE"; + } else if (annotations.containsKey("javax_ws_rs_PATCH") || type.equals("requestmethod.patch")) { + return "PATCH"; + } else { + return "-"; + } + } + + /** + * Gets the path of an operation + * + * @param pojoAnnotations the annotation map of the given pojo + * @param method The method to get the operation path of + * @return The path of an operation + * @throws IOException If no application_properties file is found + */ + public String getOperationPath(Map pojoAnnotations, Map method) throws IOException { + + String path = getPath(pojoAnnotations); + Map pathAnno = new HashMap<>(); + if (pojoAnnotations.containsKey(this.javaxPath)) { + pathAnno = (Map) pojoAnnotations.get(this.javaxPath); + } else if (pojoAnnotations.containsKey(this.requestMapping)) { + pathAnno = (Map) pojoAnnotations.get(this.requestMapping); + } + if (pathAnno.containsKey("value")) { + String toAdd = (String) pathAnno.get("value"); + if (toAdd.startsWith("/") && path.endsWith("/")) { + path += toAdd.substring(1); + } + } + return path; + } + + /** + * Gets the path of a component + * + * @param pojoAnnotations the annotation map of the given pojo + * @return The communal path of a component + * @throws IOException If no application_properties file is found + */ + public String getPath(Map pojoAnnotations) throws IOException { + + String path = extractRootPath(); + Map pathAnno = new HashMap<>(); + if (pojoAnnotations.containsKey(this.javaxPath)) { + pathAnno = (Map) pojoAnnotations.get(this.javaxPath); + } else if (pojoAnnotations.containsKey(this.requestMapping)) { + pathAnno = (Map) pojoAnnotations.get(this.requestMapping); + } + if (pathAnno.containsKey("value")) { + String toAdd = (String) pathAnno.get("value"); + if (toAdd.startsWith("/") && path.endsWith(":")) { + path += toAdd.substring(1); + } + } + return path; + } + + /** + * Checks the class path for an application.properties file and extracts the port and path from it + * + * @return The root path of the application + * @throws IOException If no application.properties file could be found + */ + private String extractRootPath() throws IOException { + + Class clazz = this.getClass(); + String t = ""; + StringBuilder sb = new StringBuilder("http://localhost:"); + try (InputStream in = clazz.getClassLoader().getResourceAsStream("application.properties")) { + if (in != null) { + try (InputStreamReader reader = new InputStreamReader(in); BufferedReader br = new BufferedReader(reader)) { + while ((t = br.readLine()) != null) { + if (t.matches("server\\.port=(\\d{0,5})") || t.matches("server\\.context-path=([^\\s]*)")) { + sb.append(t.substring(t.indexOf('=') + 1)); + } + } + return sb.toString(); + } + } else { + return ""; + } + } + } + + /** + * Helper method to find a class's specific method + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName The name of the method to be found + * @return The method object of the method to be found, null if it wasn't found + */ + private Method findMethod(Class pojoClass, String methodName) { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + for (Method m : pojoClass.getMethods()) { + if (m.getName().equals(methodName)) { + return m; + } + } + return null; + } +} diff --git a/cobigen-templates/openapi-documentation/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/documentation/DocumentationUtil.java b/cobigen-templates/openapi-documentation/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/documentation/DocumentationUtil.java new file mode 100644 index 0000000000..4eda4c9d8e --- /dev/null +++ b/cobigen-templates/openapi-documentation/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/documentation/DocumentationUtil.java @@ -0,0 +1,32 @@ +package com.devonfw.cobigen.templates.devon4j.utils.documentation; + +/** + * + */ +public class DocumentationUtil { + + /** + * Creates asciidoc code for colour coded HTTP request types + * + * @param type the HTTP request type to be coloured + * @return the asciidoc code for differently coloured request types, aqua for get, lime for post, red for delete, + * yellow for put and fuchsia for patch (HTML colour names) + */ + public String getTypeWithAsciidocColour(String type) { + + switch (type.toLowerCase()) { + case "get": + return "[aqua]#" + type.toUpperCase() + "#"; + case "post": + return "[lime]#" + type.toUpperCase() + "#"; + case "delete": + return "[red]#" + type.toUpperCase() + "#"; + case "put": + return "[yellow]#" + type.toUpperCase() + "#"; + case "patch": + return "[fuchsia]#" + type.toUpperCase() + "#"; + default: + return ""; + } + } +} diff --git a/cobigen-templates/openapi-documentation/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/documentation/OpenApiDocumentationUtil.java b/cobigen-templates/openapi-documentation/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/documentation/OpenApiDocumentationUtil.java new file mode 100644 index 0000000000..f7d9472b71 --- /dev/null +++ b/cobigen-templates/openapi-documentation/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/documentation/OpenApiDocumentationUtil.java @@ -0,0 +1,74 @@ +package com.devonfw.cobigen.templates.devon4j.utils.documentation; + +import java.util.Map; + +/** + * + */ +public class OpenApiDocumentationUtil { + + /** + * Adds indicators to a parameter name based on them being a ?query or a {path} parameter + * + * @param param the parameter to add indicators too + * @return The name of the given parameter with the prefix ? or surrounded by {} + */ + public String getParam(Map param) { + + String result = ""; + String type = (String) param.get("type"); + if (type == null || type.equals("void")) { + result = "void"; + } else { + if ((boolean) param.get("inPath")) { + result += "{" + type + "}"; + } else if ((boolean) param.get("inQuery")) { + result += "?" + type; + } + } + return result; + } + + /** + * Lists the constraints of an operations parameter as asciidoc code + * + * @param param The parameter which constraints should be listed + * @return The list of constraints as asciidoc code + */ + public String getConstraintList(Map param) { + + String result = ""; + Map constraints = (Map) param.get("constraints"); + boolean required = false; + int nrParam = 0; + if (constraints != null) { + if (constraints.containsKey("notNull")) { + required = (boolean) constraints.get("notNull"); + } + if (required) { + result += "[red]#__Required__# +" + System.lineSeparator(); + nrParam++; + } + for (String key : constraints.keySet()) { + if (!key.equals("notNull")) { + Object val = constraints.get(key); + if (val != null) { + if (val instanceof Boolean) { + if ((boolean) val) { + result += key + " +" + System.lineSeparator(); + nrParam++; + } + } else { + result += key + " = " + val + " +" + System.lineSeparator(); + nrParam++; + } + } + } + } + } + if (nrParam == 0) { + result = "-"; + } + return result; + } +} diff --git a/cobigen-templates/pom.xml b/cobigen-templates/pom.xml index 7b34bdad75..24172edfc1 100644 --- a/cobigen-templates/pom.xml +++ b/cobigen-templates/pom.xml @@ -14,7 +14,10 @@ ../pom.xml - + + 1.8 + 1.8 + templates-devon4j-utils @@ -54,6 +57,10 @@ src/main/resources src/main/templates + + src/main/java + src/main/java + @@ -87,6 +94,13 @@ org.apache.maven.plugins maven-antrun-plugin + + + ant-contrib + ant-contrib + 20020829 + + prepare-package @@ -96,6 +110,29 @@ Integrate flattened POM manually + + + + + + + + + + + + + + + + + + + + </project> + + diff --git a/cobigen-templates/pom_patch.xml b/cobigen-templates/pom_patch.xml new file mode 100644 index 0000000000..1dc364a9cb --- /dev/null +++ b/cobigen-templates/pom_patch.xml @@ -0,0 +1,4 @@ + + {maven.compiler.source} + {maven.compiler.source} + diff --git a/cobigen-templates/rest-documentation/pom.xml b/cobigen-templates/rest-documentation/pom.xml index f4b3382438..6d2c790050 100644 --- a/cobigen-templates/rest-documentation/pom.xml +++ b/cobigen-templates/rest-documentation/pom.xml @@ -10,4 +10,15 @@ ${revision} + + + javax.ws.rs + javax.ws.rs-api + 2.0 + + + com.fasterxml.jackson.core + jackson-databind + + \ No newline at end of file diff --git a/cobigen-templates/rest-documentation/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/documentation/JavaDocumentationUtil.java b/cobigen-templates/rest-documentation/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/documentation/JavaDocumentationUtil.java new file mode 100644 index 0000000000..8832368645 --- /dev/null +++ b/cobigen-templates/rest-documentation/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/documentation/JavaDocumentationUtil.java @@ -0,0 +1,346 @@ +package com.devonfw.cobigen.templates.devon4j.utils.documentation; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +/** + * + */ +public class JavaDocumentationUtil { + + /** Full qualified name of spring RequestMapping annotation */ + private final String requestMapping = "org_springframework_web_bind_annotation_RequestMapping"; + + /** Full qualified name of javax Path annotation */ + private final String javaxPath = "javax_ws_rs_Path"; + + /** + * Creates a list of parameters of a specific method as an asciidoc string, including its name, type, description, + * constraints + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName {@link String} the name of the method to get the parameter info of + * @param javaDoc the javadoc of the method, taken from the javaplugin model + * @return A list of the parameters info in asciidoc code + * @throws Exception When errors occur in invoking an annotations method + */ + public String getParams(Class pojoClass, String methodName, Map javaDoc) throws Exception { + + String result = ""; + Method m = findMethod(pojoClass, methodName); + if (m.getParameterCount() < 1) { + return "!-!-!-!-"; + } + if (m.getParameterCount() == 1) { + if (!m.getParameters()[0].getType().isPrimitive()) { + return "!-!-!-!-"; + } + } + for (Parameter param : m.getParameters()) { + // Add the name of the parameter as path or query parameter + boolean isPath = param.isAnnotationPresent(javax.ws.rs.PathParam.class); + boolean isQuery = param.isAnnotationPresent(javax.ws.rs.QueryParam.class); + if (isPath || isQuery) { + result += "!"; + if (isPath) { + result += "{" + param.getAnnotation(javax.ws.rs.PathParam.class).value() + "}" + System.lineSeparator(); + } else if (isQuery) { + result += "?" + param.getAnnotation(javax.ws.rs.QueryParam.class).value() + System.lineSeparator(); + } + + // Add the type + String type = param.getType().getSimpleName(); + result += "!" + type + System.lineSeparator(); + + // Add the constraints + result += "!"; + int counter = 0; + for (Annotation anno : param.getAnnotations()) { + String annoName = anno.annotationType().getName(); + Pattern p = Pattern.compile("javax\\.validation\\.constraints\\.([^\\.]*)"); + Matcher match = p.matcher(annoName); + if (match.find()) { + counter++; + String shortName = annoName.substring(annoName.lastIndexOf('.') + 1); + Object value; + Method method; + try { + method = anno.getClass().getMethod("value"); + value = method.invoke(anno); + result += shortName + " = " + value + " +" + System.lineSeparator(); + } catch (NoSuchMethodException e) { + result += shortName + " +" + System.lineSeparator(); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException + | SecurityException e) { + throw new Exception(e.getMessage()); + } + } + } + if (counter == 0) { + result += "-" + System.lineSeparator(); + } + + // Add Javadoc + Map params = (Map) javaDoc.get("params"); + result += "!" + getJavaDocWithoutLink(params.get(param.getName())) + " +" + System.lineSeparator(); + } + } + return result; + } + + /** + * returns the javadoc of an element, stripped of any links to other sources + * + * @param doc the javadoc to be changed + * @return the input string stripped of all links + */ + public String getJavaDocWithoutLink(String doc) { + + Pattern p = Pattern.compile("(\\{@link ([^\\}]*)\\})"); + Matcher m = p.matcher(doc); + while (m.find()) { + doc = doc.replace(m.group(1), m.group(2)); + } + return doc; + } + + /** + * Create a response in JSON format, iterating through non-primitive types to get their data as well + * + * @param pojoClass The input class + * @param methodName The name of the operation to get the response of + * @return A JSON representation of the response object + * @throws Exception When Jackson fails + */ + public String getJSONResponseBody(Class pojoClass, String methodName) throws Exception { + + Class responseType = findMethod(pojoClass, methodName).getReturnType(); + if (hasBody(pojoClass, methodName, true)) { + return getJSON(responseType); + } + return "-"; + } + + /** + * Create a request in JSON format, iterating through non-primitive types to get their data as well + * + * @param pojoClass The input class + * @param methodName The name of the operation to get the request of + * @return A JSON representation of the request object + * @throws Exception When Jackson fails + */ + public String getJSONRequestBody(Class pojoClass, String methodName) throws Exception { + + Method m = findMethod(pojoClass, methodName); + if (hasBody(pojoClass, methodName, false)) { + Parameter param = m.getParameters()[0]; + Class requestType = param.getType(); + return getJSON(requestType); + } + return "-"; + } + + /** + * Using Jackson, creates a JSON string for Asciidoc + * + * @param clazz The class to create the JSON string of + * @return A JSON representation of the given class + * @throws Exception When Jackson fails + */ + public String getJSON(Class clazz) throws Exception { + + ObjectMapper mapper = new ObjectMapper(); + mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY); + mapper.enable(SerializationFeature.INDENT_OUTPUT); + try { + Object obj = clazz.newInstance(); + return "...." + System.lineSeparator() + mapper.writeValueAsString(obj) + System.lineSeparator() + "...."; + } catch (InstantiationException | IllegalAccessException | JsonProcessingException e) { + throw new Exception(e.getMessage()); + } + } + + /** + * Checks if a request/response has a body + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName The name of the operation to be checked + * @param isResponse true if the response of the operation should be checked, false if the request should be checked + * @return true if the response/request has a body, false if not + * @throws SecurityException If no method of the given name can be found + */ + public boolean hasBody(Class pojoClass, String methodName, boolean isResponse) throws SecurityException { + + Method m = findMethod(pojoClass, methodName); + if (isResponse) { + Class returnType = m.getReturnType(); + if (!returnType.isPrimitive() && !returnType.equals(Void.TYPE)) { + return true; + } + } else { + int nr = 0; + int position = 0; + for (Parameter param : m.getParameters()) { + if (!param.isAnnotationPresent(javax.ws.rs.PathParam.class) + && !param.isAnnotationPresent(javax.ws.rs.QueryParam.class)) { + nr++; + position = Integer.parseInt(param.getName().replace("arg", "")); + } + } + if (nr == 1) { + Parameter param = m.getParameters()[position]; + Class requestType = param.getType(); + if (!requestType.isPrimitive() && !requestType.equals(Void.TYPE) + && !param.isAnnotationPresent(javax.ws.rs.PathParam.class) + && !param.isAnnotationPresent(javax.ws.rs.QueryParam.class)) { + return true; + } + } + } + return false; + } + + /** + * returns the HTTP request type corresponding to an annotation type + * + * @param annotations The annotation to get the type name of + * @return the HTTP request type name of the selected annotation + */ + public String getRequestType(Map annotations) { + + String type = ""; + if (annotations.containsKey(this.requestMapping)) { + Map method = (Map) annotations.get(this.requestMapping); + String rm = (String) method.get("method"); + type = rm.toLowerCase(); + } + if (annotations.containsKey("javax_ws_rs_GET") || type.equals("requestmethod.get")) { + return "GET"; + } else if (annotations.containsKey("javax_ws_rs_PUT") || type.equals("requestmethod.put")) { + return "PUT"; + } else if (annotations.containsKey("javax_ws_rs_POST") || type.equals("requestmethod.post")) { + return "POST"; + } else if (annotations.containsKey("javax_ws_rs_DELETE") || type.equals("requestmethod.delete")) { + return "DELETE"; + } else if (annotations.containsKey("javax_ws_rs_PATCH") || type.equals("requestmethod.patch")) { + return "PATCH"; + } else { + return "-"; + } + } + + /** + * Gets the path of an operation + * + * @param pojoAnnotations the annotation map of the given pojo + * @param method The method to get the operation path of + * @return The path of an operation + * @throws IOException If no application_properties file is found + */ + public String getOperationPath(Map pojoAnnotations, Map method) throws IOException { + + String path = getPath(pojoAnnotations); + Map pathAnno = new HashMap<>(); + if (pojoAnnotations.containsKey(this.javaxPath)) { + pathAnno = (Map) pojoAnnotations.get(this.javaxPath); + } else if (pojoAnnotations.containsKey(this.requestMapping)) { + pathAnno = (Map) pojoAnnotations.get(this.requestMapping); + } + if (pathAnno.containsKey("value")) { + String toAdd = (String) pathAnno.get("value"); + if (toAdd.startsWith("/") && path.endsWith("/")) { + path += toAdd.substring(1); + } + } + return path; + } + + /** + * Gets the path of a component + * + * @param pojoAnnotations the annotation map of the given pojo + * @return The communal path of a component + * @throws IOException If no application_properties file is found + */ + public String getPath(Map pojoAnnotations) throws IOException { + + String path = extractRootPath(); + Map pathAnno = new HashMap<>(); + if (pojoAnnotations.containsKey(this.javaxPath)) { + pathAnno = (Map) pojoAnnotations.get(this.javaxPath); + } else if (pojoAnnotations.containsKey(this.requestMapping)) { + pathAnno = (Map) pojoAnnotations.get(this.requestMapping); + } + if (pathAnno.containsKey("value")) { + String toAdd = (String) pathAnno.get("value"); + if (toAdd.startsWith("/") && path.endsWith(":")) { + path += toAdd.substring(1); + } + } + return path; + } + + /** + * Checks the class path for an application.properties file and extracts the port and path from it + * + * @return The root path of the application + * @throws IOException If no application.properties file could be found + */ + private String extractRootPath() throws IOException { + + Class clazz = this.getClass(); + String t = ""; + StringBuilder sb = new StringBuilder("http://localhost:"); + try (InputStream in = clazz.getClassLoader().getResourceAsStream("application.properties")) { + if (in != null) { + try (InputStreamReader reader = new InputStreamReader(in); BufferedReader br = new BufferedReader(reader)) { + while ((t = br.readLine()) != null) { + if (t.matches("server\\.port=(\\d{0,5})") || t.matches("server\\.context-path=([^\\s]*)")) { + sb.append(t.substring(t.indexOf('=') + 1)); + } + } + return sb.toString(); + } + } else { + return ""; + } + } + } + + /** + * Helper method to find a class's specific method + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName The name of the method to be found + * @return The method object of the method to be found, null if it wasn't found + */ + private Method findMethod(Class pojoClass, String methodName) { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + for (Method m : pojoClass.getMethods()) { + if (m.getName().equals(methodName)) { + return m; + } + } + return null; + } +} diff --git a/cobigen-templates/templates-devon4j-tests/src/test/java/com/devonfw/cobigen/templates/devon4j/test/templates/TemplatesGenerationTest.java b/cobigen-templates/templates-devon4j-tests/src/test/java/com/devonfw/cobigen/templates/devon4j/test/templates/TemplatesGenerationTest.java index 62734aa1fe..c5ef518ff1 100644 --- a/cobigen-templates/templates-devon4j-tests/src/test/java/com/devonfw/cobigen/templates/devon4j/test/templates/TemplatesGenerationTest.java +++ b/cobigen-templates/templates-devon4j-tests/src/test/java/com/devonfw/cobigen/templates/devon4j/test/templates/TemplatesGenerationTest.java @@ -5,6 +5,8 @@ import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; import java.util.stream.Stream; import org.apache.commons.io.FileUtils; @@ -64,37 +66,40 @@ public static void setupDevTemplates() throws URISyntaxException, IOException { FileUtils.copyDirectory(templatesProject.toFile(), templateSetsAdaptedFolder.toFile()); + List devTemplateSets = new ArrayList<>(); try (Stream files = Files.list(templateSetsAdaptedFolder)) { files.forEach(path -> { - if (Files.isDirectory(path)) { - Path resourcesFolder = path.resolve("src/main/resources"); - Path templatesFolder = path.resolve(ConfigurationConstants.TEMPLATE_RESOURCE_FOLDER); - if (Files.exists(resourcesFolder) && !Files.exists(templatesFolder)) { - try { - Files.move(resourcesFolder, templatesFolder); - } catch (IOException e) { - e.printStackTrace(); - } + devTemplateSets.add(path); + }); + } + + for (Path path : devTemplateSets) { + if (Files.isDirectory(path)) { + Path resourcesFolder = path.resolve("src/main/resources"); + Path templatesFolder = path.resolve(ConfigurationConstants.TEMPLATE_RESOURCE_FOLDER); + if (Files.exists(resourcesFolder) && !Files.exists(templatesFolder)) { + try { + Files.move(resourcesFolder, templatesFolder); + } catch (IOException e) { + throw new IOException("Error moving directory " + resourcesFolder, e); } } - // Replace the pom.xml in the utils project. Needed so that the utils project in the temp directory is build + // Replace the pom.xml in the template sets. Needed so that the project in the temp directory is build // properly - if (path.getFileName().toString().equals("templates-devon4j-utils")) { - if (Files.exists(path.resolve("pom.xml"))) { - try { - Files.delete(path.resolve("pom.xml")); - } catch (IOException e) { - e.printStackTrace(); - } + if (Files.exists(path.resolve("pom.xml"))) { + try { + Files.delete(path.resolve("pom.xml")); + } catch (IOException e) { + throw new IOException("Error deleting file " + path.resolve("pom.xml"), e); } try { Files.copy(utilsPom, path.resolve("pom.xml")); } catch (IOException e) { - e.printStackTrace(); + throw new IOException("Error copying file " + utilsPom, e); } } - }); + } } } } diff --git a/cobigen-templates/testdata-builder/pom.xml b/cobigen-templates/testdata-builder/pom.xml index 4834e41433..a54f9980ee 100644 --- a/cobigen-templates/testdata-builder/pom.xml +++ b/cobigen-templates/testdata-builder/pom.xml @@ -10,4 +10,10 @@ ${revision} + + + org.apache.commons + commons-lang3 + + \ No newline at end of file diff --git a/cobigen-templates/testdata-builder/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java b/cobigen-templates/testdata-builder/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java new file mode 100644 index 0000000000..2d4025071d --- /dev/null +++ b/cobigen-templates/testdata-builder/src/main/java/com/devonfw/cobigen/templates/devon4j/utils/JavaUtil.java @@ -0,0 +1,464 @@ +package com.devonfw.cobigen.templates.devon4j.utils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + +import org.apache.commons.lang3.ClassUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides type operations, mainly checks and casts for Java Primitives, to be used in the templates + * + */ +public class JavaUtil { + + /** + * Logger for this class + */ + private static final Logger LOG = LoggerFactory.getLogger(JavaUtil.class); + + /** + * The constructor. + */ + public JavaUtil() { + + // Empty for CobiGen to automatically instantiate it + } + + /** + * Returns the Object version of a Java primitive or the input if the input isn't a java primitive + * + * @param simpleType String + * @return the corresponding object wrapper type simple name of the input if the input is the name of a primitive java + * type. The input itself if not. (e.g. "int" results in "Integer") + * @throws ClassNotFoundException should not occur. + */ + public String boxJavaPrimitives(String simpleType) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return ClassUtils.primitiveToWrapper(ClassUtils.getClass(simpleType)).getSimpleName(); + } else { + return simpleType; + } + + } + + /** + * Returns the simple name of the type of a field in the pojoClass. If the type is a java primitive the name of the + * wrapper class is returned + * + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return String. The simple name of the field's type. The simple name of the wrapper class in case of java + * primitives + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String boxJavaPrimitives(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return ClassUtils.primitiveToWrapper(pojoClass.getDeclaredField(fieldName).getType()).getSimpleName(); + } else { + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + throw new IllegalAccessError("Could not find field " + fieldName + " in class " + pojoClass); + } else { + return field.getType().getSimpleName(); + } + } + } + + /** + * Checks if the given type is a Java primitive + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive + */ + public boolean equalsJavaPrimitive(String simpleType) { + + try { + return ClassUtils.getClass(simpleType).isPrimitive(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the given type is a Java primitive or wrapper + * + * @param simpleType the type to be checked + * @return true iff simpleType is a Java primitive or wrapper + */ + public boolean equalsJavaPrimitiveOrWrapper(String simpleType) { + + try { + return ClassUtils.isPrimitiveOrWrapper(ClassUtils.getClass(simpleType)); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + } + + /** + * Checks if the type of the field in the pojo's class is a java primitive + * + * @param pojoClass the {@link Class} object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff the field is a java primitive + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitive(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return field.getType().isPrimitive(); + } + } + + /** + * Checks if the given type is a Java primitive or a Java primitive array + * + * @param simpleType the Type name to be checked + * @return true iff {@link #equalsJavaPrimitive(String)} is true or if simpleType is an array with a primitive + * component + */ + public boolean equalsJavaPrimitiveIncludingArrays(String simpleType) { + + Class klasse; + + try { + klasse = ClassUtils.getClass(simpleType).getComponentType(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), simpleType); + return false; + } + return equalsJavaPrimitive(simpleType) || (klasse != null && klasse.isPrimitive()); + } + + /** + * Checks if the given field in the pojo class is a java primitive or an array of java primitives + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be checked + * @return true iff {@link #equalsJavaPrimitive(Class, String)} is true or the field is an array of primitives + * @throws NoSuchFieldException indicating something awfully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean equalsJavaPrimitiveIncludingArrays(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + return equalsJavaPrimitive(pojoClass, fieldName) || (pojoClass.getDeclaredField(fieldName).getType().isArray() + && pojoClass.getDeclaredField(fieldName).getType().getComponentType().isPrimitive()); + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param simpleType Java Type + * @param varName Variable name + * @return String either of the form '((Java Primitive Object Type)varName)' if simpleType is a primitive or the empty + * String otherwise + * @throws ClassNotFoundException should not occur + */ + public String castJavaPrimitives(String simpleType, String varName) throws ClassNotFoundException { + + if (equalsJavaPrimitive(simpleType)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(simpleType), varName); + } else { + return ""; + } + } + + /** + * Returns a cast statement for a given (java primitive, variable name) pair or nothing if the type isn't a java + * primitive + * + * @param pojoClass the class object of the pojo + * @param fieldName the name of the field to be casted + * @return if fieldName points to a primitive field then a casted statement (e.g. for an int field: + * '((Integer)field)') or an empty String otherwise + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public String castJavaPrimitives(Class pojoClass, String fieldName) + throws NoSuchFieldException, SecurityException { + + if (equalsJavaPrimitive(pojoClass, fieldName)) { + return String.format("((%1$s)%2$s)", boxJavaPrimitives(pojoClass, fieldName), fieldName); + } else { + return ""; + } + } + + /** + * @param pojoClass {@link Class} the class object of the pojo + * @param fieldName {@link String} the name of the field + * @return true if the field is an instance of java.utils.Collections + * @throws NoSuchFieldException indicating something awefully wrong in the used model + * @throws SecurityException if the field cannot be accessed. + */ + public boolean isCollection(Class pojoClass, String fieldName) throws NoSuchFieldException, SecurityException { + + if (pojoClass == null) { + return false; + } + + Field field = pojoClass.getDeclaredField(fieldName); + if (field == null) { + field = pojoClass.getField(fieldName); + } + if (field == null) { + return false; + } else { + return Collection.class.isAssignableFrom(field.getType()); + } + + } + + /** + * Returns the Ext Type to a given java type + * + * @param simpleType any java type's simple name + * @return corresponding Ext type + */ + public String getExtType(String simpleType) { + + switch (simpleType) { + case "short": + case "Short": + case "int": + case "Integer": + case "long": + case "Long": + return "Integer"; + case "float": + case "Float": + case "double": + case "Double": + return "Number"; + case "boolean": + case "Boolean": + return "Boolean"; + case "char": + case "Character": + case "String": + return "String"; + case "Date": + return "Date"; + default: + return "Field"; + } + } + + /** + * returns the Angular5 type associated with a Java primitive + * + * @param simpleType :{@link String} the type to be parsed + * @return the corresponding Angular type or 'any' otherwise + */ + public String getAngularType(String simpleType) { + + switch (simpleType) { + case "boolean": + return "boolean"; + case "Boolean": + return "boolean"; + case "short": + return "number"; + case "Short": + return "number"; + case "int": + return "number"; + case "Integer": + return "number"; + case "long": + return "number"; + case "Long": + return "number"; + case "float": + return "number"; + case "Float": + return "number"; + case "double": + return "number"; + case "Double": + return "number"; + case "char": + return "string"; + case "Character": + return "string"; + case "String": + return "string"; + case "byte": + return "number"; + default: + return "any"; + } + } + + /** + * returns the class name of the return type of a specific method. + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName {@link String} the name of the method + * @return the class name of the return type of the specified method + * @throws SecurityException If no method of the given name can be found + * @throws NoSuchMethodException If no method of the given name can be found + */ + public String getReturnType(Class pojoClass, String methodName) throws NoSuchMethodException, SecurityException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + String s = "-"; + Method method = findMethod(pojoClass, methodName); + if (method != null && !method.getReturnType().equals(Void.TYPE)) { + s = method.getReturnType().toString(); + s = s.substring(s.lastIndexOf('.') + 1, s.length()); + } + return s; + } + + /** + * + * This methods returns the return type of the method in the given pojoClass which are annotated with the parameter + * annotatedClass + * + * @param pojoClass - The class in which to find if it has methods with annotatedClass + * @param annotatedClassName - The annotation which needs to be found + * @return Return type of the method annotated with the given annotation, else "null" + * @throws ClassNotFoundException if the annotated class name could not be found in the class path + */ + @SuppressWarnings("unchecked") + public String getReturnTypeOfMethodAnnotatedWith(Class pojoClass, String annotatedClassName) + throws ClassNotFoundException { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + + Method[] methods = pojoClass.getDeclaredMethods(); + for (Method method : methods) { + if (!method.getName().startsWith("get")) { + continue; + } + for (Annotation a : method.getAnnotations()) { + // better if (method.isAnnotationPresent(classObj)) {, but postponed as of different class + // loaders of a.getClass() and pojoClass.getClass() + if (a.getClass().getCanonicalName().equals(annotatedClassName)) { + return method.getReturnType().getSimpleName(); + } + } + } + return "null"; + } + + /** + * returns the HTTP request type corresponding to an annotation type + * + * @param annotations The annotation to get the type name of + * @return the HTTP request type name of the selected annotation + */ + public String getRequestType(Map annotations) { + + if (annotations.containsKey("javax_ws_rs_GET")) { + return "GET"; + } else if (annotations.containsKey("javax_ws_rs_PUT")) { + return "PUT"; + } else if (annotations.containsKey("javax_ws_rs_POST")) { + return "POST"; + } else if (annotations.containsKey("javax_ws_rs_DELETE")) { + return "DELETE"; + } else if (annotations.containsKey("javax_ws_rs_PATCH")) { + return "PATCH"; + } else { + return "-"; + } + } + + /** + * Helper method to find a class's specific method + * + * @param pojoClass {@link Class}<?> the class object of the pojo + * @param methodName The name of the method to be found + * @return The method object of the method to be found, null if it wasn't found + */ + private Method findMethod(Class pojoClass, String methodName) { + + if (pojoClass == null) { + throw new IllegalAccessError( + "Class object is null. Cannot generate template as it might obviously depend on reflection."); + } + for (Method m : pojoClass.getMethods()) { + if (m.getName().equals(methodName)) { + return m; + } + } + return null; + } + + /** + * Checks whether the class given by the full qualified name is an enum + * + * @param className full qualified class name + * @return true if the class is an enum, false otherwise + */ + public boolean isEnum(String className) { + + try { + return ClassUtils.getClass(className).isEnum(); + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return false; + } + } + + /** + * Returns the first enum value of an enum class + * + * @param className full qualified class name + * @return the first enum value name found in order + */ + public String getFirstEnumValue(String className) { + + try { + Class enumClass = ClassUtils.getClass(className); + Field[] declaredFields = enumClass.getDeclaredFields(); + if (declaredFields.length > 0) { + return declaredFields[0].getName(); + } else { + return null; + } + } catch (ClassNotFoundException e) { + LOG.warn("{}: Could not find {}", e.getMessage(), className); + return null; + } + } +} diff --git a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/MavenUtil.java b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/MavenUtil.java index 0b221f9a63..a50d684117 100644 --- a/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/MavenUtil.java +++ b/cobigen/cobigen-core-api/src/main/java/com/devonfw/cobigen/api/util/MavenUtil.java @@ -115,6 +115,9 @@ public static Path createCachedPomFromJar(Path pomFile, Path outputPath) { Path cachedPomXml = outputPath.resolve("cached-pom.xml"); try { + if (Files.exists(cachedPomXml)) { + Files.delete(cachedPomXml); + } Files.copy(pomFile, cachedPomXml); } catch (IOException e) { throw new CobiGenRuntimeException("Unable to extract " + pomFile.toUri() + " from JAR to " + cachedPomXml, e); diff --git a/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/config/ConfigurationHolder.java b/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/config/ConfigurationHolder.java index 619076d0a1..ebf6c2cb54 100644 --- a/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/config/ConfigurationHolder.java +++ b/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/config/ConfigurationHolder.java @@ -1,14 +1,12 @@ package com.devonfw.cobigen.impl.config; -import java.io.File; -import java.io.FilenameFilter; +import java.io.IOException; import java.net.URI; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import com.devonfw.cobigen.api.constants.ConfigurationConstants; import com.devonfw.cobigen.api.exception.InvalidConfigurationException; @@ -34,22 +32,6 @@ public class ConfigurationHolder { /** The OS filesystem path of the configuration */ private URI configurationLocation; - public static String UTILS_REGEX_NAME = "templates-devon4j-utils.*"; - - /** - * Filters the files on a directory so that we can check whether the templates jar are already downloaded - */ - static FilenameFilter utilsFilter = new FilenameFilter() { - - @Override - public boolean accept(File dir, String name) { - - Pattern p = Pattern.compile(UTILS_REGEX_NAME); - Matcher m = p.matcher(name); - return m.find(); - } - }; - /** * Creates a new {@link ConfigurationHolder} which serves as a cache for CobiGen's external configuration. * @@ -97,7 +79,7 @@ public Path getConfigurationPath() { */ public TemplatesConfiguration readTemplatesConfiguration(Trigger trigger) { - Path configRoot = readContextConfiguration().getConfigRootforTrigger(trigger.getId()); + Path configRoot = readContextConfiguration().getConfigLocationforTrigger(trigger.getId(), true); Path templateFolder = Paths.get(trigger.getTemplateFolder()); if (!this.templatesConfigurations.containsKey(trigger.getId())) { TemplatesConfiguration config = new TemplatesConfiguration(configRoot, trigger, this); @@ -139,29 +121,22 @@ public boolean isTemplateSetConfiguration() { * Search for the location of the Java utils * * @return the {@link Path} of the location of the util classes or null if no location was found + * @throws IOException */ - public Path getUtilsLocation() { + public List getUtilsLocation() { + List utilsLocationPaths = new ArrayList<>(); if (isTemplateSetConfiguration()) { - Path adaptedFolder = this.configurationPath.resolve(ConfigurationConstants.ADAPTED_FOLDER); - Path downloadedFolder = this.configurationPath.resolve(ConfigurationConstants.DOWNLOADED_FOLDER); - - String[] utils; - if (Files.exists(adaptedFolder)) { - utils = adaptedFolder.toFile().list(utilsFilter); - if (utils.length > 0) { - return adaptedFolder.resolve(utils[0]); - } - } + List triggers = readContextConfiguration().getTriggers(); - if (Files.exists(downloadedFolder)) { - utils = downloadedFolder.toFile().list(utilsFilter); - if (utils.length > 0) { - return downloadedFolder.resolve(utils[0]); - } + for (Trigger trigger : triggers) { + Path configLocation = readContextConfiguration().getConfigLocationforTrigger(trigger.getId(), false); + utilsLocationPaths.add(configLocation); } - return null; + } else { + utilsLocationPaths.add(Paths.get(this.configurationLocation)); } - return Paths.get(this.configurationLocation); + + return utilsLocationPaths; } } diff --git a/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/config/ContextConfiguration.java b/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/config/ContextConfiguration.java index c5c7aa4585..fe34c1c7f0 100644 --- a/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/config/ContextConfiguration.java +++ b/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/config/ContextConfiguration.java @@ -11,6 +11,7 @@ import com.devonfw.cobigen.impl.config.reader.AbstractContextConfigurationReader; import com.devonfw.cobigen.impl.config.reader.ContextConfigurationReaderFactory; import com.devonfw.cobigen.impl.config.reader.ContextConfigurationSetReader; +import com.devonfw.cobigen.impl.util.FileSystemUtil; /** * The {@link ContextConfiguration} is a configuration data wrapper for all information about templates and the target @@ -105,15 +106,21 @@ public Path getConfigurationPath() { } /** - * @param triggerId the trigger id to get the config root directory for - * @return the {@link Path} of the config root directory of the trigger + * @param triggerId the trigger id to get the config location for + * @param fileSystemDependentPath if true and the configuration is a jar file, the file system dependent path is + * returned + * @return the {@link Path} of the config location of the trigger */ - public Path getConfigRootforTrigger(String triggerId) { + public Path getConfigLocationforTrigger(String triggerId, boolean fileSystemDependentPath) { if (this.contextConfigurationReader instanceof ContextConfigurationSetReader) { - return ((ContextConfigurationSetReader) this.contextConfigurationReader).getConfigRootForTrigger(triggerId); + Path configLocation = ((ContextConfigurationSetReader) this.contextConfigurationReader) + .getConfigLocationForTrigger(triggerId); + if (fileSystemDependentPath && FileSystemUtil.isZipFile(configLocation.toUri())) { + configLocation = FileSystemUtil.createFileSystemDependentPath(configLocation.toUri()); + } + return configLocation; } return this.contextConfigurationReader.getContextRoot(); } - } diff --git a/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/config/reader/ContextConfigurationSetReader.java b/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/config/reader/ContextConfigurationSetReader.java index 9c16535e32..7704c77534 100644 --- a/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/config/reader/ContextConfigurationSetReader.java +++ b/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/config/reader/ContextConfigurationSetReader.java @@ -21,11 +21,11 @@ /** The {@link ContextConfigurationSetReader} reads the context xml */ public class ContextConfigurationSetReader extends AbstractContextConfigurationReader { - /** Map with the paths of the config roots for a context.xml file */ - private Map configRoots = new HashMap<>(); + /** Map with the paths of the config locations for a context.xml file */ + private Map configLocations = new HashMap<>(); - /** Map with the paths of the config roots for a trigger */ - private Map triggerConfigRoots = new HashMap<>(); + /** Map with the paths of the config location for a trigger */ + private Map triggerConfigLocations = new HashMap<>(); /** * The constructor. @@ -111,7 +111,7 @@ private List loadContextFilesDownloaded(Path configRoot) { Path contextFilePath = configurationPath.resolve(ConfigurationConstants.TEMPLATE_RESOURCE_FOLDER) .resolve(ConfigurationConstants.CONTEXT_CONFIG_FILENAME); - addConfigRoot(contextFilePath, configurationPath, contextPaths); + addConfigRoot(contextFilePath, jarPath, contextPaths); } } @@ -123,28 +123,22 @@ public Map loadTriggers() { Map triggers = Maps.newHashMap(); for (Path contextFile : this.contextConfigurations.keySet()) { - Path configRoot = this.configRoots.get(contextFile); ContextConfiguration contextConfiguration = this.contextConfigurations.get(contextFile); - boolean isJarConfig = (configRoot.getParent() == null); + Path configLocation = this.configLocations.get(contextFile); + boolean isJarFile = FileSystemUtil.isZipFile(configLocation.toUri()); List triggerList = contextConfiguration.getTrigger(); if (!triggerList.isEmpty()) { // context configuration in template sets consists of only one trigger com.devonfw.cobigen.impl.config.entity.io.Trigger trigger = triggerList.get(0); - String templateFolder; - if (isJarConfig) { - templateFolder = contextFile.getParent().toString(); - } else { - templateFolder = configRoot.getFileName().resolve(ConfigurationConstants.TEMPLATE_RESOURCE_FOLDER).toString(); - configRoot = configRoot.getParent(); - } - - if (!this.triggerConfigRoots.containsKey(trigger.getId()) || !isJarConfig) { + if (!this.triggerConfigLocations.containsKey(trigger.getId()) || !isJarFile) { // prefer the adapted templates - this.triggerConfigRoots.put(trigger.getId(), configRoot); - triggers.put(trigger.getId(), new Trigger(trigger.getId(), trigger.getType(), templateFolder, - Charset.forName(trigger.getInputCharset()), loadMatchers(trigger), loadContainerMatchers(trigger))); + this.triggerConfigLocations.put(trigger.getId(), configLocation); + + triggers.put(trigger.getId(), + new Trigger(trigger.getId(), trigger.getType(), ConfigurationConstants.TEMPLATE_RESOURCE_FOLDER, + Charset.forName(trigger.getInputCharset()), loadMatchers(trigger), loadContainerMatchers(trigger))); } } } @@ -152,14 +146,14 @@ public Map loadTriggers() { } /** - * Get the configuration root directory for a given trigger + * Get the configuration location for a given trigger. Either a jar file or a folder * * @param triggerId the trigger id to search the config root for - * @return the {@link Path} of the config root for a trigger + * @return the {@link Path} of the config location for a trigger */ - public Path getConfigRootForTrigger(String triggerId) { + public Path getConfigLocationForTrigger(String triggerId) { - return this.triggerConfigRoots.get(triggerId); + return this.triggerConfigLocations.get(triggerId); } /** @@ -174,7 +168,7 @@ private void addConfigRoot(Path contextFilePath, Path configRootPath, List if (Files.exists(contextFilePath)) { contextPaths.add(contextFilePath); - this.configRoots.put(contextFilePath, configRootPath); + this.configLocations.put(contextFilePath, configRootPath); } } } diff --git a/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/generator/GenerationProcessorImpl.java b/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/generator/GenerationProcessorImpl.java index 22e550f59b..4a2029fb50 100644 --- a/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/generator/GenerationProcessorImpl.java +++ b/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/generator/GenerationProcessorImpl.java @@ -11,6 +11,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; +import java.util.ArrayList; import java.util.Collection; import java.util.Formatter; import java.util.List; @@ -262,43 +263,48 @@ private ClassLoader prependTemplatesClassloader(ClassLoader inputProjectClassLoa ClassLoader combinedClassLoader = inputProjectClassLoader != null ? inputProjectClassLoader : Thread.currentThread().getContextClassLoader(); - if (configLocation != null && this.configurationHolder.getUtilsLocation() != null) { - - Path utilsLocation = this.configurationHolder.getUtilsLocation(); - Path pomFile; - if (FileSystemUtil.isZipFile(utilsLocation.toUri())) { - Path utilsPath = FileSystemUtil.createFileSystemDependentPath(utilsLocation.toUri()); - pomFile = utilsPath.resolve("pom.xml"); - } else { - pomFile = utilsLocation.resolve("pom.xml"); - } - + if (configLocation != null && !this.configurationHolder.getUtilsLocation().isEmpty()) { + List utilsLocations = this.configurationHolder.getUtilsLocation(); Path cpCacheFile = null; try { - if (Files.exists(pomFile)) { - LOG.debug("Found templates to be configured by maven."); + List urlList = new ArrayList<>(); + for (Path utilsLocation : utilsLocations) { - String pomFileHash = MavenUtil.generatePomFileHash(pomFile); - - if (this.configurationHolder.isJarConfig()) { - cpCacheFile = configLocation - .resolveSibling(String.format(MavenConstants.CLASSPATH_CACHE_FILE, pomFileHash)); + Path pomFile; + if (FileSystemUtil.isZipFile(utilsLocation.toUri())) { + Path utilsPath = FileSystemUtil.createFileSystemDependentPath(utilsLocation.toUri()); + pomFile = utilsPath.resolve("pom.xml"); } else { - cpCacheFile = configLocation.resolve(String.format(MavenConstants.CLASSPATH_CACHE_FILE, pomFileHash)); + pomFile = utilsLocation.resolve("pom.xml"); } - combinedClassLoader = MavenUtil.addURLsFromCachedClassPathsFile(cpCacheFile, pomFile, combinedClassLoader); - } + if (Files.exists(pomFile)) { + String pomFileHash = MavenUtil.generatePomFileHash(pomFile); - // prepend jar/compiled resources as well - URL[] urls; - if (Files.isDirectory(utilsLocation) && Files.exists(pomFile)) { - compileTemplateUtils(utilsLocation); - urls = new URL[] { utilsLocation.resolve("target").resolve("classes").toUri().toURL() }; - } else { - urls = new URL[] { utilsLocation.toUri().toURL() }; + if (!this.configurationHolder.isTemplateSetConfiguration()) { + if (this.configurationHolder.isJarConfig()) { + cpCacheFile = configLocation + .resolveSibling(String.format(MavenConstants.CLASSPATH_CACHE_FILE, pomFileHash)); + } else { + cpCacheFile = configLocation.resolve(String.format(MavenConstants.CLASSPATH_CACHE_FILE, pomFileHash)); + } + } else { + cpCacheFile = configLocation.resolve(String.format(MavenConstants.CLASSPATH_CACHE_FILE, pomFileHash)); + } + + combinedClassLoader = MavenUtil.addURLsFromCachedClassPathsFile(cpCacheFile, pomFile, combinedClassLoader); + } + + if (Files.isDirectory(utilsLocation) && Files.exists(pomFile)) { + compileTemplateUtils(utilsLocation); + urlList.add(utilsLocation.resolve("target").resolve("classes").toUri().toURL()); + } else { + urlList.add(utilsLocation.toUri().toURL()); + } } + // prepend jar/compiled resources as well + URL[] urls = urlList.toArray(new URL[urlList.size()]); combinedClassLoader = new URLClassLoader(urls, combinedClassLoader); return combinedClassLoader; } catch (MalformedURLException e) { @@ -422,7 +428,7 @@ private void generate(TemplateTo template, TriggerInterpreter triggerInterpreter TextTemplateEngine templateEngine = TemplateEngineRegistry.getEngine(templateEngineName); templateEngine.setTemplateFolder(this.configurationHolder.readContextConfiguration() - .getConfigRootforTrigger(trigger.getId()).resolve(trigger.getTemplateFolder())); + .getConfigLocationforTrigger(trigger.getId(), true).resolve(trigger.getTemplateFolder())); Template templateEty = tConfig.getTemplate(template.getId()); if (templateEty == null) { diff --git a/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/util/ConfigurationClassLoaderUtil.java b/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/util/ConfigurationClassLoaderUtil.java index 408f318251..1d3ffa87ef 100644 --- a/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/util/ConfigurationClassLoaderUtil.java +++ b/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/util/ConfigurationClassLoaderUtil.java @@ -118,14 +118,16 @@ public static List> resolveUtilClasses(ConfigurationHolder configuratio List> result = new LinkedList<>(); List classLoaderUrls = new ArrayList<>(); // stores ClassLoader URLs - Path utilsLocation = configurationHolder.getUtilsLocation(); - if (FileSystemUtil.isZipFile(utilsLocation.toUri())) { - result = resolveFromJar(classLoader, configurationHolder); - } else { - ClassLoader inputClassLoader = null; - classLoaderUrls = addFoldersToClassLoaderUrls(utilsLocation); - inputClassLoader = getUrlClassLoader(classLoaderUrls.toArray(new URL[] {}), classLoader); - result = resolveFromFolder(utilsLocation, inputClassLoader); + List utilsLocations = configurationHolder.getUtilsLocation(); + for (Path utilsLocation : utilsLocations) { + if (FileSystemUtil.isZipFile(utilsLocation.toUri())) { + result.addAll(resolveFromJar(classLoader, utilsLocation)); + } else { + ClassLoader inputClassLoader = null; + classLoaderUrls = addFoldersToClassLoaderUrls(utilsLocation); + inputClassLoader = getUrlClassLoader(classLoaderUrls.toArray(new URL[] {}), classLoader); + result.addAll(resolveFromFolder(utilsLocation, inputClassLoader)); + } } return result; @@ -183,15 +185,15 @@ private static List> resolveFromFolder(Path templateRoot, ClassLoader i * @param configurationHolder configuration holder * @return List of classes to load utilities from */ - private static List> resolveFromJar(ClassLoader inputClassLoader, ConfigurationHolder configurationHolder) { + private static List> resolveFromJar(ClassLoader inputClassLoader, Path utilLocation) { - LOG.debug("Processing configuration archive {}", configurationHolder.getUtilsLocation()); + LOG.debug("Processing configuration archive {}", utilLocation); LOG.info("Searching for classes in configuration archive..."); List> result = new ArrayList<>(); List foundClasses = new LinkedList<>(); try { - foundClasses = walkJarFile(configurationHolder); + foundClasses = walkJarFile(utilLocation); } catch (IOException e) { LOG.error("Could not read templates jar file", e); } @@ -250,14 +252,14 @@ public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOExce * @return List of file paths containing class files * @throws IOException if file could not be visited */ - private static List walkJarFile(ConfigurationHolder configurationHolder) throws IOException { + private static List walkJarFile(Path utilLocation) throws IOException { List foundClasses = new LinkedList<>(); // walk the jar file - LOG.debug("Searching for classes in {}", configurationHolder.getUtilsLocation()); - Path configurationPath = configurationHolder.getUtilsLocation(); - if (FileSystemUtil.isZipFile(configurationPath.toUri())) { - configurationPath = FileSystemUtil.createFileSystemDependentPath(configurationHolder.getUtilsLocation().toUri()); + LOG.debug("Searching for classes in {}", utilLocation); + Path configurationPath = utilLocation; + if (FileSystemUtil.isZipFile(utilLocation.toUri())) { + configurationPath = FileSystemUtil.createFileSystemDependentPath(utilLocation.toUri()); } Files.walkFileTree(configurationPath, new SimpleFileVisitor() { diff --git a/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/util/ExtractTemplatesUtil.java b/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/util/ExtractTemplatesUtil.java index bf286d3557..7b3046318c 100644 --- a/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/util/ExtractTemplatesUtil.java +++ b/cobigen/cobigen-core/src/main/java/com/devonfw/cobigen/impl/util/ExtractTemplatesUtil.java @@ -18,6 +18,7 @@ import java.util.List; import java.util.Objects; +import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -166,10 +167,7 @@ private static void processJars(Path destinationPath) throws IOException { extractArchive(templateSetJar, destination); if (Files.exists(destination.resolve("com"))) { - // move com folder to src/main/java/com. Needed for the utils project - Files.createDirectories(destination.resolve("src/main/java")); - Files.move(destination.resolve("com"), destination.resolve("src/main/java/com"), - StandardCopyOption.REPLACE_EXISTING); + FileUtils.deleteDirectory(destination.resolve("com").toFile()); } } } else { diff --git a/pom.xml b/pom.xml index 966bc9d229..d3f74992d9 100644 --- a/pom.xml +++ b/pom.xml @@ -127,7 +127,7 @@ com.fasterxml.jackson.core jackson-databind - 2.10.0 + 2.13.2.2 com.google.code.gson