From 44d8f9a8adebc82b76ae5ea9c242ab6f7bba72ee Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Thu, 26 Jul 2018 11:34:33 +0200 Subject: [PATCH] Fixup BuilderTemplate decomposition 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 --- .../generator/AbstractBuilderTemplate.xtend | 205 ++++++++ .../generator/AbstractJavaGeneratedType.java | 33 +- .../java/api/generator/BuilderGenerator.java | 166 ++++++- .../api/generator/BuilderImplTemplate.xtend | 142 ++++++ .../java/api/generator/BuilderTemplate.xtend | 470 ++---------------- .../generator/TopLevelJavaGeneratedType.java | 4 - .../java/api/generator/UnionTemplate.xtend | 5 +- .../api/generator/BuilderGeneratorTest.java | 14 +- .../api/generator/test/CompilationTest.java | 9 + .../compilation/mdsal365/mdsal-365.yang | 21 + 10 files changed, 591 insertions(+), 478 deletions(-) create mode 100644 binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/AbstractBuilderTemplate.xtend create mode 100644 binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderImplTemplate.xtend create mode 100644 binding/mdsal-binding-java-api-generator/src/test/resources/compilation/mdsal365/mdsal-365.yang 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 index 0000000000..6622ee2d4b --- /dev/null +++ b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/AbstractBuilderTemplate.xtend @@ -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 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 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 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», «augmentType.importedName»> «AUGMENTATION_FIELD» = «Collections.importedName».emptyMap(); + «ENDIF» + ''' + + override generateToString(Collection 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 «AUGMENTABLE_AUGMENTATION_NAME»(«Class.importedName» 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 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 diff --git a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/AbstractJavaGeneratedType.java b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/AbstractJavaGeneratedType.java index 607451b650..9a25876d64 100644 --- a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/AbstractJavaGeneratedType.java +++ b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/AbstractJavaGeneratedType.java @@ -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 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() { diff --git a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderGenerator.java b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderGenerator.java index 57dfc75aab..77daf830b8 100644 --- a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderGenerator.java +++ b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderGenerator.java @@ -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 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 type. @@ -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 methods = new LinkedHashSet<>(); + final Type augmentType = createMethods(genType, methods); + final Set 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 genType + * and all the methods of the implemented interfaces. + * + * @returns set of method signature instances + */ + private static ParameterizedType createMethods(GeneratedType type, Set methods) { + methods.addAll(type.getMethodDefinitions()); + return collectImplementedMethods(type, methods, type.getImplements()); + } + + /** + * Adds to the methods set all the methods of the implementedIfcs + * and recursively their implemented interfaces. + * + * @param methods set of method signatures + * @param implementedIfcs list of implemented interfaces + */ + private static ParameterizedType collectImplementedMethods(GeneratedType type, Set methods, + List 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 methods. + * + * @param set of method signature instances which should be transformed to list of properties + * @return set of generated property instances which represents the getter methods + */ + private static Set propertiesFromMethods(Collection methods) { + if (methods == null || methods.isEmpty()) { + return Collections.emptySet(); + } + final Set 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 method 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 method + * @throws IllegalArgumentException
    + *
  • if the method equals null
  • + *
  • if the name of the method equals null
  • + *
  • if the name of the method is empty
  • + *
  • if the return type of the method equals null
  • + *
+ */ + 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 index 0000000000..3eb6e79894 --- /dev/null +++ b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderImplTemplate.xtend @@ -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 hashCode(). + * + * @return string with the hashCode() 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 equals(). + * + * @return string with the equals() 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», «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 diff --git a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderTemplate.xtend b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderTemplate.xtend index c808bc698e..d2be1ba763 100644 --- a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderTemplate.xtend +++ b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderTemplate.xtend @@ -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 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(); + public static val BUILDER = "Builder"; /** * Constructs new instance of this class. * @throws IllegalArgumentException if genType equals null */ - 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 genType - * and all the methods of the implemented interfaces. - * - * @returns set of method signature instances - */ - def private Set createMethods() { - val Set methods = new LinkedHashSet(); - methods.addAll(type.methodDefinitions) - collectImplementedMethods(methods, type.implements) - val Set sortedMethods = ImmutableSortedSet.orderedBy(METHOD_COMPARATOR).addAll(methods).build() - - return sortedMethods - } - - /** - * Adds to the methods set all the methods of the implementedIfcs - * and recursively their implemented interfaces. - * - * @param methods set of method signatures - * @param implementedIfcs list of implemented interfaces - */ - def private void collectImplementedMethods(Set methods, List 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 elements. - * - * @param list of elements - */ - def private first(List elements) { - elements.get(0) - } - - /** - * Creates set of generated property instances from getter methods. - * - * @param set of method signature instances which should be transformed to list of properties - * @return set of generated property instances which represents the getter methods - */ - def private propertiesFromMethods(Collection methods) { - if (methods === null || methods.isEmpty()) { - return Collections.emptySet - } - val Set 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 method 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 method - * @throws IllegalArgumentException
    - *
  • if the method equals null
  • - *
  • if the name of the method equals null
  • - *
  • if the name of the method is empty
  • - *
  • if the return type of the method equals null
  • - *
- */ - 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 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 done = ifc.getBaseIfcs» - «generateMethodFieldsFromComment(ifc)» + def private generateMethodFieldsFrom() ''' + «IF (!(targetType instanceof GeneratedTransferObject))» + «IF targetType.hasImplementsFromUses» + «val List 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», «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» augmentationType, «augmentType.importedName» augmentationValue) { + public «type.name» add«AUGMENTATION_FIELD.toFirstUpper»(«Class.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» augmentationType) { + public «type.name» remove«AUGMENTATION_FIELD.toFirstUpper»(«Class.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 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 «AUGMENTABLE_AUGMENTATION_NAME»(«Class.importedName» augmentationType) { - return (E) «AUGMENTATION_FIELD».get(«CodeHelpers.importedName».nonNullValue(augmentationType, "augmentationType")); - } - «ENDIF» - ''' - - /** - * Template method which generates the method hashCode(). - * - * @return string with the hashCode() 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 equals(). - * - * @return string with the equals() 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», «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 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. diff --git a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/TopLevelJavaGeneratedType.java b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/TopLevelJavaGeneratedType.java index 93cb83e1a7..741ede45d7 100644 --- a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/TopLevelJavaGeneratedType.java +++ b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/TopLevelJavaGeneratedType.java @@ -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 diff --git a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/UnionTemplate.xtend b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/UnionTemplate.xtend index 7e3c0682f6..84e9d78f66 100644 --- a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/UnionTemplate.xtend +++ b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/UnionTemplate.xtend @@ -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 diff --git a/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderGeneratorTest.java b/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderGeneratorTest.java index c19625563d..83e3a37946 100644 --- a/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderGeneratorTest.java +++ b/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/BuilderGeneratorTest.java @@ -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) 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) { diff --git a/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/test/CompilationTest.java b/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/test/CompilationTest.java index 6dd0fcef59..540397e12f 100644 --- a/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/test/CompilationTest.java +++ b/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/test/CompilationTest.java @@ -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 index 0000000000..bbb51e62bb --- /dev/null +++ b/binding/mdsal-binding-java-api-generator/src/test/resources/compilation/mdsal365/mdsal-365.yang @@ -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; + } + } +} + -- 2.36.6