// FIXME: 7.0.0: this interface (and others) need to be refactored:
// - getValue() is pretty much unused and its semantics are undefined
// - isReadOnly() is not related to getValue() and is not used together
-// - nullifyEmpty() is applicable only to collection types and implies non-read-only and without value
-// - this is misused by Builder spec :(
public interface GeneratedProperty extends TypeMember {
String getValue();
* @return {@code true<} if the property is declared as read-only.
*/
boolean isReadOnly();
-
- /**
- * Returns indication whether the value should be squashed from empty collection to a null. This property is valid
- * only if {@link #getReturnType()} results in a well-known collection type: List or Map.
- *
- * @return True if empty collections should be turned to nulls
- */
- boolean nullifyEmpty();
}
*
* @return Associated mechanics
*/
- ValueMechanics getMechanics();
+ @NonNull ValueMechanics getMechanics();
/**
* The Parameter interface is designed to hold the information of method
*/
package org.opendaylight.mdsal.binding.model.api.type.builder;
-import com.google.common.annotations.Beta;
import org.opendaylight.mdsal.binding.model.api.GeneratedProperty;
import org.opendaylight.mdsal.binding.model.api.Type;
*/
GeneratedPropertyBuilder setReadOnly(boolean isReadOnly);
- @Beta
- GeneratedPropertyBuilder setNullifyEmpty(boolean flag);
-
/**
* Returns <code>new</code> <i>immutable</i> instance of Generated Property. <br>
* The <code>definingType</code> param cannot be <code>null</code>. The
implements GeneratedPropertyBuilder {
private String value;
private boolean readOnly;
- private boolean nullifyEmpty;
public GeneratedPropertyBuilderImpl(final String name) {
super(name);
this.readOnly = true;
- this.nullifyEmpty = false;
}
@Override
return this;
}
- @Override
- public GeneratedPropertyBuilderImpl setNullifyEmpty(final boolean flag) {
- this.nullifyEmpty = flag;
- return this;
- }
-
@Override
protected GeneratedPropertyBuilderImpl thisInstance() {
return this;
public GeneratedProperty toInstance(final Type definingType) {
final List<AnnotationType> annotations = toAnnotationTypes();
return new GeneratedPropertyImpl(definingType, getName(), annotations, getComment(), getAccessModifier(),
- getReturnType(), isFinal(), isStatic(), this.readOnly, this.nullifyEmpty, this.value);
+ getReturnType(), isFinal(), isStatic(), this.readOnly, this.value);
}
@Override
final class GeneratedPropertyImpl extends AbstractTypeMember implements GeneratedProperty {
private final String value;
private final boolean readOnly;
- private final boolean nullifyEmpty;
GeneratedPropertyImpl(final Type definingType, final String name, final List<AnnotationType> annotations,
final String comment, final AccessModifier accessModifier, final Type returnType, final boolean isFinal,
- final boolean isStatic, final boolean isReadOnly, final boolean nullifyEmpty, final String value) {
+ final boolean isStatic, final boolean isReadOnly, final String value) {
super(definingType, name, annotations, comment, accessModifier, returnType, isFinal, isStatic);
this.value = value;
this.readOnly = isReadOnly;
- this.nullifyEmpty = nullifyEmpty;
}
@Override
return this.readOnly;
}
- @Override
- public boolean nullifyEmpty() {
- return this.nullifyEmpty;
- }
-
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
return this;
}
-
@Override
public MethodSignatureBuilder setMechanics(final ValueMechanics newMechanics) {
this.mechanics = requireNonNull(newMechanics);
import com.google.common.annotations.VisibleForTesting;
import java.util.List;
import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.mdsal.binding.model.api.AccessModifier;
import org.opendaylight.mdsal.binding.model.api.AnnotationType;
import org.opendaylight.mdsal.binding.model.api.MethodSignature;
class MethodSignatureImpl extends AbstractTypeMember implements MethodSignature {
private final List<Parameter> params;
- private final ValueMechanics mechanics;
+ private final @NonNull ValueMechanics mechanics;
private final boolean isAbstract;
private final boolean isDefault;
@Test
public void testMethodsForGeneratedPropertyImpl() {
final GeneratedPropertyImpl propertyImpl = new GeneratedPropertyImpl(null, "Test", null, "test property",
- AccessModifier.PRIVATE, null, true, true, true, true, "test value");
+ AccessModifier.PRIVATE, null, true, true, true, "test value");
assertEquals("test value", propertyImpl.getValue());
assertTrue(propertyImpl.isReadOnly());
/**
* Set of class attributes (fields) which are derived from the getter methods names.
*/
- protected val Set<GeneratedProperty> properties
+ protected val Set<BuilderGeneratedProperty> properties
/**
* GeneratedType for key type, null if this type does not have a key.
protected val GeneratedType targetType;
new(AbstractJavaGeneratedType javaType, GeneratedType type, GeneratedType targetType,
- Set<GeneratedProperty> properties, Type augmentType, Type keyType) {
+ Set<BuilderGeneratedProperty> properties, Type augmentType, Type keyType) {
super(javaType, type)
this.targetType = targetType
this.properties = properties
this.keyType = keyType
}
- new(GeneratedType type, GeneratedType targetType, Set<GeneratedProperty> properties, Type augmentType,
+ new(GeneratedType type, GeneratedType targetType, Set<BuilderGeneratedProperty> properties, Type augmentType,
Type keyType) {
super(type)
this.targetType = targetType
'''
}
- override generateToString(Collection<GeneratedProperty> properties) '''
+ override generateToString(Collection<? extends GeneratedProperty> properties) '''
«IF properties !== null»
@«OVERRIDE.importedName»
public «STRING.importedName» toString() {
def protected abstract CharSequence generateCopyKeys(List<GeneratedProperty> keyProps)
- def protected abstract CharSequence generateCopyNonKeys(Collection<GeneratedProperty> props)
+ def protected abstract CharSequence generateCopyNonKeys(Collection<BuilderGeneratedProperty> props)
def protected abstract CharSequence generateCopyAugmentation(Type implType)
return false;
}
- private def void removeProperty(Collection<GeneratedProperty> props, String name) {
+ private def void removeProperty(Collection<BuilderGeneratedProperty> props, String name) {
val iter = props.iterator
while (iter.hasNext) {
if (name.equals(iter.next.name)) {
return sb.append(lineBuilder).append(NEW_LINE).toString
}
- def protected generateToString(Collection<GeneratedProperty> properties) '''
+ def protected generateToString(Collection<? extends GeneratedProperty> properties) '''
«IF !properties.empty»
@«OVERRIDE.importedName»
public «STRING.importedName» toString() {
«ENDFOR»
'''
- def protected hashCodeResult(Collection<GeneratedProperty> properties) '''
+ def protected hashCodeResult(Collection<? extends GeneratedProperty> properties) '''
final int prime = 31;
int result = 1;
«FOR property : properties»
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, 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 java.util.Objects.requireNonNull;
+
+import java.util.List;
+import org.opendaylight.mdsal.binding.model.api.AccessModifier;
+import org.opendaylight.mdsal.binding.model.api.AnnotationType;
+import org.opendaylight.mdsal.binding.model.api.GeneratedProperty;
+import org.opendaylight.mdsal.binding.model.api.MethodSignature;
+import org.opendaylight.mdsal.binding.model.api.MethodSignature.ValueMechanics;
+import org.opendaylight.mdsal.binding.model.api.Type;
+
+final class BuilderGeneratedProperty implements GeneratedProperty {
+ private final MethodSignature method;
+ private final String name;
+
+ BuilderGeneratedProperty(final String name, final MethodSignature method) {
+ this.name = requireNonNull(name);
+ this.method = requireNonNull(method);
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public Type getReturnType() {
+ return method.getReturnType();
+ }
+
+ ValueMechanics getMechanics() {
+ return method.getMechanics();
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof BuilderGeneratedProperty)) {
+ return false;
+ }
+ final BuilderGeneratedProperty other = (BuilderGeneratedProperty) obj;
+ return name.equals(other.name) && method.equals(other.method);
+ }
+
+ @Override
+ public String getComment() {
+ throw uoe();
+ }
+
+ @Override
+ public List<AnnotationType> getAnnotations() {
+ throw uoe();
+ }
+
+ @Override
+ public AccessModifier getAccessModifier() {
+ throw uoe();
+ }
+
+ @Override
+ public boolean isStatic() {
+ throw uoe();
+ }
+
+ @Override
+ public boolean isFinal() {
+ throw uoe();
+ }
+
+ @Override
+ public Type getDefiningType() {
+ throw uoe();
+ }
+
+ @Override
+ public String getValue() {
+ throw uoe();
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ throw uoe();
+ }
+
+ private static UnsupportedOperationException uoe() {
+ return new UnsupportedOperationException("Method not supported");
+ }
+}
import org.eclipse.xtext.xbase.lib.StringExtensions;
import org.opendaylight.mdsal.binding.model.api.CodeGenerator;
import org.opendaylight.mdsal.binding.model.api.DefaultType;
-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.GeneratedPropertyBuilder;
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.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;
* @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(final Collection<MethodSignature> methods) {
+ private static Set<BuilderGeneratedProperty> propertiesFromMethods(final Collection<MethodSignature> methods) {
if (methods == null || methods.isEmpty()) {
return Collections.emptySet();
}
- final Set<GeneratedProperty> result = new LinkedHashSet<>();
+ final Set<BuilderGeneratedProperty> result = new LinkedHashSet<>();
for (MethodSignature m : methods) {
- final GeneratedProperty createdField = propertyFromGetter(m);
+ final BuilderGeneratedProperty createdField = propertyFromGetter(m);
if (createdField != null) {
result.add(createdField);
}
* <li>if the return type of the <code>method</code> equals <code>null</code></li>
* </ul>
*/
- private static GeneratedProperty propertyFromGetter(final MethodSignature method) {
+ private static BuilderGeneratedProperty propertyFromGetter(final MethodSignature method) {
checkArgument(method != null);
checkArgument(method.getReturnType() != null);
checkArgument(method.getName() != null);
if (method.isDefault()) {
return null;
}
+
final String prefix = BindingMapping.getGetterPrefix(Types.BOOLEAN.equals(method.getReturnType()));
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"));
- final GeneratedPropertyBuilder builder = tmpGenTO.addProperty(fieldName).setReturnType(method.getReturnType());
- switch (method.getMechanics()) {
- case NULLIFY_EMPTY:
- builder.setNullifyEmpty(true);
- break;
- default:
- break;
- }
- return tmpGenTO.build().getProperties().get(0);
+ return new BuilderGeneratedProperty(StringExtensions.toFirstLower(method.getName().substring(prefix.length())),
+ method);
}
}
import org.opendaylight.mdsal.binding.model.util.Types
import org.opendaylight.mdsal.binding.spec.naming.BindingMapping
import org.opendaylight.yangtools.yang.binding.AbstractAugmentable
+import org.opendaylight.mdsal.binding.model.api.MethodSignature.ValueMechanics
class BuilderImplTemplate extends AbstractBuilderTemplate {
val Type builderType;
«ENDFOR»
'''
- override protected CharSequence generateCopyNonKeys(Collection<GeneratedProperty> props) '''
+ override protected CharSequence generateCopyNonKeys(Collection<BuilderGeneratedProperty> props) '''
«FOR field : props»
- «IF field.nullifyEmpty»
+ «IF field.mechanics === ValueMechanics.NULLIFY_EMPTY»
this.«field.fieldName» = «CODEHELPERS.importedName».emptyToNull(base.«field.getterMethodName»());
«ELSE»
this.«field.fieldName» = base.«field.getterMethodName»();
* Constructs new instance of this class.
* @throws IllegalArgumentException if <code>genType</code> equals <code>null</code>
*/
- new(GeneratedType genType, GeneratedType targetType, Set<GeneratedProperty> properties, Type augmentType,
+ new(GeneratedType genType, GeneratedType targetType, Set<BuilderGeneratedProperty> properties, Type augmentType,
Type keyType) {
super(genType, targetType, properties, augmentType, keyType)
}
override protected generateCopyKeys(List<GeneratedProperty> keyProps) '''
this.key = base.«BindingMapping.IDENTIFIABLE_KEY_NAME»();
- «generateCopyNonKeys(keyProps)»
+ «FOR field : keyProps»
+ this.«field.fieldName» = base.«field.getterMethodName»();
+ «ENDFOR»
'''
-
- override protected CharSequence generateCopyNonKeys(Collection<GeneratedProperty> props) '''
+ override protected CharSequence generateCopyNonKeys(Collection<BuilderGeneratedProperty> props) '''
«FOR field : props»
this.«field.fieldName» = base.«field.getterMethodName»();
«ENDFOR»
--- /dev/null
+/*
+ * Copyright (c) 2020 PANTHEON.tech, 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.test.model;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.List;
+import java.util.Map;
+import org.junit.Test;
+import org.opendaylight.yang.gen.v1.mdsal442.keydef.norev.Def;
+import org.opendaylight.yang.gen.v1.mdsal442.keydef.norev.DefBuilder;
+import org.opendaylight.yang.gen.v1.mdsal442.keydef.norev.grp.Lst;
+import org.opendaylight.yang.gen.v1.mdsal442.keydef.norev.grp.LstBuilder;
+import org.opendaylight.yang.gen.v1.mdsal442.keydef.norev.grp.LstKey;
+import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.Container;
+import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.ContainerBuilder;
+import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.Keyed;
+import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.KeyedBuilder;
+import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.KeyedKey;
+import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.Unkeyed;
+import org.opendaylight.yang.gen.v1.urn.test.opendaylight.mdsal298.rev180129.container.UnkeyedBuilder;
+import org.opendaylight.yang.gen.v1.urn.test.pattern.rev170101.Cont;
+import org.opendaylight.yang.gen.v1.urn.test.pattern.rev170101.ContBuilder;
+
+public class TestListSquashing {
+ @Test
+ public void testEmptyLeafList() {
+ final Cont obj = new ContBuilder().setTest3(List.of()).build();
+ // Eventhough return type is List, it should be retained
+ assertEquals(List.of(), obj.getTest3());
+ }
+
+ @Test
+ public void testEmptyUserOrderedList() {
+ final Container cont = new ContainerBuilder()
+ .setKeyed(List.of())
+ .setUnkeyed(List.of())
+ .build();
+ // Empty Lists should become null
+ assertNull(cont.getKeyed());
+ assertNull(cont.getUnkeyed());
+ }
+
+ @Test
+ public void testUserOrderedList() {
+ final Keyed keyed = new KeyedBuilder().withKey(new KeyedKey("a")).build();
+ final Unkeyed unkeyed = new UnkeyedBuilder().build();
+ final Container cont = new ContainerBuilder()
+ .setKeyed(List.of(keyed))
+ .setUnkeyed(List.of(unkeyed))
+ .build();
+ // Non-empty Lists should be retained
+ assertEquals(List.of(keyed), cont.getKeyed());
+ assertEquals(List.of(unkeyed), cont.getUnkeyed());
+ }
+
+ @Test
+ public void testEmptySystemOrderedList() {
+ final Def cont = new DefBuilder().setLst(Map.of()).build();
+ // Empty Map should become null
+ assertNull(cont.getLst());
+ }
+
+ @Test
+ public void testSystemOrderedList() {
+ final Lst lst = new LstBuilder().withKey(new LstKey("a")).build();
+ final Def cont = new DefBuilder().setLst(Map.of(lst.key(), lst)).build();
+ // Non-empty Map should be retained
+ assertEquals(Map.of(lst.key(), lst), cont.getLst());
+ }
+}