Bug 1411-5 #10: MDSAL Binding2 Java API Generator 00/46900/9
authorMartin Ciglan <mciglan@cisco.com>
Thu, 12 Jan 2017 09:13:56 +0000 (10:13 +0100)
committerMartin Ciglan <mciglan@cisco.com>
Thu, 12 Jan 2017 09:13:56 +0000 (10:13 +0100)
- yangModelBindingProvider interface + templates
- bit of a code clean-up

Change-Id: If58ef3945b98dc2671482ec8b4489f394a82329f
Signed-off-by: Filip Gregor <fgregor@cisco.com>
Signed-off-by: Martin Ciglan <mciglan@cisco.com>
binding2/mdsal-binding2-java-api-generator/src/main/java/org/opendaylight/mdsal/binding2/java/api/generator/renderers/YangModuleInfoTemplateRenderer.java [new file with mode: 0644]
binding2/mdsal-binding2-java-api-generator/src/main/twirl/org/opendaylight/mdsal/binding2/modelProviderTemplate.scala.txt [new file with mode: 0644]
binding2/mdsal-binding2-java-api-generator/src/main/twirl/org/opendaylight/mdsal/binding2/yangModuleInfoTemplate.scala.txt [new file with mode: 0644]
binding2/mdsal-binding2-spec/src/main/java/org/opendaylight/mdsal/binding2/spec/YangModelBindingProvider.java [new file with mode: 0644]

diff --git a/binding2/mdsal-binding2-java-api-generator/src/main/java/org/opendaylight/mdsal/binding2/java/api/generator/renderers/YangModuleInfoTemplateRenderer.java b/binding2/mdsal-binding2-java-api-generator/src/main/java/org/opendaylight/mdsal/binding2/java/api/generator/renderers/YangModuleInfoTemplateRenderer.java
new file mode 100644 (file)
index 0000000..d297f45
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2016 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.mdsal.binding2.java.api.generator.renderers;
+
+import static org.opendaylight.mdsal.binding2.generator.util.Binding2Mapping.MODEL_BINDING_PROVIDER_CLASS_NAME;
+import static org.opendaylight.mdsal.binding2.generator.util.Binding2Mapping.getRootPackageName;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import org.opendaylight.mdsal.binding2.generator.util.Types;
+import org.opendaylight.mdsal.binding2.model.api.ParameterizedType;
+import org.opendaylight.mdsal.binding2.model.api.Type;
+import org.opendaylight.mdsal.binding2.model.api.WildcardType;
+import org.opendaylight.mdsal.binding2.spec.YangModelBindingProvider;
+import org.opendaylight.mdsal.binding2.spec.YangModuleInfo;
+import org.opendaylight.mdsal.binding2.txt.modelProviderTemplate;
+import org.opendaylight.mdsal.binding2.txt.yangModuleInfoTemplate;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class YangModuleInfoTemplateRenderer {
+
+    private final Module module;
+    private final SchemaContext ctx;
+    private final Map<String, String> importMap = new HashMap<>();
+    private final String packageName;
+    private final String modelBindingProviderName;
+
+    public YangModuleInfoTemplateRenderer(final Module module, final SchemaContext ctx) {
+
+        Preconditions.checkArgument(module != null, "Module must not be null.");
+        this.module = module;
+        this.ctx = ctx;
+        this.packageName = getRootPackageName(module);
+
+        final StringBuilder sb = new StringBuilder();
+        sb.append(packageName)
+            .append('.')
+            .append(MODEL_BINDING_PROVIDER_CLASS_NAME);
+        this.modelBindingProviderName = sb.toString();
+    }
+
+    protected String body() {
+        /**
+         * list of all imported names for template
+         */
+        final Map<String, String> importedNames = new HashMap<>();
+
+        importedNames.put("string", importedName(String.class));
+        importedNames.put("stringBuilder", importedName(StringBuilder.class));
+        importedNames.put("set", importedName(Set.class));
+        importedNames.put("hashSet", importedName(HashSet.class));
+        importedNames.put("collections", importedName(Collections.class));
+        importedNames.put("immutableSet", importedName(ImmutableSet.class));
+        importedNames.put("inputStream", importedName(InputStream.class));
+        importedNames.put("iOException", importedName(IOException.class));
+        importedNames.put("yangModuleInfo", importedName(YangModuleInfo.class));
+
+        return yangModuleInfoTemplate.render(module, ctx, importedNames).body();
+    }
+
+    /**
+     * builds template
+     * @return generated final template
+     */
+    public String generateTemplate() {
+        final StringBuilder sb = new StringBuilder();
+        /* body must be filled before imports method call */
+        final String templateBody = body();
+        sb.append("package ")
+            .append(packageName)
+            .append(";\n")
+            .append(imports())
+            .append(templateBody);
+        return sb.toString();
+    }
+
+    public String generateModelProvider() {
+        return modelProviderTemplate.render(packageName, YangModelBindingProvider.class.getName(), YangModuleInfo.class
+        .getName()).body();
+    }
+
+    /**
+     * walks through map of imports
+     * @return string of imports for template
+     */
+    private String imports() {
+        final StringBuilder sb = new StringBuilder();
+        for (Map.Entry<String, String> entry : importMap.entrySet()) {
+            if (!getRootPackageName(module).equals(entry.getValue())) {
+                sb.append("import ")
+                    .append(entry.getValue())
+                    .append('.')
+                    .append(entry.getKey())
+                    .append(";\n");
+            }
+        }
+        return sb.toString();
+    }
+
+    private String importedName(final Class<?> cls) {
+        final Type inType = Types.typeForClass(cls);
+        putTypeIntoImports(inType);
+        return getExplicitType(inType);
+    }
+
+    private void putTypeIntoImports(final Type type) {
+        final String typeName = type.getName();
+        final String typePackageName = type.getPackageName();
+        if (typePackageName.startsWith("java.lang") || typePackageName.isEmpty()) {
+            return;
+        }
+        if (!importMap.containsKey(typeName)) {
+            importMap.put(typeName, typePackageName);
+        }
+        if (type instanceof ParameterizedType) {
+            final Type[] params = ((ParameterizedType) type).getActualTypeArguments();
+            if (params != null) {
+                for (Type param : params) {
+                    putTypeIntoImports(param);
+                }
+            }
+        }
+    }
+
+    private String getExplicitType(final Type type) {
+        final String typePackageName = type.getPackageName();
+        final String typeName = type.getName();
+        final String importedPackageName = importMap.get(typeName);
+        final StringBuilder sb;
+        if (typePackageName.equals(importedPackageName)) {
+            sb = new StringBuilder(type.getName());
+            if (sb.toString().equals("Void")) {
+                return "void";
+            }
+            addActualTypeParameters(sb, type);
+        } else {
+            if (type.equals(Types.voidType())) {
+                return "void";
+            }
+            sb = new StringBuilder();
+            if (!typePackageName.isEmpty()) {
+                sb.append(typePackageName)
+                    .append('.')
+                    .append(type.getName());
+            } else {
+                sb.append(type.getName());
+            }
+            addActualTypeParameters(sb, type);
+        }
+        return sb.toString();
+    }
+
+    private StringBuilder addActualTypeParameters(final StringBuilder sb, final Type type) {
+        if (type instanceof ParameterizedType) {
+            final Type[] pTypes = ((ParameterizedType) type).getActualTypeArguments();
+            sb.append('<');
+            sb.append(getParameters(pTypes));
+            sb.append('>');
+        }
+        return sb;
+    }
+
+    private String getParameters(final Type[] pTypes) {
+        if (pTypes == null || pTypes.length == 0) {
+            return "?";
+        }
+        final StringBuilder sb = new StringBuilder();
+        int i = 0;
+        for (Type pType : pTypes) {
+            final Type type = pTypes[i];
+            String separator = ",";
+            if (i == (pTypes.length - 1)) {
+                separator = "";
+            }
+            String wildcardParam = "";
+            if (type.equals(Types.voidType())) {
+                sb.append("java.lang.Void" + separator);
+            } else {
+                if (type instanceof WildcardType) {
+                    wildcardParam = "? extends ";
+                }
+                sb.append(wildcardParam + getExplicitType(type) + separator);
+                i = i + 1;
+            }
+        }
+        return sb.toString();
+    }
+
+    public static Module getSortedQName(final Set<Module> modules, final String name) {
+        final TreeMap<Date, Module> sorted = new TreeMap<>();
+        for (Module module : modules) {
+            if (name.equals(module.getName())) {
+                sorted.put(module.getRevision(), module);
+            }
+        }
+        return sorted.lastEntry().getValue();
+    }
+}
\ No newline at end of file
diff --git a/binding2/mdsal-binding2-java-api-generator/src/main/twirl/org/opendaylight/mdsal/binding2/modelProviderTemplate.scala.txt b/binding2/mdsal-binding2-java-api-generator/src/main/twirl/org/opendaylight/mdsal/binding2/modelProviderTemplate.scala.txt
new file mode 100644 (file)
index 0000000..aac79e4
--- /dev/null
@@ -0,0 +1,20 @@
+@*
+ * Copyright (c) 2016 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
+ *@
+
+@import org.opendaylight.mdsal.binding2.generator.util.Binding2Mapping.MODULE_INFO_CLASS_NAME
+@import org.opendaylight.mdsal.binding2.generator.util.Binding2Mapping.MODEL_BINDING_PROVIDER_CLASS_NAME
+
+@(packageName: String, yangModelBindingProviderName: String, yangModuleInfoName: String)
+package @{packageName};
+
+public final class @{MODEL_BINDING_PROVIDER_CLASS_NAME} implements @{yangModelBindingProviderName} {
+
+    public @{yangModuleInfoName} getModuleInfo() {
+        return @{MODULE_INFO_CLASS_NAME}.getInstance();
+    }
+}
\ No newline at end of file
diff --git a/binding2/mdsal-binding2-java-api-generator/src/main/twirl/org/opendaylight/mdsal/binding2/yangModuleInfoTemplate.scala.txt b/binding2/mdsal-binding2-java-api-generator/src/main/twirl/org/opendaylight/mdsal/binding2/yangModuleInfoTemplate.scala.txt
new file mode 100644 (file)
index 0000000..e7efcca
--- /dev/null
@@ -0,0 +1,138 @@
+@*
+ * Copyright (c) 2016 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
+ *@
+
+@import org.opendaylight.mdsal.binding2.generator.util.Binding2Mapping.getClassName
+@import org.opendaylight.mdsal.binding2.generator.util.Binding2Mapping.getRootPackageName
+@import org.opendaylight.mdsal.binding2.generator.util.Binding2Mapping.MODULE_INFO_CLASS_NAME
+@import org.opendaylight.mdsal.binding2.java.api.generator.util.TextTemplateUtil.getFormattedRevision
+@import org.opendaylight.mdsal.binding2.java.api.generator.util.TextTemplateUtil.getSourcePath
+@import org.opendaylight.mdsal.binding2.java.api.generator.renderers.YangModuleInfoTemplateRenderer.getSortedQName
+@import org.opendaylight.yangtools.yang.model.api.Module
+@import org.opendaylight.yangtools.yang.model.api.SchemaContext
+
+@(module: Module, ctx: SchemaContext, importedNames: Map[String, String])
+@if(module != null && ctx != null) {
+public final class @{MODULE_INFO_CLASS_NAME} implements @{importedNames.get("yangModuleInfo")} {
+
+    private static final @{importedNames.get("yangModuleInfo")} INSTANCE = new @{MODULE_INFO_CLASS_NAME}();
+
+    private final @{importedNames.get("string")} name = "@{module.getName}";
+    private final @{importedNames.get("string")} namespace = "@{module.getNamespace.toString}";
+    private final @{importedNames.get("string")} revision = "@{getFormattedRevision(module.getRevision)}";
+    private final @{importedNames.get("string")} resourcePath = "@{getSourcePath(module)}";
+    private final @{importedNames.get("set")}<YangModuleInfo> importedModules;
+
+    public static @{importedNames.get("yangModuleInfo")} getInstance() {
+        return INSTANCE;
+    }
+
+    @{classBody(module, MODULE_INFO_CLASS_NAME)}
+}
+}
+
+@generateSubInfo(module: Module) = {
+@for(submodule <- module.getSubmodules) {
+    private static final class @{getClassName(submodule.getName)}Info implements @{importedNames.get("yangModuleInfo")} {
+
+        private static final @{importedNames.get("yangModuleInfo")} INSTANCE = new @{getClassName(submodule.getName)}Info();
+
+        private final @{importedNames.get("string")} name = "@{submodule.getName}";
+        private final @{importedNames.get("string")} namespace = "@{submodule.getNamespace.toString}";
+        private final @{importedNames.get("string")} revision = "@{getFormattedRevision(submodule.getRevision)}";
+        private final @{importedNames.get("string")} resourcePath = "@{getSourcePath(submodule)}";
+        private final @{importedNames.get("set")}<YangModuleInfo> importedModules;
+
+        public static @{importedNames.get("yangModuleInfo")} getInstance() {
+            return INSTANCE;
+        }
+
+        @{classBody(submodule, getClassName(submodule.getName + "Info"))}
+    }
+}
+}
+
+@classBody(module: Module, className: String) = {
+    private @{className}() {
+        @if(!module.getImports.isEmpty || !module.getSubmodules.isEmpty) {
+            @{importedNames.get("set")}<@{importedNames.get("yangModuleInfo")}> set = new @{importedNames.get("hashSet")}<>();
+        }
+        @if(!module.getImports.isEmpty) {
+            @for(moduleImport <- module.getImports) {
+                @if(moduleImport.getRevision == null) {
+                    set.add(@{getRootPackageName(getSortedQName(ctx.getModules, moduleImport.getModuleName))}.@{MODULE_INFO_CLASS_NAME}.getInstance());
+                } else {
+                    set.add(@{getRootPackageName(ctx.findModuleByName(moduleImport.getModuleName, moduleImport.getRevision))}.@{MODULE_INFO_CLASS_NAME}.getInstance());
+                }
+            }
+        }
+        @if(!module.getSubmodules.isEmpty) {
+            @for(submodule <- module.getSubmodules) {
+                set.add(@{getClassName(submodule.getName)}Info.getInstance());
+            }
+        }
+        @if(!module.getImports.isEmpty && !module.getSubmodules.isEmpty) {
+            importedModules = @{importedNames.get("collections")}.emptySet();
+        } else {
+            importedModules = @{importedNames.get("immutableSet")}.copyOf(set);
+        }
+
+        @{importedNames.get("inputStream")} stream = @{MODULE_INFO_CLASS_NAME}.class.getResourceAsStream(resourcePath);
+        if (stream == null) {
+            throw new IllegalStateException("Resource '" + resourcePath + "' is missing");
+        }
+        try {
+            stream.close();
+        } catch (@{importedNames.get("iOException")} e) {
+        // Resource leak, but there is nothing we can do
+        }
+    }
+
+    @@Override
+    public @{importedNames.get("string")} getName() {
+        return name;
+    }
+
+    @@Override
+    public @{importedNames.get("string")} getRevision() {
+        return revision;
+    }
+
+    @@Override
+    public @{importedNames.get("string")} getNamespace() {
+        return namespace;
+    }
+
+    @@Override
+    public @{importedNames.get("inputStream")} getModuleSourceStream() throws IOException {
+        @{importedNames.get("inputStream")} stream = @{MODULE_INFO_CLASS_NAME}.class.getResourceAsStream(resourcePath);
+        if (stream == null) {
+            throw new @{importedNames.get("iOException")}("Resource " + resourcePath + " is missing");
+        }
+        return stream;
+    }
+
+    @@Override
+    public @{importedNames.get("set")}<@{importedNames.get("YangModuleInfo")}> getImportedModules() {
+        return importedModules;
+    }
+
+    @@Override
+    public @{importedNames.get("string")} toString() {
+        @{importedNames.get("stringBuilder")} sb = new @{importedNames.get("stringBuilder")}(this.getClass().getCanonicalName());
+        sb.append("[");
+        sb.append("name = " + name);
+        sb.append(", namespace = " + namespace);
+        sb.append(", revision = " + revision);
+        sb.append(", resourcePath = " + resourcePath);
+        sb.append(", imports = " + importedModules);
+        sb.append("]");
+        return sb.toString();
+    }
+
+    @{generateSubInfo(module)}
+}
\ No newline at end of file
diff --git a/binding2/mdsal-binding2-spec/src/main/java/org/opendaylight/mdsal/binding2/spec/YangModelBindingProvider.java b/binding2/mdsal-binding2-spec/src/main/java/org/opendaylight/mdsal/binding2/spec/YangModelBindingProvider.java
new file mode 100644 (file)
index 0000000..dae067a
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016 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.mdsal.binding2.spec;
+
+import java.util.ServiceLoader;
+
+/**
+ *
+ * Provider of YangModuleInfo for specified package / model.
+ *
+ * Implementation of this interface should be discoverable
+ * via {@link ServiceLoader}
+ *
+ */
+public interface YangModelBindingProvider {
+
+    /**
+     * YangModuleInfo associated to package
+     *
+     * @return YangModuleInfo
+     */
+    YangModuleInfo getModuleInfo();
+
+}