/* * 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 com.google.common.base.Optional 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.* import org.opendaylight.yangtools.yang.common.QName import org.slf4j.Logger import org.slf4j.LoggerFactory public class AbsModuleGeneratedObjectFactory { public GeneratedObject toGeneratedObject(ModuleMXBeanEntry mbe, Optional copyright) { FullyQualifiedName abstractFQN = new FullyQualifiedName(mbe.getPackageName(), mbe.getAbstractModuleName()) Optional classJavaDoc = Optional.fromNullable(mbe.getNullableDescription()) AbstractModuleTemplate abstractModuleTemplate = TemplateFactory.abstractModuleTemplateFromMbe(mbe) Optional header = abstractModuleTemplate.headerString; List implementedInterfaces = abstractModuleTemplate.getTypeDeclaration().getImplemented().collect { FullyQualifiedName.fromString(it) } Optional 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.yangModuleQName ) } public GeneratedObject toGeneratedObject(FullyQualifiedName abstractFQN, Optional copyright, Optional header, Optional classJavaDoc, List implementedInterfaces, List moduleFields, Optional maybeRegistratorType, List 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); implementedInterfaces.each { b.addImplementsFQN(it) } if (classJavaDoc.isPresent()) { b.addClassAnnotation("@${Description.canonicalName}(value=\"${classJavaDoc.get()}\")") } // add logger: b.addToBody(getLogger(abstractFQN)); b.addToBody("//attributes start"); b.addToBody(moduleFields.collect { it.toString() }.join("\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 methods) { String result = """ // getters and setters """ result += methods.collect{it.toString()}.join("\n") return result } private static String getEqualsAndHashCode(FullyQualifiedName abstractFQN) { return """ @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ${abstractFQN.typeName} that = (${abstractFQN.typeName}) o; return identifier.equals(that.identifier); } @Override public int hashCode() { return identifier.hashCode(); } """ } private static String getReuseLogic(List moduleFields, FullyQualifiedName abstractFQN) { String result = """ public boolean canReuseInstance(${abstractFQN.typeName} oldModule){ // allow reusing of old instance if no parameters was changed return isSame(oldModule); } public ${AutoCloseable.canonicalName} reuseInstance(${AutoCloseable.canonicalName} oldInstance){ // implement if instance reuse should be supported. Override canReuseInstance to change the criteria. return oldInstance; } """ // isSame method that detects changed fields result += """ public boolean isSame(${abstractFQN.typeName} other) { if (other == null) { throw new IllegalArgumentException("Parameter 'other' is null"); } """ // loop through fields, do deep equals on each field result += moduleFields.collect { field -> if (field.isListOfDependencies()) { return """ if (${field.name}Dependency.equals(other.${field.name}Dependency) == false) { return false; } for (int idx = 0; idx < ${field.name}Dependency.size(); idx++) { if (${field.name}Dependency.get(idx) != other.${field.name}Dependency.get(idx)) { return false; } } """ } else if (field.isDependent()) { return """ if (${field.name}Dependency != other.${field.name}Dependency) { // reference to dependency must be same return false; } """ } else { return """ if (java.util.Objects.deepEquals(${field.name}, other.${field.name}) == false) { return false; } """ } }.join("\n") result += """ return true; } """ return result } private static String getGetInstance(List moduleFields) { String result = """ @Override public final ${AutoCloseable.canonicalName} getInstance() { if(instance==null) { """ // 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 resolveDependenciesMap = moduleFields.findAll { it.isDependent() }.collectEntries { ModuleField field -> [field, field.isList() ? """ ${field.name}Dependency = new java.util.ArrayList<${field.dependency.sie.exportedOsgiClassName}>(); for(javax.management.ObjectName dep : ${field.name}) { ${field.name}Dependency.add(dependencyResolver.resolveInstance(${ field.dependency.sie.exportedOsgiClassName }.class, dep, ${field.name}JmxAttribute)); } """ : """ ${field.name}Dependency = dependencyResolver.resolveInstance(${ field.dependency.sie.exportedOsgiClassName }.class, ${field.name}, ${field.name}JmxAttribute); """ ] } // wrap each field resolvation statement with if !=null when dependency is not mandatory def wrapWithNullCheckClosure = {Map map, predicate -> map.collect { ModuleField key, String value -> predicate(key) ? """ if(${key.name}!=null) { ${value} } """ : value }.join("\n") } result += wrapWithNullCheckClosure(resolveDependenciesMap, {ModuleField key -> key.getDependency().isMandatory() == false} ) // add code to inject dependency resolver to fields that support it Map injectDepsMap = moduleFields.findAll { it.needsDepResolver }.collectEntries { field -> if (field.isList()) { return [field,""" for(${field.genericInnerType} candidate : ${field.name}) { candidate.injectDependencyResolver(dependencyResolver); } """] } else { return [field, "${field.name}.injectDependencyResolver(dependencyResolver);"] } } result += wrapWithNullCheckClosure(injectDepsMap, {true}) // identity refs need to be injected with dependencyResolver and base class Map resolveIdentityMap = moduleFields.findAll { it.isIdentityRef() }.collectEntries { IdentityRefModuleField field -> [field, "set${field.attributeName}(${field.name}.resolveIdentity(dependencyResolver, ${field.identityBaseClass}.class));"] } result += wrapWithNullCheckClosure(resolveIdentityMap, {true}) // create instance end: reuse and recreate logic result += """ if(oldInstance!=null && canReuseInstance(oldModule)) { instance = reuseInstance(oldInstance); } else { if(oldInstance!=null) { try { oldInstance.close(); } catch(Exception e) { logger.error("An error occurred while closing old instance " + oldInstance, e); } } instance = createInstance(); if (instance == null) { throw new IllegalStateException("Error in createInstance - null is not allowed as return value"); } } } return instance; } public abstract ${AutoCloseable.canonicalName} createInstance(); """ return result } private static String getCommonFields(FullyQualifiedName abstractFQN) { return """ private final ${abstractFQN.typeName} oldModule; private final ${AutoCloseable.canonicalName} oldInstance; private ${AutoCloseable.canonicalName} instance; private final ${DependencyResolver.canonicalName} dependencyResolver; private final ${ModuleIdentifier.canonicalName} identifier; @Override public ${ModuleIdentifier.canonicalName} getIdentifier() { return identifier; } """ } private static String getCachesOfResolvedIdentityRefs(List moduleFields) { return moduleFields.findAll { it.isIdentityRef() }.collect { IdentityRefModuleField field -> "private ${field.identityClassType} ${field.identityClassName};" }.join("\n") } private static String getCachesOfResolvedDependencies(List moduleFields) { return moduleFields.findAll { it.dependent }.collect { field -> if (field.isList()) { return """ private java.util.List<${field.dependency.sie.exportedOsgiClassName}> ${ field.name }Dependency = new java.util.ArrayList<${field.dependency.sie.exportedOsgiClassName}>(); protected final java.util.List<${field.dependency.sie.exportedOsgiClassName}> get${ field.attributeName }Dependency(){ return ${field.name}Dependency; } """ } else { return """ private ${field.dependency.sie.exportedOsgiClassName} ${field.name}Dependency; protected final ${field.dependency.sie.exportedOsgiClassName} get${field.attributeName}Dependency(){ return ${field.name}Dependency; } """ } }.join("\n") } private static String getRuntimeRegistratorCode(Optional maybeRegistratorType) { if (maybeRegistratorType.isPresent()) { String registratorType = maybeRegistratorType.get() return """ private ${registratorType} rootRuntimeBeanRegistratorWrapper; public ${registratorType} getRootRuntimeBeanRegistratorWrapper(){ return rootRuntimeBeanRegistratorWrapper; } @Override public void setRuntimeBeanRegistrator(${RootRuntimeBeanRegistrator.canonicalName} rootRuntimeRegistrator){ this.rootRuntimeBeanRegistratorWrapper = new ${registratorType}(rootRuntimeRegistrator); } """ } else { return "" } } private static String getValidationMethods(List moduleFields) { String result = """ @Override public void validate() { """ // validate each mandatory dependency List lines = moduleFields.findAll{(it.dependent && it.dependency.mandatory)}.collect { field -> if (field.isList()) { return "" + "for(javax.management.ObjectName dep : ${field.name}) {\n" + " dependencyResolver.validateDependency(${field.dependency.sie.fullyQualifiedName}.class, dep, ${field.name}JmxAttribute);\n" + "}\n" } else { return "dependencyResolver.validateDependency(${field.dependency.sie.fullyQualifiedName}.class, ${field.name}, ${field.name}JmxAttribute);" } } result += lines.findAll { it.isEmpty() == false }.join("\n") result += """ customValidation(); } protected void customValidation(){ } """ return result } private static String getLogger(FullyQualifiedName fqn) { return "private static final ${Logger.canonicalName} logger = ${LoggerFactory.canonicalName}.getLogger(${fqn.toString()}.class);" } // 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 parameters, String after) { return "public ${fqn.typeName}(" + parameters.collect { it.key + " " + it.value }.join(",") + ") {\n" + parameters.values().collect { "this.${it}=${it};\n" }.join() + after + "}\n" } private static String getNewConstructor(FullyQualifiedName abstractFQN) { LinkedHashMap parameters = [ (ModuleIdentifier.canonicalName): "identifier", (DependencyResolver.canonicalName): "dependencyResolver" ] String setToNulls = ["oldInstance", "oldModule"].collect { "this.${it}=null;\n" }.join() return getConstructorStart(abstractFQN, parameters, setToNulls) } private static String getCopyFromOldConstructor(FullyQualifiedName abstractFQN) { LinkedHashMap parameters = [ (ModuleIdentifier.canonicalName): "identifier", (DependencyResolver.canonicalName): "dependencyResolver", (abstractFQN.typeName): "oldModule", (AutoCloseable.canonicalName): "oldInstance" ] return getConstructorStart(abstractFQN, parameters, "") } }