Map identities to proper objects 17/100117/20
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 14 Mar 2022 09:31:49 +0000 (10:31 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Thu, 14 Apr 2022 20:09:25 +0000 (22:09 +0200)
Change the binding type mapping from Class<? extends BaseIdetity>
to proper objects. While this imposes some runtime footprint in terms of
loaded classes (there is an anonymous implementation of each identity)
and objects (that implementation is an eager singleton), it removes a
metric ton of irregularities. This this change, all mapped objects
objects can be checked via 'instanceof' and most of them have
implementedInterface() for exact contract matches.

JIRA: MDSAL-733
Change-Id: Iab7dfb7656db87850613f567dfe40ef31494f6c4
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
31 files changed:
binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/LeafDefaultValueTest.java
binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingIdentityCodec.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CompositeValueCodec.java
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/IdentityCodec.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/LeafReferenceTest.java
binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/Generator.java
binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/IdentityGenerator.java
binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/TypeReference.java
binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/DefaultBindingGeneratorTest.java
binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/IdentityrefTypeTest.java
binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Mdsal269Test.java
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BaseTemplate.xtend
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/ByTypeMemberComparator.java
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/ClassTemplate.xtend
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/InterfaceTemplate.xtend
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/CompilationTest.java
binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/Mdsal732Test.java
binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/UnionWithIdentityrefTest.java
binding/mdsal-binding-model-ri/src/main/java/org/opendaylight/mdsal/binding/model/ri/BindingTypes.java
binding/mdsal-binding-runtime-api/src/main/java/org/opendaylight/mdsal/binding/runtime/api/AbstractBindingRuntimeContext.java
binding/mdsal-binding-runtime-api/src/main/java/org/opendaylight/mdsal/binding/runtime/api/BindingRuntimeContext.java
binding/mdsal-binding-runtime-spi/src/main/java/org/opendaylight/mdsal/binding/runtime/spi/ForwardingBindingRuntimeContext.java
binding/mdsal-binding-spec-util/src/main/java/org/opendaylight/mdsal/binding/spec/naming/BindingMapping.java
binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/BaseIdentity.java
binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/BindingContract.java [new file with mode: 0644]
binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/CodeHelpers.java
binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/DataContainer.java
binding/yang-binding/src/test/java/org/opendaylight/yangtools/yang/binding/CodeHelpersTest.java

index 72f7486bec802d070968a94d3db74dc9ef6dfd83..64631f3d6f1017ce0542c398e1711c99272a4592 100644 (file)
@@ -5,12 +5,12 @@
  * 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.dom.adapter;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
 import java.util.Arrays;
@@ -21,6 +21,7 @@ import org.opendaylight.mdsal.binding.api.ReadTransaction;
 import org.opendaylight.mdsal.binding.api.WriteTransaction;
 import org.opendaylight.mdsal.binding.dom.adapter.test.AbstractDataBrokerTest;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns._default.value.test._2.rev160111.MyDerivedImportedIdentity;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns._default.value.test.norev.BigIntContainer;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns._default.value.test.norev.BigIntContainerBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns._default.value.test.norev.BigUintContainer;
@@ -37,6 +38,8 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns._default.valu
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns._default.value.test.norev.EnumContainerBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns._default.value.test.norev.IdentityrefContainer;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns._default.value.test.norev.IdentityrefContainerBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns._default.value.test.norev.MyDerivedIdentity;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns._default.value.test.norev.MyDerivedIdentity2;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns._default.value.test.norev.NormalIntContainer;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns._default.value.test.norev.NormalIntContainerBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns._default.value.test.norev.NormalUintContainer;
@@ -390,11 +393,11 @@ public class LeafDefaultValueTest extends AbstractDataBrokerTest {
 
         IdentityrefContainer idrefCont = identityrefContainerNode.get();
         assertNull(idrefCont.getIdentityrefLeaf());
-        assertEquals("MyDerivedIdentity", idrefCont.getIdentityrefLeaf2().getSimpleName());
-        assertEquals("MyDerivedIdentity", idrefCont.getIdentityrefLeaf3().getSimpleName());
-        assertEquals("MyDerivedIdentity2", idrefCont.getIdentityrefLeaf4().getSimpleName());
-        assertEquals("MyDerivedImportedIdentity", idrefCont.getIdentityrefLeaf5().getSimpleName());
-        assertEquals("MyDerivedIdentity", idrefCont.getIdentityrefLeaf6().getSimpleName());
+        assertSame(MyDerivedIdentity.VALUE, idrefCont.getIdentityrefLeaf2());
+        assertSame(MyDerivedIdentity.VALUE, idrefCont.getIdentityrefLeaf3());
+        assertSame(MyDerivedIdentity2.VALUE, idrefCont.getIdentityrefLeaf4());
+        assertSame(MyDerivedImportedIdentity.VALUE, idrefCont.getIdentityrefLeaf5());
+        assertSame(MyDerivedIdentity.VALUE, idrefCont.getIdentityrefLeaf6());
         assertNull(idrefCont.getIdentityrefLeaf7());
     }
 }
index c0f37fed8d54e1de501262ca26e06f8f3b855524..23186036a76eeb66b8ddc1df3aba4320437cc2bd 100644 (file)
@@ -21,19 +21,21 @@ public interface BindingIdentityCodec extends Immutable {
     /**
      * Convert a QNname to its corresponding Binding class.
      *
+     * @param <T> Expected identity type
      * @param qname Identity QName
-     * @return A binding Class corresponding to the QName
+     * @return A binding value corresponding to the QName
      * @throws IllegalArgumentException if the qname does not map to an identity
-     * @throws NullPointerException if qname is null
+     * @throws NullPointerException if {@code qname} is null
      */
-    @NonNull Class<? extends BaseIdentity> toBinding(@NonNull QName qname);
+    <T extends BaseIdentity> @NonNull T toBinding(@NonNull QName qname);
 
     /**
-     * Concert a Binding class to its QName equivalent.
+     * Convert a Binding value to its QName equivalent.
      *
-     * @param bindingClass Binding class to convert
-     * @return QName corresponding to the binding class
+     * @param bindingValue Binding value to convert
+     * @return QName corresponding to the binding value
+     * @throws IllegalArgumentException if the supplied value does not map to a known identity
      * @throws NullPointerException if bindingClass is null
      */
-    @NonNull QName fromBinding(Class<? extends BaseIdentity> bindingClass);
+    @NonNull QName fromBinding(@NonNull BaseIdentity bindingValue);
 }
index 35596c90ef9fce81aff6df477d3c4b053c0d6e66..b85065ab39727587522423f68273e87966278b53 100644 (file)
@@ -58,6 +58,7 @@ import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.util.ClassLoaderUtils;
 import org.opendaylight.yangtools.yang.binding.Action;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
 import org.opendaylight.yangtools.yang.binding.BaseNotification;
 import org.opendaylight.yangtools.yang.binding.DataContainer;
 import org.opendaylight.yangtools.yang.binding.DataObject;
@@ -421,7 +422,7 @@ public final class BindingCodecContext extends AbstractBindingNormalizedNodeSeri
 
     // FIXME: this is probably not right w.r.t. nulls
     IllegalArgumentCodec<Object, Object> getCodec(final Class<?> valueType, final TypeDefinition<?> instantiatedType) {
-        if (Class.class.equals(valueType)) {
+        if (BaseIdentity.class.isAssignableFrom(valueType)) {
             @SuppressWarnings({ "unchecked", "rawtypes" })
             final IllegalArgumentCodec<Object, Object> casted = (IllegalArgumentCodec) identityCodec;
             return casted;
index 50ff799ed774694627a49c0b3f609024851caf13..a0184f936628a3670fdc3c2b7cfe643b0fb252c8 100644 (file)
@@ -29,14 +29,8 @@ abstract class CompositeValueCodec extends AbstractIllegalArgumentCodec<Object,
 
         @Override
         Object bindingToDom(final Object bindingValue) {
-            checkArgument(bindingValue instanceof Class, "Unexpected Binding value %s", bindingValue);
-            final Class<? extends BaseIdentity> identity;
-            try {
-                identity = ((Class<?>) bindingValue).asSubclass(BaseIdentity.class);
-            } catch (ClassCastException e) {
-                throw new IllegalArgumentException("Unexpected Binding value " + bindingValue, e);
-            }
-            return valueCodec.fromBinding(identity);
+            checkArgument(bindingValue instanceof BaseIdentity, "Unexpected Binding value %s", bindingValue);
+            return valueCodec.fromBinding((BaseIdentity) bindingValue);
         }
 
         @Override
index 06fbb0b9397bb99bf7fcc10414096302fa7f8803..1297b0ddc9da90d7ab9a829b1ab6b61c22ed1893 100644 (file)
@@ -7,17 +7,58 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.impl;
 
-import static com.google.common.base.Preconditions.checkArgument;
 import static java.util.Objects.requireNonNull;
 
+import com.google.common.base.Throwables;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.concurrent.ExecutionException;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingIdentityCodec;
 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
+import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
 import org.opendaylight.yangtools.concepts.AbstractIllegalArgumentCodec;
 import org.opendaylight.yangtools.yang.binding.BaseIdentity;
 import org.opendaylight.yangtools.yang.common.QName;
 
-final class IdentityCodec extends AbstractIllegalArgumentCodec<QName, Class<?>> implements BindingIdentityCodec {
+final class IdentityCodec extends AbstractIllegalArgumentCodec<QName, BaseIdentity> implements BindingIdentityCodec {
+    private final LoadingCache<@NonNull QName, @NonNull BaseIdentity> values = CacheBuilder.newBuilder()
+        .build(new CacheLoader<>() {
+            @Override
+            public BaseIdentity load(final QName key) {
+                final var clazz = context.getIdentityClass(key);
+                final Field field;
+                try {
+                    field = clazz.getField(BindingMapping.VALUE_STATIC_FIELD_NAME);
+                } catch (NoSuchFieldException e) {
+                    throw new LinkageError(clazz + " does not define required field "
+                        + BindingMapping.VALUE_STATIC_FIELD_NAME, e);
+                }
+                if (!Modifier.isStatic(field.getModifiers())) {
+                    throw new LinkageError(field + " is not static");
+                }
+
+                final Object value;
+                try {
+                    value = clazz.cast(field.get(null));
+                } catch (IllegalAccessException e) {
+                    throw new LinkageError(field + " is not accesssible", e);
+                }
+                if (value == null) {
+                    throw new LinkageError(field + " is null");
+                }
+                try {
+                    return clazz.cast(value);
+                } catch (ClassCastException e) {
+                    throw new LinkageError(field + " value " + value + " has illegal type", e);
+                }
+            }
+        });
+
     private final BindingRuntimeContext context;
 
     IdentityCodec(final BindingRuntimeContext context) {
@@ -25,25 +66,28 @@ final class IdentityCodec extends AbstractIllegalArgumentCodec<QName, Class<?>>
     }
 
     @Override
-    protected Class<?> deserializeImpl(final QName input) {
-        return context.getIdentityClass(input);
+    protected BaseIdentity deserializeImpl(final QName input) {
+        return toBinding(input);
     }
 
     @Override
-    protected QName serializeImpl(final Class<?> input) {
-        checkArgument(BaseIdentity.class.isAssignableFrom(input), "%s is not an identity", input);
-        return BindingReflections.findQName(input);
+    protected QName serializeImpl(final BaseIdentity input) {
+        return fromBinding(input);
     }
 
     @Override
-    public Class<? extends BaseIdentity> toBinding(final QName qname) {
-        final Class<?> identity = context.getIdentityClass(requireNonNull(qname));
-        checkArgument(BaseIdentity.class.isAssignableFrom(identity), "%s resolves to non-identity %s", qname, identity);
-        return identity.asSubclass(BaseIdentity.class);
+    @SuppressWarnings("unchecked")
+    public <T extends BaseIdentity> T toBinding(final QName qname) {
+        try {
+            return (T) values.get(requireNonNull(qname));
+        } catch (ExecutionException e) {
+            Throwables.throwIfUnchecked(e.getCause());
+            throw new IllegalStateException("Unexpected error translating " + qname, e);
+        }
     }
 
     @Override
-    public QName fromBinding(final Class<? extends BaseIdentity> bindingClass) {
-        return BindingReflections.getQName(bindingClass);
+    public QName fromBinding(final BaseIdentity bindingValue) {
+        return BindingReflections.getQName(bindingValue.implementedInterface());
     }
 }
index 1917d18f2512cd57f4bca5e13d39a2e73efaa2d4..25015fae3d5b4fc8e5ac5f4998e2f36236a1bfca 100644 (file)
@@ -32,8 +32,8 @@ public class LeafReferenceTest extends AbstractBindingCodecTest {
     @Test
     public void testCaseWithLeafReferencesType() {
         final TreeComplexLeaves binding = new TreeComplexLeavesBuilder()
-            .setIdentity(ThirdParty.class)
-            .setIdentityRef(ThirdParty.class)
+            .setIdentity(ThirdParty.VALUE)
+            .setIdentityRef(ThirdParty.VALUE)
             .setSimpleType(10)
             .setSimpleTypeRef(10)
             .setSchemaUnawareUnion(new Int32StringUnion("foo"))
index 6fd9a25e5ab8f4a558db980d9c3a6f30164ece90..23690d1603009ba56e43d207f7048bfbe153273d 100644 (file)
@@ -386,7 +386,7 @@ public abstract class Generator implements Iterable<Generator> {
     private static MethodSignatureBuilder defineImplementedInterfaceMethod(final GeneratedTypeBuilder typeBuilder,
             final Type classType) {
         final MethodSignatureBuilder ret = typeBuilder
-                .addMethod(BindingMapping.DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME)
+                .addMethod(BindingMapping.BINDING_CONTRACT_IMPLEMENTED_INTERFACE_NAME)
                 .setAccessModifier(AccessModifier.PUBLIC)
                 .setReturnType(classType(classType));
         ret.addAnnotation(OVERRIDE_ANNOTATION);
index 95e7db5dabf26ebb8743a6324ea51dc3e4cb59f2..efffb79ecc0b9e4e018eaee8cda25994fd8e76e4 100644 (file)
@@ -18,6 +18,8 @@ import org.opendaylight.mdsal.binding.model.api.Type;
 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilder;
 import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilderBase;
 import org.opendaylight.mdsal.binding.runtime.api.IdentityRuntimeType;
+import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
 import org.opendaylight.yangtools.yang.model.api.stmt.BaseEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.IdentityEffectiveStatement;
 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
@@ -62,9 +64,14 @@ public final class IdentityGenerator
             builder.addImplementsType(BASE_IDENTITY);
         }
 
+        narrowImplementedInterface(builder);
+
         final ModuleGenerator module = currentModule();
         module.addQNameConstant(builder, localName());
 
+        // Constant implementation
+        builder.addConstant(Type.of(builder), BindingMapping.VALUE_STATIC_FIELD_NAME, BaseIdentity.class);
+
         builderFactory.addCodegenInformation(module, statement(), builder);
         builder.setModuleName(module.statement().argument().getLocalName());
 //        builder.setSchemaPath(identity.getPath());
index 629306c8db50ea49e1ee57c5fa2689f6b77e2852..cde69a941abc1487d43e28f969d14b3efc21fd37 100644 (file)
@@ -10,11 +10,8 @@ package org.opendaylight.mdsal.binding.generator.impl.reactor;
 import static java.util.Objects.requireNonNull;
 
 import java.util.List;
-import java.util.stream.Collectors;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.mdsal.binding.model.api.GeneratedType;
-import org.opendaylight.mdsal.binding.model.api.ParameterizedType;
 import org.opendaylight.mdsal.binding.model.api.Type;
 import org.opendaylight.mdsal.binding.model.ri.Types;
 
@@ -22,7 +19,7 @@ abstract class TypeReference {
     private static final class Identityref extends TypeReference {
         private final List<IdentityGenerator> referencedGenerators;
 
-        private ParameterizedType returnType;
+        private Type returnType;
 
         Identityref(final List<IdentityGenerator> referencedGenerators) {
             this.referencedGenerators = requireNonNull(referencedGenerators);
@@ -31,15 +28,15 @@ abstract class TypeReference {
         @Override
         Type methodReturnType(final TypeBuilderFactory builderFactory) {
             if (returnType == null) {
-                final List<GeneratedType> referencedTypes = referencedGenerators.stream()
-                    .map(gen -> gen.getGeneratedType(builderFactory))
-                    .collect(Collectors.toUnmodifiableList());
                 // FIXME: This deals only with RFC6020 semantics. In order to deal with full RFC7950 semantics, we need
                 //        to analyze all the types and come up with the lowest-common denominator and use that as the
                 //        return type. We also need to encode restrictions, so that builder generator ends up checking
                 //        identities being passed -- because the identities may be completely unrelated, in which case
                 //        we cannot generate type-safe code.
-                returnType = Types.classType(Types.wildcardTypeFor(referencedTypes.get(0).getIdentifier()));
+                returnType = referencedGenerators.stream()
+                    .map(gen -> gen.getGeneratedType(builderFactory))
+                    .findFirst()
+                    .orElseThrow();
             }
             return returnType;
         }
index 99417d7016eba689018e8ccbd02fdbcbae801870..d05186818d5515560b1feb6bb5cfae5fb4f27c89 100644 (file)
@@ -95,7 +95,7 @@ public class DefaultBindingGeneratorTest {
 
     @Test
     public void generatedTypeForExtendedDefinitionTypeWithIdentityrefBaseTypeTest() {
-        assertEquals(Types.parameterizedTypeFor(Types.CLASS, Type.of(JavaTypeName.create(TEST_TYPE_PROVIDER, "Aes"))),
+        assertEquals(Type.of(JavaTypeName.create(TEST_TYPE_PROVIDER, "Aes")),
             assertGeneratedMethod(CONSTRUCTION_TYPE_TEST, "getAesIdentityrefType").getReturnType());
     }
 
@@ -188,8 +188,7 @@ public class DefaultBindingGeneratorTest {
 
     @Test
     public void javaTypeForSchemaDefinitionIdentityrefExtTypeTest() {
-        assertEquals(Types.parameterizedTypeFor(Types.CLASS,
-            Types.wildcardTypeFor(JavaTypeName.create(TEST_TYPE_PROVIDER, "CryptoAlg"))),
+        assertEquals(Type.of(JavaTypeName.create(TEST_TYPE_PROVIDER, "CryptoAlg")),
             assertGeneratedMethod(TEST_TYPE_PROVIDER_FOO, "getCrypto").getReturnType());
     }
 
index a4ec36e57fad1e572bb36378d09f3742644ddf38..85437007c85f436bc632eaa5674ff66145b332cf 100644 (file)
@@ -7,8 +7,6 @@
  */
 package org.opendaylight.mdsal.binding.generator.impl;
 
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 
 import java.io.File;
@@ -20,9 +18,6 @@ import org.junit.Before;
 import org.junit.Test;
 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
 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.ri.Types;
 import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
 
 public class IdentityrefTypeTest {
@@ -68,15 +63,7 @@ public class IdentityrefTypeTest {
         assertEquals("getLf", methodSignature.getName());
         assertEquals("requireLf", methodSignatures.get(1).getName());
 
-        Type returnType = methodSignature.getReturnType();
-        assertThat(returnType, instanceOf(ParameterizedType.class));
-        ParameterizedType parameterized = (ParameterizedType) returnType;
-        assertEquals(Types.CLASS, parameterized.getRawType());
-
-        Type[] actualTypes = parameterized.getActualTypeArguments();
-        assertEquals("Incorrect number of type parameters", 1, actualTypes.length);
-        assertEquals("Return type has incorrect actual parameter",
-            "org.opendaylight.yang.gen.v1.urn.identityref.module.rev131109.SomeIdentity",
-            actualTypes[0].getFullyQualifiedName());
+        assertEquals("org.opendaylight.yang.gen.v1.urn.identityref.module.rev131109.SomeIdentity",
+            methodSignature.getReturnType().getFullyQualifiedName());
     }
 }
index 3d0aba923b0877d37039d9362a58f64cf52ff75f..151177406c7b83c1beb67b4a35cf57da279dd09c 100644 (file)
@@ -40,6 +40,7 @@ public class Mdsal269Test {
             general.getReturnType().getFullyQualifiedName());
 
         assertEquals("mplsLabelSpecialPurpose", special.getName());
-        assertEquals("java.lang.Class", special.getReturnType().getFullyQualifiedName());
+        assertEquals("org.opendaylight.yang.gen.v1.mdsal269.rev180130.MplsLabelSpecialPurposeValue",
+            special.getReturnType().getFullyQualifiedName());
     }
 }
index ea60bba40f39ccba8a267d537e06bb6a7c31443a..56f135cd41b90839ca00024ed2510822b16b4043 100644 (file)
@@ -8,6 +8,8 @@
 package org.opendaylight.mdsal.binding.java.api.generator
 
 import static extension org.opendaylight.mdsal.binding.generator.BindingGeneratorUtil.encodeAngleBrackets
+import static org.opendaylight.mdsal.binding.model.ri.Types.STRING;
+import static org.opendaylight.mdsal.binding.model.ri.Types.objectType;
 
 import com.google.common.base.CharMatcher
 import com.google.common.base.Splitter
@@ -31,6 +33,7 @@ import org.opendaylight.mdsal.binding.model.api.Type
 import org.opendaylight.mdsal.binding.model.api.TypeMemberComment
 import org.opendaylight.mdsal.binding.model.ri.TypeConstants
 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping
+import org.opendaylight.yangtools.yang.binding.BaseIdentity
 
 @SuppressModernizer
 abstract class BaseTemplate extends JavaFileTemplate {
@@ -293,6 +296,34 @@ abstract class BaseTemplate extends JavaFileTemplate {
         Â«IF BindingMapping.QNAME_STATIC_FIELD_NAME.equals(c.name)»
             Â«val entry = c.value as Entry<JavaTypeName, String>»
             public static final Â«c.type.importedNonNull» Â«c.name» = Â«entry.key.importedName».«BindingMapping.MODULE_INFO_QNAMEOF_METHOD_NAME»("«entry.value»");
+        Â«ELSEIF BindingMapping.VALUE_STATIC_FIELD_NAME.equals(c.name) && BaseIdentity.equals(c.value)»
+            Â«val typeName = c.type.importedName»
+            Â«val override = OVERRIDE.importedName»
+            /**
+             * Singleton value representing the {@link Â«typeName»} identity.
+             */
+            public static final Â«c.type.importedNonNull» Â«c.name» = new Â«typeName»() {
+                @«override»
+                public Â«CLASS.importedName»<«typeName»> Â«BindingMapping.BINDING_CONTRACT_IMPLEMENTED_INTERFACE_NAME»() {
+                    return Â«typeName».class;
+                }
+
+                @«override»
+                public int hashCode() {
+                    return Â«typeName».class.hashCode();
+                }
+
+                @«override»
+                public boolean equals(final Â«objectType.importedName» obj) {
+                    return obj == this || obj instanceof Â«typeName»
+                        && Â«typeName».class.equals(((«typeName») obj).«BindingMapping.BINDING_CONTRACT_IMPLEMENTED_INTERFACE_NAME»());
+                }
+
+                @«override»
+                public Â«STRING.importedName» toString() {
+                    return Â«MOREOBJECTS.importedName».toStringHelper("«c.type.name»").add("qname", QNAME).toString();
+                }
+            };
         Â«ELSE»
             public static final Â«c.type.importedName» Â«c.name» = Â«c.value»;
         Â«ENDIF»
index 7dfd93a55154be5201a35d9904e9ed7052af6b74..dbc7846d57b4a36d7bcfbfa8bc098e5a58be80ac 100644 (file)
@@ -11,7 +11,7 @@ import static extension org.apache.commons.text.StringEscapeUtils.escapeJava
 import static org.opendaylight.mdsal.binding.model.ri.BindingTypes.DATA_OBJECT
 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTABLE_AUGMENTATION_NAME
 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTATION_FIELD
-import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME
+import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.BINDING_CONTRACT_IMPLEMENTED_INTERFACE_NAME
 
 import com.google.common.collect.ImmutableList
 import com.google.common.collect.ImmutableSet
@@ -254,22 +254,11 @@ class BuilderTemplate extends AbstractBuilderTemplate {
         if (ownGetterType instanceof ParameterizedType) {
             val itemType = ownGetterType.actualTypeArguments.get(0)
             if (Types.isListType(ownGetterType)) {
-                val importedClass = importedClass(itemType)
-                if (importedClass !== null) {
-                    return printPropertySetter(retrieveProperty, propertyName, "checkListFieldCastIdentity", importedClass)
-                }
                 return printPropertySetter(retrieveProperty, propertyName, "checkListFieldCast", itemType.importedName)
             }
             if (Types.isSetType(ownGetterType)) {
-                val importedClass = importedClass(itemType)
-                if (importedClass !== null) {
-                    return printPropertySetter(retrieveProperty, propertyName, "checkSetFieldCastIdentity", importedClass)
-                }
                 return printPropertySetter(retrieveProperty, propertyName, "checkSetFieldCast", itemType.importedName)
             }
-            if (Types.CLASS.equals(ownGetterType)) {
-                return printPropertySetter(retrieveProperty, propertyName, "checkFieldCastIdentity", itemType.identifier.importedName)
-            }
         }
         return printPropertySetter(retrieveProperty, propertyName, "checkFieldCast", ownGetterType.importedName)
     }
@@ -277,15 +266,6 @@ class BuilderTemplate extends AbstractBuilderTemplate {
     def private printPropertySetter(String retrieveProperty, String propertyName, String checkerName, String className) '''
             this._«propertyName» = Â«CODEHELPERS.importedName».«checkerName»(«className».class, "«propertyName»", Â«retrieveProperty»)'''
 
-    private def importedClass(Type type) {
-        if (type instanceof ParameterizedType) {
-            if (Types.CLASS.equals(type.rawType)) {
-                return type.actualTypeArguments.get(0).identifier.importedName
-            }
-        }
-        return null
-    }
-
     private def List<Type> getBaseIfcs(GeneratedType type) {
         val List<Type> baseIfcs = new ArrayList();
         for (ifc : type.implements) {
@@ -440,7 +420,7 @@ class BuilderTemplate extends AbstractBuilderTemplate {
               * @throws NullPointerException if {@code augmentation} is null
               */
             public Â«type.name» addAugmentation(«augmentTypeRef» augmentation) {
-                Â«jlClassRef»<? extends Â«augmentTypeRef»> augmentationType = augmentation.«DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME»();
+                Â«jlClassRef»<? extends Â«augmentTypeRef»> augmentationType = augmentation.«BINDING_CONTRACT_IMPLEMENTED_INTERFACE_NAME»();
                 if (!(this.«AUGMENTATION_FIELD» instanceof Â«hashMapRef»)) {
                     this.«AUGMENTATION_FIELD» = new Â«hashMapRef»<>();
                 }
index 256848371ee5a94bf66ad97d2a92335b61c533e3..2e11f12bfdb35d62f1ad745d918906f129c529eb 100644 (file)
@@ -66,8 +66,7 @@ final class ByTypeMemberComparator<T extends TypeMember> implements Comparator<T
         BaseYangTypes.UINT32_TYPE,
         BaseYangTypes.UINT64_TYPE,
         BaseYangTypes.BOOLEAN_TYPE,
-        BaseYangTypes.EMPTY_TYPE,
-        Types.CLASS);
+        BaseYangTypes.EMPTY_TYPE);
 
     /**
      * Singleton instance.
@@ -136,7 +135,7 @@ final class ByTypeMemberComparator<T extends TypeMember> implements Comparator<T
     }
 
     private static int rankOf(final Type type) {
-        if (FIXED_TYPES.contains(type)) {
+        if (FIXED_TYPES.contains(type) || BindingTypes.isIdentityType(type)) {
             return RANK_FIXED_SIZE;
         }
         if (type.equals(BaseYangTypes.STRING_TYPE) || type.equals(Types.BYTE_ARRAY)) {
index 4b4c9b4518f9a8fdbc148860cecbcc19f1f0907a..08935e81aa35ddfa171daa7da35c4d0790ce9b84 100644 (file)
@@ -26,6 +26,7 @@ import static org.opendaylight.mdsal.binding.model.ri.BindingTypes.SCALAR_TYPE_O
 import static org.opendaylight.mdsal.binding.model.ri.Types.BOOLEAN
 import static org.opendaylight.mdsal.binding.model.ri.Types.STRING;
 import static extension org.apache.commons.text.StringEscapeUtils.escapeJava
+import static extension org.opendaylight.mdsal.binding.model.ri.BindingTypes.isBitsType
 
 import com.google.common.base.Preconditions
 import com.google.common.collect.ImmutableList
@@ -51,7 +52,6 @@ import org.opendaylight.mdsal.binding.model.api.Type
 import org.opendaylight.mdsal.binding.model.ri.TypeConstants
 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping
 import org.opendaylight.yangtools.yang.common.Empty
-import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition
 
 /**
  * Template for generating JAVA class.
@@ -177,7 +177,7 @@ class ClassTemplate extends BaseTemplate {
 
             Â«propertyMethods»
 
-            Â«IF (genTO.isTypedef() && genTO.getBaseType instanceof BitsTypeDefinition)»
+            Â«IF genTO.isBitsType»
                 Â«generateGetValueForBitsTypeDef»
             Â«ENDIF»
 
index 9f81b5e8a348670433dfca4aae6a73799d6f0812..95f963372ddd49e36be2a61b0d333ab968c0d3f6 100644 (file)
@@ -16,10 +16,10 @@ import static org.opendaylight.mdsal.binding.model.ri.Types.BOOLEAN
 import static org.opendaylight.mdsal.binding.model.ri.Types.STRING
 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.REQUIRE_PREFIX
 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.AUGMENTATION_FIELD
+import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.BINDING_CONTRACT_IMPLEMENTED_INTERFACE_NAME
 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.BINDING_EQUALS_NAME
 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.BINDING_HASHCODE_NAME
 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.BINDING_TO_STRING_NAME
-import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME
 
 import com.google.common.annotations.VisibleForTesting;
 import java.util.List
@@ -208,7 +208,7 @@ class InterfaceTemplate extends BaseTemplate {
             generateRequireMethod(method)
         } else {
             switch method.name {
-                case DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME : generateDefaultImplementedInterface
+                case BINDING_CONTRACT_IMPLEMENTED_INTERFACE_NAME : generateDefaultImplementedInterface
                 default :
                     if (VOID == method.returnType.identifier) {
                         generateNoopVoidInterfaceMethod(method)
@@ -269,7 +269,7 @@ class InterfaceTemplate extends BaseTemplate {
 
     def private generateDefaultImplementedInterface() '''
         @«OVERRIDE.importedName»
-        default Â«CLASS.importedName»<«type.fullyQualifiedName»> Â«DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME»() {
+        default Â«CLASS.importedName»<«type.fullyQualifiedName»> Â«BINDING_CONTRACT_IMPLEMENTED_INTERFACE_NAME»() {
             return Â«type.fullyQualifiedName».class;
         }
     '''
index 4779907d5792efe1578be54ec177c27bb2f85a7b..600bb69cd2910f40d25d63795e668c580ea7eb60 100644 (file)
@@ -7,20 +7,22 @@
  */
 package org.opendaylight.mdsal.binding.java.api.generator
 
-import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.BINARY_TYPE;
-import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.BOOLEAN_TYPE;
-import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.EMPTY_TYPE;
-import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.STRING_TYPE;
-import static org.opendaylight.mdsal.binding.model.ri.Types.STRING;
-import static org.opendaylight.mdsal.binding.model.ri.Types.getOuterClassName;
+import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.BINARY_TYPE
+import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.BOOLEAN_TYPE
+import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.EMPTY_TYPE
+import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.STRING_TYPE
+import static org.opendaylight.mdsal.binding.model.ri.Types.STRING
+import static org.opendaylight.mdsal.binding.model.ri.Types.getOuterClassName
+import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.BINDING_CONTRACT_IMPLEMENTED_INTERFACE_NAME
 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.BUILDER_SUFFIX
+import static extension org.opendaylight.mdsal.binding.model.ri.BindingTypes.isBitsType
+import static extension org.opendaylight.mdsal.binding.model.ri.BindingTypes.isIdentityType
 
 import java.util.Base64;
 import org.gaul.modernizer_maven_annotations.SuppressModernizer
-import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
 import org.opendaylight.mdsal.binding.model.api.Enumeration
+import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
 import org.opendaylight.mdsal.binding.model.api.Type
-import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition
 
 /**
  * Template for generating JAVA class.
@@ -140,6 +142,9 @@ class UnionTemplate extends ClassTemplate {
                 Â«ELSEIF propRet.isBitsType»
                     Â«Â«Â« generated bits typedef
                 return Â«JU_ARRAYS.importedName».toString(«field».getValue());
+                Â«ELSEIF propRet.isIdentityType»
+                    Â«Â«Â« generated identity
+                return Â«field».«BINDING_CONTRACT_IMPLEMENTED_INTERFACE_NAME»().toString();
                 Â«ELSE»
                     Â«Â«Â« generated type
                 return Â«field».getValue().toString();
@@ -150,13 +155,6 @@ class UnionTemplate extends ClassTemplate {
         }
     '''
 
-    private static def isBitsType(Type type) {
-        if (type instanceof GeneratedTransferObject) {
-            return type.typedef && type.baseType instanceof BitsTypeDefinition
-        }
-        return false
-    }
-
     private static def Type typedefReturnType(Type type) {
         if (!(type instanceof GeneratedTransferObject)) {
             return null
@@ -192,5 +190,4 @@ class UnionTemplate extends ClassTemplate {
             Â«ENDFOR»
         }
     '''
-
 }
index c7957ec1d1c76ee4ac3f3091b53c2b2e0458874a..f3d640aa8dda14d476f6f399c6db447eee1113a3 100644 (file)
@@ -751,17 +751,9 @@ public class CompilationTest extends BaseCompilationTest {
 
     private static void testReturnTypeIdentityref(final Class<?> clazz, final String methodName,
             final String returnTypeStr) throws NoSuchMethodException {
-        Method method = clazz.getMethod(methodName);
-        assertEquals(java.lang.Class.class, method.getReturnType());
-        Type returnType = method.getGenericReturnType();
-        assertTrue(returnType instanceof ParameterizedType);
-        final ParameterizedType pt = (ParameterizedType) returnType;
-        final Type[] parameters = pt.getActualTypeArguments();
-        assertEquals(1, parameters.length);
-        final Type parameter = parameters[0];
-        assertTrue(parameter instanceof WildcardType);
-        final WildcardType wildcardType = (WildcardType) parameter;
-        assertEquals("? extends " + returnTypeStr, wildcardType.toString());
+        Class<?> returnType = clazz.getMethod(methodName).getReturnType();
+        assertTrue(returnType.isInterface());
+        assertEquals(returnTypeStr, returnType.getName());
     }
 
     private static void testReturnTypeInstanceIdentitifer(final ClassLoader loader, final Class<?> clazz,
index 2c214bfaee4afeea4a18e27b9e751cb8e13c722c..f05db66943d224f126a2ef1e5d3abbaec14a1ffe 100644 (file)
@@ -41,17 +41,17 @@ public class Mdsal732Test extends BaseCompilationTest {
         final var content = Files.readString(xyzzyBuilder.toPath());
         FileSearchUtil.assertFileContainsConsecutiveLines(xyzzyBuilder, content,
             "    public XyzzyBuilder(Grp arg) {",
-            "        this._foo = CodeHelpers.checkFieldCastIdentity(Foo.class, \"foo\", arg.getFoo());",
-            "        this._bar = CodeHelpers.checkSetFieldCastIdentity(Foo.class, \"bar\", arg.getBar());",
-            "        this._baz = CodeHelpers.checkListFieldCastIdentity(Foo.class, \"baz\", arg.getBaz());",
+            "        this._foo = CodeHelpers.checkFieldCast(Foo.class, \"foo\", arg.getFoo());",
+            "        this._bar = CodeHelpers.checkSetFieldCast(Foo.class, \"bar\", arg.getBar());",
+            "        this._baz = CodeHelpers.checkListFieldCast(Foo.class, \"baz\", arg.getBaz());",
             "    }");
         FileSearchUtil.assertFileContainsConsecutiveLines(xyzzyBuilder, content,
             "    public void fieldsFrom(DataObject arg) {",
             "        boolean isValidArg = false;",
             "        if (arg instanceof Grp) {",
-            "            this._foo = CodeHelpers.checkFieldCastIdentity(Foo.class, \"foo\", ((Grp)arg).getFoo());",
-            "            this._bar = CodeHelpers.checkSetFieldCastIdentity(Foo.class, \"bar\", ((Grp)arg).getBar());",
-            "            this._baz = CodeHelpers.checkListFieldCastIdentity(Foo.class, \"baz\", ((Grp)arg).getBaz());",
+            "            this._foo = CodeHelpers.checkFieldCast(Foo.class, \"foo\", ((Grp)arg).getFoo());",
+            "            this._bar = CodeHelpers.checkSetFieldCast(Foo.class, \"bar\", ((Grp)arg).getBar());",
+            "            this._baz = CodeHelpers.checkListFieldCast(Foo.class, \"baz\", ((Grp)arg).getBaz());",
             "            isValidArg = true;",
             "        }",
             "        CodeHelpers.validValue(isValidArg, arg, \"[Grp]\");",
index f7b94396417a3cc91996a343011eab3c9c19db36..9555718976a49ad2c0ddf29458275fc89d172818 100644 (file)
@@ -16,6 +16,7 @@ import java.lang.reflect.Method;
 import java.net.URL;
 import java.net.URLClassLoader;
 import org.junit.Test;
+import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
 
 /**
  * Union constructor with indentityref. Previously identityref was ignored so that there is no constructor for
@@ -40,10 +41,12 @@ public class UnionWithIdentityrefTest extends BaseCompilationTest {
         Class<?> unionTypeClass = Class.forName(CompilationTestUtils.BASE_PKG
             + ".urn.opendaylight.yang.union.test.rev160509.UnionType", true, loader);
 
+        Object identOneValue = identOneClass.getDeclaredField(BindingMapping.VALUE_STATIC_FIELD_NAME).get(null);
+
         // test UnionType with IdentOne argument
         Constructor<?> unionTypeIdentBaseConstructor = CompilationTestUtils.assertContainsConstructor(unionTypeClass,
-            Class.class);
-        Object unionType = unionTypeIdentBaseConstructor.newInstance(identOneClass);
+            identBaseClass);
+        Object unionType = unionTypeIdentBaseConstructor.newInstance(identOneValue);
 
         Method getUint8 = unionTypeClass.getDeclaredMethod("getUint8");
         Object actualUint8 = getUint8.invoke(unionType);
@@ -51,7 +54,7 @@ public class UnionWithIdentityrefTest extends BaseCompilationTest {
 
         Method getIdentityref = unionTypeClass.getDeclaredMethod("getIdentityref");
         Object actualIdentityref = getIdentityref.invoke(unionType);
-        assertEquals(identOneClass, actualIdentityref);
+        assertEquals(identOneValue, actualIdentityref);
 
         CompilationTestUtils.cleanUp(sourcesOutputDir, compiledOutputDir);
     }
index c4beb7b174144d2a046e2a00e67efafee327a20b..26dd49ef12b0bf093c0da489dcb698e51ba45693 100644 (file)
@@ -9,10 +9,13 @@ package org.opendaylight.mdsal.binding.model.ri;
 
 import static org.opendaylight.mdsal.binding.model.ri.Types.parameterizedTypeFor;
 import static org.opendaylight.mdsal.binding.model.ri.Types.typeForClass;
+import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.VALUE_STATIC_FIELD_NAME;
 
 import com.google.common.annotations.VisibleForTesting;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.model.api.ConcreteType;
+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.ParameterizedType;
 import org.opendaylight.mdsal.binding.model.api.Type;
@@ -43,6 +46,7 @@ import org.opendaylight.yangtools.yang.binding.TypeObject;
 import org.opendaylight.yangtools.yang.binding.annotations.RoutingContext;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
 
 public final class BindingTypes {
 
@@ -272,4 +276,42 @@ public final class BindingTypes {
     public static ParameterizedType scalarTypeObject(final Type type) {
         return parameterizedTypeFor(SCALAR_TYPE_OBJECT, type);
     }
+
+    /**
+     * Check if specified type is generated for a {@code type bits}.
+     *
+     * @param type Type to examine
+     * @return {@code true} if the type is generated for a {@code type bits}
+     */
+    public static boolean isBitsType(final Type type) {
+        return type instanceof GeneratedTransferObject && isBitsType((GeneratedTransferObject) type);
+    }
+
+    /**
+     * Check if specified type is generated for a {@code type bits}.
+     *
+     * @param gto Type to examine
+     * @return {@code true} if the type is generated for a {@code type bits}
+     */
+    public static boolean isBitsType(final GeneratedTransferObject gto) {
+        return gto.isTypedef() && gto.getBaseType() instanceof BitsTypeDefinition;
+    }
+
+    /**
+     * Check if specified type is generated for an identity.
+     *
+     * @param type Type to examine
+     * @return {@code true} if the type is generated for an identity
+     */
+    public static boolean isIdentityType(final Type type) {
+        if (type instanceof GeneratedType) {
+            for (var constant : ((GeneratedType) type).getConstantDefinitions()) {
+                if (VALUE_STATIC_FIELD_NAME.equals(constant.getName())
+                    && BaseIdentity.class.equals(constant.getValue())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
 }
index dbed269c4af0d895127351403becc4af7433da88..3fed0a6a7677d9d5fd5840252153555fbbb1676a 100644 (file)
@@ -8,14 +8,19 @@
 package org.opendaylight.mdsal.binding.runtime.api;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.Beta;
+import com.google.common.base.Throwables;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
+import java.util.concurrent.ExecutionException;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
 import org.opendaylight.yangtools.yang.binding.Action;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
 import org.opendaylight.yangtools.yang.binding.Notification;
 import org.opendaylight.yangtools.yang.binding.RpcInput;
 import org.opendaylight.yangtools.yang.binding.RpcOutput;
@@ -28,16 +33,18 @@ import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absol
  */
 @Beta
 public abstract class AbstractBindingRuntimeContext implements BindingRuntimeContext {
-    private final LoadingCache<QName, Class<?>> identityClasses = CacheBuilder.newBuilder().weakValues().build(
-        new CacheLoader<QName, Class<?>>() {
+    private final LoadingCache<@NonNull QName, @NonNull Class<? extends BaseIdentity>> identityClasses =
+        CacheBuilder.newBuilder().weakValues().build(new CacheLoader<>() {
             @Override
-            public Class<?> load(final QName key) {
+            public Class<? extends BaseIdentity> load(final QName key) {
                 final var type = getTypes().findIdentity(key).orElseThrow(
                     () -> new IllegalArgumentException("Supplied QName " + key + " is not a valid identity"));
                 try {
-                    return loadClass(type.getIdentifier());
-                } catch (final ClassNotFoundException e) {
+                    return loadClass(type.getIdentifier()).asSubclass(BaseIdentity.class);
+                } catch (ClassNotFoundException e) {
                     throw new IllegalArgumentException("Required class " + type + " was not found.", e);
+                } catch (ClassCastException e) {
+                    throw new IllegalArgumentException(key + " resolves to a non-identity class", e);
                 }
             }
         });
@@ -79,8 +86,13 @@ public abstract class AbstractBindingRuntimeContext implements BindingRuntimeCon
     }
 
     @Override
-    public final Class<?> getIdentityClass(final QName input) {
-        return identityClasses.getUnchecked(input);
+    public final Class<? extends BaseIdentity> getIdentityClass(final QName input) {
+        try {
+            return identityClasses.get(requireNonNull(input));
+        } catch (ExecutionException e) {
+            Throwables.throwIfUnchecked(e.getCause());
+            throw new IllegalStateException("Unexpected error looking up " + input, e);
+        }
     }
 
     @Override
index 109656a7bd6337f0b9eb142ddba6d2febf7f6d11..321295d2b748a4e8b98dc8e8eb96cd80b03b0a3c 100644 (file)
@@ -15,6 +15,7 @@ import org.opendaylight.mdsal.binding.model.api.Type;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.yang.binding.Action;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
 import org.opendaylight.yangtools.yang.binding.RpcInput;
 import org.opendaylight.yangtools.yang.binding.RpcOutput;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -100,5 +101,5 @@ public interface BindingRuntimeContext extends EffectiveModelContextProvider, Im
     // FIXME: 9.0.0: this needs to accept an EffectiveStatementInference
     @NonNull Class<?> getClassForSchema(Absolute schema);
 
-    @NonNull Class<?> getIdentityClass(QName input);
+    @NonNull Class<? extends BaseIdentity> getIdentityClass(QName input);
 }
index eec58a27db605fee8215f2fa158f4c47e406de67..d74288914e499851cbfa472332a2443be45036ee 100644 (file)
@@ -18,6 +18,7 @@ import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
 import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
 import org.opendaylight.yangtools.yang.binding.Action;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
+import org.opendaylight.yangtools.yang.binding.BaseIdentity;
 import org.opendaylight.yangtools.yang.binding.RpcInput;
 import org.opendaylight.yangtools.yang.binding.RpcOutput;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -59,7 +60,7 @@ public abstract class ForwardingBindingRuntimeContext extends ForwardingObject i
     }
 
     @Override
-    public Class<?> getIdentityClass(final QName input) {
+    public Class<? extends BaseIdentity> getIdentityClass(final QName input) {
         return delegate().getIdentityClass(input);
     }
 
index 7690f6d9fef1d4dcf8b9f00452c6d3ef727f1a21..57dae3eac9dfb31eb391fca8f223177ee4f84857 100644 (file)
@@ -25,7 +25,7 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.yang.binding.Augmentable;
-import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.opendaylight.yangtools.yang.binding.BindingContract;
 import org.opendaylight.yangtools.yang.binding.Identifiable;
 import org.opendaylight.yangtools.yang.binding.ScalarTypeObject;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -64,6 +64,7 @@ public final class BindingMapping {
     public static final @NonNull String BUILDER_SUFFIX = "Builder";
     public static final @NonNull String KEY_SUFFIX = "Key";
     public static final @NonNull String QNAME_STATIC_FIELD_NAME = "QNAME";
+    public static final @NonNull String VALUE_STATIC_FIELD_NAME = "VALUE";
     public static final @NonNull String PACKAGE_PREFIX = "org.opendaylight.yang.gen.v1";
     public static final @NonNull String AUGMENTATION_FIELD = "augmentation";
 
@@ -88,9 +89,9 @@ public final class BindingMapping {
     public static final @NonNull String IDENTIFIABLE_KEY_NAME = "key";
 
     /**
-     * Name of {@link DataContainer#implementedInterface()}.
+     * Name of {@link BindingContract#implementedInterface()}.
      */
-    public static final @NonNull String DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME = "implementedInterface";
+    public static final @NonNull String BINDING_CONTRACT_IMPLEMENTED_INTERFACE_NAME = "implementedInterface";
 
     /**
      * Name of default {@link Object#hashCode()} implementation for instantiated DataObjects. Each such generated
index 905890bda8c9da05fa157fb8d3c2ab5ff8451ce7..5c2859d2c4a8997e5aafc9e1e338b5b0c854cb1b 100644 (file)
@@ -8,8 +8,8 @@
 package org.opendaylight.yangtools.yang.binding;
 
 /**
- * Base Identity.
+ * Base Identity. Interface generated for {@code identity} statements extend this interface.
  */
-public interface BaseIdentity {
+public interface BaseIdentity extends BindingObject, BindingContract<BaseIdentity> {
 
 }
diff --git a/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/BindingContract.java b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/BindingContract.java
new file mode 100644 (file)
index 0000000..0c06c73
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022 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.yangtools.yang.binding;
+
+import com.google.common.annotations.Beta;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Base interface for all interfaces generated to capture a specific contract.
+ *
+ * @param <T> Type of the captured contract
+ */
+@Beta
+// FIXME: evaluate integrating with BindingObject
+public interface BindingContract<T extends BindingContract<T>> {
+    /**
+     * Return the interface implemented by this object. This method differs from {@link Object#getClass()} in that it
+     * returns the interface contract, not a concrete implementation class.
+     *
+     * @return Implemented contract
+     */
+    @NonNull Class<? extends T> implementedInterface();
+}
index ff57748811c94c48270d5c81aba46ed3b6c67b98..028543b2cd7cf216f7408f6935288660e512bf49 100644 (file)
@@ -350,6 +350,25 @@ public final class CodeHelpers {
         return wrapHashCode(Arrays.hashCode(obj));
     }
 
+    /**
+     * The constant '31' is the result of folding this code:
+     * <pre>
+     *   <code>
+     *     final int prime = 31;
+     *     int result = 1;
+     *     result = result * prime + Objects.hashCode(obj);
+     *     return result;
+     *   </code>
+     * </pre>
+     * when hashCode is returned as 0, such as due to obj being null or its hashCode being 0.
+     *
+     * @param hash Wrapped object hash
+     * @return Wrapper object hash
+     */
+    private static int wrapHashCode(final int hash) {
+        return hash == 0 ? 31 : hash;
+    }
+
     /**
      * Utility method for checking whether a target object is a compatible DataObject.
      *
@@ -383,31 +402,6 @@ public final class CodeHelpers {
         }
     }
 
-    /**
-     * Utility method for checking whether a target object is compatible with a particular identity class.
-     *
-     * @param requiredClass Required class
-     * @param fieldName name of the field being filled
-     * @param obj Object to check, may be null
-     * @return Object cast to required class, if it class matches requirement, or null
-     * @throws IllegalArgumentException if {@code obj} is not an Class representing {@code requiredClass}
-     * @throws NullPointerException if {@code requiredClass} or {@code fieldName} is null
-     */
-    public static <T extends BaseIdentity> @Nullable Class<? extends T> checkFieldCastIdentity(
-            final @NonNull Class<T> requiredClass, final @NonNull String fieldName, final @Nullable Object obj) {
-        if (obj == null) {
-            return null;
-        }
-        checkArgument(obj instanceof Class, "Invalid input value \"%s\" for property \"%s\"", obj, fieldName);
-
-        try {
-            return ((Class<?>) obj).asSubclass(requiredClass);
-        } catch (ClassCastException e) {
-            throw new IllegalArgumentException("Invalid input value \"" + obj + "\" for property \"" + fieldName + "\"",
-                e);
-        }
-    }
-
     /**
      * Utility method for checking whether the items of target list is compatible.
      *
@@ -425,23 +419,6 @@ public final class CodeHelpers {
         return (List<T>) list;
     }
 
-    /**
-     * Utility method for checking whether the items of a target list are compatible with a particular identity class.
-     *
-     * @param requiredClass Required class
-     * @param fieldName name of the field being filled
-     * @param list List, which items should be checked
-     * @return Type-checked List
-     * @throws IllegalArgumentException if a list item is not a Class representing {@code requiredClass}
-     * @throws NullPointerException if {@code requiredClass} or {@code fieldName} is null
-     */
-    @SuppressWarnings("unchecked")
-    public static <T extends BaseIdentity> @Nullable List<Class<? extends T>> checkListFieldCastIdentity(
-            final @NonNull Class<T> requiredClass, final @NonNull String fieldName, final @Nullable List<?> list) {
-        checkCollectionFieldIdentity(requiredClass, fieldName, list);
-        return (List<Class<? extends T>>) list;
-    }
-
     /**
      * Utility method for checking whether the items of target set is compatible.
      *
@@ -459,23 +436,6 @@ public final class CodeHelpers {
         return (Set<T>) set;
     }
 
-    /**
-     * Utility method for checking whether the items of a target set are compatible with a particular identity class.
-     *
-     * @param requiredClass Required class
-     * @param fieldName name of the field being filled
-     * @param set Set, which items should be checked
-     * @return Type-checked Set
-     * @throws IllegalArgumentException if a set item is not a Class representing {@code requiredClass}
-     * @throws NullPointerException if {@code requiredClass} or {@code fieldName} is null
-     */
-    @SuppressWarnings("unchecked")
-    public static <T extends BaseIdentity> @Nullable Set<Class<? extends T>> checkSetFieldCastIdentity(
-            final @NonNull Class<T> requiredClass, final @NonNull String fieldName, final @Nullable Set<?> set) {
-        checkCollectionFieldIdentity(requiredClass, fieldName, set);
-        return (Set<Class<? extends T>>) set;
-    }
-
     @SuppressFBWarnings(value = "DCN_NULLPOINTER_EXCEPTION",
         justification = "Internal NPE->IAE conversion")
     private static void checkCollectionField(final @NonNull Class<?> requiredClass,
@@ -489,37 +449,4 @@ public final class CodeHelpers {
             }
         }
     }
-
-    @SuppressFBWarnings(value = "DCN_NULLPOINTER_EXCEPTION",
-        justification = "Internal NPE->IAE conversion")
-    private static void checkCollectionFieldIdentity(final @NonNull Class<?> requiredClass,
-            final @NonNull String fieldName, final @Nullable Collection<?> collection) {
-        if (collection != null) {
-            try {
-                collection.forEach(item -> ((Class<?>) item).asSubclass(requiredClass));
-            } catch (ClassCastException | NullPointerException e) {
-                throw new IllegalArgumentException("Invalid input item for property \"" + requireNonNull(fieldName)
-                    + "\"", e);
-            }
-        }
-    }
-
-    /**
-     * The constant '31' is the result of folding this code:
-     * <pre>
-     *   <code>
-     *     final int prime = 31;
-     *     int result = 1;
-     *     result = result * prime + Objects.hashCode(obj);
-     *     return result;
-     *   </code>
-     * </pre>
-     * when hashCode is returned as 0, such as due to obj being null or its hashCode being 0.
-     *
-     * @param hash Wrapped object hash
-     * @return Wrapper object hash
-     */
-    private static int wrapHashCode(final int hash) {
-        return hash == 0 ? 31 : hash;
-    }
 }
index 82b32d60e754ee1d7401a64eb0bd4e9136ea890a..78983fe810439cda3e65df7b5c702d538c04d53d 100644 (file)
@@ -7,26 +7,18 @@
  */
 package org.opendaylight.yangtools.yang.binding;
 
-import org.eclipse.jdt.annotation.NonNull;
-
 /**
  * Data Container - object contains structured data. Marker interface which must be implemented by all interfaces
  * generated for YANG:
  * <ul>
- * <li>Rpc Input
- * <li>Output
- * <li>Notification
- * <li>Container
- * <li>List
- * <li>Case
+ *   <li>Rpc Input</li>
+ *   <li>Output</li>
+ *   <li>Notification</li>
+ *   <li>Container</li>
+ *   <li>List</li>
+ *   <li>Case</li>
  * </ul>
  */
-public interface DataContainer {
-    /**
-     * Return the interface implemented by this object. This method differs from {@link Object#getClass()} in that it
-     * returns the interface contract, not a concrete implementation class.
-     *
-     * @return Implemented contract
-     */
-    @NonNull Class<? extends DataContainer> implementedInterface();
+public interface DataContainer extends BindingContract<DataContainer> {
+
 }
index 102ce23fcbce7b46541991d139ae18bdca2715e1..a3d81ea2d2fe2650134c12f64e25f3deafa2d8a1 100644 (file)
@@ -7,10 +7,7 @@
  */
 package org.opendaylight.yangtools.yang.binding;
 
-import static org.hamcrest.CoreMatchers.allOf;
-import static org.hamcrest.CoreMatchers.endsWith;
 import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.CoreMatchers.startsWith;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
@@ -32,24 +29,6 @@ public class CodeHelpersTest {
         assertThat(iae.getCause(), instanceOf(ClassCastException.class));
     }
 
-    @Test
-    public void testCheckedFieldCastIdentity() {
-        assertNull(CodeHelpers.checkFieldCastIdentity(Identity.class, "foo", null));
-        assertSame(Identity.class, CodeHelpers.checkFieldCastIdentity(Identity.class, "foo", Identity.class));
-        assertSame(DerivedIdentity.class, CodeHelpers.checkFieldCastIdentity(Identity.class, "foo",
-            DerivedIdentity.class));
-
-        IllegalArgumentException iae = assertThrows(IllegalArgumentException.class,
-            () -> CodeHelpers.checkFieldCastIdentity(Identity.class, "foo", new Object()));
-        assertThat(iae.getMessage(), allOf(
-            startsWith("Invalid input value \"java.lang.Object"),
-            endsWith("\" for property \"foo\"")));
-
-        iae = assertThrows(IllegalArgumentException.class,
-            () -> CodeHelpers.checkFieldCastIdentity(Identity.class, "foo", BaseIdentity.class));
-        assertThat(iae.getCause(), instanceOf(ClassCastException.class));
-    }
-
     @Test
     public void testCheckListFieldCast() {
         assertNull(CodeHelpers.checkListFieldCast(CodeHelpersTest.class, "foo", null));
@@ -66,29 +45,6 @@ public class CodeHelpersTest {
         assertThat(iae.getCause(), instanceOf(ClassCastException.class));
     }
 
-    @Test
-    public void testCheckListFieldCastIdentity() {
-        assertNull(CodeHelpers.checkListFieldCastIdentity(Identity.class, "foo", null));
-        assertSame(List.of(), CodeHelpers.checkListFieldCastIdentity(Identity.class, "foo", List.of()));
-
-        final var list = List.of(Identity.class);
-        assertSame(list, CodeHelpers.checkListFieldCastIdentity(Identity.class, "foo", list));
-        final var derivedList = List.of(DerivedIdentity.class);
-        assertSame(derivedList, CodeHelpers.checkListFieldCastIdentity(Identity.class, "foo", derivedList));
-
-        IllegalArgumentException iae = assertThrows(IllegalArgumentException.class,
-            () -> CodeHelpers.checkListFieldCastIdentity(Identity.class, "foo", Collections.singletonList(null)));
-        assertThat(iae.getCause(), instanceOf(NullPointerException.class));
-
-        iae = assertThrows(IllegalArgumentException.class,
-            () -> CodeHelpers.checkListFieldCastIdentity(Identity.class, "foo", List.of(new Object())));
-        assertThat(iae.getCause(), instanceOf(ClassCastException.class));
-
-        iae = assertThrows(IllegalArgumentException.class,
-            () -> CodeHelpers.checkListFieldCastIdentity(Identity.class, "foo", List.of(BaseIdentity.class)));
-        assertThat(iae.getCause(), instanceOf(ClassCastException.class));
-    }
-
     @Test
     public void testCheckSetFieldCast() {
         assertNull(CodeHelpers.checkSetFieldCast(CodeHelpersTest.class, "foo", null));
@@ -104,35 +60,4 @@ public class CodeHelpersTest {
             () -> CodeHelpers.checkSetFieldCast(CodeHelpersTest.class, "foo", Set.of(new Object())));
         assertThat(iae.getCause(), instanceOf(ClassCastException.class));
     }
-
-    @Test
-    public void testCheckSetFieldCastIdentity() {
-        assertNull(CodeHelpers.checkSetFieldCastIdentity(Identity.class, "foo", null));
-        assertSame(Set.of(), CodeHelpers.checkSetFieldCastIdentity(Identity.class, "foo", Set.of()));
-
-        final var set = Set.of(Identity.class);
-        assertSame(set, CodeHelpers.checkSetFieldCastIdentity(Identity.class, "foo", set));
-        final var derivedSet = Set.of(DerivedIdentity.class);
-        assertSame(derivedSet, CodeHelpers.checkSetFieldCastIdentity(Identity.class, "foo", derivedSet));
-
-        IllegalArgumentException iae = assertThrows(IllegalArgumentException.class,
-            () -> CodeHelpers.checkSetFieldCastIdentity(Identity.class, "foo", Collections.singleton(null)));
-        assertThat(iae.getCause(), instanceOf(NullPointerException.class));
-
-        iae = assertThrows(IllegalArgumentException.class,
-            () -> CodeHelpers.checkSetFieldCastIdentity(Identity.class, "foo", Set.of(new Object())));
-        assertThat(iae.getCause(), instanceOf(ClassCastException.class));
-
-        iae = assertThrows(IllegalArgumentException.class,
-            () -> CodeHelpers.checkSetFieldCastIdentity(Identity.class, "foo", Set.of(BaseIdentity.class)));
-        assertThat(iae.getCause(), instanceOf(ClassCastException.class));
-    }
-
-    private interface Identity extends BaseIdentity {
-
-    }
-
-    private interface DerivedIdentity extends Identity {
-
-    }
 }