Fixup BuilderTemplate decomposition 27/74527/5
authorRobert Varga <robert.varga@pantheon.tech>
Thu, 26 Jul 2018 09:34:33 +0000 (11:34 +0200)
committerRobert Varga <nite@hq.sk>
Thu, 26 Jul 2018 16:47:36 +0000 (16:47 +0000)
This patch reworks BuilderTemplate to correctly represent its
generated class layout into GeneratedType hierarchy. This eliminates
a special-case hack in AbstractJavaGeneratedType and fixes overlap
between inherited and imported classes.

JIRA: MDSAL-365
Change-Id: I10affa8fc6e6b6447024744473e95ac1e600cd6b
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/AbstractBuilderTemplate.xtend [new file with mode: 0644]
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/AbstractJavaGeneratedType.java
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderGenerator.java
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderImplTemplate.xtend [new file with mode: 0644]
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderTemplate.xtend
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/TopLevelJavaGeneratedType.java
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/UnionTemplate.xtend
binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderGeneratorTest.java
binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/test/CompilationTest.java
binding/mdsal-binding-java-api-generator/src/test/resources/compilation/mdsal365/mdsal-365.yang [new file with mode: 0644]

diff --git a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/AbstractBuilderTemplate.xtend b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/AbstractBuilderTemplate.xtend
new file mode 100644 (file)
index 0000000..6622ee2
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.binding.java.api.generator
+
+import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTABLE_AUGMENTATION_NAME
+import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTATION_FIELD
+
+import com.google.common.base.MoreObjects
+import java.util.Collection
+import java.util.Collections
+import java.util.Map
+import java.util.Set
+import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
+import org.opendaylight.mdsal.binding.model.api.GeneratedType
+import org.opendaylight.mdsal.binding.model.api.Type
+import org.opendaylight.yangtools.yang.binding.CodeHelpers
+import java.util.ArrayList
+import org.opendaylight.mdsal.binding.model.util.Types
+import org.opendaylight.yangtools.yang.binding.Identifiable
+import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
+import org.opendaylight.mdsal.binding.spec.naming.BindingMapping
+import com.google.common.collect.ImmutableMap
+import org.opendaylight.yangtools.yang.binding.AugmentationHolder
+import java.util.HashMap
+
+abstract class AbstractBuilderTemplate extends BaseTemplate {
+    /**
+     * Generated property is set if among methods is found one with the name GET_AUGMENTATION_METHOD_NAME.
+     */
+    protected val Type augmentType
+
+    /**
+     * Set of class attributes (fields) which are derived from the getter methods names.
+     */
+    protected val Set<GeneratedProperty> properties
+
+    /**
+     * GeneratedType for key type, null if this type does not have a key.
+     */
+    protected val Type keyType
+
+    protected val GeneratedType targetType;
+
+    new(AbstractJavaGeneratedType javaType, GeneratedType type, GeneratedType targetType,
+            Set<GeneratedProperty> properties, Type augmentType, Type keyType) {
+        super(javaType, type)
+        this.targetType = targetType
+        this.properties = properties
+        this.augmentType = augmentType
+        this.keyType = keyType
+    }
+
+    new(GeneratedType type, GeneratedType targetType, Set<GeneratedProperty> properties, Type augmentType,
+            Type keyType) {
+        super(type)
+        this.targetType = targetType
+        this.properties = properties
+        this.augmentType = augmentType
+        this.keyType = keyType
+    }
+
+    /**
+     * Template method which generates class attributes.
+     *
+     * @param makeFinal value which specify whether field is|isn't final
+     * @return string with class attributes and their types
+     */
+    def generateFields(boolean makeFinal) '''
+        «IF properties !== null»
+            «FOR f : properties»
+                private«IF makeFinal» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
+            «ENDFOR»
+        «ENDIF»
+        «IF keyType !== null»
+            private«IF makeFinal» final«ENDIF» «keyType.importedName» key;
+        «ENDIF»
+    '''
+
+    def generateAugmentField(boolean isPrivate) '''
+        «IF augmentType !== null»
+            «IF isPrivate»private «ENDIF»«Map.importedName»<«Class.importedName»<? extends «augmentType.importedName»>, «augmentType.importedName»> «AUGMENTATION_FIELD» = «Collections.importedName».emptyMap();
+        «ENDIF»
+    '''
+
+    override generateToString(Collection<GeneratedProperty> properties) '''
+        «IF properties !== null»
+            @«Override.importedName»
+            public «String.importedName» toString() {
+                final «MoreObjects.importedName».ToStringHelper helper = «MoreObjects.importedName».toStringHelper("«targetType.name»");
+                «FOR property : properties»
+                    «CodeHelpers.importedName».appendValue(helper, "«property.fieldName»", «property.fieldName»);
+                «ENDFOR»
+                «IF augmentType !== null»
+                    «CodeHelpers.importedName».appendValue(helper, "«AUGMENTATION_FIELD»", «AUGMENTATION_FIELD».values());
+                «ENDIF»
+                return helper.toString();
+            }
+        «ENDIF»
+    '''
+
+    /**
+     * Template method which generate getter methods for IMPL class.
+     *
+     * @return string with getter methods
+     */
+    def generateGetters(boolean addOverride) '''
+        «IF keyType !== null»
+            «IF addOverride»@«Override.importedName»«ENDIF»
+            public «keyType.importedName» «BindingMapping.IDENTIFIABLE_KEY_NAME»() {
+                return key;
+            }
+
+        «ENDIF»
+        «IF !properties.empty»
+            «FOR field : properties SEPARATOR '\n'»
+                «IF addOverride»@«Override.importedName»«ENDIF»
+                «field.getterMethod»
+            «ENDFOR»
+        «ENDIF»
+        «IF augmentType !== null»
+
+            @SuppressWarnings("unchecked")
+            «IF addOverride»@«Override.importedName»«ENDIF»
+            public <E extends «augmentType.importedName»> E «AUGMENTABLE_AUGMENTATION_NAME»(«Class.importedName»<E> augmentationType) {
+                return (E) «AUGMENTATION_FIELD».get(«CodeHelpers.importedName».nonNullValue(augmentationType, "augmentationType"));
+            }
+        «ENDIF»
+    '''
+
+    def CharSequence generateCopyConstructor(boolean impl, Type fromType, Type implType) '''
+        «IF impl»private«ELSE»public«ENDIF» «type.name»(«fromType.importedName» base) {
+            «val allProps = new ArrayList(properties)»
+            «val isList = implementsIfc(targetType, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), targetType))»
+            «IF isList && keyType !== null»
+                «val keyProps = new ArrayList((keyType as GeneratedTransferObject).properties)»
+                «Collections.sort(keyProps, [ p1, p2 | return p1.name.compareTo(p2.name) ])»
+                «FOR field : keyProps»
+                    «removeProperty(allProps, field.name)»
+                «ENDFOR»
+                if (base.«BindingMapping.IDENTIFIABLE_KEY_NAME»() == null) {
+                    this.key = new «keyType.importedName»(
+                        «FOR keyProp : keyProps SEPARATOR ", "»
+                            base.«keyProp.getterMethodName»()
+                        «ENDFOR»
+                    );
+                    «FOR field : keyProps»
+                        this.«field.fieldName» = base.«field.getterMethodName»();
+                    «ENDFOR»
+                } else {
+                    this.key = base.«BindingMapping.IDENTIFIABLE_KEY_NAME»();
+                    «FOR field : keyProps»
+                           this.«field.fieldName» = key.«field.getterMethodName»();
+                    «ENDFOR»
+                }
+            «ENDIF»
+            «FOR field : allProps»
+                this.«field.fieldName» = base.«field.getterMethodName»();
+            «ENDFOR»
+            «IF augmentType !== null»
+                «IF impl»
+                    this.«AUGMENTATION_FIELD» = «ImmutableMap.importedName».copyOf(base.«AUGMENTATION_FIELD»);
+                «ELSE»
+                    if (base instanceof «implType.importedName») {
+                        «implType.importedName» impl = («implType.importedName») base;
+                        if (!impl.«AUGMENTATION_FIELD».isEmpty()) {
+                            this.«AUGMENTATION_FIELD» = new «HashMap.importedName»<>(impl.«AUGMENTATION_FIELD»);
+                        }
+                    } else if (base instanceof «AugmentationHolder.importedName») {
+                        @SuppressWarnings("unchecked")
+                        «AugmentationHolder.importedName»<«fromType.importedName»> casted =(«AugmentationHolder.importedName»<«fromType.importedName»>) base;
+                        if (!casted.augmentations().isEmpty()) {
+                            this.«AUGMENTATION_FIELD» = new «HashMap.importedName»<>(casted.augmentations());
+                        }
+                    }
+                «ENDIF»
+            «ENDIF»
+        }
+    '''
+
+    private def boolean implementsIfc(GeneratedType type, Type impl) {
+        for (Type ifc : type.implements) {
+            if (ifc.equals(impl)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private def void removeProperty(Collection<GeneratedProperty> props, String name) {
+        var GeneratedProperty toRemove = null
+        for (p : props) {
+            if (p.name.equals(name)) {
+                toRemove = p;
+            }
+        }
+        if (toRemove !== null) {
+            props.remove(toRemove);
+        }
+    }
+}
\ No newline at end of file
index 607451b650161a17fec4a0c6b2989a6704f9071c..9a25876d64be5f5ffbc17c0f94abef31caaa31ac 100644 (file)
@@ -14,7 +14,6 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMap.Builder;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
-import com.google.common.collect.Streams;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
@@ -50,21 +49,29 @@ abstract class AbstractJavaGeneratedType {
         for (GeneratedType type : Iterables.concat(genType.getEnclosedTypes(), genType.getEnumerations())) {
             b.put(type.getIdentifier().simpleName(), new NestedJavaGeneratedType(this, type));
         }
+        collectInheritedEnclosedTypes(b, genType);
+
         enclosedTypes = b.build();
-        conflictingNames = genType instanceof Enumeration
-                ? ((Enumeration) genType).getValues().stream().map(Pair::getMappedName).collect(toImmutableSet())
-                        : ImmutableSet.of();
-    }
 
-    AbstractJavaGeneratedType(final JavaTypeName name, final GeneratedType genType) {
-        this.name = requireNonNull(name);
-        enclosedTypes = ImmutableMap.of();
+        if (genType instanceof Enumeration) {
+            conflictingNames = ((Enumeration) genType).getValues().stream().map(Pair::getMappedName)
+                    .collect(toImmutableSet());
+        } else {
+            conflictingNames = ImmutableSet.of();
+        }
+    }
 
-        // This is a workaround for BuilderTemplate, which does not model itself correctly -- it should generate
-        // a GeneratedType for the Builder with a nested type for the implementation, which really should be
-        // a different template which gets generated as an inner type.
-        conflictingNames = Streams.concat(genType.getEnclosedTypes().stream(), genType.getEnumerations().stream())
-        .map(type -> type.getIdentifier().simpleName()).collect(toImmutableSet());
+    private void collectInheritedEnclosedTypes(Builder<String, NestedJavaGeneratedType> builder,
+            GeneratedType type) {
+        for (Type impl : type.getImplements()) {
+            if (impl instanceof GeneratedType) {
+                final GeneratedType genType = (GeneratedType) impl;
+                for (GeneratedType inner : Iterables.concat(genType.getEnclosedTypes(), genType.getEnumerations())) {
+                    builder.put(inner.getIdentifier().simpleName(), new NestedJavaGeneratedType(this, inner));
+                }
+                collectInheritedEnclosedTypes(builder, genType);
+            }
+        }
     }
 
     final JavaTypeName getName() {
index 57dfc75aab5a8a3e9bdf2ca54c6117da121ccdc5..77daf830b83cb1a959f9a224f2f4bd56577c6217 100644 (file)
@@ -7,10 +7,34 @@
  */
 package org.opendaylight.mdsal.binding.java.api.generator;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTABLE_AUGMENTATION_NAME;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableSortedSet;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import org.eclipse.xtext.xbase.lib.StringExtensions;
 import org.opendaylight.mdsal.binding.model.api.CodeGenerator;
+import org.opendaylight.mdsal.binding.model.api.GeneratedProperty;
 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject;
 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
+import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
+import org.opendaylight.mdsal.binding.model.api.MethodSignature;
+import org.opendaylight.mdsal.binding.model.api.ParameterizedType;
 import org.opendaylight.mdsal.binding.model.api.Type;
+import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTOBuilder;
+import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilder;
+import org.opendaylight.mdsal.binding.model.util.ReferencedTypeImpl;
+import org.opendaylight.mdsal.binding.model.util.Types;
+import org.opendaylight.mdsal.binding.model.util.generated.type.builder.CodegenGeneratedTOBuilder;
+import org.opendaylight.mdsal.binding.model.util.generated.type.builder.CodegenGeneratedTypeBuilder;
+import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
 import org.opendaylight.yangtools.yang.binding.Augmentable;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
 
@@ -22,11 +46,19 @@ import org.opendaylight.yangtools.yang.binding.Augmentation;
  *
  */
 public final class BuilderGenerator implements CodeGenerator {
+    private static final Comparator<MethodSignature> METHOD_COMPARATOR = new AlphabeticallyTypeMemberComparator<>();
+    private static final Type AUGMENTATION_RET_TYPE;
 
-    /**
-     * Constant used as sufix for builder name.
-     */
-    public static final String BUILDER = "Builder";
+    static {
+        final Method m;
+        try {
+            m = Augmentable.class.getDeclaredMethod(AUGMENTABLE_AUGMENTATION_NAME, Class.class);
+        } catch (NoSuchMethodException e) {
+            throw new ExceptionInInitializerError(e);
+        }
+
+        AUGMENTATION_RET_TYPE = new ReferencedTypeImpl(JavaTypeName.create(m.getReturnType()));
+    }
 
     /**
      * Passes via list of implemented types in <code>type</code>.
@@ -60,16 +92,134 @@ public final class BuilderGenerator implements CodeGenerator {
     @Override
     public String generate(Type type) {
         if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) {
-            final GeneratedType genType = (GeneratedType) type;
-            final BuilderTemplate template = new BuilderTemplate(genType);
-            return template.generate();
+            return templateForType((GeneratedType) type).generate();
         }
         return "";
     }
 
     @Override
     public String getUnitName(Type type) {
-        return type.getName() + BUILDER;
+        return type.getName() + BuilderTemplate.BUILDER;
+    }
+
+    @VisibleForTesting
+    static BuilderTemplate templateForType(GeneratedType type) {
+        final GeneratedType genType = type;
+        final JavaTypeName origName = genType.getIdentifier();
+
+        final Set<MethodSignature> methods = new LinkedHashSet<>();
+        final Type augmentType = createMethods(genType, methods);
+        final Set<MethodSignature> sortedMethods = ImmutableSortedSet.orderedBy(METHOD_COMPARATOR)
+                .addAll(methods).build();
+
+        final GeneratedTypeBuilder builderTypeBuilder = new CodegenGeneratedTypeBuilder(
+            origName.createSibling(origName.simpleName() + BuilderTemplate.BUILDER));
+
+        final GeneratedTOBuilder implTypeBuilder = builderTypeBuilder.addEnclosingTransferObject(
+            origName.simpleName() + "Impl");
+        implTypeBuilder.addImplementsType(genType);
+
+        return new BuilderTemplate(builderTypeBuilder.build(), genType, propertiesFromMethods(sortedMethods),
+            augmentType, getKey(genType));
     }
 
+    private static Type getKey(GeneratedType type) {
+        for (MethodSignature m : type.getMethodDefinitions()) {
+            if (BindingMapping.IDENTIFIABLE_KEY_NAME.equals(m.getName())) {
+                return m.getReturnType();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns set of method signature instances which contains all the methods of the <code>genType</code>
+     * and all the methods of the implemented interfaces.
+     *
+     * @returns set of method signature instances
+     */
+    private static ParameterizedType createMethods(GeneratedType type, Set<MethodSignature> methods) {
+        methods.addAll(type.getMethodDefinitions());
+        return collectImplementedMethods(type, methods, type.getImplements());
+    }
+
+    /**
+     * Adds to the <code>methods</code> set all the methods of the <code>implementedIfcs</code>
+     * and recursively their implemented interfaces.
+     *
+     * @param methods set of method signatures
+     * @param implementedIfcs list of implemented interfaces
+     */
+    private static ParameterizedType collectImplementedMethods(GeneratedType type, Set<MethodSignature> methods,
+            List<Type> implementedIfcs) {
+        if (implementedIfcs == null || implementedIfcs.isEmpty()) {
+            return null;
+        }
+
+        ParameterizedType augmentType = null;
+        for (Type implementedIfc : implementedIfcs) {
+            if (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject)) {
+                final GeneratedType ifc = (GeneratedType) implementedIfc;
+                methods.addAll(ifc.getMethodDefinitions());
+
+                final ParameterizedType t = collectImplementedMethods(type, methods, ifc.getImplements());
+                if (t != null && augmentType == null) {
+                    augmentType = t;
+                }
+            } else if (Augmentable.class.getName().equals(implementedIfc.getFullyQualifiedName())) {
+                augmentType = Types.parameterizedTypeFor(AUGMENTATION_RET_TYPE,
+                    new ReferencedTypeImpl(type.getIdentifier()));
+            }
+        }
+
+        return augmentType;
+    }
+
+    /**
+     * Creates set of generated property instances from getter <code>methods</code>.
+     *
+     * @param set of method signature instances which should be transformed to list of properties
+     * @return set of generated property instances which represents the getter <code>methods</code>
+     */
+    private static Set<GeneratedProperty> propertiesFromMethods(Collection<MethodSignature> methods) {
+        if (methods == null || methods.isEmpty()) {
+            return Collections.emptySet();
+        }
+        final Set<GeneratedProperty> result = new LinkedHashSet<>();
+        for (MethodSignature m : methods) {
+            final GeneratedProperty createdField = propertyFromGetter(m);
+            if (createdField != null) {
+                result.add(createdField);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Creates generated property instance from the getter <code>method</code> name and return type.
+     *
+     * @param method method signature from which is the method name and return type obtained
+     * @return generated property instance for the getter <code>method</code>
+     * @throws IllegalArgumentException<ul>
+     *  <li>if the <code>method</code> equals <code>null</code></li>
+     *  <li>if the name of the <code>method</code> equals <code>null</code></li>
+     *  <li>if the name of the <code>method</code> is empty</li>
+     *  <li>if the return type of the <code>method</code> equals <code>null</code></li>
+     * </ul>
+     */
+    private static GeneratedProperty propertyFromGetter(MethodSignature method) {
+        checkArgument(method != null);
+        checkArgument(method.getReturnType() != null);
+        checkArgument(method.getName() != null);
+        checkArgument(!method.getName().isEmpty());
+        final String prefix = Types.BOOLEAN.equals(method.getReturnType()) ? "is" : "get";
+        if (!method.getName().startsWith(prefix)) {
+            return null;
+        }
+
+        final String fieldName = StringExtensions.toFirstLower(method.getName().substring(prefix.length()));
+        final GeneratedTOBuilder tmpGenTO = new CodegenGeneratedTOBuilder(JavaTypeName.create("foo", "foo"));
+        tmpGenTO.addProperty(fieldName).setReturnType(method.getReturnType());
+        return tmpGenTO.build().getProperties().get(0);
+    }
 }
diff --git a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderImplTemplate.xtend b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderImplTemplate.xtend
new file mode 100644 (file)
index 0000000..3eb6e79
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.binding.java.api.generator
+
+import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTATION_FIELD
+import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTABLE_AUGMENTATION_NAME
+
+import java.util.Arrays
+import java.util.Objects
+import java.util.Map
+import org.opendaylight.mdsal.binding.model.api.GeneratedType
+import org.opendaylight.mdsal.binding.model.api.Type
+import org.opendaylight.yangtools.yang.binding.DataObject
+
+class BuilderImplTemplate extends AbstractBuilderTemplate {
+    val Type builderType;
+
+    new(BuilderTemplate builder, GeneratedType type) {
+        super(builder.javaType.getEnclosedType(type.identifier), type, builder.targetType, builder.properties,
+            builder.augmentType, builder.keyType)
+        this.builderType = builder.type
+    }
+
+    override body() '''
+        private static final class «type.name» implements «targetType.importedName» {
+
+            «generateFields(true)»
+
+            «generateAugmentField(true)»
+
+            «generateCopyConstructor(true, builderType, type)»
+
+            @«Override.importedName»
+            public «Class.importedName»<«targetType.importedName»> getImplementedInterface() {
+                return «targetType.importedName».class;
+            }
+
+            «generateGetters(true)»
+
+            «generateHashCode()»
+
+            «generateEquals()»
+
+            «generateToString(properties)»
+        }
+    '''
+
+    /**
+     * Template method which generates the method <code>hashCode()</code>.
+     *
+     * @return string with the <code>hashCode()</code> method definition in JAVA format
+     */
+    def protected generateHashCode() '''
+        «IF !properties.empty || augmentType !== null»
+            private int hash = 0;
+            private volatile boolean hashValid = false;
+
+            @«Override.importedName»
+            public int hashCode() {
+                if (hashValid) {
+                    return hash;
+                }
+
+                final int prime = 31;
+                int result = 1;
+                «FOR property : properties»
+                    «IF property.returnType.name.contains("[")»
+                    result = prime * result + «Arrays.importedName».hashCode(«property.fieldName»);
+                    «ELSE»
+                    result = prime * result + «Objects.importedName».hashCode(«property.fieldName»);
+                    «ENDIF»
+                «ENDFOR»
+                «IF augmentType !== null»
+                    result = prime * result + «Objects.importedName».hashCode(«AUGMENTATION_FIELD»);
+                «ENDIF»
+
+                hash = result;
+                hashValid = true;
+                return result;
+            }
+        «ENDIF»
+    '''
+
+    /**
+     * Template method which generates the method <code>equals()</code>.
+     *
+     * @return string with the <code>equals()</code> method definition in JAVA format
+     */
+    def protected generateEquals() '''
+        «IF !properties.empty || augmentType !== null»
+            @«Override.importedName»
+            public boolean equals(«Object.importedName» obj) {
+                if (this == obj) {
+                    return true;
+                }
+                if (!(obj instanceof «DataObject.importedName»)) {
+                    return false;
+                }
+                if (!«targetType.importedName».class.equals(((«DataObject.importedName»)obj).getImplementedInterface())) {
+                    return false;
+                }
+                «targetType.importedName» other = («targetType.importedName»)obj;
+                «FOR property : properties»
+                    «val fieldName = property.fieldName»
+                    «IF property.returnType.name.contains("[")»
+                    if (!«Arrays.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
+                    «ELSE»
+                    if (!«Objects.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
+                    «ENDIF»
+                        return false;
+                    }
+                «ENDFOR»
+                «IF augmentType !== null»
+                    if (getClass() == obj.getClass()) {
+                        // Simple case: we are comparing against self
+                        «type.name» otherImpl = («type.name») obj;
+                        if (!«Objects.importedName».equals(«AUGMENTATION_FIELD», otherImpl.«AUGMENTATION_FIELD»)) {
+                            return false;
+                        }
+                    } else {
+                        // Hard case: compare our augments with presence there...
+                        for («Map.importedName».Entry<«Class.importedName»<? extends «augmentType.importedName»>, «augmentType.importedName»> e : «AUGMENTATION_FIELD».entrySet()) {
+                            if (!e.getValue().equals(other.«AUGMENTABLE_AUGMENTATION_NAME»(e.getKey()))) {
+                                return false;
+                            }
+                        }
+                        // .. and give the other one the chance to do the same
+                        if (!obj.equals(this)) {
+                            return false;
+                        }
+                    }
+                «ENDIF»
+                return true;
+            }
+        «ENDIF»
+    '''
+}
\ No newline at end of file
index c808bc698e48e5ffb1ae7b76c86f493ba496f53b..d2be1ba763ddd6244458c0a13194efec61b92048 100644 (file)
@@ -9,186 +9,44 @@ package org.opendaylight.mdsal.binding.java.api.generator
 
 import static extension org.apache.commons.text.StringEscapeUtils.escapeJava
 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTATION_FIELD
-import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTABLE_AUGMENTATION_NAME
 
-import com.google.common.base.MoreObjects
-import com.google.common.collect.ImmutableMap
-import com.google.common.collect.ImmutableSortedSet
 import com.google.common.collect.ImmutableList
 import java.util.ArrayList
-import java.util.Arrays
 import java.util.Collection
-import java.util.Collections
 import java.util.HashMap
 import java.util.HashSet
-import java.util.LinkedHashSet
 import java.util.List
 import java.util.Map
-import java.util.Objects
 import java.util.Set
 import java.util.regex.Pattern
 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
 import org.opendaylight.mdsal.binding.model.api.GeneratedType
 import org.opendaylight.mdsal.binding.model.api.JavaTypeName
-import org.opendaylight.mdsal.binding.model.api.MethodSignature
-import org.opendaylight.mdsal.binding.model.api.Type
 import org.opendaylight.mdsal.binding.model.api.ParameterizedType
-import org.opendaylight.mdsal.binding.model.util.ReferencedTypeImpl
-import org.opendaylight.mdsal.binding.model.util.Types
-import org.opendaylight.mdsal.binding.model.util.generated.type.builder.CodegenGeneratedTOBuilder
+import org.opendaylight.mdsal.binding.model.api.Type
 import org.opendaylight.mdsal.binding.model.util.TypeConstants
-import org.opendaylight.mdsal.binding.spec.naming.BindingMapping
+import org.opendaylight.mdsal.binding.model.util.Types
 import org.opendaylight.yangtools.concepts.Builder
-import org.opendaylight.yangtools.yang.binding.Augmentable
-import org.opendaylight.yangtools.yang.binding.AugmentationHolder
 import org.opendaylight.yangtools.yang.binding.CodeHelpers
 import org.opendaylight.yangtools.yang.binding.DataObject
-import org.opendaylight.yangtools.yang.binding.Identifiable
 
 /**
  * Template for generating JAVA builder classes.
  */
-
-class BuilderTemplate extends BaseTemplate {
-    /**
-     * Constant with the suffix for builder classes.
-     */
-    val static BUILDER = 'Builder'
-
-    /**
-     * Constant with suffix for the classes which are generated from the builder classes.
-     */
-    val static IMPL = 'Impl'
-
-    /**
-     * Generated property is set if among methods is found one with the name GET_AUGMENTATION_METHOD_NAME.
-     */
-    var Type augmentType
-
-    /**
-     * Set of class attributes (fields) which are derived from the getter methods names.
-     */
-    val Set<GeneratedProperty> properties
-
+class BuilderTemplate extends AbstractBuilderTemplate {
     /**
-     * GeneratedType for key type, null if this type does not have a key.
+     * Constant used as suffix for builder name.
      */
-    val Type keyType
-
-    static val METHOD_COMPARATOR = new AlphabeticallyTypeMemberComparator<MethodSignature>();
+    public static val BUILDER = "Builder";
 
     /**
      * Constructs new instance of this class.
      * @throws IllegalArgumentException if <code>genType</code> equals <code>null</code>
      */
-    new(GeneratedType genType) {
-        super(new TopLevelJavaGeneratedType(builderName(genType), genType), genType)
-        this.properties = propertiesFromMethods(createMethods)
-        keyType = genType.key
-    }
-
-    def static builderName(GeneratedType genType) {
-        val name = genType.identifier
-        name.createSibling(name.simpleName + "Builder")
-    }
-
-    /**
-     * Returns set of method signature instances which contains all the methods of the <code>genType</code>
-     * and all the methods of the implemented interfaces.
-     *
-     * @returns set of method signature instances
-     */
-    def private Set<MethodSignature> createMethods() {
-        val Set<MethodSignature> methods = new LinkedHashSet();
-        methods.addAll(type.methodDefinitions)
-        collectImplementedMethods(methods, type.implements)
-        val Set<MethodSignature> sortedMethods = ImmutableSortedSet.orderedBy(METHOD_COMPARATOR).addAll(methods).build()
-
-        return sortedMethods
-    }
-
-    /**
-     * Adds to the <code>methods</code> set all the methods of the <code>implementedIfcs</code>
-     * and recursively their implemented interfaces.
-     *
-     * @param methods set of method signatures
-     * @param implementedIfcs list of implemented interfaces
-     */
-    def private void collectImplementedMethods(Set<MethodSignature> methods, List<Type> implementedIfcs) {
-        if (implementedIfcs === null || implementedIfcs.empty) {
-            return
-        }
-        for (implementedIfc : implementedIfcs) {
-            if ((implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))) {
-                val ifc = implementedIfc as GeneratedType
-                methods.addAll(ifc.methodDefinitions)
-                collectImplementedMethods(methods, ifc.implements)
-            } else if (implementedIfc.fullyQualifiedName == Augmentable.name) {
-                val m = Augmentable.getDeclaredMethod(AUGMENTABLE_AUGMENTATION_NAME, Class)
-                val identifier = JavaTypeName.create(m.returnType)
-                val refType = new ReferencedTypeImpl(identifier)
-                val generic = new ReferencedTypeImpl(type.identifier)
-                augmentType = Types.parameterizedTypeFor(refType, generic)
-            }
-        }
-    }
-
-    /**
-     * Returns the first element of the list <code>elements</code>.
-     *
-     * @param list of elements
-     */
-    def private <E> first(List<E> elements) {
-        elements.get(0)
-    }
-
-    /**
-     * Creates set of generated property instances from getter <code>methods</code>.
-     *
-     * @param set of method signature instances which should be transformed to list of properties
-     * @return set of generated property instances which represents the getter <code>methods</code>
-     */
-    def private propertiesFromMethods(Collection<MethodSignature> methods) {
-        if (methods === null || methods.isEmpty()) {
-            return Collections.emptySet
-        }
-        val Set<GeneratedProperty> result = new LinkedHashSet
-        for (m : methods) {
-            val createdField = m.propertyFromGetter
-            if (createdField !== null) {
-                result.add(createdField)
-            }
-        }
-        return result
-    }
-
-    /**
-     * Creates generated property instance from the getter <code>method</code> name and return type.
-     *
-     * @param method method signature from which is the method name and return type obtained
-     * @return generated property instance for the getter <code>method</code>
-     * @throws IllegalArgumentException<ul>
-     *  <li>if the <code>method</code> equals <code>null</code></li>
-     *  <li>if the name of the <code>method</code> equals <code>null</code></li>
-     *  <li>if the name of the <code>method</code> is empty</li>
-     *  <li>if the return type of the <code>method</code> equals <code>null</code></li>
-     * </ul>
-     */
-    def private GeneratedProperty propertyFromGetter(MethodSignature method) {
-        if (method === null || method.name === null || method.name.empty || method.returnType === null) {
-            throw new IllegalArgumentException("Method, method name, method return type reference cannot be NULL or empty!")
-        }
-        var prefix = "get";
-        if (Types.BOOLEAN.equals(method.returnType)) {
-            prefix = "is";
-        }
-        if (method.name.startsWith(prefix)) {
-            val fieldName = method.getName().substring(prefix.length()).toFirstLower
-            val tmpGenTO = new CodegenGeneratedTOBuilder(JavaTypeName.create("foo", "foo"))
-            tmpGenTO.addProperty(fieldName).setReturnType(method.returnType)
-            return tmpGenTO.build.properties.first
-        }
+    new(GeneratedType genType, GeneratedType targetType, Set<GeneratedProperty> properties, Type augmentType,
+            Type keyType) {
+        super(genType, targetType, properties, augmentType, keyType)
     }
 
     override isLocalInnerClass(JavaTypeName name) {
@@ -203,7 +61,7 @@ class BuilderTemplate extends BaseTemplate {
      */
     override body() '''
         «wrapToDocumentation(formatDataForJavaDoc(type))»
-        public class «type.name»«BUILDER» implements «Builder.importedName»<«type.importedName»> {
+        public class «type.name» implements «Builder.importedName»<«targetType.importedName»> {
 
             «generateFields(false)»
 
@@ -211,52 +69,33 @@ class BuilderTemplate extends BaseTemplate {
 
             «generateAugmentField(false)»
 
-            «generateConstructorsFromIfcs(type
+            «generateConstructorsFromIfcs()»
 
-            «generateCopyConstructor(false)»
+            «generateCopyConstructor(false, targetType, type.enclosedTypes.get(0)
 
-            «generateMethodFieldsFrom(type
+            «generateMethodFieldsFrom()»
 
             «generateGetters(false)»
 
             «generateSetters»
 
             @«Override.importedName»
-            public «type.name» build() {
-                return new «type.name»«IMPL»(this);
-            }
-
-            private static final class «type.name»«IMPL» implements «type.name» {
-
-                «implementedInterfaceGetter»
-
-                «generateFields(true)»
-
-                «generateAugmentField(true)»
-
-                «generateCopyConstructor(true)»
-
-                «generateGetters(true)»
-
-                «generateHashCode()»
-
-                «generateEquals()»
-
-                «generateToString(properties)»
+            public «targetType.name» build() {
+                return new «type.enclosedTypes.get(0).importedName»(this);
             }
 
+            «new BuilderImplTemplate(this, type.enclosedTypes.get(0)).body»
         }
     '''
 
     /**
      * Generate default constructor and constructor for every implemented interface from uses statements.
      */
-    def private generateConstructorsFromIfcs(Type type) '''
-        public «type.name»«BUILDER»() {
+    def private generateConstructorsFromIfcs() '''
+        public «type.name»() {
         }
-        «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
-            «val ifc = type as GeneratedType»
-            «FOR impl : ifc.implements»
+        «IF (!(targetType instanceof GeneratedTransferObject))»
+            «FOR impl : targetType.implements»
                 «generateConstructorFromIfc(impl)»
             «ENDFOR»
         «ENDIF»
@@ -268,7 +107,7 @@ class BuilderTemplate extends BaseTemplate {
     def private Object generateConstructorFromIfc(Type impl) '''
         «IF (impl instanceof GeneratedType)»
             «IF !(impl.methodDefinitions.empty)»
-                public «type.name»«BUILDER»(«impl.fullyQualifiedName» arg) {
+                public «type.name»(«impl.fullyQualifiedName» arg) {
                     «printConstructorPropertySetter(impl)»
                 }
             «ENDIF»
@@ -293,18 +132,17 @@ class BuilderTemplate extends BaseTemplate {
     /**
      * Generate 'fieldsFrom' method to set builder properties based on type of given argument.
      */
-    def private generateMethodFieldsFrom(Type type) '''
-        «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
-            «val ifc = type as GeneratedType»
-            «IF ifc.hasImplementsFromUses»
-                «val List<Type> done = ifc.getBaseIfcs»
-                «generateMethodFieldsFromComment(ifc)»
+    def private generateMethodFieldsFrom() '''
+        «IF (!(targetType instanceof GeneratedTransferObject))»
+            «IF targetType.hasImplementsFromUses»
+                «val List<Type> done = targetType.getBaseIfcs»
+                «generateMethodFieldsFromComment(targetType)»
                 public void fieldsFrom(«DataObject.importedName» arg) {
                     boolean isValidArg = false;
-                    «FOR impl : ifc.getAllIfcs»
+                    «FOR impl : targetType.getAllIfcs»
                         «generateIfCheck(impl, done)»
                     «ENDFOR»
-                    «CodeHelpers.importedName».validValue(isValidArg, arg, "«ifc.getAllIfcs.toListOfNames»");
+                    «CodeHelpers.importedName».validValue(isValidArg, arg, "«targetType.getAllIfcs.toListOfNames»");
                 }
             «ENDIF»
         «ENDIF»
@@ -388,29 +226,6 @@ class BuilderTemplate extends BaseTemplate {
         return names
     }
 
-    /**
-     * Template method which generates class attributes.
-     *
-     * @param boolean value which specify whether field is|isn't final
-     * @return string with class attributes and their types
-     */
-    def private generateFields(boolean _final) '''
-        «IF properties !== null»
-            «FOR f : properties»
-                private«IF _final» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
-            «ENDFOR»
-        «ENDIF»
-        «IF keyType !== null»
-            private«IF _final» final«ENDIF» «keyType.importedName» key;
-        «ENDIF»
-    '''
-
-    def private generateAugmentField(boolean isPrivate) '''
-        «IF augmentType !== null»
-            «IF isPrivate»private «ENDIF»«Map.importedName»<«Class.importedName»<? extends «augmentType.importedName»>, «augmentType.importedName»> «AUGMENTATION_FIELD» = «Collections.importedName».emptyMap();
-        «ENDIF»
-    '''
-
     def private constantsDeclarations() '''
         «FOR c : type.getConstantDefinitions»
             «IF c.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)»
@@ -436,7 +251,7 @@ class BuilderTemplate extends BaseTemplate {
         «IF restrictions !== null»
             «generateCheckers(field, restrictions, actualType)»
         «ENDIF»
-        public «type.getName»Builder set«field.getName.toFirstUpper»(final «field.returnType.importedName» values) {
+        public «type.getName» set«field.getName.toFirstUpper»(final «field.returnType.importedName» values) {
         «IF restrictions !== null»
             if (values != null) {
                for («actualType.getFullyQualifiedName» value : values) {
@@ -456,7 +271,7 @@ class BuilderTemplate extends BaseTemplate {
             «generateCheckers(field, restrictions, actualType)»
         «ENDIF»
 
-        public «type.getName»Builder set«field.getName.toFirstUpper»(final «field.returnType.importedName» value) {
+        public «type.getName» set«field.getName.toFirstUpper»(final «field.returnType.importedName» value) {
         «IF restrictions !== null»
             if (value != null) {
                 «checkArgument(field, restrictions, actualType, "value")»
@@ -478,7 +293,7 @@ class BuilderTemplate extends BaseTemplate {
      */
     def private generateSetters() '''
         «IF keyType !== null»
-            public «type.getName»Builder withKey(final «keyType.importedName» key) {
+            public «type.getName» withKey(final «keyType.importedName» key) {
                 this.key = key;
                 return this;
             }
@@ -492,7 +307,7 @@ class BuilderTemplate extends BaseTemplate {
         «ENDFOR»
 
         «IF augmentType !== null»
-            public «type.name»«BUILDER» add«AUGMENTATION_FIELD.toFirstUpper»(«Class.importedName»<? extends «augmentType.importedName»> augmentationType, «augmentType.importedName» augmentationValue) {
+            public «type.name» add«AUGMENTATION_FIELD.toFirstUpper»(«Class.importedName»<? extends «augmentType.importedName»> augmentationType, «augmentType.importedName» augmentationValue) {
                 if (augmentationValue == null) {
                     return remove«AUGMENTATION_FIELD.toFirstUpper»(augmentationType);
                 }
@@ -505,7 +320,7 @@ class BuilderTemplate extends BaseTemplate {
                 return this;
             }
 
-            public «type.name»«BUILDER» remove«AUGMENTATION_FIELD.toFirstUpper»(«Class.importedName»<? extends «augmentType.importedName»> augmentationType) {
+            public «type.name» remove«AUGMENTATION_FIELD.toFirstUpper»(«Class.importedName»<? extends «augmentType.importedName»> augmentationType) {
                 if (this.«AUGMENTATION_FIELD» instanceof «HashMap.importedName») {
                     this.«AUGMENTATION_FIELD».remove(augmentationType);
                 }
@@ -514,227 +329,6 @@ class BuilderTemplate extends BaseTemplate {
         «ENDIF»
     '''
 
-    def private CharSequence generateCopyConstructor(boolean impl) '''
-        «IF impl»private«ELSE»public«ENDIF» «type.name»«IF impl»«IMPL»«ELSE»«BUILDER»«ENDIF»(«type.name»«IF impl»«BUILDER»«ENDIF» base) {
-            «val allProps = new ArrayList(properties)»
-            «val isList = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), type))»
-            «IF isList && keyType !== null»
-                «val keyProps = new ArrayList((keyType as GeneratedTransferObject).properties)»
-                «Collections.sort(keyProps, [ p1, p2 | return p1.name.compareTo(p2.name) ])»
-                «FOR field : keyProps»
-                    «removeProperty(allProps, field.name)»
-                «ENDFOR»
-                if (base.«BindingMapping.IDENTIFIABLE_KEY_NAME»() == null) {
-                    this.key = new «keyType.importedName»(
-                        «FOR keyProp : keyProps SEPARATOR ", "»
-                            base.«keyProp.getterMethodName»()
-                        «ENDFOR»
-                    );
-                    «FOR field : keyProps»
-                        this.«field.fieldName» = base.«field.getterMethodName»();
-                    «ENDFOR»
-                } else {
-                    this.key = base.«BindingMapping.IDENTIFIABLE_KEY_NAME»();
-                    «FOR field : keyProps»
-                           this.«field.fieldName» = key.«field.getterMethodName»();
-                    «ENDFOR»
-                }
-            «ENDIF»
-            «FOR field : allProps»
-                this.«field.fieldName» = base.«field.getterMethodName»();
-            «ENDFOR»
-            «IF augmentType !== null»
-                «IF impl»
-                    this.«AUGMENTATION_FIELD» = «ImmutableMap.importedName».copyOf(base.«AUGMENTATION_FIELD»);
-                «ELSE»
-                    if (base instanceof «type.name»«IMPL») {
-                        «type.name»«IMPL» impl = («type.name»«IMPL») base;
-                        if (!impl.«AUGMENTATION_FIELD».isEmpty()) {
-                            this.«AUGMENTATION_FIELD» = new «HashMap.importedName»<>(impl.«AUGMENTATION_FIELD»);
-                        }
-                    } else if (base instanceof «AugmentationHolder.importedName») {
-                        @SuppressWarnings("unchecked")
-                        «AugmentationHolder.importedName»<«type.importedName»> casted =(«AugmentationHolder.importedName»<«type.importedName»>) base;
-                        if (!casted.augmentations().isEmpty()) {
-                            this.«AUGMENTATION_FIELD» = new «HashMap.importedName»<>(casted.augmentations());
-                        }
-                    }
-                «ENDIF»
-            «ENDIF»
-        }
-    '''
-
-    private def boolean implementsIfc(GeneratedType type, Type impl) {
-        for (Type ifc : type.implements) {
-            if (ifc.equals(impl)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private def getKey(GeneratedType type) {
-        for (m : type.methodDefinitions) {
-            if (BindingMapping.IDENTIFIABLE_KEY_NAME.equals(m.name)) {
-                return m.returnType;
-            }
-        }
-    }
-
-    private def void removeProperty(Collection<GeneratedProperty> props, String name) {
-        var GeneratedProperty toRemove = null
-        for (p : props) {
-            if (p.name.equals(name)) {
-                toRemove = p;
-            }
-        }
-        if (toRemove !== null) {
-            props.remove(toRemove);
-        }
-    }
-
-    /**
-     * Template method which generate getter methods for IMPL class.
-     *
-     * @return string with getter methods
-     */
-    def private generateGetters(boolean addOverride) '''
-        «IF keyType !== null»
-            «IF addOverride»@«Override.importedName»«ENDIF»
-            public «keyType.importedName» «BindingMapping.IDENTIFIABLE_KEY_NAME»() {
-                return key;
-            }
-
-        «ENDIF»
-        «IF !properties.empty»
-            «FOR field : properties SEPARATOR '\n'»
-                «IF addOverride»@«Override.importedName»«ENDIF»
-                «field.getterMethod»
-            «ENDFOR»
-        «ENDIF»
-        «IF augmentType !== null»
-
-            @SuppressWarnings("unchecked")
-            «IF addOverride»@«Override.importedName»«ENDIF»
-            public <E extends «augmentType.importedName»> E «AUGMENTABLE_AUGMENTATION_NAME»(«Class.importedName»<E> augmentationType) {
-                return (E) «AUGMENTATION_FIELD».get(«CodeHelpers.importedName».nonNullValue(augmentationType, "augmentationType"));
-            }
-        «ENDIF»
-    '''
-
-    /**
-     * Template method which generates the method <code>hashCode()</code>.
-     *
-     * @return string with the <code>hashCode()</code> method definition in JAVA format
-     */
-    def protected generateHashCode() '''
-        «IF !properties.empty || augmentType !== null»
-            private int hash = 0;
-            private volatile boolean hashValid = false;
-
-            @«Override.importedName»
-            public int hashCode() {
-                if (hashValid) {
-                    return hash;
-                }
-
-                final int prime = 31;
-                int result = 1;
-                «FOR property : properties»
-                    «IF property.returnType.name.contains("[")»
-                    result = prime * result + «Arrays.importedName».hashCode(«property.fieldName»);
-                    «ELSE»
-                    result = prime * result + «Objects.importedName».hashCode(«property.fieldName»);
-                    «ENDIF»
-                «ENDFOR»
-                «IF augmentType !== null»
-                    result = prime * result + «Objects.importedName».hashCode(«AUGMENTATION_FIELD»);
-                «ENDIF»
-
-                hash = result;
-                hashValid = true;
-                return result;
-            }
-        «ENDIF»
-    '''
-
-    /**
-     * Template method which generates the method <code>equals()</code>.
-     *
-     * @return string with the <code>equals()</code> method definition in JAVA format
-     */
-    def protected generateEquals() '''
-        «IF !properties.empty || augmentType !== null»
-            @«Override.importedName»
-            public boolean equals(«Object.importedName» obj) {
-                if (this == obj) {
-                    return true;
-                }
-                if (!(obj instanceof «DataObject.importedName»)) {
-                    return false;
-                }
-                if (!«type.importedName».class.equals(((«DataObject.importedName»)obj).getImplementedInterface())) {
-                    return false;
-                }
-                «type.importedName» other = («type.importedName»)obj;
-                «FOR property : properties»
-                    «val fieldName = property.fieldName»
-                    «IF property.returnType.name.contains("[")»
-                    if (!«Arrays.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
-                    «ELSE»
-                    if (!«Objects.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
-                    «ENDIF»
-                        return false;
-                    }
-                «ENDFOR»
-                «IF augmentType !== null»
-                    if (getClass() == obj.getClass()) {
-                        // Simple case: we are comparing against self
-                        «type.name»«IMPL» otherImpl = («type.name»«IMPL») obj;
-                        if (!«Objects.importedName».equals(«AUGMENTATION_FIELD», otherImpl.«AUGMENTATION_FIELD»)) {
-                            return false;
-                        }
-                    } else {
-                        // Hard case: compare our augments with presence there...
-                        for («Map.importedName».Entry<«Class.importedName»<? extends «augmentType.importedName»>, «augmentType.importedName»> e : «AUGMENTATION_FIELD».entrySet()) {
-                            if (!e.getValue().equals(other.«AUGMENTABLE_AUGMENTATION_NAME»(e.getKey()))) {
-                                return false;
-                            }
-                        }
-                        // .. and give the other one the chance to do the same
-                        if (!obj.equals(this)) {
-                            return false;
-                        }
-                    }
-                «ENDIF»
-                return true;
-            }
-        «ENDIF»
-    '''
-
-    override generateToString(Collection<GeneratedProperty> properties) '''
-        «IF properties !== null»
-            @«Override.importedName»
-            public «String.importedName» toString() {
-                final «MoreObjects.importedName».ToStringHelper helper = «MoreObjects.importedName».toStringHelper("«type.name»");
-                «FOR property : properties»
-                    «CodeHelpers.importedName».appendValue(helper, "«property.fieldName»", «property.fieldName»);
-                «ENDFOR»
-                «IF augmentType !== null»
-                    «CodeHelpers.importedName».appendValue(helper, "«AUGMENTATION_FIELD»", «AUGMENTATION_FIELD».values());
-                «ENDIF»
-                return helper.toString();
-            }
-        «ENDIF»
-    '''
-
-    def implementedInterfaceGetter() '''
-    @«Override.importedName»
-    public «Class.importedName»<«type.importedName»> getImplementedInterface() {
-        return «type.importedName».class;
-    }
-    '''
-
     private def createDescription(GeneratedType type) {
         return '''
         Class that builds {@link «type.importedName»} instances.
index 93cb83e1a7276c7f859a5edb4cd28640f2e655e3..741ede45d7427d920e9878f76eb1ea1fdff61486 100644 (file)
@@ -30,10 +30,6 @@ final class TopLevelJavaGeneratedType extends AbstractJavaGeneratedType {
         super(genType);
     }
 
-    TopLevelJavaGeneratedType(final JavaTypeName name, final GeneratedType genType) {
-        super(name, genType);
-    }
-
     @Override
     String localTypeName(@NonNull final JavaTypeName type) {
         // Locally-anchored type, this is simple: just strip the first local name component and concat the others
index 7e3c0682f6d77dc7e79ba3286f8df0a2a3b66fdc..84e9d78f665b750f94a0cb63371878b1cff1b442 100644 (file)
@@ -114,10 +114,9 @@ class UnionTemplate extends ClassTemplate {
                 return new «String.importedName»(«field»);
                 «ELSEIF propRet.fullyQualifiedName.startsWith("java.lang") || propRet instanceof Enumeration
                         || propRet.fullyQualifiedName.startsWith("java.math")»
-                   ««« type int*, uint, decimal64 or enumeration*
+                    ««« type int*, uint, decimal64 or enumeration*
                 return «field».toString();
-                «ELSEIF propRet instanceof GeneratedTransferObject
-                        && (propRet as GeneratedTransferObject).unionType»
+                «ELSEIF propRet instanceof GeneratedTransferObject && (propRet as GeneratedTransferObject).unionType»
                     ««« union type
                 return «field».stringValue();
                 «ELSEIF propRet instanceof GeneratedTransferObject // Is it a GeneratedTransferObject
index c19625563ddd7827171391f62a39b02612e32681..83e3a37946492fdb6044622712af26ad66a7d2c0 100644 (file)
@@ -12,12 +12,9 @@ import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 
-import java.lang.reflect.Field;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.List;
 import org.junit.Test;
-import org.opendaylight.mdsal.binding.model.api.GeneratedProperty;
 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
 import org.opendaylight.mdsal.binding.model.api.MethodSignature;
@@ -126,16 +123,9 @@ public class BuilderGeneratorTest {
         return genType;
     }
 
-    @SuppressWarnings("unchecked")
     private static CharSequence genToString(final GeneratedType genType) {
-        try {
-            final BuilderTemplate bt = new BuilderTemplate(genType);
-            final Field propertiesField = bt.getClass().getDeclaredField(PROPERTIES_FIELD_NAME);
-            propertiesField.setAccessible(true);
-            return bt.generateToString((Collection<GeneratedProperty>) propertiesField.get(bt));
-        } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
-            throw new RuntimeException(e);
-        }
+        final BuilderTemplate bt = BuilderGenerator.templateForType(genType);
+        return bt.generateToString(bt.properties);
     }
 
     private static GeneratedType mockGenType(final String methodeName) {
index 6dd0fcef59a8a27df2973cf009abbc4059db45ba..540397e12f399dd604907384da26e2b344cffe4a 100644 (file)
@@ -629,6 +629,15 @@ public class CompilationTest extends BaseCompilationTest {
         CompilationTestUtils.cleanUp(sourcesOutputDir, compiledOutputDir);
     }
 
+    @Test
+    public void testMdsal365() throws Exception {
+        final File sourcesOutputDir = CompilationTestUtils.generatorOutput("mdsal365");
+        final File compiledOutputDir = CompilationTestUtils.compilerOutput("mdsal365");
+        generateTestSources("/compilation/mdsal365", sourcesOutputDir);
+        CompilationTestUtils.testCompilation(sourcesOutputDir, compiledOutputDir);
+        CompilationTestUtils.cleanUp(sourcesOutputDir, compiledOutputDir);
+    }
+
     @Test
     public void classNamesColisionTest() throws Exception {
         final File sourcesOutputDir = CompilationTestUtils.generatorOutput("class-name-collision");
diff --git a/binding/mdsal-binding-java-api-generator/src/test/resources/compilation/mdsal365/mdsal-365.yang b/binding/mdsal-binding-java-api-generator/src/test/resources/compilation/mdsal365/mdsal-365.yang
new file mode 100644 (file)
index 0000000..bbb51e6
--- /dev/null
@@ -0,0 +1,21 @@
+module mdsal-365 {
+    namespace "mdsal-365";
+    prefix "mdsal365";
+
+    grouping special-next-hop-grouping {
+        leaf special-next-hop {
+            type enumeration {
+                enum blackhole {
+                    description "Silently discard the packet.";
+                }
+            }
+        }
+    }
+
+    choice next-hop-options {
+        case special-next-hop {
+            uses special-next-hop-grouping;
+        }
+    }
+}
+