1 package org.onap.graphinventory.generate;
3 import java.io.IOException;
4 import java.nio.file.Paths;
5 import java.util.ArrayList;
6 import java.util.Arrays;
7 import java.util.Comparator;
10 import java.util.Map.Entry;
11 import java.util.regex.Matcher;
12 import java.util.stream.Collectors;
13 import javax.lang.model.element.Modifier;
14 import org.apache.commons.lang3.ArrayUtils;
15 import org.apache.commons.lang3.tuple.Pair;
16 import org.apache.maven.plugin.logging.Log;
17 import com.fasterxml.jackson.core.JsonProcessingException;
18 import com.google.common.base.CaseFormat;
19 import com.squareup.javapoet.ClassName;
20 import com.squareup.javapoet.CodeBlock;
21 import com.squareup.javapoet.FieldSpec;
22 import com.squareup.javapoet.JavaFile;
23 import com.squareup.javapoet.MethodSpec;
24 import com.squareup.javapoet.ParameterSpec;
25 import com.squareup.javapoet.ParameterizedTypeName;
26 import com.squareup.javapoet.TypeSpec;
28 public class FluentGenerator {
30 private final Map<String, ObjectType> doc;
31 private final String location;
32 private final String CLASSPATH;
34 private final String singularBuilderClass;
35 private final String pluralBuilderClass;
36 private final String topLevelBuilderClass;
37 private final String baseBuilderClass;
38 private final String singularClass;
39 private final String pluralClass;
40 private final String builderName;
41 private final String nameClass;
43 public FluentGenerator(Log log, String location, String destinationClasspath, String swaggerLocation,
44 String builderName, String singularBuilderClass, String pluralBuilderClass, String topLevelBuilderClass,
45 String baseBuilderClass, String singularClass, String pluralClass, String nameClass)
46 throws JsonProcessingException {
48 this.location = location;
49 this.CLASSPATH = destinationClasspath;
50 this.builderName = builderName;
51 this.singularBuilderClass = singularBuilderClass;
52 this.pluralBuilderClass = pluralBuilderClass;
53 this.topLevelBuilderClass = topLevelBuilderClass;
54 this.baseBuilderClass = baseBuilderClass;
55 this.singularClass = singularClass;
56 this.pluralClass = pluralClass;
57 this.nameClass = nameClass;
58 doc = new SwaggerConverter(log).getDoc(swaggerLocation);
61 public void run() throws IOException {
62 List<JavaFile> files = new ArrayList<>();
63 for (Entry<String, ObjectType> entry : doc.entrySet()) {
64 // String key = "routing-instance";
65 // ObjectType oType = test;
66 String key = entry.getKey();
67 ObjectType oType = entry.getValue();
68 MethodSpec.Builder constructor = MethodSpec.constructorBuilder().addModifiers(Modifier.PROTECTED);
69 List<ParameterSpec> constructorParams = new ArrayList<>();
70 List<FieldSpec> classFields = new ArrayList<>();
72 if (!oType.getType().equals("top level")) {
73 Pair<String, String> path = splitClasspath(this.baseBuilderClass);
74 ClassName parameterizedTypeName = ClassName.get(path.getLeft(), path.getRight());
75 constructorParams.add(ParameterSpec.builder(parameterizedTypeName, "parentObj").build());
76 classFields.add(FieldSpec.builder(parameterizedTypeName, "parentObj")
77 .addModifiers(Modifier.PRIVATE, Modifier.FINAL).build());
79 List<ParameterSpec> typeParams = new ArrayList<>();
81 for (ObjectField oF : oType.getFields()) {
82 if (oF.getType().equals("string")) {
83 typeParams.add(ParameterSpec.builder(String.class, lowerCamel(makeValidJavaVariable(oF.getName())))
85 classFields.add(FieldSpec.builder(String.class, lowerCamel(makeValidJavaVariable(oF.getName())))
86 .addModifiers(Modifier.PRIVATE, Modifier.FINAL).build());
87 } else if (oF.getType().equals("integer")) {
89 ParameterSpec.builder(int.class, lowerCamel(makeValidJavaVariable(oF.getName()))).build());
90 classFields.add(FieldSpec.builder(int.class, lowerCamel(makeValidJavaVariable(oF.getName())))
91 .addModifiers(Modifier.PRIVATE, Modifier.FINAL).build());
94 constructorParams.addAll(typeParams);
95 constructor.addParameters(constructorParams);
96 for (ParameterSpec p : constructorParams) {
97 constructor.addStatement("this.$L = $L", p.name, p.name);
100 List<MethodSpec> methods = new ArrayList<>();
101 methods.add(constructor.build());
103 methods.addAll(createChildMethods(oType));
105 methods.addAll(createInterfaceMethods(oType, typeParams));
107 ClassName superType = null;
108 if (oType.getType().equals("top level")) {
109 Pair<String, String> path = splitClasspath(this.topLevelBuilderClass);
110 superType = ClassName.get(path.getLeft(), path.getRight());
112 if (oType.getType().equals("singular")) {
113 Pair<String, String> path = splitClasspath(this.singularBuilderClass);
114 superType = ClassName.get(path.getLeft(), path.getRight());
116 } else if (oType.getType().equals("plural")) {
117 Pair<String, String> path = splitClasspath(this.pluralBuilderClass);
118 superType = ClassName.get(path.getLeft(), path.getRight());
122 TypeSpec type = TypeSpec.classBuilder(upperCamel(key)).addModifiers(Modifier.PUBLIC, Modifier.FINAL)
123 .addType(createInnerInfoClass(oType)).addSuperinterface(superType).addFields(classFields)
124 .addMethods(methods).build();
126 files.add(JavaFile.builder(CLASSPATH, type).build());
130 files.add(createBuilderClass());
132 files.stream().forEach(javaFile -> {
134 javaFile.writeTo(Paths.get(location, "fluent"));
135 } catch (IOException e) {
136 throw new RuntimeException(e);
142 protected List<MethodSpec> createInterfaceMethods(ObjectType oType, List<ParameterSpec> typeParams) {
144 List<MethodSpec> methods = new ArrayList<>();
146 CodeBlock.Builder uriTemplateCodeBlock = CodeBlock.builder();
147 if (!oType.getType().equals("top level")) {
148 uriTemplateCodeBlock.add("return this.parentObj.uriTemplate() + Info.partialUri");
150 uriTemplateCodeBlock.add("return Info.partialUri");
153 methods.add(MethodSpec.methodBuilder("uriTemplate").returns(String.class).addModifiers(Modifier.PUBLIC)
154 .addStatement(uriTemplateCodeBlock.build()).addAnnotation(Override.class).build());
156 ClassName arrayUtils = ClassName.get(ArrayUtils.class);
158 CodeBlock.Builder valuesReturn = CodeBlock.builder();
160 if (oType.getType().equals("top level")) {
161 valuesReturn.add("return new Object[0]");
163 if (!typeParams.isEmpty()) {
164 valuesReturn.add("return $T.addAll(this.parentObj.values(), $L)", arrayUtils, String.join(", ",
165 typeParams.stream().map(item -> "this." + item.name).collect(Collectors.toList())));
167 valuesReturn.add("return this.parentObj.values()");
170 methods.add(MethodSpec.methodBuilder("values").returns(Object[].class).addModifiers(Modifier.PUBLIC)
171 .addAnnotation(Override.class).addStatement(valuesReturn.build()).build());
173 if (!oType.getType().equals("top level")) {
174 ClassName returnType = null;
175 CodeBlock.Builder block = CodeBlock.builder();
176 if (oType.getType().equals("singular")) {
177 Pair<String, String> path = splitClasspath(this.singularClass);
178 returnType = ClassName.get(path.getLeft(), path.getRight());
179 block.add("return new $T(this.parentObj.uriTemplate(), Info.partialUri, Info.name, false)", returnType);
180 } else if (oType.getType().equals("plural")) {
181 Pair<String, String> path = splitClasspath(this.pluralClass);
182 returnType = ClassName.get(path.getLeft(), path.getRight());
183 block.add("return new $T(Info.name, this.parentObj.uriTemplate(), Info.partialUri)", returnType);
186 methods.add(MethodSpec.methodBuilder("build").returns(returnType).addModifiers(Modifier.PUBLIC)
187 .addAnnotation(Override.class).addStatement(block.build()).build());
194 protected List<MethodSpec> createChildMethods(ObjectType oType) {
195 List<MethodSpec> methods = new ArrayList<>();
196 for (String child : oType.getChildren()) {
197 methods.add(createAccessMethod(doc.get(child), true, false));
203 protected MethodSpec createAccessMethod(ObjectType oType, boolean isChild, boolean isStatic) {
205 ClassName childClass = ClassName.get(CLASSPATH, upperCamel(oType.getName()));
206 MethodSpec.Builder b = MethodSpec.methodBuilder(lowerCamel(oType.getName())).returns(childClass);
207 List<Modifier> modifiers = new ArrayList<>();
209 modifiers.add(Modifier.STATIC);
211 modifiers.add(Modifier.PUBLIC);
212 b.addModifiers(modifiers);
213 List<ParameterSpec> params = new ArrayList<>();
214 for (ObjectField oF : doc.get(oType.getName()).getFields()) {
215 if (oF.getType().equals("string")) {
217 ParameterSpec.builder(String.class, lowerCamel(makeValidJavaVariable(oF.getName()))).build());
218 } else if (oF.getType().equals("integer")) {
219 params.add(ParameterSpec.builder(int.class, lowerCamel(makeValidJavaVariable(oF.getName()))).build());
222 List<String> paramNames = params.stream().map(item -> item.name).collect(Collectors.toList());
224 paramNames.add(0, "this");
226 b.addParameters(params).addStatement("return new $T($L)", childClass, String.join(", ", paramNames));
231 protected JavaFile createBuilderClass() {
233 List<MethodSpec> methods = doc.values().stream().filter(item -> item.getType().equals("top level"))
234 .map(item -> createAccessMethod(item, false, true)).collect(Collectors.toList());
236 TypeSpec type = TypeSpec.classBuilder(this.builderName).addModifiers(Modifier.PUBLIC, Modifier.FINAL)
237 .addMethods(methods).addType(createTypes()).build();
239 return JavaFile.builder(CLASSPATH, type).build();
243 protected TypeSpec createTypes() {
244 List<FieldSpec> params = doc.values().stream().filter(item -> item.getType().equals("singular"))
245 .sorted(Comparator.comparing(item -> item.getName())).map(item -> {
247 ClassName.get(CLASSPATH, CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, item.getName()))
248 .nestedClass("Info");
249 FieldSpec field = FieldSpec
250 .builder(nameType, CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, item.getName()),
251 Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
252 .initializer("new $T()", nameType).build();
254 }).collect(Collectors.toList());
256 TypeSpec type = TypeSpec.classBuilder("Types").addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
257 .addFields(params).build();
263 protected TypeSpec createInnerInfoClass(ObjectType oType) {
264 List<FieldSpec> classFields = new ArrayList<>();
265 List<MethodSpec> methods = new ArrayList<>();
267 classFields.add(FieldSpec.builder(String.class, "partialUri")
268 .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
269 .initializer("$S", oType.getPartialUri()).build());
271 classFields.add(FieldSpec.builder(ParameterizedTypeName.get(List.class, String.class), "paths")
272 .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
273 .initializer("$T.asList($L)", ClassName.get(Arrays.class),
274 "\"" + oType.getPaths().stream().collect(Collectors.joining("\", \"")) + "\"")
277 ClassName superInterface;
279 if (oType.getType().equals("plural")) {
280 Pair<String, String> path = splitClasspath(this.pluralBuilderClass);
281 superInterface = ClassName.get(path.getLeft(), path.getRight());
282 name = oType.getAdditionalName();
283 } else if (oType.getType().equals("singular")) {
284 Pair<String, String> path = splitClasspath(this.singularBuilderClass);
285 superInterface = ClassName.get(path.getLeft(), path.getRight());
286 name = oType.getName();
288 Pair<String, String> path = splitClasspath(this.topLevelBuilderClass);
289 superInterface = ClassName.get(path.getLeft(), path.getRight());
290 name = oType.getName();
292 superInterface = superInterface.nestedClass("Info");
293 methods.add(MethodSpec.methodBuilder("getPaths").returns(ParameterizedTypeName.get(List.class, String.class))
294 .addModifiers(Modifier.PUBLIC).addAnnotation(Override.class).addStatement("return Info.paths").build());
295 methods.add(MethodSpec.methodBuilder("getPartialUri").returns(String.class).addModifiers(Modifier.PUBLIC)
296 .addAnnotation(Override.class).addStatement("return Info.partialUri").build());
297 if (!oType.getType().equals("top level")) {
298 classFields.add(FieldSpec.builder(String.class, "name")
299 .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC).initializer("$S", name).build());
300 classFields.add(FieldSpec.builder(ClassName.get("", "UriParams"), "uriParams")
301 .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
302 .initializer("new $T()", ClassName.get("", "UriParams")).build());
303 methods.add(MethodSpec.methodBuilder("getName").returns(String.class).addModifiers(Modifier.PUBLIC)
304 .addAnnotation(Override.class).addStatement("return Info.name").build());
306 methods.add(MethodSpec.methodBuilder("getUriParams").returns(ClassName.get("", "UriParams"))
307 .addModifiers(Modifier.PUBLIC).addAnnotation(Override.class).addStatement("return Info.uriParams")
310 TypeSpec.Builder returnTypeSpec = TypeSpec.classBuilder("Info").addModifiers(Modifier.PUBLIC, Modifier.STATIC)
311 .addSuperinterface(superInterface).addFields(classFields).addMethods(methods);
312 if (!oType.getType().equals("top level")) {
313 returnTypeSpec.addType(createUriParamsClass(superInterface, oType));
315 return returnTypeSpec.build();
319 protected TypeSpec createUriParamsClass(ClassName parent, ObjectType oType) {
321 List<FieldSpec> classFields = new ArrayList<>();
322 Matcher params = Patterns.urlTemplatePattern.matcher(oType.getPartialUri());
324 while (params.find()) {
328 if (params.group(2) != null) {
329 name = params.group(2);
331 name = params.group(1);
333 value = params.group(1);
335 name = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, name);
337 classFields.add(FieldSpec.builder(String.class, name, Modifier.PUBLIC, Modifier.FINAL)
338 .initializer("$S", value).build());
341 return TypeSpec.classBuilder("UriParams").addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
342 .addFields(classFields).addSuperinterface(parent.nestedClass("UriParams")).build();
345 protected String makeValidJavaVariable(String name) {
347 return name.replace(".", "_");
350 protected Pair<String, String> splitClasspath(String path) {
352 return Pair.of(path.substring(0, path.lastIndexOf(".")),
353 path.substring(path.lastIndexOf(".") + 1, path.length()));
356 protected String lowerCamel(String s) {
357 return CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, s);
360 protected String upperCamel(String s) {
361 return CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, s);