f9c664389b729b3fec2b4184916d3e77fa10507b
[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.stream.Collectors;
12 import javax.lang.model.element.Modifier;
13 import org.apache.commons.lang3.ArrayUtils;
14 import org.apache.commons.lang3.tuple.Pair;
15 import org.apache.maven.plugin.logging.Log;
16 import com.fasterxml.jackson.core.JsonProcessingException;
17 import com.google.common.base.CaseFormat;
18 import com.squareup.javapoet.ClassName;
19 import com.squareup.javapoet.CodeBlock;
20 import com.squareup.javapoet.FieldSpec;
21 import com.squareup.javapoet.JavaFile;
22 import com.squareup.javapoet.MethodSpec;
23 import com.squareup.javapoet.ParameterSpec;
24 import com.squareup.javapoet.ParameterizedTypeName;
25 import com.squareup.javapoet.TypeSpec;
26
27 public class FluentGenerator {
28
29     private final Map<String, ObjectType> doc;
30     private final String location;
31     private final String CLASSPATH;
32
33     private final String singularBuilderClass;
34     private final String pluralBuilderClass;
35     private final String topLevelBuilderClass;
36     private final String baseBuilderClass;
37     private final String singularClass;
38     private final String pluralClass;
39     private final String builderName;
40     private final String nameClass;
41
42     public FluentGenerator(Log log, String location, String destinationClasspath, String swaggerLocation,
43             String builderName, String singularBuilderClass, String pluralBuilderClass, String topLevelBuilderClass,
44             String baseBuilderClass, String singularClass, String pluralClass, String nameClass)
45             throws JsonProcessingException {
46
47         this.location = location;
48         this.CLASSPATH = destinationClasspath;
49         this.builderName = builderName;
50         this.singularBuilderClass = singularBuilderClass;
51         this.pluralBuilderClass = pluralBuilderClass;
52         this.topLevelBuilderClass = topLevelBuilderClass;
53         this.baseBuilderClass = baseBuilderClass;
54         this.singularClass = singularClass;
55         this.pluralClass = pluralClass;
56         this.nameClass = nameClass;
57         doc = new SwaggerConverter(log).getDoc(swaggerLocation);
58     }
59
60     public void run() throws IOException {
61         List<JavaFile> files = new ArrayList<>();
62         for (Entry<String, ObjectType> entry : doc.entrySet()) {
63             // String key = "routing-instance";
64             // ObjectType oType = test;
65             String key = entry.getKey();
66             ObjectType oType = entry.getValue();
67             MethodSpec.Builder constructor = MethodSpec.constructorBuilder().addModifiers(Modifier.PROTECTED);
68             List<ParameterSpec> constructorParams = new ArrayList<>();
69             List<FieldSpec> classFields = new ArrayList<>();
70
71             if (!oType.getType().equals("top level")) {
72                 Pair<String, String> path = splitClasspath(this.baseBuilderClass);
73                 ClassName parameterizedTypeName = ClassName.get(path.getLeft(), path.getRight());
74                 constructorParams.add(ParameterSpec.builder(parameterizedTypeName, "parentObj").build());
75                 classFields.add(FieldSpec.builder(parameterizedTypeName, "parentObj")
76                         .addModifiers(Modifier.PRIVATE, Modifier.FINAL).build());
77             }
78             List<ParameterSpec> typeParams = new ArrayList<>();
79
80             for (ObjectField oF : oType.getFields()) {
81                 if (oF.getType().equals("string")) {
82                     typeParams.add(ParameterSpec.builder(String.class, lowerCamel(makeValidJavaVariable(oF.getName())))
83                             .build());
84                     classFields.add(FieldSpec.builder(String.class, lowerCamel(makeValidJavaVariable(oF.getName())))
85                             .addModifiers(Modifier.PRIVATE, Modifier.FINAL).build());
86                 } else if (oF.getType().equals("integer")) {
87                     typeParams.add(
88                             ParameterSpec.builder(int.class, lowerCamel(makeValidJavaVariable(oF.getName()))).build());
89                     classFields.add(FieldSpec.builder(int.class, lowerCamel(makeValidJavaVariable(oF.getName())))
90                             .addModifiers(Modifier.PRIVATE, Modifier.FINAL).build());
91                 }
92             }
93             constructorParams.addAll(typeParams);
94             constructor.addParameters(constructorParams);
95             for (ParameterSpec p : constructorParams) {
96                 constructor.addStatement("this.$L = $L", p.name, p.name);
97
98             }
99             List<MethodSpec> methods = new ArrayList<>();
100             methods.add(constructor.build());
101
102             methods.addAll(createChildMethods(oType));
103
104             methods.addAll(createInterfaceMethods(oType, typeParams));
105
106             ClassName superType = null;
107             if (oType.getType().equals("top level")) {
108                 Pair<String, String> path = splitClasspath(this.topLevelBuilderClass);
109                 superType = ClassName.get(path.getLeft(), path.getRight());
110             } else {
111                 if (oType.getType().equals("singular")) {
112                     Pair<String, String> path = splitClasspath(this.singularBuilderClass);
113                     superType = ClassName.get(path.getLeft(), path.getRight());
114
115                 } else if (oType.getType().equals("plural")) {
116                     Pair<String, String> path = splitClasspath(this.pluralBuilderClass);
117                     superType = ClassName.get(path.getLeft(), path.getRight());
118                 }
119             }
120
121             TypeSpec type = TypeSpec.classBuilder(upperCamel(key)).addModifiers(Modifier.PUBLIC, Modifier.FINAL)
122                     .addType(createInnerInfoClass(oType)).addSuperinterface(superType).addFields(classFields)
123                     .addMethods(methods).build();
124
125             files.add(JavaFile.builder(CLASSPATH, type).build());
126
127         }
128
129         files.add(createBuilderClass());
130
131         files.stream().forEach(javaFile -> {
132             try {
133                 javaFile.writeTo(Paths.get(location, "fluent"));
134             } catch (IOException e) {
135                 throw new RuntimeException(e);
136             }
137         });
138
139     }
140
141     protected List<MethodSpec> createInterfaceMethods(ObjectType oType, List<ParameterSpec> typeParams) {
142
143         List<MethodSpec> methods = new ArrayList<>();
144
145         CodeBlock.Builder uriTemplateCodeBlock = CodeBlock.builder();
146         if (!oType.getType().equals("top level")) {
147             uriTemplateCodeBlock.add("return this.parentObj.uriTemplate() + Info.partialUri");
148         } else {
149             uriTemplateCodeBlock.add("return Info.partialUri");
150
151         }
152         methods.add(MethodSpec.methodBuilder("uriTemplate").returns(String.class).addModifiers(Modifier.PUBLIC)
153                 .addStatement(uriTemplateCodeBlock.build()).addAnnotation(Override.class).build());
154
155         ClassName arrayUtils = ClassName.get(ArrayUtils.class);
156
157         CodeBlock.Builder valuesReturn = CodeBlock.builder();
158
159         if (oType.getType().equals("top level")) {
160             valuesReturn.add("return new Object[0]");
161         } else {
162             if (!typeParams.isEmpty()) {
163                 valuesReturn.add("return $T.addAll(this.parentObj.values(), $L)", arrayUtils, String.join(", ",
164                         typeParams.stream().map(item -> "this." + item.name).collect(Collectors.toList())));
165             } else {
166                 valuesReturn.add("return this.parentObj.values()");
167             }
168         }
169         methods.add(MethodSpec.methodBuilder("values").returns(Object[].class).addModifiers(Modifier.PUBLIC)
170                 .addAnnotation(Override.class).addStatement(valuesReturn.build()).build());
171
172         if (!oType.getType().equals("top level")) {
173             ClassName returnType = null;
174             CodeBlock.Builder block = CodeBlock.builder();
175             if (oType.getType().equals("singular")) {
176                 Pair<String, String> path = splitClasspath(this.singularClass);
177                 returnType = ClassName.get(path.getLeft(), path.getRight());
178                 block.add("return new $T(this.parentObj.uriTemplate(), Info.partialUri, Info.name, false)", returnType);
179             } else if (oType.getType().equals("plural")) {
180                 Pair<String, String> path = splitClasspath(this.pluralClass);
181                 returnType = ClassName.get(path.getLeft(), path.getRight());
182                 block.add("return new $T(Info.name, this.parentObj.uriTemplate(), Info.partialUri)", returnType);
183             }
184
185             methods.add(MethodSpec.methodBuilder("build").returns(returnType).addModifiers(Modifier.PUBLIC)
186                     .addAnnotation(Override.class).addStatement(block.build()).build());
187
188         }
189
190         return methods;
191     }
192
193     protected List<MethodSpec> createChildMethods(ObjectType oType) {
194         List<MethodSpec> methods = new ArrayList<>();
195         for (String child : oType.getChildren()) {
196             methods.add(createAccessMethod(doc.get(child), true, false));
197         }
198
199         return methods;
200     }
201
202     protected MethodSpec createAccessMethod(ObjectType oType, boolean isChild, boolean isStatic) {
203
204         ClassName childClass = ClassName.get(CLASSPATH, upperCamel(oType.getName()));
205         MethodSpec.Builder b = MethodSpec.methodBuilder(lowerCamel(oType.getName())).returns(childClass);
206         List<Modifier> modifiers = new ArrayList<>();
207         if (isStatic) {
208             modifiers.add(Modifier.STATIC);
209         }
210         modifiers.add(Modifier.PUBLIC);
211         b.addModifiers(modifiers);
212         List<ParameterSpec> params = new ArrayList<>();
213         for (ObjectField oF : doc.get(oType.getName()).getFields()) {
214             if (oF.getType().equals("string")) {
215                 params.add(
216                         ParameterSpec.builder(String.class, lowerCamel(makeValidJavaVariable(oF.getName()))).build());
217             } else if (oF.getType().equals("integer")) {
218                 params.add(ParameterSpec.builder(int.class, lowerCamel(makeValidJavaVariable(oF.getName()))).build());
219             }
220         }
221         List<String> paramNames = params.stream().map(item -> item.name).collect(Collectors.toList());
222         if (isChild) {
223             paramNames.add(0, "this");
224         }
225         b.addParameters(params).addStatement("return new $T($L)", childClass, String.join(", ", paramNames));
226
227         return b.build();
228     }
229
230     protected JavaFile createBuilderClass() {
231
232         List<MethodSpec> methods = doc.values().stream().filter(item -> item.getType().equals("top level"))
233                 .map(item -> createAccessMethod(item, false, true)).collect(Collectors.toList());
234
235         TypeSpec type = TypeSpec.classBuilder(this.builderName).addModifiers(Modifier.PUBLIC, Modifier.FINAL)
236                 .addMethods(methods).addType(createTypes()).build();
237
238         return JavaFile.builder(CLASSPATH, type).build();
239
240     }
241
242     protected TypeSpec createTypes() {
243         Pair<String, String> path = splitClasspath(this.nameClass);
244         ClassName nameType = ClassName.get(path.getLeft(), path.getRight());
245
246         List<FieldSpec> params =
247                 doc.values().stream().filter(item -> item.getType().equals("singular"))
248                         .sorted(Comparator
249                                 .comparing(item -> item.getName()))
250                         .map(item -> FieldSpec
251                                 .builder(nameType,
252                                         CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_UNDERSCORE, item.getName()),
253                                         Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
254                                 .initializer("new $T($S)", nameType, item.getName()).build())
255                         .collect(Collectors.toList());
256
257         TypeSpec type = TypeSpec.classBuilder("Types").addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
258                 .addFields(params).build();
259
260         return type;
261
262     }
263
264     protected TypeSpec createInnerInfoClass(ObjectType oType) {
265         List<FieldSpec> classFields = new ArrayList<>();
266         List<MethodSpec> methods = new ArrayList<>();
267
268         classFields.add(FieldSpec.builder(String.class, "partialUri")
269                 .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
270                 .initializer("$S", oType.getPartialUri()).build());
271
272         classFields.add(FieldSpec.builder(ParameterizedTypeName.get(List.class, String.class), "paths")
273                 .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
274                 .initializer("$T.asList($L)", ClassName.get(Arrays.class),
275                         "\"" + oType.getPaths().stream().collect(Collectors.joining("\", \"")) + "\"")
276                 .build());
277
278         ClassName superInterface;
279         if (oType.getType().equals("plural")) {
280             classFields.add(FieldSpec.builder(String.class, "name")
281                     .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC)
282                     .initializer("$S", oType.getAdditionalName()).build());
283             superInterface = ClassName.get(this.pluralBuilderClass, "Info");
284         } else if (oType.getType().equals("singular")) {
285             classFields.add(FieldSpec.builder(String.class, "name")
286                     .addModifiers(Modifier.PRIVATE, Modifier.FINAL, Modifier.STATIC).initializer("$S", oType.getName())
287                     .build());
288             superInterface = ClassName.get(this.singularBuilderClass, "Info");
289         } else {
290             superInterface = ClassName.get(this.topLevelBuilderClass, "Info");
291         }
292         methods.add(MethodSpec.methodBuilder("getPaths").returns(ParameterizedTypeName.get(List.class, String.class))
293                 .addModifiers(Modifier.PUBLIC).addAnnotation(Override.class).addStatement("return Info.paths").build());
294         methods.add(MethodSpec.methodBuilder("getPartialUri").returns(String.class).addModifiers(Modifier.PUBLIC)
295                 .addAnnotation(Override.class).addStatement("return Info.partialUri").build());
296         if (!oType.getType().equals("top level")) {
297             methods.add(MethodSpec.methodBuilder("getName").returns(String.class).addModifiers(Modifier.PUBLIC)
298                     .addAnnotation(Override.class).addStatement("return Info.name").build());
299         }
300         return TypeSpec.classBuilder("Info").addModifiers(Modifier.PUBLIC, Modifier.STATIC)
301                 .addSuperinterface(superInterface).addFields(classFields).addMethods(methods).build();
302
303     }
304
305     protected String makeValidJavaVariable(String name) {
306
307         return name.replace(".", "_");
308     }
309
310     protected Pair<String, String> splitClasspath(String path) {
311
312         return Pair.of(path.substring(0, path.lastIndexOf(".")),
313                 path.substring(path.lastIndexOf(".") + 1, path.length()));
314     }
315
316     protected String lowerCamel(String s) {
317         return CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, s);
318     }
319
320     protected String upperCamel(String s) {
321         return CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, s);
322     }
323
324 }