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