From: Robert Varga Date: Mon, 14 Mar 2022 09:31:49 +0000 (+0100) Subject: Map identities to proper objects X-Git-Tag: v10.0.0~109 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=mdsal.git;a=commitdiff_plain;h=4c2b08900d7a4a06bf99698801f339c8bf22e07c Map identities to proper objects Change the binding type mapping from Class 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 --- diff --git a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/LeafDefaultValueTest.java b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/LeafDefaultValueTest.java index 72f7486bec..64631f3d6f 100644 --- a/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/LeafDefaultValueTest.java +++ b/binding/mdsal-binding-dom-adapter/src/test/java/org/opendaylight/mdsal/binding/dom/adapter/LeafDefaultValueTest.java @@ -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()); } } diff --git a/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingIdentityCodec.java b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingIdentityCodec.java index c0f37fed8d..23186036a7 100644 --- a/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingIdentityCodec.java +++ b/binding/mdsal-binding-dom-codec-api/src/main/java/org/opendaylight/mdsal/binding/dom/codec/api/BindingIdentityCodec.java @@ -21,19 +21,21 @@ public interface BindingIdentityCodec extends Immutable { /** * Convert a QNname to its corresponding Binding class. * + * @param 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 toBinding(@NonNull QName qname); + @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 bindingClass); + @NonNull QName fromBinding(@NonNull BaseIdentity bindingValue); } diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java index 35596c90ef..b85065ab39 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/BindingCodecContext.java @@ -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 getCodec(final Class valueType, final TypeDefinition instantiatedType) { - if (Class.class.equals(valueType)) { + if (BaseIdentity.class.isAssignableFrom(valueType)) { @SuppressWarnings({ "unchecked", "rawtypes" }) final IllegalArgumentCodec casted = (IllegalArgumentCodec) identityCodec; return casted; diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CompositeValueCodec.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CompositeValueCodec.java index 50ff799ed7..a0184f9366 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CompositeValueCodec.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/CompositeValueCodec.java @@ -29,14 +29,8 @@ abstract class CompositeValueCodec extends AbstractIllegalArgumentCodec 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 diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/IdentityCodec.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/IdentityCodec.java index 06fbb0b939..1297b0ddc9 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/IdentityCodec.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/IdentityCodec.java @@ -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> implements BindingIdentityCodec { +final class IdentityCodec extends AbstractIllegalArgumentCodec 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> } @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 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 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 bindingClass) { - return BindingReflections.getQName(bindingClass); + public QName fromBinding(final BaseIdentity bindingValue) { + return BindingReflections.getQName(bindingValue.implementedInterface()); } } diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/LeafReferenceTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/LeafReferenceTest.java index 1917d18f25..25015fae3d 100644 --- a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/LeafReferenceTest.java +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/LeafReferenceTest.java @@ -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")) diff --git a/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/Generator.java b/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/Generator.java index 6fd9a25e5a..23690d1603 100644 --- a/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/Generator.java +++ b/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/Generator.java @@ -386,7 +386,7 @@ public abstract class Generator implements Iterable { 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); diff --git a/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/IdentityGenerator.java b/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/IdentityGenerator.java index 95e7db5dab..efffb79ecc 100644 --- a/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/IdentityGenerator.java +++ b/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/IdentityGenerator.java @@ -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()); diff --git a/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/TypeReference.java b/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/TypeReference.java index 629306c8db..cde69a941a 100644 --- a/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/TypeReference.java +++ b/binding/mdsal-binding-generator/src/main/java/org/opendaylight/mdsal/binding/generator/impl/reactor/TypeReference.java @@ -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 referencedGenerators; - private ParameterizedType returnType; + private Type returnType; Identityref(final List referencedGenerators) { this.referencedGenerators = requireNonNull(referencedGenerators); @@ -31,15 +28,15 @@ abstract class TypeReference { @Override Type methodReturnType(final TypeBuilderFactory builderFactory) { if (returnType == null) { - final List 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; } diff --git a/binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/DefaultBindingGeneratorTest.java b/binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/DefaultBindingGeneratorTest.java index 99417d7016..d05186818d 100644 --- a/binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/DefaultBindingGeneratorTest.java +++ b/binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/DefaultBindingGeneratorTest.java @@ -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()); } diff --git a/binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/IdentityrefTypeTest.java b/binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/IdentityrefTypeTest.java index a4ec36e57f..85437007c8 100644 --- a/binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/IdentityrefTypeTest.java +++ b/binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/IdentityrefTypeTest.java @@ -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()); } } diff --git a/binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Mdsal269Test.java b/binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Mdsal269Test.java index 3d0aba923b..151177406c 100644 --- a/binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Mdsal269Test.java +++ b/binding/mdsal-binding-generator/src/test/java/org/opendaylight/mdsal/binding/generator/impl/Mdsal269Test.java @@ -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()); } } diff --git a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BaseTemplate.xtend b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BaseTemplate.xtend index ea60bba40f..56f135cd41 100644 --- a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BaseTemplate.xtend +++ b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/BaseTemplate.xtend @@ -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» 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» 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 7dfd93a551..dbc7846d57 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 @@ -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 getBaseIfcs(GeneratedType type) { val List 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» augmentationType = augmentation.«DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME»(); + «jlClassRef» augmentationType = augmentation.«BINDING_CONTRACT_IMPLEMENTED_INTERFACE_NAME»(); if (!(this.«AUGMENTATION_FIELD» instanceof «hashMapRef»)) { this.«AUGMENTATION_FIELD» = new «hashMapRef»<>(); } diff --git a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/ByTypeMemberComparator.java b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/ByTypeMemberComparator.java index 256848371e..2e11f12bfd 100644 --- a/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/ByTypeMemberComparator.java +++ b/binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/mdsal/binding/java/api/generator/ByTypeMemberComparator.java @@ -66,8 +66,7 @@ final class ByTypeMemberComparator implements Comparator implements Comparator «DATA_CONTAINER_IMPLEMENTED_INTERFACE_NAME»() { + default «CLASS.importedName»<«type.fullyQualifiedName»> «BINDING_CONTRACT_IMPLEMENTED_INTERFACE_NAME»() { return «type.fullyQualifiedName».class; } ''' 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 4779907d57..600bb69cd2 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 @@ -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» } ''' - } diff --git a/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/CompilationTest.java b/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/CompilationTest.java index c7957ec1d1..f3d640aa8d 100644 --- a/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/CompilationTest.java +++ b/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/CompilationTest.java @@ -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, diff --git a/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/Mdsal732Test.java b/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/Mdsal732Test.java index 2c214bfaee..f05db66943 100644 --- a/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/Mdsal732Test.java +++ b/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/Mdsal732Test.java @@ -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]\");", diff --git a/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/UnionWithIdentityrefTest.java b/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/UnionWithIdentityrefTest.java index f7b9439641..9555718976 100644 --- a/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/UnionWithIdentityrefTest.java +++ b/binding/mdsal-binding-java-api-generator/src/test/java/org/opendaylight/mdsal/binding/java/api/generator/UnionWithIdentityrefTest.java @@ -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); } diff --git a/binding/mdsal-binding-model-ri/src/main/java/org/opendaylight/mdsal/binding/model/ri/BindingTypes.java b/binding/mdsal-binding-model-ri/src/main/java/org/opendaylight/mdsal/binding/model/ri/BindingTypes.java index c4beb7b174..26dd49ef12 100644 --- a/binding/mdsal-binding-model-ri/src/main/java/org/opendaylight/mdsal/binding/model/ri/BindingTypes.java +++ b/binding/mdsal-binding-model-ri/src/main/java/org/opendaylight/mdsal/binding/model/ri/BindingTypes.java @@ -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; + } } diff --git a/binding/mdsal-binding-runtime-api/src/main/java/org/opendaylight/mdsal/binding/runtime/api/AbstractBindingRuntimeContext.java b/binding/mdsal-binding-runtime-api/src/main/java/org/opendaylight/mdsal/binding/runtime/api/AbstractBindingRuntimeContext.java index dbed269c4a..3fed0a6a76 100644 --- a/binding/mdsal-binding-runtime-api/src/main/java/org/opendaylight/mdsal/binding/runtime/api/AbstractBindingRuntimeContext.java +++ b/binding/mdsal-binding-runtime-api/src/main/java/org/opendaylight/mdsal/binding/runtime/api/AbstractBindingRuntimeContext.java @@ -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> identityClasses = CacheBuilder.newBuilder().weakValues().build( - new CacheLoader>() { + private final LoadingCache<@NonNull QName, @NonNull Class> identityClasses = + CacheBuilder.newBuilder().weakValues().build(new CacheLoader<>() { @Override - public Class load(final QName key) { + public Class 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 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 diff --git a/binding/mdsal-binding-runtime-api/src/main/java/org/opendaylight/mdsal/binding/runtime/api/BindingRuntimeContext.java b/binding/mdsal-binding-runtime-api/src/main/java/org/opendaylight/mdsal/binding/runtime/api/BindingRuntimeContext.java index 109656a7bd..321295d2b7 100644 --- a/binding/mdsal-binding-runtime-api/src/main/java/org/opendaylight/mdsal/binding/runtime/api/BindingRuntimeContext.java +++ b/binding/mdsal-binding-runtime-api/src/main/java/org/opendaylight/mdsal/binding/runtime/api/BindingRuntimeContext.java @@ -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 getIdentityClass(QName input); } diff --git a/binding/mdsal-binding-runtime-spi/src/main/java/org/opendaylight/mdsal/binding/runtime/spi/ForwardingBindingRuntimeContext.java b/binding/mdsal-binding-runtime-spi/src/main/java/org/opendaylight/mdsal/binding/runtime/spi/ForwardingBindingRuntimeContext.java index eec58a27db..d74288914e 100644 --- a/binding/mdsal-binding-runtime-spi/src/main/java/org/opendaylight/mdsal/binding/runtime/spi/ForwardingBindingRuntimeContext.java +++ b/binding/mdsal-binding-runtime-spi/src/main/java/org/opendaylight/mdsal/binding/runtime/spi/ForwardingBindingRuntimeContext.java @@ -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 getIdentityClass(final QName input) { return delegate().getIdentityClass(input); } diff --git a/binding/mdsal-binding-spec-util/src/main/java/org/opendaylight/mdsal/binding/spec/naming/BindingMapping.java b/binding/mdsal-binding-spec-util/src/main/java/org/opendaylight/mdsal/binding/spec/naming/BindingMapping.java index 7690f6d9fe..57dae3eac9 100644 --- a/binding/mdsal-binding-spec-util/src/main/java/org/opendaylight/mdsal/binding/spec/naming/BindingMapping.java +++ b/binding/mdsal-binding-spec-util/src/main/java/org/opendaylight/mdsal/binding/spec/naming/BindingMapping.java @@ -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 diff --git a/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/BaseIdentity.java b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/BaseIdentity.java index 905890bda8..5c2859d2c4 100644 --- a/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/BaseIdentity.java +++ b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/BaseIdentity.java @@ -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 { } 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 index 0000000000..0c06c73841 --- /dev/null +++ b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/BindingContract.java @@ -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 Type of the captured contract + */ +@Beta +// FIXME: evaluate integrating with BindingObject +public interface BindingContract> { + /** + * 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 implementedInterface(); +} diff --git a/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/CodeHelpers.java b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/CodeHelpers.java index ff57748811..028543b2cd 100644 --- a/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/CodeHelpers.java +++ b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/CodeHelpers.java @@ -350,6 +350,25 @@ public final class CodeHelpers { return wrapHashCode(Arrays.hashCode(obj)); } + /** + * The constant '31' is the result of folding this code: + *
+     *   
+     *     final int prime = 31;
+     *     int result = 1;
+     *     result = result * prime + Objects.hashCode(obj);
+     *     return result;
+     *   
+     * 
+ * 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 @Nullable Class checkFieldCastIdentity( - final @NonNull Class 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) 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 @Nullable List> checkListFieldCastIdentity( - final @NonNull Class requiredClass, final @NonNull String fieldName, final @Nullable List list) { - checkCollectionFieldIdentity(requiredClass, fieldName, list); - return (List>) list; - } - /** * Utility method for checking whether the items of target set is compatible. * @@ -459,23 +436,6 @@ public final class CodeHelpers { return (Set) 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 @Nullable Set> checkSetFieldCastIdentity( - final @NonNull Class requiredClass, final @NonNull String fieldName, final @Nullable Set set) { - checkCollectionFieldIdentity(requiredClass, fieldName, set); - return (Set>) 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: - *
-     *   
-     *     final int prime = 31;
-     *     int result = 1;
-     *     result = result * prime + Objects.hashCode(obj);
-     *     return result;
-     *   
-     * 
- * 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; - } } diff --git a/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/DataContainer.java b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/DataContainer.java index 82b32d60e7..78983fe810 100644 --- a/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/DataContainer.java +++ b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/DataContainer.java @@ -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: *
    - *
  • Rpc Input - *
  • Output - *
  • Notification - *
  • Container - *
  • List - *
  • Case + *
  • Rpc Input
  • + *
  • Output
  • + *
  • Notification
  • + *
  • Container
  • + *
  • List
  • + *
  • Case
  • *
*/ -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 implementedInterface(); +public interface DataContainer extends BindingContract { + } diff --git a/binding/yang-binding/src/test/java/org/opendaylight/yangtools/yang/binding/CodeHelpersTest.java b/binding/yang-binding/src/test/java/org/opendaylight/yangtools/yang/binding/CodeHelpersTest.java index 102ce23fcb..a3d81ea2d2 100644 --- a/binding/yang-binding/src/test/java/org/opendaylight/yangtools/yang/binding/CodeHelpersTest.java +++ b/binding/yang-binding/src/test/java/org/opendaylight/yangtools/yang/binding/CodeHelpersTest.java @@ -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 { - - } }