2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.config.yangjmxgenerator.plugin.gofactory;
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.lang.String.format;
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;
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;
45 public class AbsModuleGeneratedObjectFactory {
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();
53 List<FullyQualifiedName> implementedInterfaces =
54 Lists.transform(abstractModuleTemplate.getTypeDeclaration().getImplemented(),
55 FullyQualifiedName::fromString);
57 Optional<FullyQualifiedName> extended =
58 Optional.fromNullable(
60 Collections2.transform(abstractModuleTemplate.getTypeDeclaration().getExtended(),
61 FullyQualifiedName::fromString), null));
63 Optional<FullyQualifiedName> maybeRegistratorType;
64 if (abstractModuleTemplate.isRuntime()) {
65 maybeRegistratorType = Optional.of(FullyQualifiedName.fromString(abstractModuleTemplate.getRegistratorType()));
67 maybeRegistratorType = Optional.absent();
70 return toGeneratedObject(abstractFQN, copyright, header, classJavaDoc, extended, implementedInterfaces,
71 abstractModuleTemplate.getModuleFields(), maybeRegistratorType, abstractModuleTemplate.getMethods(),
72 mbe.getYangModuleQName());
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();
87 Annotation moduleQNameAnnotation = Annotation.createModuleQNameANnotation(yangModuleQName);
88 b.addClassAnnotation(moduleQNameAnnotation);
90 b.setFqn(abstractFQN);
91 b.setTypeName(TypeName.absClassType);
93 b.setCopyright(copyright);
95 b.setClassJavaDoc(classJavaDoc);
96 for(FullyQualifiedName implemented: implementedInterfaces) {
97 b.addImplementsFQN(implemented);
99 if(extended.isPresent()) {
100 b.addExtendsFQN(extended.get());
102 if (classJavaDoc.isPresent()) {
103 b.addClassAnnotation(format("@%s(value=\"%s\")", Description.class.getCanonicalName(), classJavaDoc.get()));
107 b.addToBody(getLoggerDefinition(abstractFQN));
109 b.addToBody("//attributes start");
110 for(ModuleField moduleField: moduleFields) {
111 b.addToBody(moduleField.toString() +"\n");
114 b.addToBody("//attributes end");
117 b.addToBody(getNewConstructor(abstractFQN));
118 b.addToBody(getCopyFromOldConstructor(abstractFQN));
120 b.addToBody(getRuntimeRegistratorCode(maybeRegistratorType));
121 b.addToBody(getValidationMethods(moduleFields));
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));
129 b.addToBody(getMethods(methods));
130 b.addToBody(getGetLogger());
132 return new GeneratedObjectBuilder(b.build()).toGeneratedObject();
135 private static String getMethods(List<? extends Method> methods) {
136 StringBuilder result = new StringBuilder("\n// getters and setters\n");
137 for (Method method : methods) {
138 result.append(method).append("\n");
140 return result.toString();
143 private static String getEqualsAndHashCode(FullyQualifiedName abstractFQN) {
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"+
154 "public int hashCode() {\n"+
155 "return identifier.hashCode();\n"+
159 private static String getReuseLogic(List<ModuleField> moduleFields, FullyQualifiedName abstractFQN) {
160 StringBuilder result = new StringBuilder("\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" +
166 format("public %s reuseInstance(%1$s oldInstance){\n", AutoCloseable.class.getCanonicalName()) +
167 "// implement if instance reuse should be supported." +
168 "Override canReuseInstance to change the criteria.\n" +
169 "return oldInstance;\n" +
171 // isSame method that detects changed fields
173 .append(format("public boolean isSame(%s other) {\n", abstractFQN.getTypeName()))
174 .append("if (other == null) {\n")
175 .append("throw new IllegalArgumentException(\"Parameter 'other' is null\");\n")
178 // loop through fields, do deep equals on each field
179 for (ModuleField moduleField : moduleFields) {
180 result.append(format(
181 "if (!java.util.Objects.deepEquals(%s, other.%1$s)) {\n" +
183 "}\n", moduleField.getName()));
185 if (moduleField.isListOfDependencies()) {
186 result.append(format(
187 "for (int idx = 0; idx < %1$s.size(); idx++) {\n" +
188 "if (!dependencyResolver.canReuseDependency(%1$s.get(idx), %1$sJmxAttribute)) {\n" +
191 "}\n", moduleField.getName()));
192 } else if (moduleField.isDependent()) {
193 result.append(format(
194 // If a reference is null (ie optional reference) it makes no sens to call canReuse on it
195 // In such case we continue in the isSame method because if we have null here, the previous
196 // value was null as well
197 // If the previous value was not null and current is or vice verse, the deepEquals comparison
198 // would return false
199 "if(%1$s!= null) {\n" +
200 "// reference to dependency must be reusable as well\n" +
201 "if (!dependencyResolver.canReuseDependency(%1$s, %1$sJmxAttribute)) {\n" +
204 "}\n", moduleField.getName()));
212 return result.toString();
215 private static String getResolveDependencies(final List<ModuleField> moduleFields) {
216 // loop through dependent fields, use dependency resolver to instantiate dependencies. Do it in loop in case field represents list of dependencies.
217 Map<ModuleField, String> resolveDependenciesMap = new HashMap<>();
218 for(ModuleField moduleField: moduleFields) {
219 if (moduleField.isDependent()) {
221 String osgi = moduleField.getDependency().getSie().getExportedOsgiClassName();
222 if (moduleField.isList()) {
224 "%sDependency = new java.util.ArrayList<%s>();\n"+
225 "for(javax.management.ObjectName dep : %1$s) {\n"+
226 "%1$sDependency.add(dependencyResolver.resolveInstance(%2$s.class, dep, %1$sJmxAttribute));\n"+
227 "}\n", moduleField.getName(), osgi);
230 "%1$sDependency = dependencyResolver.resolveInstance(%2$s.class, %1$s, %1$sJmxAttribute);\n",
231 moduleField.getName(), osgi);
233 resolveDependenciesMap.put(moduleField, str);
237 StringBuilder result = new StringBuilder("\n" +
238 "protected final void resolveDependencies() {\n");
239 // wrap each field resolvation statement with if !=null when dependency is not mandatory
240 for (Map.Entry<ModuleField, String> entry : resolveDependenciesMap.entrySet()) {
241 if (!entry.getKey().getDependency().isMandatory()) {
242 checkState(entry.getValue().endsWith(";\n"));
243 result.append(format("if (%s!=null) {\n%s}\n", entry.getKey().getName(), entry.getValue()));
245 result.append(entry.getValue());
249 // add code to inject dependency resolver to fields that support it
250 for(ModuleField moduleField: moduleFields) {
251 if (moduleField.isNeedsDepResolver()) {
252 result.append(format("if (%s!=null){\n", moduleField.getName()));
253 if (moduleField.isList()) {
254 result.append(format(
255 "for(%s candidate : %s) {\n" +
256 "candidate.injectDependencyResolver(dependencyResolver);\n" +
257 "}\n", moduleField.getGenericInnerType(), moduleField.getName()));
259 result.append(format("%s.injectDependencyResolver(dependencyResolver);\n", moduleField.getName()));
261 result.append("}\n");
265 // identity refs need to be injected with dependencyResolver and base class
266 for (ModuleField moduleField : moduleFields) {
267 if (moduleField.isIdentityRef()) {
268 result.append(format("if (%s!=null) {", moduleField.getName()));
269 result.append(format("set%s(%s.resolveIdentity(dependencyResolver, %s.class));",
270 moduleField.getAttributeName(), moduleField.getName(),
271 ((IdentityRefModuleField) moduleField).getIdentityBaseClass()));
272 result.append("}\n");
275 result.append("}\n");
276 return result.toString();
279 private static String getCachesOfResolvedIdentityRefs(List<ModuleField> moduleFields) {
280 StringBuilder result = new StringBuilder();
281 for (ModuleField moduleField : moduleFields) {
282 if (moduleField.isIdentityRef()) {
283 IdentityRefModuleField field = (IdentityRefModuleField) moduleField;
284 result.append(format("private %s %s;\n", field.getIdentityClassType(), field.getIdentityClassName()));
287 return result.toString();
290 private static String getCachesOfResolvedDependencies(List<ModuleField> moduleFields) {
291 StringBuilder result = new StringBuilder();
292 for (ModuleField moduleField: moduleFields) {
293 if (moduleField.isDependent()) {
294 String osgi = moduleField.getDependency().getSie().getExportedOsgiClassName();
295 if (moduleField.isList()) {
297 .append(format("private java.util.List<%s> %sDependency = new java.util.ArrayList<%s>();", osgi, moduleField.getName(), osgi))
298 .append(format("protected final java.util.List<%s> get%sDependency(){\n", osgi, moduleField.getAttributeName()))
299 .append(format("return %sDependency;\n", moduleField.getName()))
302 result.append(format(
303 "private %s %sDependency;\n"+
304 "protected final %s get%sDependency(){\n"+
305 "return %sDependency;\n"+
307 osgi, moduleField.getName(), osgi, moduleField.getAttributeName(), moduleField.getName()));
311 return result.toString();
314 private static String getRuntimeRegistratorCode(Optional<FullyQualifiedName> maybeRegistratorType) {
315 if (maybeRegistratorType.isPresent()) {
316 String registratorType = maybeRegistratorType.get().toString();
319 format("private %s rootRuntimeBeanRegistratorWrapper;\n", registratorType)+
321 format("public %s getRootRuntimeBeanRegistratorWrapper(){\n", registratorType)+
322 "return rootRuntimeBeanRegistratorWrapper;\n"+
326 format("public void setRuntimeBeanRegistrator(%s rootRuntimeRegistrator){\n", RootRuntimeBeanRegistrator.class.getCanonicalName())+
327 format("this.rootRuntimeBeanRegistratorWrapper = new %s(rootRuntimeRegistrator);\n", registratorType)+
334 private static String getValidationMethods(List<ModuleField> moduleFields) {
335 StringBuilder result = new StringBuilder("\n" +
337 "public void validate() {\n");
338 // validate each mandatory dependency
339 for (ModuleField moduleField : moduleFields) {
340 if (moduleField.isDependent()) {
341 if (moduleField.isList()) {
342 result.append(format("for(javax.management.ObjectName dep : %s) {\n", moduleField.getName()))
343 .append(format(" dependencyResolver.validateDependency(%s.class, dep, %sJmxAttribute);" +
345 moduleField.getDependency().getSie().getFullyQualifiedName(),
346 moduleField.getName()))
349 if (!moduleField.getDependency().isMandatory()) {
350 result.append(format("if(%s != null) {\n", moduleField.getName()));
352 result.append(format("dependencyResolver.validateDependency(%s.class, %s, %sJmxAttribute);\n",
353 moduleField.getDependency().getSie().getFullyQualifiedName(), moduleField.getName(),
354 moduleField.getName()));
355 if (!moduleField.getDependency().isMandatory()) {
356 result.append("}\n");
362 "customValidation();\n" +
365 "protected void customValidation() {\n" +
367 return result.toString();
370 private static String getLoggerDefinition(FullyQualifiedName fqn) {
371 return format("private static final %s LOG = %s.getLogger(%s.class);",
372 Logger.class.getCanonicalName(), LoggerFactory.class.getCanonicalName(), fqn);
375 // assumes that each parameter name corresponds to an field in this class, constructs lines setting this.field = field;
376 private static String getConstructorStart(FullyQualifiedName fqn,
377 LinkedHashMap<String, String> parameters, String after) {
378 String paramString = Joiner.on(",").withKeyValueSeparator(" ").join(parameters);
379 return format("public %s(", fqn.getTypeName()) +
386 private static String getNewConstructor(FullyQualifiedName abstractFQN) {
387 LinkedHashMap<String, String> parameters = new LinkedHashMap<>();
388 parameters.put(ModuleIdentifier.class.getCanonicalName(), "identifier");
389 parameters.put(DependencyResolver.class.getCanonicalName(), "dependencyResolver");
390 String init = "super(identifier, dependencyResolver);\n";
391 return getConstructorStart(abstractFQN, parameters, init);
394 private static String getCopyFromOldConstructor(FullyQualifiedName abstractFQN) {
395 LinkedHashMap<String, String> parameters = new LinkedHashMap<>();
396 parameters.put(ModuleIdentifier.class.getCanonicalName(), "identifier");
397 parameters.put(DependencyResolver.class.getCanonicalName(), "dependencyResolver");
398 parameters.put(abstractFQN.getTypeName(), "oldModule");
399 parameters.put(AutoCloseable.class.getCanonicalName(), "oldInstance");
400 String init = "super(identifier, dependencyResolver, oldModule, oldInstance);\n";
401 return getConstructorStart(abstractFQN, parameters, init);
404 public String getGetLogger() {
405 return new MethodDefinition(Logger.class.getCanonicalName(), "getLogger", Collections.<Field>emptyList(), "return LOG;").toString();