Bug 1411-5 #4: MDSAL Binding2 Java API Generator 90/46890/14
authorMartin Ciglan <mciglan@cisco.com>
Tue, 22 Nov 2016 08:02:05 +0000 (09:02 +0100)
committerRobert Varga <nite@hq.sk>
Tue, 13 Dec 2016 14:46:23 +0000 (14:46 +0000)
- top parent class for all renderers
- bit of cleanup, review comments implemented

Change-Id: If84b1e4505f7e16e462ae51dfe4ac4f3c6dbcc26
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/BaseRenderer.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/BaseRenderer.java b/binding2/mdsal-binding2-java-api-generator/src/main/java/org/opendaylight/mdsal/binding2/java/api/generator/renderers/BaseRenderer.java
new file mode 100644 (file)
index 0000000..4917369
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * 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 com.google.common.base.Preconditions.checkArgument;
+import static org.opendaylight.mdsal.binding2.java.api.generator.util.TextTemplateUtil.fieldName;
+import static org.opendaylight.mdsal.binding2.java.api.generator.util.TextTemplateUtil.getterMethodName;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.mdsal.binding2.generator.util.Types;
+import org.opendaylight.mdsal.binding2.model.api.Constant;
+import org.opendaylight.mdsal.binding2.model.api.GeneratedProperty;
+import org.opendaylight.mdsal.binding2.model.api.GeneratedTransferObject;
+import org.opendaylight.mdsal.binding2.model.api.GeneratedType;
+import org.opendaylight.mdsal.binding2.model.api.MethodSignature;
+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.yangtools.yang.common.QName;
+
+public abstract class BaseRenderer {
+    private static final String COMMA = ",";
+    private static final String DOT = ".";
+
+    private final GeneratedType type;
+    private final Map<String, String> importMap;
+
+    protected BaseRenderer(final GeneratedType type) {
+        this.type = Preconditions.checkNotNull(type);
+        this.importMap = new HashMap<>();
+    }
+
+    /**
+     * Implementation needs to call Scala template render() method to generate string body
+     * @return rendered body
+     */
+    protected abstract String body();
+
+    protected GeneratedType getType() {
+        return type;
+    }
+
+    protected String getFromImportMap(@NonNull String typeName) {
+        return importMap.get(typeName);
+    }
+
+    protected void putToImportMap(@NonNull String typeName, String typePackageName) {
+        importMap.put(typeName, typePackageName);
+    }
+
+    protected Map<String, String> getImportMap() {
+        return ImmutableMap.copyOf(importMap);
+    }
+
+    /**
+     * @param intype type to format and add to imports
+     * @return formatted type
+     */
+    protected String importedName(final Type intype) {
+        putTypeIntoImports(type, intype);
+        return getExplicitType(type, intype);
+    }
+
+    protected String importedName(final Class<?> cls) {
+        return importedName(Types.typeForClass(cls));
+    }
+
+    /**
+     * @return package definition for template
+     */
+    private String packageDefinition() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("package ")
+                .append(type.getPackageName())
+                .append(";\n");
+        return sb.toString();
+    }
+
+    /**
+     * walks through map of imports
+     * @return string of imports for template
+     */
+    private String imports() {
+        final StringBuilder sb = new StringBuilder();
+        if (!importMap.isEmpty()) {
+            for (Map.Entry<String, String> entry : importMap.entrySet()) {
+                if (!hasSamePackage(entry.getValue())) {
+                    sb.append("import ")
+                            .append(entry.getValue())
+                            .append('.')
+                            .append(entry.getKey())
+                            .append(";\n");
+                }
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Template method which generates method parameters with their types from <code>parameters</code>.
+     *
+     * @param parameters
+     * group of generated property instances which are transformed to the method parameters
+     * @return string with the list of the method parameters with their types in JAVA format
+     */
+    protected String asArgumentsDeclaration(final Collection<GeneratedProperty> parameters) {
+        final List<CharSequence> strings = new LinkedList<>();
+        if (parameters.iterator().hasNext()) {
+            for (GeneratedProperty parameter : parameters) {
+                final StringBuilder sb = new StringBuilder();
+                sb.append(importedName(parameter.getReturnType()));
+                sb.append(' ');
+                sb.append(fieldName(parameter));
+                strings.add(sb);
+            }
+        }
+        return String.join(", ", strings);
+    }
+
+    /**
+     * Checks if package of generated type and imported type is the same
+     * @param importedTypePackageName imported types package name
+     * @return equals packages
+     */
+    private boolean hasSamePackage(final String importedTypePackageName) {
+        return type.getPackageName().equals(importedTypePackageName);
+    }
+
+    /**
+     * Evaluates if it is necessary to add the package name for type to the map of imports for parentGenType
+     * If it is so the package name is saved to the map imports.
+     *
+     * @param parentGenType generated type for which is the map of necessary imports build
+     * @param type JAVA type for which is the necessary of the package import evaluated
+     */
+    private void putTypeIntoImports(final GeneratedType parentGenType, final Type type) {
+        checkArgument(parentGenType != null, "Parent Generated Type parameter MUST be specified and cannot be NULL!");
+        checkArgument(type != null, "Type parameter MUST be specified and cannot be NULL!");
+        checkArgument(parentGenType.getPackageName() != null,
+                "Parent Generated Type cannot have Package Name referenced as NULL!");
+
+        final String typeName = Preconditions.checkNotNull(type.getName());
+        final String typePackageName = Preconditions.checkNotNull(type.getPackageName());
+        final String parentTypeName = Preconditions.checkNotNull(parentGenType.getName());
+        if (typeName.equals(parentTypeName) || typePackageName.startsWith("java.lang") || typePackageName.isEmpty()) {
+            return;
+        }
+        if (!importMap.containsKey(typeName)) {
+            importMap.put(typeName, typePackageName);
+        }
+        if (type instanceof ParameterizedType) {
+            final ParameterizedType paramType = (ParameterizedType) type;
+            final Type[] params = paramType.getActualTypeArguments();
+            if (params != null) {
+                for (Type param : params) {
+                    putTypeIntoImports(parentGenType, param);
+                }
+            }
+        }
+    }
+
+    /**
+     * Builds the string which contains either the full path to the type (package name with type) or only type name
+     * if the package is among imports.
+     *
+     * @param parentGenType generated type which contains type
+     * @param type JAVA type for which is the string with type info generated
+     * @return string with type name for type in the full format or in the short format
+     */
+    private String getExplicitType(final GeneratedType parentGenType, final Type type) {
+        checkArgument(type != null, "Type parameter MUST be specified and cannot be NULL!");
+        checkArgument(importMap != null, "Imports Map cannot be NULL!");
+
+        final String typePackageName = Preconditions.checkNotNull(type.getPackageName());
+        final String typeName = Preconditions.checkNotNull(type.getName());
+        final String importedPackageName = importMap.get(typeName);
+        final StringBuilder sb;
+        if (typePackageName.equals(importedPackageName)) {
+            sb = new StringBuilder(typeName);
+            addActualTypeParameters(sb, type, parentGenType);
+            if (sb.toString().equals("Void")) {
+                return "void";
+            }
+        } else {
+            sb = new StringBuilder();
+            if (!typePackageName.isEmpty()) {
+                sb.append(typePackageName).append(DOT).append(typeName);
+            } else {
+                sb.append(type.getName());
+            }
+            if (type.equals(Types.voidType())) {
+                return "void";
+            }
+            addActualTypeParameters(sb, type, parentGenType);
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Adds actual type parameters from type to builder if type is ParametrizedType.
+     *
+     * @param sb string builder which contains type name
+     * @param type JAVA Type for which is the string with type info generated
+     * @param parentGenType generated type which contains type
+     * @return adds actual type parameters to builder
+     */
+    private StringBuilder addActualTypeParameters(final StringBuilder sb, final Type type, final GeneratedType parentGenType) {
+        if (type instanceof ParameterizedType) {
+            final ParameterizedType pType = (ParameterizedType) type;
+            final Type[] pTypes = pType.getActualTypeArguments();
+            sb.append('<');
+            sb.append(getParameters(parentGenType, pTypes));
+            sb.append('>');
+        }
+        return sb;
+    }
+
+    protected GeneratedProperty findProperty(final GeneratedTransferObject gto, String name) {
+        for (GeneratedProperty prop : gto.getProperties()) {
+            if (name.equals(prop.getName())) {
+                return prop;
+            }
+        }
+        final GeneratedTransferObject parent = gto.getSuperType();
+        if (parent != null) {
+            return findProperty(parent, name);
+        }
+        return null;
+    }
+
+    /**
+     * @param constant
+     * @return string with constant wrapped in code
+     */
+    protected String emitConstant(final Constant constant) {
+        final StringBuilder sb = new StringBuilder();
+        final Object qname = constant.getValue();
+        sb.append("public static final ")
+                .append(importedName(constant.getType()))
+                .append(' ')
+                .append(constant.getName())
+                .append(" = ");
+        if (qname instanceof QName) {
+            sb.append(QName.class.getName())
+                    .append(".create(\"")
+                    .append(((QName) qname).getNamespace().toString())
+                    .append("\", \"")
+                    .append(((QName) qname).getFormattedRevision())
+                    .append("\", \"")
+                    .append(((QName) qname).getLocalName())
+                    .append("\").intern()");
+        } else {
+            sb.append(qname);
+        }
+        sb.append(";\n");
+        return sb.toString();
+    }
+
+    /**
+     * Generates the string with all actual type parameters from
+     *
+     * @param parentGenType generated type for which is the JAVA code generated
+     * @param pTypes array of Type instances = actual type parameters
+     * @return string with all actual type parameters from pTypes
+     */
+    private String getParameters(final GeneratedType parentGenType, final Type[] pTypes) {
+        if (pTypes == null || pTypes.length == 0) {
+            return "?";
+        }
+        final StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < pTypes.length; i++) {
+            final Type t = pTypes[i];
+
+            String separator = COMMA;
+            if (i == (pTypes.length - 1)) {
+                separator = "";
+            }
+
+
+            if (t.equals(Types.voidType())) {
+                sb.append("java.lang.Void")
+                        .append(separator);
+                continue;
+            } else {
+
+                String wildcardParam = "";
+                if (t instanceof WildcardType) {
+                    wildcardParam = "? extends ";
+                }
+
+                sb.append(wildcardParam).append(getExplicitType(parentGenType, t)).append(separator);
+            }
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Template method which generates method parameters with their types from <code>parameters</code>.
+     * InterfaceTemplate / UnionTemaplate
+     *
+     * @param parameters list of parameter instances which are transformed to the method parameters
+     * @return string with the list of the method parameters with their types in JAVA format
+     */
+    protected String generateParameters(final List<MethodSignature.Parameter> parameters) {
+        final List<CharSequence> strings = new LinkedList<>();
+        if (!parameters.isEmpty()) {
+            for (MethodSignature.Parameter parameter : parameters) {
+                final StringBuilder sb = new StringBuilder();
+                sb.append(importedName(parameter.getType()));
+                sb.append(' ');
+                sb.append(parameter.getName());
+                strings.add(sb);
+            }
+        }
+        return String.join(", ", strings);
+    }
+
+    /**
+     * Template method which generates the getter method for field
+     *
+     * @param field generated property with data about field which is generated as the getter method
+     * @return string with the getter method source code in JAVA format
+     */
+    protected String getterMethod(final GeneratedProperty field) {
+        final StringBuilder sb = new StringBuilder();
+        final String name = fieldName(field);
+        final String importedName = Preconditions.checkNotNull(importedName(field.getReturnType()));
+        sb.append("public ")
+                .append(importedName)
+                .append(' ')
+                .append(getterMethodName(field))
+                .append("() {")
+                .append("return ")
+                .append(name);
+        if (importedName.contains("[]")) {
+            sb.append(" == null ? null : ")
+                    .append(name)
+                    .append(".clone()");
+        }
+        sb.append(";}");
+        return sb.toString();
+    }
+
+    /**
+     * builds template
+     * @return generated final template
+     */
+    public String generateTemplate() {
+        final StringBuilder sb = new StringBuilder();
+        /* sb body must be filled before imports method call */
+        final String templateBody = body();
+        sb.append(packageDefinition())
+                .append(imports())
+                .append(templateBody);
+        return sb.toString();
+    }
+}
\ No newline at end of file