package org.onap.graphinventory.generate; import java.io.IOException; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.stream.Collectors; import javax.lang.model.element.Modifier; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.maven.plugin.logging.Log; import com.fasterxml.jackson.core.JsonProcessingException; import com.google.common.base.CaseFormat; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeSpec; public class FluentGenerator { private final Map doc; private final String location; private final String CLASSPATH; private final String singularBuilderClass; private final String pluralBuilderClass; private final String topLevelBuilderClass; private final String baseBuilderClass; private final String singularClass; private final String pluralClass; private final String builderName; private final String nameClass; public FluentGenerator(Log log, String location, String destinationClasspath, String swaggerLocation, String builderName, String singularBuilderClass, String pluralBuilderClass, String topLevelBuilderClass, String baseBuilderClass, String singularClass, String pluralClass, String nameClass) throws JsonProcessingException { this.location = location; this.CLASSPATH = destinationClasspath; this.builderName = builderName; this.singularBuilderClass = singularBuilderClass; this.pluralBuilderClass = pluralBuilderClass; this.topLevelBuilderClass = topLevelBuilderClass; this.baseBuilderClass = baseBuilderClass; this.singularClass = singularClass; this.pluralClass = pluralClass; this.nameClass = nameClass; doc = new SwaggerConverter(log).getDoc(swaggerLocation); } public void run() throws IOException { List files = new ArrayList<>(); for (Entry entry : doc.entrySet()) { // String key = "routing-instance"; // ObjectType oType = test; String key = entry.getKey(); ObjectType oType = entry.getValue(); MethodSpec.Builder constructor = MethodSpec.constructorBuilder().addModifiers(Modifier.PROTECTED); List constructorParams = new ArrayList<>(); List classFields = new ArrayList<>(); if (!oType.getType().equals("top level")) { Pair path = splitClasspath(this.baseBuilderClass); ClassName parameterizedTypeName = ClassName.get(path.getLeft(), path.getRight()); constructorParams.add(ParameterSpec.builder(parameterizedTypeName, "parentObj").build()); classFields.add(FieldSpec.builder(parameterizedTypeName, "parentObj") .addModifiers(Modifier.PRIVATE, Modifier.FINAL).build()); } List typeParams = new ArrayList<>(); for (ObjectField oF : oType.getFields()) { if (oF.getType().equals("string")) { typeParams.add(ParameterSpec.builder(String.class, lowerCamel(makeValidJavaVariable(oF.getName()))) .build()); classFields.add(FieldSpec.builder(String.class, lowerCamel(makeValidJavaVariable(oF.getName()))) .addModifiers(Modifier.PRIVATE, Modifier.FINAL).build()); } else if (oF.getType().equals("integer")) { typeParams.add( ParameterSpec.builder(int.class, lowerCamel(makeValidJavaVariable(oF.getName()))).build()); classFields.add(FieldSpec.builder(int.class, lowerCamel(makeValidJavaVariable(oF.getName()))) .addModifiers(Modifier.PRIVATE, Modifier.FINAL).build()); } } constructorParams.addAll(typeParams); constructor.addParameters(constructorParams); for (ParameterSpec p : constructorParams) { constructor.addStatement("this.$L = $L", p.name, p.name); } List methods = new ArrayList<>(); methods.add(constructor.build()); methods.addAll(createChildMethods(oType)); methods.addAll(createInterfaceMethods(oType, typeParams)); ClassName superType = null; if (oType.getType().equals("top level")) { Pair path = splitClasspath(this.topLevelBuilderClass); superType = ClassName.get(path.getLeft(), path.getRight()); } else { if (oType.getType().equals("singular")) { Pair path = splitClasspath(this.singularBuilderClass); superType = ClassName.get(path.getLeft(), path.getRight()); } else if (oType.getType().equals("plural")) { Pair path = splitClasspath(this.pluralBuilderClass); superType = ClassName.get(path.getLeft(), path.getRight()); } } TypeSpec type = TypeSpec.classBuilder(upperCamel(key)).addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addType(createInnerInfoClass(oType)).addSuperinterface(superType).addFields(classFields) .addMethods(methods).build(); files.add(JavaFile.builder(CLASSPATH, type).build()); } files.add(createBuilderClass()); files.stream().forEach(javaFile -> { try { javaFile.writeTo(Paths.get(location, "fluent")); } catch (IOException e) { throw new RuntimeException(e); } }); } protected List createInterfaceMethods(ObjectType oType, List typeParams) { List methods = new ArrayList<>(); CodeBlock.Builder uriTemplateCodeBlock = CodeBlock.builder(); if (!oType.getType().equals("top level")) { uriTemplateCodeBlock.add("return this.parentObj.uriTemplate() + Info.partialUri"); } else { uriTemplateCodeBlock.add("return Info.partialUri"); } methods.add(MethodSpec.methodBuilder("uriTemplate").returns(String.class).addModifiers(Modifier.PUBLIC) .addStatement(uriTemplateCodeBlock.build()).addAnnotation(Override.class).build()); ClassName arrayUtils = ClassName.get(ArrayUtils.class); CodeBlock.Builder valuesReturn = CodeBlock.builder(); if (oType.getType().equals("top level")) { valuesReturn.add("return new Object[0]"); } else { if (!typeParams.isEmpty()) { valuesReturn.add("return $T.addAll(this.parentObj.values(), $L)", arrayUtils, String.join(", ", typeParams.stream().map(item -> "this." + item.name).collect(Collectors.toList()))); } else { valuesReturn.add("return this.parentObj.values()"); } } methods.add(MethodSpec.methodBuilder("values").returns(Object[].class).addModifiers(Modifier.PUBLIC) .addAnnotation(Override.class).addStatement(valuesReturn.build()).build()); if (!oType.getType().equals("top level")) { ClassName returnType = null; CodeBlock.Builder block = CodeBlock.builder(); if (oType.getType().equals("singular")) { Pair path = splitClasspath(this.singularClass); returnType = ClassName.get(path.getLeft(), path.getRight()); block.add("return new $T(this.parentObj.uriTemplate(), Info.partialUri, Info.name, false)", returnType); } else if (oType.getType().equals("plural")) { Pair path = splitClasspath(this.pluralClass); returnType = ClassName.get(path.getLeft(), path.getRight()); block.add("return new $T(Info.name, this.parentObj.uriTemplate(), Info.partialUri)", returnType); } methods.add(MethodSpec.methodBuilder("build").returns(returnType).addModifiers(Modifier.PUBLIC) .addAnnotation(Override.class).addStatement(block.build()).build()); } return methods; } protected List createChildMethods(ObjectType oType) { List methods = new ArrayList<>(); for (String child : oType.getChildren()) { methods.add(createAccessMethod(doc.get(child), true, false)); } return methods; } protected MethodSpec createAccessMethod(ObjectType oType, boolean isChild, boolean isStatic) { ClassName childClass = ClassName.get(CLASSPATH, upperCamel(oType.getName())); MethodSpec.Builder b = MethodSpec.methodBuilder(lowerCamel(oType.getName())).returns(childClass); List modifiers = new ArrayList<>(); if (isStatic) { modifiers.add(Modifier.STATIC); } modifiers.add(Modifier.PUBLIC); b.addModifiers(modifiers); List params = new ArrayList<>(); for (ObjectField oF : doc.get(oType.getName()).getFields()) { if (oF.getType().equals("string")) { params.add( ParameterSpec.builder(String.class, lowerCamel(makeValidJavaVariable(oF.getName()))).build()); } else if (oF.getType().equals("integer")) { params.add(ParameterSpec.builder(int.class, lowerCamel(makeValidJavaVariable(oF.getName()))).build()); } } List paramNames = params.stream().map(item -> item.name).collect(Collectors.toList()); if (isChild) { paramNames.add(0, "this"); } b.addParameters(params).addStatement("return new $T($L)", childClass, String.join(", ", paramNames)); return b.build(); } protected JavaFile createBuilderClass() { List methods = doc.values().stream().filter(item -> item.getType().equals("top level")) .map(item -> createAccessMethod(item, false, true)).collect(Collectors.toList()); TypeSpec type = TypeSpec.classBuilder(this.builderName).addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethods(methods).addType(createTypes()).build(); return JavaFile.builder(CLASSPATH, type).build(); } protected TypeSpec createTypes() { List params = doc.values().stream().filter(item -> item.getType().equals("singular")) .sorted(Comparator.comparing(item -> item.getName())).map(item -> { ClassName nameType = ClassName.get(CLASSPATH, CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, item.getName())) .nestedClass("Info"); FieldSpec field = FieldSpec .builder(nameType, CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, item.getName()), Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .initializer("new $T()", nameType).build(); return field; }).collect(Collectors.toList()); TypeSpec type = TypeSpec.classBuilder("Types").addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC) .addFields(params).build(); return type; } protected TypeSpec createInnerInfoClass(ObjectType oType) { List classFields = new ArrayList<>(); List methods = new ArrayList<>(); classFields.add(FieldSpec.builder(String.class, "partialUri") .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC) .initializer("$S", oType.getPartialUri()).build()); classFields.add(FieldSpec.builder(ParameterizedTypeName.get(List.class, String.class), "paths") .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC) .initializer("$T.asList($L)", ClassName.get(Arrays.class), "\"" + oType.getPaths().stream().collect(Collectors.joining("\", \"")) + "\"") .build()); ClassName superInterface; String name; if (oType.getType().equals("plural")) { Pair path = splitClasspath(this.pluralBuilderClass); superInterface = ClassName.get(path.getLeft(), path.getRight()); name = oType.getAdditionalName(); } else if (oType.getType().equals("singular")) { Pair path = splitClasspath(this.singularBuilderClass); superInterface = ClassName.get(path.getLeft(), path.getRight()); name = oType.getName(); } else { Pair path = splitClasspath(this.topLevelBuilderClass); superInterface = ClassName.get(path.getLeft(), path.getRight()); name = oType.getName(); } superInterface = superInterface.nestedClass("Info"); methods.add(MethodSpec.methodBuilder("getPaths").returns(ParameterizedTypeName.get(List.class, String.class)) .addModifiers(Modifier.PUBLIC).addAnnotation(Override.class).addStatement("return Info.paths").build()); methods.add(MethodSpec.methodBuilder("getPartialUri").returns(String.class).addModifiers(Modifier.PUBLIC) .addAnnotation(Override.class).addStatement("return Info.partialUri").build()); if (!oType.getType().equals("top level")) { classFields.add(FieldSpec.builder(String.class, "name") .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC).initializer("$S", name).build()); classFields.add(FieldSpec.builder(ClassName.get("", "UriParams"), "uriParams") .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC) .initializer("new $T()", ClassName.get("", "UriParams")).build()); methods.add(MethodSpec.methodBuilder("getName").returns(String.class).addModifiers(Modifier.PUBLIC) .addAnnotation(Override.class).addStatement("return Info.name").build()); methods.add(MethodSpec.methodBuilder("getUriParams").returns(ClassName.get("", "UriParams")) .addModifiers(Modifier.PUBLIC).addAnnotation(Override.class).addStatement("return Info.uriParams") .build()); } TypeSpec.Builder returnTypeSpec = TypeSpec.classBuilder("Info").addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addSuperinterface(superInterface).addFields(classFields).addMethods(methods); if (!oType.getType().equals("top level")) { returnTypeSpec.addType(createUriParamsClass(superInterface, oType)); } return returnTypeSpec.build(); } protected TypeSpec createUriParamsClass(ClassName parent, ObjectType oType) { List classFields = new ArrayList<>(); Matcher params = Patterns.urlTemplatePattern.matcher(oType.getPartialUri()); while (params.find()) { String value; String name; value = params.group(1); name = params.group(2); name = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, name); classFields.add(FieldSpec.builder(String.class, name, Modifier.PUBLIC, Modifier.FINAL) .initializer("$S", value).build()); } return TypeSpec.classBuilder("UriParams").addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .addFields(classFields).addSuperinterface(parent.nestedClass("UriParams")).build(); } protected String makeValidJavaVariable(String name) { return name.replace(".", "_"); } protected Pair splitClasspath(String path) { return Pair.of(path.substring(0, path.lastIndexOf(".")), path.substring(path.lastIndexOf(".") + 1, path.length())); } protected String lowerCamel(String s) { return CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, s); } protected String upperCamel(String s) { return CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, s); } }