Resolve Bug:853 - remove groovy from config code generator.
[controller.git] / opendaylight / config / yang-jmx-generator-plugin / src / main / java / org / opendaylight / controller / config / yangjmxgenerator / plugin / gofactory / AbsModuleGeneratedObjectFactory.java
diff --git a/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/AbsModuleGeneratedObjectFactory.java b/opendaylight/config/yang-jmx-generator-plugin/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/plugin/gofactory/AbsModuleGeneratedObjectFactory.java
new file mode 100644 (file)
index 0000000..aa06cb9
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.config.yangjmxgenerator.plugin.gofactory;
+
+import static java.lang.String.format;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.annotations.Description;
+import org.opendaylight.controller.config.api.runtime.RootRuntimeBeanRegistrator;
+import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.AbstractModuleTemplate;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.IdentityRefModuleField;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Method;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.ModuleField;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.FullyQualifiedName;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.GeneratedObject;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.GeneratedObjectBuilder;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.JavaFileInputBuilder;
+import org.opendaylight.controller.config.yangjmxgenerator.plugin.java.TypeName;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AbsModuleGeneratedObjectFactory {
+
+    public GeneratedObject toGeneratedObject(ModuleMXBeanEntry mbe, Optional<String> copyright) {
+        FullyQualifiedName abstractFQN = new FullyQualifiedName(mbe.getPackageName(), mbe.getAbstractModuleName());
+        Optional<String> classJavaDoc = Optional.fromNullable(mbe.getNullableDescription());
+        AbstractModuleTemplate abstractModuleTemplate = TemplateFactory.abstractModuleTemplateFromMbe(mbe);
+        Optional<String> header = abstractModuleTemplate.getHeaderString();
+
+        List<FullyQualifiedName> implementedInterfaces = new ArrayList<>();
+        for(String implemented: abstractModuleTemplate.getTypeDeclaration().getImplemented()) {
+            implementedInterfaces.add(FullyQualifiedName.fromString(implemented));
+        }
+        Optional<FullyQualifiedName> maybeRegistratorType;
+        if (abstractModuleTemplate.isRuntime()) {
+            maybeRegistratorType = Optional.of(FullyQualifiedName.fromString(abstractModuleTemplate.getRegistratorType()));
+        } else {
+            maybeRegistratorType = Optional.absent();
+        }
+
+        return toGeneratedObject(abstractFQN, copyright, header, classJavaDoc, implementedInterfaces,
+                abstractModuleTemplate.getModuleFields(), maybeRegistratorType, abstractModuleTemplate.getMethods(),
+                mbe.getYangModuleQName());
+    }
+
+    public GeneratedObject toGeneratedObject(FullyQualifiedName abstractFQN,
+                                             Optional<String> copyright,
+                                             Optional<String> header,
+                                             Optional<String> classJavaDoc,
+                                             List<FullyQualifiedName> implementedInterfaces,
+                                             List<ModuleField> moduleFields,
+                                             Optional<FullyQualifiedName> maybeRegistratorType,
+                                             List<? extends Method> methods,
+                                             QName yangModuleQName) {
+        JavaFileInputBuilder b = new JavaFileInputBuilder();
+
+        Annotation moduleQNameAnnotation = Annotation.createModuleQNameANnotation(yangModuleQName);
+        b.addClassAnnotation(moduleQNameAnnotation);
+
+        b.setFqn(abstractFQN);
+        b.setTypeName(TypeName.absClassType);
+
+        b.setCopyright(copyright);
+        b.setHeader(header);
+        b.setClassJavaDoc(classJavaDoc);
+        for(FullyQualifiedName implemented: implementedInterfaces) {
+            b.addImplementsFQN(implemented);
+        }
+        if (classJavaDoc.isPresent()) {
+            b.addClassAnnotation(format("@%s(value=\"%s\")", Description.class.getCanonicalName(), classJavaDoc.get()));
+        }
+
+        // add logger:
+        b.addToBody(getLogger(abstractFQN));
+
+        b.addToBody("//attributes start");
+        for(ModuleField moduleField: moduleFields) {
+            b.addToBody(moduleField.toString() +"\n");
+        }
+
+        b.addToBody("//attributes end");
+
+
+        b.addToBody(getCommonFields(abstractFQN));
+
+
+        b.addToBody(getNewConstructor(abstractFQN));
+        b.addToBody(getCopyFromOldConstructor(abstractFQN));
+
+        b.addToBody(getRuntimeRegistratorCode(maybeRegistratorType));
+        b.addToBody(getValidationMethods(moduleFields));
+
+        b.addToBody(getCachesOfResolvedDependencies(moduleFields));
+        b.addToBody(getCachesOfResolvedIdentityRefs(moduleFields));
+        b.addToBody(getGetInstance(moduleFields));
+        b.addToBody(getReuseLogic(moduleFields, abstractFQN));
+        b.addToBody(getEqualsAndHashCode(abstractFQN));
+
+        b.addToBody(getMethods(methods));
+
+        return new GeneratedObjectBuilder(b.build()).toGeneratedObject();
+    }
+
+    private static String getMethods(List<? extends Method>  methods) {
+        String result = "\n// getters and setters\n";
+        for(Method method: methods) {
+            result += method.toString()+"\n";
+        }
+        return result;
+    }
+
+    private static String getEqualsAndHashCode(FullyQualifiedName abstractFQN) {
+        return "\n"+
+            "@Override\n"+
+            "public boolean equals(Object o) {\n"+
+                "if (this == o) return true;\n"+
+                "if (o == null || getClass() != o.getClass()) return false;\n"+
+                format("%s that = (%1$s) o;\n", abstractFQN.getTypeName())+
+                "return identifier.equals(that.identifier);\n"+
+            "}\n"+
+            "\n"+
+            "@Override\n"+
+            "public int hashCode() {\n"+
+                "return identifier.hashCode();\n"+
+            "}\n";
+    }
+
+    private static String getReuseLogic(List<ModuleField> moduleFields, FullyQualifiedName abstractFQN) {
+        String result = "\n"+
+            format("public boolean canReuseInstance(%s oldModule){\n", abstractFQN.getTypeName())+
+                "// allow reusing of old instance if no parameters was changed\n"+
+                "return isSame(oldModule);\n"+
+            "}\n"+
+            "\n"+
+            format("public %s reuseInstance(%1$s oldInstance){\n", AutoCloseable.class.getCanonicalName())+
+                "// implement if instance reuse should be supported. Override canReuseInstance to change the criteria.\n"+
+                "return oldInstance;\n"+
+            "}\n";
+        // isSame method that detects changed fields
+        result += "\n"+
+            format("public boolean isSame(%s other) {\n", abstractFQN.getTypeName())+
+                "if (other == null) {\n"+
+                    "throw new IllegalArgumentException(\"Parameter 'other' is null\");\n"+
+                "}\n";
+        // loop through fields, do deep equals on each field
+
+        for (ModuleField moduleField : moduleFields) {
+            if (moduleField.isListOfDependencies()) {
+                result += format(
+                    "if (%1$sDependency.equals(other.%1$sDependency) == false) {\n"+
+                        "return false;\n"+
+                    "}\n"+
+                    "for (int idx = 0; idx < %1$sDependency.size(); idx++) {\n"+
+                        "if (%1$sDependency.get(idx) != other.%1$sDependency.get(idx)) {\n"+
+                            "return false;\n"+
+                        "}\n"+
+                    "}\n" ,moduleField.getName());
+            } else if (moduleField.isDependent()) {
+                result += format(
+                    "if (%sDependency != other.%1$sDependency) { // reference to dependency must be same\n"+
+                        "return false;\n"+
+                    "}\n",moduleField.getName());
+            } else {
+                result += format(
+                    "if (java.util.Objects.deepEquals(%s, other.%1$s) == false) {\n"+
+                        "return false;\n"+
+                    "}\n", moduleField.getName());
+            }
+        }
+        result += "\n"+
+                "return true;\n"+
+            "}\n";
+
+        return result;
+    }
+
+    private static String getGetInstance(List<ModuleField> moduleFields) {
+        String result = "\n"+
+            "@Override\n"+
+            format("public final %s getInstance() {\n", AutoCloseable.class.getCanonicalName())+
+                "if(instance==null) {\n";
+        // create instance start
+
+        // loop through dependent fields, use dependency resolver to instantiate dependencies. Do it in loop in case field represents list of dependencies.
+        Map<ModuleField, String> resolveDependenciesMap = new HashMap<>();
+        for(ModuleField moduleField: moduleFields) {
+            if (moduleField.isDependent()) {
+                String str;
+                String osgi = moduleField.getDependency().getSie().getExportedOsgiClassName();
+                if (moduleField.isList()) {
+                    str = format(
+                        "%sDependency = new java.util.ArrayList<%s>();\n"+
+                        "for(javax.management.ObjectName dep : %1$s) {\n"+
+                            "%1$sDependency.add(dependencyResolver.resolveInstance(%2$s.class, dep, %1$sJmxAttribute));\n"+
+                        "}\n", moduleField.getName(), osgi);
+                } else {
+                    str = format(
+                        "%1$sDependency = dependencyResolver.resolveInstance(%2$s.class, %1$s, %1$sJmxAttribute);",
+                        moduleField.getName(), osgi);
+                }
+                resolveDependenciesMap.put(moduleField, str);
+            }
+        }
+
+        // wrap each field resolvation statement with if !=null when dependency is not mandatory
+        for (Map.Entry<ModuleField, String> entry : resolveDependenciesMap.entrySet()) {
+            if (entry.getKey().getDependency().isMandatory() == false) {
+                result += format("if (%s!=null) {\n%s;\n}", entry.getKey().getName(), entry.getValue());
+            } else {
+                result += entry.getValue();
+            }
+        }
+
+        // add code to inject dependency resolver to fields that support it
+        for(ModuleField moduleField: moduleFields) {
+            if (moduleField.isNeedsDepResolver()) {
+                result += format("if (%s!=null){\n", moduleField.getName());
+                if (moduleField.isList()) {
+                    result += format(
+                        "for(%s candidate : %s) {\n"+
+                            "candidate.injectDependencyResolver(dependencyResolver);\n"+
+                        "}\n", moduleField.getGenericInnerType(), moduleField.getName());
+                } else {
+                    result += format("%s.injectDependencyResolver(dependencyResolver);\n", moduleField.getName());
+                }
+                result += "}\n";
+            }
+        }
+
+        // identity refs need to be injected with dependencyResolver and base class
+        for (ModuleField moduleField : moduleFields) {
+            if (moduleField.isIdentityRef()) {
+                result += format("if (%s!=null) {", moduleField.getName());
+                result += format("set%s(%s.resolveIdentity(dependencyResolver, %s.class));",
+                        moduleField.getAttributeName(), moduleField.getName(),
+                        ((IdentityRefModuleField)moduleField).getIdentityBaseClass());
+                result += "}\n";
+            }
+        }
+
+        // create instance end: reuse and recreate logic
+        result +=   "if(oldInstance!=null && canReuseInstance(oldModule)) {\n"+
+                        "instance = reuseInstance(oldInstance);\n"+
+                    "} else {\n"+
+                        "if(oldInstance!=null) {\n"+
+                           "try {\n"+
+                                "oldInstance.close();\n"+
+                            "} catch(Exception e) {\n"+
+                                "logger.error(\"An error occurred while closing old instance \" + oldInstance, e);\n"+
+                            "}\n"+
+                        "}\n"+
+                        "instance = createInstance();\n"+
+                        "if (instance == null) {\n"+
+                            "throw new IllegalStateException(\"Error in createInstance - null is not allowed as return value\");\n"+
+                        "}\n"+
+                    "}\n"+
+                "}\n"+
+                "return instance;\n"+
+            "}\n"+
+            format("public abstract %s createInstance();\n", AutoCloseable.class.getCanonicalName());
+
+        return result;
+    }
+
+    private static String getCommonFields(FullyQualifiedName abstractFQN) {
+        return "\n"+
+            format("private final %s oldModule;\n", abstractFQN.getTypeName())+
+            format("private final %s oldInstance;\n", AutoCloseable.class.getCanonicalName())+
+            format("private %s instance;\n", AutoCloseable.class.getCanonicalName())+
+            format("private final %s dependencyResolver;\n", DependencyResolver.class.getCanonicalName())+
+            format("private final %s identifier;\n", ModuleIdentifier.class.getCanonicalName())+
+            "@Override\n"+
+            format("public %s getIdentifier() {\n", ModuleIdentifier.class.getCanonicalName())+
+                "return identifier;\n"+
+            "}\n";
+    }
+
+    private static String getCachesOfResolvedIdentityRefs(List<ModuleField> moduleFields) {
+        StringBuilder result = new StringBuilder();
+        for (ModuleField moduleField : moduleFields) {
+            if (moduleField.isIdentityRef()) {
+                IdentityRefModuleField field = (IdentityRefModuleField) moduleField;
+                result.append(format("private %s %s;\n", field.getIdentityClassType(), field.getIdentityClassName()));
+            }
+        }
+        return result.toString();
+    }
+
+    private static String getCachesOfResolvedDependencies(List<ModuleField> moduleFields) {
+        StringBuilder result = new StringBuilder();
+        for (ModuleField moduleField: moduleFields) {
+            if (moduleField.isDependent()) {
+                String osgi = moduleField.getDependency().getSie().getExportedOsgiClassName();
+                if (moduleField.isList()) {
+                    result
+                            .append(format("private java.util.List<%s> %sDependency = new java.util.ArrayList<%s>();", osgi, moduleField.getName(), osgi))
+                            .append(format("protected final java.util.List<%s> get%sDependency(){\n", osgi, moduleField.getAttributeName()))
+                            .append(format("return %sDependency;\n", moduleField.getName()))
+                            .append("}\n");
+                } else {
+                    result.append(format(
+                    "private %s %sDependency;\n"+
+                    "protected final %s get%sDependency(){\n"+
+                        "return %sDependency;\n"+
+                    "}",
+                    osgi, moduleField.getName(), osgi, moduleField.getAttributeName(), moduleField.getName()));
+                }
+            }
+        }
+        return result.toString();
+    }
+
+    private static String getRuntimeRegistratorCode(Optional<FullyQualifiedName> maybeRegistratorType) {
+        if (maybeRegistratorType.isPresent()) {
+            String registratorType = maybeRegistratorType.get().toString();
+
+            return "\n"+
+                format("private %s rootRuntimeBeanRegistratorWrapper;\n", registratorType)+
+                "\n"+
+                format("public %s getRootRuntimeBeanRegistratorWrapper(){\n", registratorType)+
+                    "return rootRuntimeBeanRegistratorWrapper;\n"+
+                "}\n"+
+                "\n"+
+                "@Override\n"+
+                format("public void setRuntimeBeanRegistrator(%s rootRuntimeRegistrator){\n", RootRuntimeBeanRegistrator.class.getCanonicalName())+
+                    format("this.rootRuntimeBeanRegistratorWrapper = new %s(rootRuntimeRegistrator);\n", registratorType)+
+                "}\n";
+        } else {
+            return "";
+        }
+    }
+
+    private static String getValidationMethods(List<ModuleField> moduleFields) {
+        String result = "\n"+
+            "@Override\n"+
+            "public void validate() {\n";
+        // validate each mandatory dependency
+        for(ModuleField moduleField: moduleFields) {
+            if (moduleField.isDependent() && moduleField.getDependency().isMandatory()) {
+                if (moduleField.isList()) {
+                    result += "" +
+                            format("for(javax.management.ObjectName dep : %s) {\n", moduleField.getName()) +
+                            format("    dependencyResolver.validateDependency(%s.class, dep, %sJmxAttribute);\n",
+                                    moduleField.getDependency().getSie().getFullyQualifiedName(), moduleField.getName()) +
+                            "}\n";
+                } else {
+                    result += format("dependencyResolver.validateDependency(%s.class, %s, %sJmxAttribute);",
+                            moduleField.getDependency().getSie().getFullyQualifiedName(), moduleField.getName(), moduleField.getName());
+                }
+            }
+        }
+        result += "\n"+
+                "customValidation();\n"+
+            "}\n"+
+            "\n"+
+            "protected void customValidation() {\n"+
+            "}\n";
+        return result;
+    }
+
+    private static String getLogger(FullyQualifiedName fqn) {
+        return format("private static final %s logger = %s.getLogger(%s.class);",
+                Logger.class.getCanonicalName(), LoggerFactory.class.getCanonicalName(), fqn);
+    }
+
+    // assumes that each parameter name corresponds to an field in this class, constructs lines setting this.field = field;
+    private static String getConstructorStart(FullyQualifiedName fqn,
+                                              LinkedHashMap<String, String> parameters, String after) {
+        String paramString = Joiner.on(",").withKeyValueSeparator(" ").join(parameters);
+        String setters = "";
+        for (String paramName : parameters.values()) {
+            setters += format("this.%s = %1$s;\n", paramName);
+        }
+        return format("public %s(", fqn.getTypeName()) +
+                paramString +
+                ") {\n" +
+                setters +
+                after +
+                "}\n";
+    }
+
+    private static String getNewConstructor(FullyQualifiedName abstractFQN) {
+        LinkedHashMap<String, String> parameters = new LinkedHashMap<>();
+        parameters.put(ModuleIdentifier.class.getCanonicalName(), "identifier");
+        parameters.put(DependencyResolver.class.getCanonicalName(), "dependencyResolver");
+
+        String setToNulls = "this.oldInstance=null;\n;" +
+                "this.oldModule=null;\n";
+        return getConstructorStart(abstractFQN, parameters, setToNulls);
+    }
+
+    private static String getCopyFromOldConstructor(FullyQualifiedName abstractFQN) {
+        LinkedHashMap<String, String> parameters = new LinkedHashMap<>();
+        parameters.put(ModuleIdentifier.class.getCanonicalName(), "identifier");
+        parameters.put(DependencyResolver.class.getCanonicalName(), "dependencyResolver");
+        parameters.put(abstractFQN.getTypeName(), "oldModule");
+        parameters.put(AutoCloseable.class.getCanonicalName(), "oldInstance");
+        return getConstructorStart(abstractFQN, parameters, "");
+    }
+}