Increased version of binding-generator to 0.5.5-SNAPSHOT.
[controller.git] / opendaylight / sal / yang-prototype / code-generator / binding-java-api-generator / src / main / java / org / opendaylight / controller / sal / java / api / generator / BuilderClassDescriptor.java
1 package org.opendaylight.controller.sal.java.api.generator;
2
3 import java.lang.reflect.Method;
4 import java.util.ArrayList;
5 import java.util.LinkedHashMap;
6 import java.util.LinkedHashSet;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Set;
10
11 import org.opendaylight.controller.sal.binding.model.api.GeneratedTransferObject;
12 import org.opendaylight.controller.sal.binding.model.api.GeneratedType;
13 import org.opendaylight.controller.sal.binding.model.api.MethodSignature;
14 import org.opendaylight.controller.sal.binding.model.api.ParameterizedType;
15 import org.opendaylight.controller.sal.binding.model.api.Type;
16 import org.opendaylight.yangtools.yang.binding.Augmentable;
17
18 public class BuilderClassDescriptor {
19
20     private static final String GET_PREFIX = "get";
21     private static final String JAVA_UTIL = "java.util";
22     private static final String HASH_MAP = "HashMap";
23     private static final String MAP = "Map";
24     private static final String GET_AUGMENTATION_METHOD_NAME = "getAugmentation";
25
26     private final GeneratedType genType;
27     private Map<String, String> imports;
28     private final String packageName;
29     private final String className;
30     private final Set<MethodDeclaration> methods;
31     private final Set<FieldDeclaration> fields;
32     private final List<String> importsNames;
33     private FieldDeclaration augmentField;
34
35     class TypeDeclaration {
36
37         private final static String JAVA_LANG_PREFIX = "java.lang";
38         private final String name;
39         private final TypeDeclaration[] generics;
40
41         public TypeDeclaration(String pkg, String name, TypeDeclaration... generics) {
42             this.name = removeJavaLangPkgName(getRightTypeName(pkg, name));
43             if (generics != null && generics.length > 0) {
44                 this.generics = generics;
45             } else {
46                 this.generics = null;
47             }
48         }
49
50         public TypeDeclaration(final Type type) {
51             if (type == null) {
52                 throw new IllegalArgumentException("Type cannot be NULL");
53             }
54
55             this.name = removeJavaLangPkgName(getRightTypeName(type.getPackageName(), type.getName()));
56             TypeDeclaration[] generics = null;
57             if (type instanceof ParameterizedType) {
58                 final ParameterizedType pType = (ParameterizedType) type;
59                 final Type[] actualTypeArguments = pType.getActualTypeArguments();
60                 generics = new TypeDeclaration[actualTypeArguments.length];
61                 for (int i = 0; i < actualTypeArguments.length; i++) {
62                     generics[i] = new TypeDeclaration(actualTypeArguments[i].getPackageName(),
63                             actualTypeArguments[i].getName());
64                 }
65             }
66             if (generics != null && generics.length > 0) {
67                 this.generics = generics;
68             } else {
69                 this.generics = null;
70             }
71         }
72
73         private String removeJavaLangPkgName(final String typeName) {
74             if (typeName.startsWith(JAVA_LANG_PREFIX)) {
75                 return typeName.substring(typeName.lastIndexOf(Constants.DOT) + 1);
76             }
77             return typeName;
78         }
79
80         private String getRightTypeName(final String pkg, final String name) {
81             if (name == null) {
82                 throw new IllegalArgumentException("Name cannot be NULL!");
83             }
84
85             if (imports == null) {
86                 return name;
87             }
88             final String pkgFromImports = imports.get(name);
89             if (pkgFromImports == null || pkgFromImports.equals(pkg)) {
90                 return name;
91             }
92             return (pkg == null ? "" : pkg) + Constants.DOT + name;
93         }
94
95         public String getName() {
96             return name;
97         }
98
99         public TypeDeclaration[] getGenerics() {
100             return generics;
101         }
102
103     }
104
105     class ParameterDeclaration {
106
107         private final TypeDeclaration type;
108         private final String name;
109
110         public ParameterDeclaration(TypeDeclaration type, String name) {
111             this.type = type;
112             this.name = name;
113         }
114
115         public TypeDeclaration getType() {
116             return type;
117         }
118
119         public String getName() {
120             return name;
121         }
122
123     }
124
125     class MethodDeclaration {
126
127         private final TypeDeclaration returnType;
128         private final String name;
129         private final List<ParameterDeclaration> parameters;
130
131         public MethodDeclaration(final TypeDeclaration returnType, final String name,
132                 final List<ParameterDeclaration> parameters) {
133             this.returnType = returnType;
134             this.name = name;
135             if (parameters != null && !parameters.isEmpty()) {
136                 this.parameters = parameters;
137             } else {
138                 this.parameters = null;
139             }
140         }
141
142         public TypeDeclaration getReturnType() {
143             return returnType;
144         }
145
146         public String getName() {
147             return name;
148         }
149
150         public List<ParameterDeclaration> getParameters() {
151             return parameters;
152         }
153
154     }
155
156     class FieldDeclaration extends ParameterDeclaration {
157
158         public FieldDeclaration(final TypeDeclaration type, final String name) {
159             super(type, name);
160         }
161
162     }
163
164     public BuilderClassDescriptor(final GeneratedType genType) {
165         if (genType == null) {
166             throw new IllegalArgumentException("Generated type reference cannot be NULL!");
167         }
168         this.genType = genType;
169         this.imports = GeneratorUtil.createImports(genType);
170         addToImports(genType.getPackageName(), genType.getName());
171         packageName = genType.getPackageName();
172         className = genType.getName();
173         methods = createMethods();
174         fields = createFieldsFromMethods();
175         importsNames = createImportsNames();
176     }
177
178     private Set<MethodDeclaration> createMethods() {
179         final Set<MethodDeclaration> methods = new LinkedHashSet<>();
180         storeMethodsOfIfc(methods, genType);
181         storeMethodsOfImplementedIfcs(methods, genType.getImplements());
182         return methods;
183     }
184
185     private void storeMethodsOfIfc(final Set<MethodDeclaration> methodStorage, final GeneratedType ifc) {
186         for (MethodSignature methodSignature : ifc.getMethodDefinitions()) {
187             final List<ParameterDeclaration> parameterDeclarations = getParameterDeclarationsFrom(methodSignature
188                     .getParameters());
189             methodStorage.add(new MethodDeclaration(new TypeDeclaration(methodSignature.getReturnType()),
190                     methodSignature.getName(), parameterDeclarations));
191         }
192         if (ifc.getEnclosedTypes() != null && !ifc.getEnclosedTypes().isEmpty()) {
193             addToImports(ifc.getPackageName(), ifc.getName() + ".*");
194         }
195     }
196
197     private List<ParameterDeclaration> getParameterDeclarationsFrom(final List<MethodSignature.Parameter> parameters) {
198         final List<ParameterDeclaration> parameterDeclarations = new ArrayList<>();
199         for (MethodSignature.Parameter mp : parameters) {
200             parameterDeclarations.add(new ParameterDeclaration(new TypeDeclaration(mp.getType()), mp.getName()));
201         }
202         return parameterDeclarations;
203     }
204
205     private void storeMethodsOfImplementedIfcs(final Set<MethodDeclaration> methodStorage,
206             final List<Type> implementedIfcs) {
207         if (implementedIfcs == null || implementedIfcs.isEmpty()) {
208             return;
209         }
210         for (Type implementedIfc : implementedIfcs) {
211             if ((implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))) {
212                 final GeneratedType ifc = ((GeneratedType) implementedIfc);
213                 storeMethodsOfIfc(methodStorage, ifc);
214                 storeMethodsOfImplementedIfcs(methodStorage, ifc.getImplements());
215             } else if (implementedIfc.getFullyQualifiedName().equals(Augmentable.class.getName())) {
216                 for (Method m : Augmentable.class.getMethods()) {
217                     if (m.getName().equals(GET_AUGMENTATION_METHOD_NAME)) {
218                         addToImports(JAVA_UTIL, HASH_MAP);
219                         addToImports(JAVA_UTIL, MAP);
220                         java.lang.reflect.Type returnType = m.getReturnType();
221                         final String fullyQualifiedName = ((Class<?>) returnType).getName();
222                         addToImports(getPackageFrom(fullyQualifiedName), getNameFrom(fullyQualifiedName));
223                         TypeDeclaration augmentMethodType = new TypeDeclaration(getPackageFrom(fullyQualifiedName),
224                                 getNameFrom(fullyQualifiedName), new TypeDeclaration(genType));
225                         augmentField = createFieldFromGetMethod(new MethodDeclaration(augmentMethodType, m.getName(),
226                                 null));
227                     }
228                 }
229             }
230         }
231     }
232
233     private void addToImports(final String pkg, final String name) {
234         if (imports == null) {
235             imports = new LinkedHashMap<>();
236         }
237         if (imports.get(name) == null) {
238             imports.put(name, pkg);
239         }
240     }
241
242     private String getPackageFrom(final String fullyQualifiedName) {
243         final int lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT);
244         return lastDotIndex == -1 ? "" : fullyQualifiedName.substring(0, lastDotIndex);
245     }
246
247     private String getNameFrom(final String fullyQualifiedName) {
248         final int lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT);
249         return lastDotIndex == -1 ? fullyQualifiedName : fullyQualifiedName.substring(lastDotIndex + 1);
250     }
251
252     private Set<FieldDeclaration> createFieldsFromMethods() {
253         final Set<FieldDeclaration> result = new LinkedHashSet<>();
254
255         if (methods == null || methods.isEmpty()) {
256             return result;
257         }
258
259         for (MethodDeclaration m : methods) {
260             final FieldDeclaration createdField = createFieldFromGetMethod(m);
261             if (createdField != null) {
262                 result.add(createdField);
263             }
264         }
265         return result;
266     }
267
268     private FieldDeclaration createFieldFromGetMethod(final MethodDeclaration method) {
269         if (method == null || method.getName() == null || method.getName().isEmpty()) {
270             return null;
271         } else if (method.getName().startsWith(GET_PREFIX)) {
272             final String fieldNameFromMethod = method.getName().substring(GET_PREFIX.length());
273             final String fieldName = Character.toLowerCase(fieldNameFromMethod.charAt(0))
274                     + fieldNameFromMethod.substring(1);
275             return new FieldDeclaration(method.getReturnType(), fieldName);
276         }
277         return null;
278     }
279
280     private List<String> createImportsNames() {
281         final List<String> result = new ArrayList<>();
282
283         if (imports == null || imports.isEmpty()) {
284             return result;
285         }
286
287         for (Map.Entry<String, String> entry : imports.entrySet()) {
288             final String typeName = entry.getKey();
289             final String packageName = entry.getValue();
290             result.add(packageName + Constants.DOT + typeName);
291         }
292         return result;
293     }
294
295     public String getPackageName() {
296         return packageName;
297     }
298
299     /**
300      * @return list of imports or empty list
301      */
302     public List<String> getImportsNames() {
303         return importsNames;
304     }
305
306     public String getClassName() {
307         return className;
308     }
309
310     /**
311      * @return set of methods or empty set
312      */
313     public Set<FieldDeclaration> getFields() {
314         return fields;
315     }
316
317     /**
318      * @return set of methods or empty set
319      */
320     public Set<MethodDeclaration> getMethods() {
321         return methods;
322     }
323
324     /**
325      * @return declaration of augment field or NULL
326      */
327     public FieldDeclaration getAugmentField() {
328         return augmentField;
329     }
330
331 }