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