b562a432df56eb817addac45dbc2c893898669b8
[controller.git] / opendaylight / config / yang-jmx-generator-plugin / src / main / java / org / opendaylight / controller / config / yangjmxgenerator / plugin / gofactory / AbsModuleGeneratedObjectFactory.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.config.yangjmxgenerator.plugin.gofactory;
9
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.lang.String.format;
12
13 import com.google.common.base.Joiner;
14 import com.google.common.base.Optional;
15 import com.google.common.collect.Collections2;
16 import com.google.common.collect.Iterables;
17 import com.google.common.collect.Lists;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.LinkedHashMap;
21 import java.util.List;
22 import java.util.Map;
23 import org.opendaylight.controller.config.api.DependencyResolver;
24 import org.opendaylight.controller.config.api.ModuleIdentifier;
25 import org.opendaylight.controller.config.api.annotations.Description;
26 import org.opendaylight.controller.config.api.runtime.RootRuntimeBeanRegistrator;
27 import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
28 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.AbstractModuleTemplate;
29 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory;
30 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation;
31 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field;
32 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.IdentityRefModuleField;
33 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Method;
34 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDefinition;
35 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.ModuleField;
36 import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.FullyQualifiedName;
37 import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.GeneratedObject;
38 import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.GeneratedObjectBuilder;
39 import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.JavaFileInputBuilder;
40 import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.TypeName;
41 import org.opendaylight.yangtools.yang.common.QName;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 public class AbsModuleGeneratedObjectFactory {
46
47     public GeneratedObject toGeneratedObject(ModuleMXBeanEntry mbe, Optional<String> copyright) {
48         FullyQualifiedName abstractFQN = new FullyQualifiedName(mbe.getPackageName(), mbe.getAbstractModuleName());
49         Optional<String> classJavaDoc = Optional.fromNullable(mbe.getNullableDescription());
50         AbstractModuleTemplate abstractModuleTemplate = TemplateFactory.abstractModuleTemplateFromMbe(mbe);
51         Optional<String> header = abstractModuleTemplate.getHeaderString();
52
53         List<FullyQualifiedName> implementedInterfaces =
54                 Lists.transform(abstractModuleTemplate.getTypeDeclaration().getImplemented(),
55                         FullyQualifiedName::fromString);
56
57         Optional<FullyQualifiedName> extended =
58                 Optional.fromNullable(
59                         Iterables.getFirst(
60                                 Collections2.transform(abstractModuleTemplate.getTypeDeclaration().getExtended(),
61                                         FullyQualifiedName::fromString), null));
62
63         Optional<FullyQualifiedName> maybeRegistratorType;
64         if (abstractModuleTemplate.isRuntime()) {
65             maybeRegistratorType = Optional.of(FullyQualifiedName.fromString(abstractModuleTemplate.getRegistratorType()));
66         } else {
67             maybeRegistratorType = Optional.absent();
68         }
69
70         return toGeneratedObject(abstractFQN, copyright, header, classJavaDoc, extended, implementedInterfaces,
71                 abstractModuleTemplate.getModuleFields(), maybeRegistratorType, abstractModuleTemplate.getMethods(),
72                 mbe.getYangModuleQName());
73     }
74
75     public GeneratedObject toGeneratedObject(FullyQualifiedName abstractFQN,
76                                              Optional<String> copyright,
77                                              Optional<String> header,
78                                              Optional<String> classJavaDoc,
79                                              Optional<FullyQualifiedName> extended,
80                                              List<FullyQualifiedName> implementedInterfaces,
81                                              List<ModuleField> moduleFields,
82                                              Optional<FullyQualifiedName> maybeRegistratorType,
83                                              List<? extends Method> methods,
84                                              QName yangModuleQName) {
85         JavaFileInputBuilder b = new JavaFileInputBuilder();
86
87         Annotation moduleQNameAnnotation = Annotation.createModuleQNameANnotation(yangModuleQName);
88         b.addClassAnnotation(moduleQNameAnnotation);
89
90         b.setFqn(abstractFQN);
91         b.setTypeName(TypeName.absClassType);
92
93         b.setCopyright(copyright);
94         b.setHeader(header);
95         b.setClassJavaDoc(classJavaDoc);
96         for(FullyQualifiedName implemented: implementedInterfaces) {
97             b.addImplementsFQN(implemented);
98         }
99         if(extended.isPresent()) {
100             b.addExtendsFQN(extended.get());
101         }
102         if (classJavaDoc.isPresent()) {
103             b.addClassAnnotation(format("@%s(value=\"%s\")", Description.class.getCanonicalName(), classJavaDoc.get()));
104         }
105
106         // add logger:
107         b.addToBody(getLoggerDefinition(abstractFQN));
108
109         b.addToBody("//attributes start");
110         for(ModuleField moduleField: moduleFields) {
111             b.addToBody(moduleField.toString() +"\n");
112         }
113
114         b.addToBody("//attributes end");
115
116
117         b.addToBody(getNewConstructor(abstractFQN));
118         b.addToBody(getCopyFromOldConstructor(abstractFQN));
119
120         b.addToBody(getRuntimeRegistratorCode(maybeRegistratorType));
121         b.addToBody(getValidationMethods(moduleFields));
122
123         b.addToBody(getCachesOfResolvedDependencies(moduleFields));
124         b.addToBody(getCachesOfResolvedIdentityRefs(moduleFields));
125         b.addToBody(getResolveDependencies(moduleFields));
126         b.addToBody(getReuseLogic(moduleFields, abstractFQN));
127         b.addToBody(getEqualsAndHashCode(abstractFQN));
128
129         b.addToBody(getMethods(methods));
130         b.addToBody(getGetLogger());
131
132         return new GeneratedObjectBuilder(b.build()).toGeneratedObject();
133     }
134
135     private static String getMethods(List<? extends Method>  methods) {
136         String result = "\n// getters and setters\n";
137         for(Method method: methods) {
138             result += method.toString()+"\n";
139         }
140         return result;
141     }
142
143     private static String getEqualsAndHashCode(FullyQualifiedName abstractFQN) {
144         return "\n"+
145             "@Override\n"+
146             "public boolean equals(Object o) {\n"+
147                 "if (this == o) { return true; }\n"+
148                 "if (o == null || getClass() != o.getClass()) { return false; }\n"+
149                 format("%s that = (%1$s) o;\n", abstractFQN.getTypeName())+
150                 "return identifier.equals(that.identifier);\n"+
151             "}\n"+
152             "\n"+
153             "@Override\n"+
154             "public int hashCode() {\n"+
155                 "return identifier.hashCode();\n"+
156             "}\n";
157     }
158
159     private static String getReuseLogic(List<ModuleField> moduleFields, FullyQualifiedName abstractFQN) {
160         String result = "\n"+
161             format("public boolean canReuseInstance(%s oldModule){\n", abstractFQN.getTypeName())+
162                 "// allow reusing of old instance if no parameters was changed\n"+
163                 "return isSame(oldModule);\n"+
164             "}\n"+
165             "\n"+
166             format("public %s reuseInstance(%1$s oldInstance){\n", AutoCloseable.class.getCanonicalName())+
167                 "// implement if instance reuse should be supported. Override canReuseInstance to change the criteria.\n"+
168                 "return oldInstance;\n"+
169             "}\n";
170         // isSame method that detects changed fields
171         result += "\n"+
172             format("public boolean isSame(%s other) {\n", abstractFQN.getTypeName())+
173                 "if (other == null) {\n"+
174                     "throw new IllegalArgumentException(\"Parameter 'other' is null\");\n"+
175                 "}\n";
176         // loop through fields, do deep equals on each field
177
178         for (ModuleField moduleField : moduleFields) {
179             result += format(
180                 "if (!java.util.Objects.deepEquals(%s, other.%1$s)) {\n"+
181                     "return false;\n"+
182                 "}\n", moduleField.getName());
183
184             if (moduleField.isListOfDependencies()) {
185                 result += format(
186                         "for (int idx = 0; idx < %1$s.size(); idx++) {\n"+
187                             "if (!dependencyResolver.canReuseDependency(%1$s.get(idx), %1$sJmxAttribute)) {\n"+
188                                 "return false;\n"+
189                             "}\n"+
190                         "}\n" , moduleField.getName());
191             } else if (moduleField.isDependent()) {
192                 result += format(
193                         // If a reference is null (ie optional reference) it makes no sens to call canReuse on it
194                         // In such case we continue in the isSame method because if we have null here, the previous value was null as well
195                         // If the previous value was not null and current is or vice verse, the deepEquals comparison would return false
196                         "if(%1$s!= null) {\n" +
197                             "if (!dependencyResolver.canReuseDependency(%1$s, %1$sJmxAttribute)) { // reference to dependency must be reusable as well\n" +
198                                 "return false;\n" +
199                             "}\n" +
200                         "}\n", moduleField.getName());
201             }
202         }
203
204         result += "\n"+
205                 "return true;\n"+
206             "}\n";
207
208         return result;
209     }
210
211     private static String getResolveDependencies(final List<ModuleField> moduleFields) {
212         // loop through dependent fields, use dependency resolver to instantiate dependencies. Do it in loop in case field represents list of dependencies.
213         Map<ModuleField, String> resolveDependenciesMap = new HashMap<>();
214         for(ModuleField moduleField: moduleFields) {
215             if (moduleField.isDependent()) {
216                 String str;
217                 String osgi = moduleField.getDependency().getSie().getExportedOsgiClassName();
218                 if (moduleField.isList()) {
219                     str = format(
220                             "%sDependency = new java.util.ArrayList<%s>();\n"+
221                                     "for(javax.management.ObjectName dep : %1$s) {\n"+
222                                     "%1$sDependency.add(dependencyResolver.resolveInstance(%2$s.class, dep, %1$sJmxAttribute));\n"+
223                                     "}\n", moduleField.getName(), osgi);
224                 } else {
225                     str = format(
226                             "%1$sDependency = dependencyResolver.resolveInstance(%2$s.class, %1$s, %1$sJmxAttribute);\n",
227                             moduleField.getName(), osgi);
228                 }
229                 resolveDependenciesMap.put(moduleField, str);
230             }
231         }
232
233         String result = "\n"
234                 + "protected final void resolveDependencies() {\n";
235         // wrap each field resolvation statement with if !=null when dependency is not mandatory
236         for (Map.Entry<ModuleField, String> entry : resolveDependenciesMap.entrySet()) {
237             if (entry.getKey().getDependency().isMandatory() == false) {
238                 checkState(entry.getValue().endsWith(";\n"));
239                 result += format("if (%s!=null) {\n%s}\n", entry.getKey().getName(), entry.getValue());
240             } else {
241                 result += entry.getValue();
242             }
243         }
244
245         // add code to inject dependency resolver to fields that support it
246         for(ModuleField moduleField: moduleFields) {
247             if (moduleField.isNeedsDepResolver()) {
248                 result += format("if (%s!=null){\n", moduleField.getName());
249                 if (moduleField.isList()) {
250                     result += format(
251                             "for(%s candidate : %s) {\n"+
252                                     "candidate.injectDependencyResolver(dependencyResolver);\n"+
253                                     "}\n", moduleField.getGenericInnerType(), moduleField.getName());
254                 } else {
255                     result += format("%s.injectDependencyResolver(dependencyResolver);\n", moduleField.getName());
256                 }
257                 result += "}\n";
258             }
259         }
260
261         // identity refs need to be injected with dependencyResolver and base class
262         for (ModuleField moduleField : moduleFields) {
263             if (moduleField.isIdentityRef()) {
264                 result += format("if (%s!=null) {", moduleField.getName());
265                 result += format("set%s(%s.resolveIdentity(dependencyResolver, %s.class));",
266                         moduleField.getAttributeName(), moduleField.getName(),
267                         ((IdentityRefModuleField)moduleField).getIdentityBaseClass());
268                 result += "}\n";
269             }
270         }
271         result += "}\n";
272         return result;
273     }
274
275     private static String getCachesOfResolvedIdentityRefs(List<ModuleField> moduleFields) {
276         StringBuilder result = new StringBuilder();
277         for (ModuleField moduleField : moduleFields) {
278             if (moduleField.isIdentityRef()) {
279                 IdentityRefModuleField field = (IdentityRefModuleField) moduleField;
280                 result.append(format("private %s %s;\n", field.getIdentityClassType(), field.getIdentityClassName()));
281             }
282         }
283         return result.toString();
284     }
285
286     private static String getCachesOfResolvedDependencies(List<ModuleField> moduleFields) {
287         StringBuilder result = new StringBuilder();
288         for (ModuleField moduleField: moduleFields) {
289             if (moduleField.isDependent()) {
290                 String osgi = moduleField.getDependency().getSie().getExportedOsgiClassName();
291                 if (moduleField.isList()) {
292                     result
293                             .append(format("private java.util.List<%s> %sDependency = new java.util.ArrayList<%s>();", osgi, moduleField.getName(), osgi))
294                             .append(format("protected final java.util.List<%s> get%sDependency(){\n", osgi, moduleField.getAttributeName()))
295                             .append(format("return %sDependency;\n", moduleField.getName()))
296                             .append("}\n");
297                 } else {
298                     result.append(format(
299                         "private %s %sDependency;\n"+
300                         "protected final %s get%sDependency(){\n"+
301                             "return %sDependency;\n"+
302                         "}",
303                         osgi, moduleField.getName(), osgi, moduleField.getAttributeName(), moduleField.getName()));
304                 }
305             }
306         }
307         return result.toString();
308     }
309
310     private static String getRuntimeRegistratorCode(Optional<FullyQualifiedName> maybeRegistratorType) {
311         if (maybeRegistratorType.isPresent()) {
312             String registratorType = maybeRegistratorType.get().toString();
313
314             return "\n"+
315                 format("private %s rootRuntimeBeanRegistratorWrapper;\n", registratorType)+
316                 "\n"+
317                 format("public %s getRootRuntimeBeanRegistratorWrapper(){\n", registratorType)+
318                     "return rootRuntimeBeanRegistratorWrapper;\n"+
319                 "}\n"+
320                 "\n"+
321                 "@Override\n"+
322                 format("public void setRuntimeBeanRegistrator(%s rootRuntimeRegistrator){\n", RootRuntimeBeanRegistrator.class.getCanonicalName())+
323                     format("this.rootRuntimeBeanRegistratorWrapper = new %s(rootRuntimeRegistrator);\n", registratorType)+
324                 "}\n";
325         } else {
326             return "";
327         }
328     }
329
330     private static String getValidationMethods(List<ModuleField> moduleFields) {
331         String result = "\n"+
332             "@Override\n"+
333             "public void validate() {\n";
334         // validate each mandatory dependency
335         for(ModuleField moduleField: moduleFields) {
336             if (moduleField.isDependent()) {
337                 if (moduleField.isList()) {
338                     result += "" +
339                             format("for(javax.management.ObjectName dep : %s) {\n", moduleField.getName()) +
340                             format("    dependencyResolver.validateDependency(%s.class, dep, %sJmxAttribute);\n",
341                                     moduleField.getDependency().getSie().getFullyQualifiedName(), moduleField.getName()) +
342                             "}\n";
343                 } else {
344                     if(moduleField.getDependency().isMandatory() == false) {
345                         result += format("if(%s != null) {\n", moduleField.getName());
346                     }
347                     result += format("dependencyResolver.validateDependency(%s.class, %s, %sJmxAttribute);\n",
348                             moduleField.getDependency().getSie().getFullyQualifiedName(), moduleField.getName(), moduleField.getName());
349                     if(moduleField.getDependency().isMandatory() == false) {
350                         result += "}\n";
351                     }
352                 }
353             }
354         }
355         result += "\n"+
356                 "customValidation();\n"+
357             "}\n"+
358             "\n"+
359             "protected void customValidation() {\n"+
360             "}\n";
361         return result;
362     }
363
364     private static String getLoggerDefinition(FullyQualifiedName fqn) {
365         return format("private static final %s LOGGER = %s.getLogger(%s.class);",
366                 Logger.class.getCanonicalName(), LoggerFactory.class.getCanonicalName(), fqn);
367     }
368
369     // assumes that each parameter name corresponds to an field in this class, constructs lines setting this.field = field;
370     private static String getConstructorStart(FullyQualifiedName fqn,
371                                               LinkedHashMap<String, String> parameters, String after) {
372         String paramString = Joiner.on(",").withKeyValueSeparator(" ").join(parameters);
373         return format("public %s(", fqn.getTypeName()) +
374                 paramString +
375                 ") {\n" +
376                 after +
377                 "}\n";
378     }
379
380     private static String getNewConstructor(FullyQualifiedName abstractFQN) {
381         LinkedHashMap<String, String> parameters = new LinkedHashMap<>();
382         parameters.put(ModuleIdentifier.class.getCanonicalName(), "identifier");
383         parameters.put(DependencyResolver.class.getCanonicalName(), "dependencyResolver");
384         String init = "super(identifier, dependencyResolver);\n";
385         return getConstructorStart(abstractFQN, parameters, init);
386     }
387
388     private static String getCopyFromOldConstructor(FullyQualifiedName abstractFQN) {
389         LinkedHashMap<String, String> parameters = new LinkedHashMap<>();
390         parameters.put(ModuleIdentifier.class.getCanonicalName(), "identifier");
391         parameters.put(DependencyResolver.class.getCanonicalName(), "dependencyResolver");
392         parameters.put(abstractFQN.getTypeName(), "oldModule");
393         parameters.put(AutoCloseable.class.getCanonicalName(), "oldInstance");
394         String init = "super(identifier, dependencyResolver, oldModule, oldInstance);\n";
395         return getConstructorStart(abstractFQN, parameters, init);
396     }
397
398     public String getGetLogger() {
399         return new MethodDefinition(Logger.class.getCanonicalName(), "getLogger", Collections.<Field>emptyList(), "return LOGGER;").toString();
400     }
401 }