Bug 1411-5 #2: MDSAL Binding2 Java API Generator 88/46888/10
authorMartin Ciglan <mciglan@cisco.com>
Fri, 4 Nov 2016 10:53:29 +0000 (11:53 +0100)
committerRobert Varga <nite@hq.sk>
Mon, 14 Nov 2016 13:42:16 +0000 (13:42 +0000)
-new utility class for various string processings
-added comparator
-Javadocs added for all public methods

Change-Id: I26747a53544dd6deafcef21a4e8330790ed4165f
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/util/AlphabeticallyTypeMemberComparator.java [new file with mode: 0644]
binding2/mdsal-binding2-java-api-generator/src/main/java/org/opendaylight/mdsal/binding2/java/api/generator/util/TextTemplateUtil.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/util/AlphabeticallyTypeMemberComparator.java b/binding2/mdsal-binding2-java-api-generator/src/main/java/org/opendaylight/mdsal/binding2/java/api/generator/util/AlphabeticallyTypeMemberComparator.java
new file mode 100644 (file)
index 0000000..6fee31a
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.util;
+
+import java.util.Comparator;
+import org.opendaylight.mdsal.binding2.model.api.TypeMember;
+
+/**
+ * Alphabetically type member {@link Comparator} which provides sorting by name for type members
+ * (variables and methods) in generated class.
+ *
+ * @param <T>
+ */
+public class AlphabeticallyTypeMemberComparator<T extends TypeMember> implements Comparator<T>{
+
+    @Override
+    public int compare(T member1, T member2) {
+        return member1.getName().compareTo(member2.getName());
+    }
+}
diff --git a/binding2/mdsal-binding2-java-api-generator/src/main/java/org/opendaylight/mdsal/binding2/java/api/generator/util/TextTemplateUtil.java b/binding2/mdsal-binding2-java-api-generator/src/main/java/org/opendaylight/mdsal/binding2/java/api/generator/util/TextTemplateUtil.java
new file mode 100644 (file)
index 0000000..9e00a46
--- /dev/null
@@ -0,0 +1,495 @@
+/*
+ * 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.util;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+import org.opendaylight.mdsal.binding2.generator.util.Types;
+import org.opendaylight.mdsal.binding2.model.api.AccessModifier;
+import org.opendaylight.mdsal.binding2.model.api.ConcreteType;
+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.Restrictions;
+import org.opendaylight.mdsal.binding2.model.api.Type;
+import org.opendaylight.mdsal.binding2.model.api.TypeMember;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+public class TextTemplateUtil {
+
+    public static final String DOT = ".";
+    public static final String PATTERN_CONSTANT_NAME = "PATTERN_CONSTANTS";
+
+    private static final char NEW_LINE = '\n';
+    private static final String COMMA = ",";
+    private static final String UNDERSCORE = "_";
+    private static final CharMatcher NEWLINE_OR_TAB = CharMatcher.anyOf("\n\t");
+    private static final CharMatcher NL_MATCHER = CharMatcher.is(NEW_LINE);
+    private static final CharMatcher AMP_MATCHER = CharMatcher.is('&');
+    private static final CharMatcher GT_MATCHER = CharMatcher.is('>');
+    private static final CharMatcher LT_MATCHER = CharMatcher.is('<');
+    private static final Splitter NL_SPLITTER = Splitter.on(NL_MATCHER);
+
+    private TextTemplateUtil() {
+        throw new AssertionError("Instantiating utility class.");
+    }
+
+    /**
+     * Makes start of getter name LowerCase
+     *
+     * @param s getter name without prefix
+     * @return getter name starting in LowerCase
+     */
+    public static String toFirstLower(String s) {
+        return s != null && s.length() != 0 ? (Character.isLowerCase(s.charAt(0)) ? s : (s.length() == 1 ?
+                s.toLowerCase() : s.substring(0, 1).toLowerCase() + s.substring(1))) : s;
+    }
+
+    /**
+     * Wraps text as documentation, used in enum description
+     *
+     * @param text text for wrapping
+     * @return wrapped text
+     */
+    public static String wrapToDocumentation(String text) {
+        if (text.isEmpty()) {
+            return "";
+        }
+        final StringBuilder sb = new StringBuilder("/**");
+        sb.append(NEW_LINE);
+        Iterable<String> lineSplitText = NL_SPLITTER.split(text);
+        for (final String t : lineSplitText) {
+            if (!t.isEmpty()) {
+                sb.append(" *");
+                sb.append(" ");
+                sb.append(t);
+                sb.append(NEW_LINE);
+            }
+        }
+        sb.append(" */");
+        return sb.toString();
+    }
+
+    /**
+     * Returns formatted Javadoc, based on type
+     * @param typeName
+     * @return formatted Javadoc, based on type
+     */
+    public static String formatDataForJavaDocBuilder(String typeName) {
+        final StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("Class that builds {@link ")
+                .append(typeName)
+                .append("} instances.")
+                .append(NEW_LINE)
+                .append("@see ")
+                .append(typeName);
+        return stringBuilder.toString();
+    }
+
+    /**
+     * Returns formatted Javadoc with possible additional comment, based on type
+     * @param type
+     * @param additionalComment
+     * @return formatted Javadoc with possible additional comment, based on type
+     */
+    public static String formatDataForJavaDoc(GeneratedType type, String additionalComment) {
+        final StringBuilder javaDoc = new StringBuilder();
+        if (type.getDescription().isPresent()) {
+            javaDoc.append(type.getDescription())
+                    .append(NEW_LINE)
+                    .append(NEW_LINE)
+                    .append(NEW_LINE);
+        }
+        javaDoc.append(additionalComment);
+        return javaDoc.toString();
+    }
+
+    /**
+     * Returns properties names in formatted string
+     * @param properties
+     * @return properties names in formatted string
+     */
+    //FIXME: this needs further clarification in future patch
+    public static String valueForBits(List<GeneratedProperty> properties) {
+        final List<String> strings = new LinkedList<>();
+        for (GeneratedProperty property : properties) {
+            strings.add(fieldName(property));
+        }
+        return String.join(",", strings);
+    }
+
+    /**
+     * Returns formatted type description
+     * @param type
+     * @return formatted type description
+     */
+    public static String formatDataForJavaDoc(GeneratedType type) {
+        final String description = type.getDescription().isPresent() ? type.getDescription().get() : "";
+        return encodeJavadocSymbols(description);
+    }
+
+    /**
+     * Returns parameter name, based on given Type
+     * @param returnType
+     * @param paramName
+     * @return parameter name, based on given Type
+     */
+    public static String paramValue(Type returnType, String paramName) {
+        if (returnType instanceof ConcreteType) {
+            return paramName;
+        } else {
+            return paramName + ".getValue()";
+        }
+    }
+
+    /**
+     * Template method which generates JAVA comments. InterfaceTemplate
+     *
+     * @param comment comment string with the comment for whole JAVA class
+     * @return string with comment in JAVA format
+     */
+    public static String asJavadoc(String comment) {
+        if (comment == null) {
+            return "";
+        }
+        return wrapToDocumentation(formatToParagraph(comment.trim(), 0));
+    }
+
+    private static String formatDataForJavaDoc(TypeMember type, String additionalComment) {
+        final StringBuilder javaDoc = new StringBuilder();
+        if (type.getComment() != null || !type.getComment().isEmpty()) {
+            javaDoc.append(formatToParagraph(type.getComment(), 0))
+                    .append(NEW_LINE)
+                    .append(NEW_LINE)
+                    .append(NEW_LINE);
+        }
+        javaDoc.append(additionalComment);
+        return wrapToDocumentation(javaDoc.toString());
+    }
+
+    /**
+     * Returns related Javadoc
+     * @param methodSignature
+     * @return related Javadoc
+     */
+    public static String getJavaDocForInterface(MethodSignature methodSignature) {
+        final StringBuilder javaDoc = new StringBuilder();
+        javaDoc.append("@return ")
+                .append(asCode(methodSignature.getReturnType().getFullyQualifiedName()))
+                .append(" ")
+                .append(asCode(propertyNameFromGetter(methodSignature)))
+                .append(", or ")
+                .append(asCode("null"))
+                .append(" if not present");
+        return formatDataForJavaDoc(methodSignature, javaDoc.toString());
+    }
+
+    private static String asCode(String text) {
+        return "<code>" + text + "</code>";
+    }
+
+    /**
+     * Encodes angle brackets in yang statement description
+     * @param description description of a yang statement which is used to generate javadoc comments
+     * @return string with encoded angle brackets
+     */
+    public static String encodeAngleBrackets(String description) {
+        if (description != null) {
+            description = LT_MATCHER.replaceFrom(description, "&lt;");
+            description = GT_MATCHER.replaceFrom(description, "&gt;");
+        }
+        return description;
+    }
+
+    /**
+     * Returns collection of properties as formatted String
+     * @param properties
+     * @return generated properties as formatted String
+     */
+    public static String propsAsArgs(Iterable<GeneratedProperty> properties) {
+        final List<String> strings = new LinkedList<>();
+        if (properties.iterator().hasNext()) {
+            for (GeneratedProperty property : properties) {
+                final StringBuilder stringBuilder = new StringBuilder();
+                stringBuilder.append("\"")
+                        .append(property.getName())
+                        .append("\"");
+                strings.add(stringBuilder.toString());
+            }
+        }
+        return String.join(",", strings);
+    }
+
+    /**
+     * Returns properties as formatted String
+     * @param properties
+     * @param booleanName
+     * @return Properties as formatted String
+     */
+    public static String propsAsList(Iterable<GeneratedProperty> properties, String booleanName) {
+        final List<String> strings = new LinkedList<>();
+        if (properties.iterator().hasNext()) {
+            for (GeneratedProperty property : properties) {
+                final StringBuilder stringBuilder = new StringBuilder();
+                stringBuilder.append("properties.get(i++).equals(defaultValue) ? ")
+                        .append(booleanName)
+                        .append(".TRUE : null");
+                strings.add(stringBuilder.toString());
+            }
+        }
+        return String.join(",", strings);
+    }
+
+    /**
+     * Extracts available restrictions from given type
+     * @param currentType
+     * @return restrictions from given type
+     */
+    public static Restrictions getRestrictions(Type currentType) {
+        Restrictions restrictions = null;
+        if (currentType instanceof ConcreteType) {
+            restrictions = ((ConcreteType) currentType).getRestrictions();
+        } else if (currentType instanceof GeneratedTransferObject) {
+            restrictions = ((GeneratedTransferObject) currentType).getRestrictions();
+        }
+        return restrictions;
+    }
+
+    /**
+     * sets fieldname according to property for return type
+     * method(type fieldname)
+     *
+     * @param property type from getter
+     */
+    public static String fieldName(GeneratedProperty property) {
+        final String name = Preconditions.checkNotNull(property.getName());
+        return UNDERSCORE.concat(name);
+    }
+
+    /**
+     * Template method which generates sequence of the names of the class attributes
+     *
+     * @param parameters group of generated property instances which are transformed to the sequence of parameter names
+     * @return string with the list of the parameter names
+     */
+    public static String asArguments(List<GeneratedProperty> parameters) {
+        final List<String> strings = new LinkedList<>();
+        if (!parameters.isEmpty()) {
+            for (GeneratedProperty parameter : parameters) {
+                strings.add((fieldName(parameter)));
+            }
+        }
+        return String.join(", ", strings);
+    }
+
+    /**
+     * Helper method for building getter
+     *
+     * @param field property name
+     * @return getter for propery
+     */
+    public static String getterMethodName(GeneratedProperty field) {
+        final Type type = Preconditions.checkNotNull(field.getReturnType());
+        final String name = Preconditions.checkNotNull(field.getName());
+        final String prefix = Types.BOOLEAN.equals(type) ? "is" : "get";
+        return prefix.concat(toFirstUpper(name));
+    }
+
+    /**
+     * Returns built setter method body
+     * @param field
+     * @param typeName
+     * @param returnTypeName
+     * @return built setter method body
+     */
+    public static String setterMethod(GeneratedProperty field, String typeName, String returnTypeName) {
+        final StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append("public ")
+                .append(typeName)
+                .append(" set")
+                .append(toFirstUpper(field.getName()))
+                .append("(")
+                .append(returnTypeName)
+                .append(" value) {\n    this.")
+                .append(fieldName(field))
+                .append(" = value;\n    return this;\n}\n");
+        return stringBuilder.toString();
+    }
+
+    /**
+     * Returns simple name of underlying class
+     * @return Simple name of underlying class
+     */
+    public static String getSimpleNameForBuilder() {
+        return Builder.class.getSimpleName();
+    }
+
+    /**
+     * Makes start of getter name uppercase
+     *
+     * @param s getter name without prefix
+     * @return getter name starting in uppercase
+     */
+    public static String toFirstUpper(String s) {
+        return s != null && s.length() != 0 ? (Character.isUpperCase(s.charAt(0)) ? s : (s.length() == 1 ?
+                s.toUpperCase() : s.substring(0, 1).toUpperCase() + s.substring(1))) : s;
+    }
+
+    /**
+     * Cuts prefix from getter name
+     *
+     * @param getter getter name
+     * @return getter name without prefix
+     */
+    public static String propertyNameFromGetter(MethodSignature getter) {
+        final String name = Preconditions.checkNotNull(getter.getName());
+        int prefix;
+        if (name.startsWith("is")) {
+            prefix = 2;
+        } else if (name.startsWith("get")) {
+            prefix = 3;
+        } else {
+            throw new IllegalArgumentException("Not a getter");
+        }
+        return toFirstLower(name.substring(prefix));
+    }
+
+    /**
+     * Returns list of properties as formatted String
+     * @param properties
+     * @return formatted property list as String
+     */
+    public static String getPropertyList(List<GeneratedProperty> properties) {
+        final List<String> strings = new LinkedList<>();
+        if (!properties.isEmpty()) {
+            for (GeneratedProperty property : properties) {
+                final StringBuilder stringBuilder = new StringBuilder();
+                stringBuilder.append("base.")
+                        .append(getterMethodName(property))
+                        .append("()");
+                strings.add(stringBuilder.toString());
+            }
+        }
+        return String.join(", ", strings);
+    }
+
+    /**
+     * util method for unionTemplateBuilderTemplate
+     * @return string with clarification for javadoc
+     */
+    public static String getClarification() {
+        final StringBuilder clarification = new StringBuilder();
+        clarification.append("The purpose of generated class in src/main/java for Union types is to create new instances of unions from a string representation.\n")
+                .append("In some cases it is very difficult to automate it since there can be unions such as (uint32 - uint16), or (string - uint32).\n")
+                .append("\n")
+                .append("The reason behind putting it under src/main/java is:\n")
+                .append("This class is generated in form of a stub and needs to be finished by the user. This class is generated only once to prevent\n")
+                .append("loss of user code.\n")
+                .append("\n");
+        return clarification.toString();
+    }
+
+    /**
+     * Returns revision Date as String
+     * @param revision
+     * @return formatted Revision as String
+     */
+    public static String getFormattedRevision(Date revision) {
+        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
+        return simpleDateFormat.format(revision);
+    }
+
+    /**
+     * Returns source path as String
+     * @param module
+     * @return formatted String source path
+     */
+    public static String getSourcePath(Module module) {
+        return "/".concat(module.getModuleSourcePath().replace(java.io.File.separatorChar, '/'));
+    }
+
+    /**
+     * util method for unionTemplateBuilderTemplate
+     * @return needed access modifier
+     */
+    public static String getAccessModifier(AccessModifier modifier) {
+        switch (modifier) {
+            case PUBLIC: return "public ";
+            case PROTECTED: return "protected ";
+            case PRIVATE: return "private ";
+            default: return "";
+        }
+    }
+
+    /**
+     * @param text Content of tag description
+     * @param nextLineIndent Number of spaces from left side default is 12
+     * @return formatted description
+     */
+    private static String formatToParagraph(final String text, final int nextLineIndent) {
+        if (Strings.isNullOrEmpty(text)) {
+            return "";
+        }
+        boolean isFirstElementOnNewLineEmptyChar = false;
+        final StringBuilder sb = new StringBuilder();
+        final StringBuilder lineBuilder = new StringBuilder();
+        final String lineIndent = Strings.repeat(" ", nextLineIndent);
+        final String textToFormat = NEWLINE_OR_TAB.removeFrom(text);
+        final String formattedText = textToFormat.replaceAll(" +", " ");
+        final StringTokenizer tokenizer = new StringTokenizer(formattedText, " ", true);
+
+        while (tokenizer.hasMoreElements()) {
+            final String nextElement = tokenizer.nextElement().toString();
+
+            if (lineBuilder.length() + nextElement.length() > 80) {
+                // Trim trailing whitespace
+                for (int i = lineBuilder.length() - 1; i >= 0 && lineBuilder.charAt(i) != ' '; --i) {
+                    lineBuilder.setLength(i);
+                }
+                // Trim leading whitespace
+                while (lineBuilder.charAt(0) == ' ') {
+                    lineBuilder.deleteCharAt(0);
+                }
+                sb.append(lineBuilder).append('\n');
+                lineBuilder.setLength(0);
+
+                if (nextLineIndent > 0) {
+                    sb.append(lineIndent);
+                }
+
+                if (" ".equals(nextElement)) {
+                    isFirstElementOnNewLineEmptyChar = true;
+                }
+            }
+            if (isFirstElementOnNewLineEmptyChar) {
+                isFirstElementOnNewLineEmptyChar = false;
+            } else {
+                lineBuilder.append(nextElement);
+            }
+        }
+        return sb.append(lineBuilder).append('\n').toString();
+    }
+
+    private static String encodeJavadocSymbols(String description) {
+        if (description == null || description.isEmpty()) {
+            return description;
+        }
+        final String ret = description.replace("*/", "&#42;&#47;");
+        return AMP_MATCHER.replaceFrom(ret, "&amp;");
+    }
+}
\ No newline at end of file