23a1a812dfa62d1a6f6341111f654fd75f7a7f0d
[so.git] /
1 package org.onap.graphinventory.generate;
2
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;
8 import java.util.List;
9 import java.util.Map;
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;
27
28 public class FluentGenerator {
29
30     private final Map<String, ObjectType> doc;
31     private final String location;
32     private final String CLASSPATH;
33
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;
42
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 {
47
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);
59     }
60
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<>();
71
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());
78             }
79             List<ParameterSpec> typeParams = new ArrayList<>();
80
81             for (ObjectField oF : oType.getFields()) {
82                 if (oF.getType().equals("string")) {
83                     typeParams.add(ParameterSpec.builder(String.class, lowerCamel(makeValidJavaVariable(oF.getName())))
84                             .build());
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")) {
88                     typeParams.add(
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());
92                 }
93             }
94             constructorParams.addAll(typeParams);
95             constructor.addParameters(constructorParams);
96             for (ParameterSpec p : constructorParams) {
97                 constructor.addStatement("this.$L = $L", p.name, p.name);
98
99             }
100             List<MethodSpec> methods = new ArrayList<>();
101             methods.add(constructor.build());
102
103             methods.addAll(createChildMethods(oType));
104
105             methods.addAll(createInterfaceMethods(oType, typeParams));
106
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());
111             } else {
112                 if (oType.getType().equals("singular")) {
113                     Pair<String, String> path = splitClasspath(this.singularBuilderClass);
114                     superType = ClassName.get(path.getLeft(), path.getRight());
115
116                 } else if (oType.getType().equals("plural")) {
117                     Pair<String, String> path = splitClasspath(this.pluralBuilderClass);
118                     superType = ClassName.get(path.getLeft(), path.getRight());
119                 }
120             }
121
122             TypeSpec type = TypeSpec.classBuilder(upperCamel(key)).addModifiers(Modifier.PUBLIC, Modifier.FINAL)
123                     .addType(createInnerInfoClass(oType)).addSuperinterface(superType).addFields(classFields)
124                     .addMethods(methods).build();
125
126             files.add(JavaFile.builder(CLASSPATH, type).build());
127
128         }
129
130         files.add(createBuilderClass());
131
132         files.stream().forEach(javaFile -> {
133             try {
134                 javaFile.writeTo(Paths.get(location, "fluent"));
135             } catch (IOException e) {
136                 throw new RuntimeException(e);
137             }
138         });
139
140     }
141
142     protected List<MethodSpec> createInterfaceMethods(ObjectType oType, List<ParameterSpec> typeParams) {
143
144         List<MethodSpec> methods = new ArrayList<>();
145
146         CodeBlock.Builder uriTemplateCodeBlock = CodeBlock.builder();
147         if (!oType.getType().equals("top level")) {
148             uriTemplateCodeBlock.add("return this.parentObj.uriTemplate() + Info.partialUri");
149         } else {
150             uriTemplateCodeBlock.add("return Info.partialUri");
151
152         }
153         methods.add(MethodSpec.methodBuilder("uriTemplate").returns(String.class).addModifiers(Modifier.PUBLIC)
154                 .addStatement(uriTemplateCodeBlock.build()).addAnnotation(Override.class).build());
155
156         ClassName arrayUtils = ClassName.get(ArrayUtils.class);
157
158         CodeBlock.Builder valuesReturn = CodeBlock.builder();
159
160         if (oType.getType().equals("top level")) {
161             valuesReturn.add("return new Object[0]");
162         } else {
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())));
166             } else {
167                 valuesReturn.add("return this.parentObj.values()");
168             }
169         }
170         methods.add(MethodSpec.methodBuilder("values").returns(Object[].class).addModifiers(Modifier.PUBLIC)
171                 .addAnnotation(Override.class).addStatement(valuesReturn.build()).build());
172
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);
184             }
185
186             methods.add(MethodSpec.methodBuilder("build").returns(returnType).addModifiers(Modifier.PUBLIC)
187                     .addAnnotation(Override.class).addStatement(block.build()).build());
188
189         }
190
191         return methods;
192     }
193
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));
198         }
199
200         return methods;
201     }
202
203     protected MethodSpec createAccessMethod(ObjectType oType, boolean isChild, boolean isStatic) {
204
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<>();
208         if (isStatic) {
209             modifiers.add(Modifier.STATIC);
210         }
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")) {
216                 params.add(
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());
220             }
221         }
222         List<String> paramNames = params.stream().map(item -> item.name).collect(Collectors.toList());
223         if (isChild) {
224             paramNames.add(0, "this");
225         }
226         b.addParameters(params).addStatement("return new $T($L)", childClass, String.join(", ", paramNames));
227
228         return b.build();
229     }
230
231     protected JavaFile createBuilderClass() {
232
233         List<MethodSpec> methods = doc.values().stream().filter(item -> item.getType().equals("top level"))
234                 .map(item -> createAccessMethod(item, false, true)).collect(Collectors.toList());
235
236         TypeSpec type = TypeSpec.classBuilder(this.builderName).addModifiers(Modifier.PUBLIC, Modifier.FINAL)
237                 .addMethods(methods).addType(createTypes()).build();
238
239         return JavaFile.builder(CLASSPATH, type).build();
240
241     }
242
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 -> {
246                     ClassName nameType =
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();
253                     return field;
254                 }).collect(Collectors.toList());
255
256         TypeSpec type = TypeSpec.classBuilder("Types").addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
257                 .addFields(params).build();
258
259         return type;
260
261     }
262
263     protected TypeSpec createInnerInfoClass(ObjectType oType) {
264         List<FieldSpec> classFields = new ArrayList<>();
265         List<MethodSpec> methods = new ArrayList<>();
266
267         classFields.add(FieldSpec.builder(String.class, "partialUri")
268                 .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
269                 .initializer("$S", oType.getPartialUri()).build());
270
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("\", \"")) + "\"")
275                 .build());
276
277         ClassName superInterface;
278         String name;
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();
287         } else {
288             Pair<String, String> path = splitClasspath(this.topLevelBuilderClass);
289             superInterface = ClassName.get(path.getLeft(), path.getRight());
290             name = oType.getName();
291         }
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());
305
306             methods.add(MethodSpec.methodBuilder("getUriParams").returns(ClassName.get("", "UriParams"))
307                     .addModifiers(Modifier.PUBLIC).addAnnotation(Override.class).addStatement("return Info.uriParams")
308                     .build());
309         }
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));
314         }
315         return returnTypeSpec.build();
316
317     }
318
319     protected TypeSpec createUriParamsClass(ClassName parent, ObjectType oType) {
320
321         List<FieldSpec> classFields = new ArrayList<>();
322         Matcher params = Patterns.urlTemplatePattern.matcher(oType.getPartialUri());
323
324         while (params.find()) {
325             String value;
326             String name;
327
328             if (params.group(2) != null) {
329                 name = params.group(2);
330             } else {
331                 name = params.group(1);
332             }
333             value = params.group(1);
334
335             name = CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, name);
336
337             classFields.add(FieldSpec.builder(String.class, name, Modifier.PUBLIC, Modifier.FINAL)
338                     .initializer("$S", value).build());
339         }
340
341         return TypeSpec.classBuilder("UriParams").addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
342                 .addFields(classFields).addSuperinterface(parent.nestedClass("UriParams")).build();
343     }
344
345     protected String makeValidJavaVariable(String name) {
346
347         return name.replace(".", "_");
348     }
349
350     protected Pair<String, String> splitClasspath(String path) {
351
352         return Pair.of(path.substring(0, path.lastIndexOf(".")),
353                 path.substring(path.lastIndexOf(".") + 1, path.length()));
354     }
355
356     protected String lowerCamel(String s) {
357         return CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, s);
358     }
359
360     protected String upperCamel(String s) {
361         return CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, s);
362     }
363
364 }