Improve EnumerationCodec
authorRobert Varga <robert.varga@pantheon.tech>
Sun, 11 Mar 2018 15:25:40 +0000 (16:25 +0100)
committerAnil Belur <abelur@linuxfoundation.org>
Wed, 19 Jun 2024 00:41:22 +0000 (10:41 +1000)
Now that we have proper information from enumerations, we can use
build the codec map as known at generation time and check it against
the type we are receiving, adding proper warnings.

JIRA: MDSAL-317
Change-Id: Ia5bd3d73e44eba2568818b578bc44985fafb6294
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/EnumerationCodec.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/mdsal/binding/dom/codec/impl/EnumerationCodecTest.java

index ebe9c7d5d5c399d2f294a3c6da231138fb61da54..2e16b0c616c0144b0ceee188b6ae47f0d4b90388 100644 (file)
@@ -8,58 +8,73 @@
 package org.opendaylight.mdsal.binding.dom.codec.impl;
 
 import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
 import static java.util.Objects.requireNonNull;
 
-import com.google.common.collect.BiMap;
 import com.google.common.collect.ImmutableBiMap;
-import com.google.common.collect.ImmutableBiMap.Builder;
+import com.google.common.collect.Maps;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.Callable;
 import java.util.stream.Collectors;
 import org.opendaylight.mdsal.binding.dom.codec.impl.ValueTypeCodec.SchemaUnawareCodec;
-import org.opendaylight.yangtools.yang.binding.BindingMapping;
+import org.opendaylight.yangtools.yang.binding.Enumeration;
 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 final class EnumerationCodec extends ReflectionBasedCodec implements SchemaUnawareCodec {
-    private final ImmutableBiMap<String, Enum<?>> yangValueToBinding;
+    private static final Logger LOG = LoggerFactory.getLogger(EnumerationCodec.class);
 
-    EnumerationCodec(final Class<? extends Enum<?>> enumeration, final ImmutableBiMap<String, Enum<?>> schema) {
+    private final ImmutableBiMap<String, Enum<?>> nameToEnum;
+
+    EnumerationCodec(final Class<? extends Enum<?>> enumeration, final Map<String, Enum<?>> nameToEnum) {
         super(enumeration);
-        yangValueToBinding = requireNonNull(schema);
+        this.nameToEnum = ImmutableBiMap.copyOf(nameToEnum);
     }
 
-    static Callable<EnumerationCodec> loader(final Class<?> returnType, final EnumTypeDefinition enumSchema) {
+    static Callable<EnumerationCodec> loader(final Class<?> returnType, final EnumTypeDefinition def) {
         checkArgument(Enum.class.isAssignableFrom(returnType));
-        @SuppressWarnings({ "rawtypes", "unchecked" })
-        final Class<? extends Enum<?>> enumType = (Class) returnType;
+        @SuppressWarnings("unchecked")
+        final Class<? extends Enum<?>> enumType = (Class<? extends Enum<?>>) returnType;
         return () -> {
-            final BiMap<String, String> identifierToYang = BindingMapping.mapEnumAssignedNames(
-                enumSchema.getValues().stream().map(EnumPair::getName).collect(Collectors.toList())).inverse();
+            final Map<String, Enum<?>> mapping = Maps.uniqueIndex(Arrays.asList(enumType.getEnumConstants()),
+                value -> {
+                    checkArgument(value instanceof Enumeration,
+                        "Enumeration constant %s.%s is not implementing Enumeration", enumType.getName(), value);
+                    return ((Enumeration) value).getName();
+                });
 
-            final Builder<String, Enum<?>> builder = ImmutableBiMap.builder();
-            for (Enum<?> enumValue : enumType.getEnumConstants()) {
-                final String yangName = identifierToYang.get(enumValue.name());
-                checkState(yangName != null, "Failed to find enumeration constant %s in mapping %s", enumValue,
-                        identifierToYang);
-                builder.put(yangName, enumValue);
+            // Check if mapping is a bijection
+            final Set<String> assignedNames =  def.getValues().stream().map(EnumPair::getName)
+                    .collect(Collectors.toSet());
+            for (String name : assignedNames) {
+                if (!mapping.containsKey(name)) {
+                    LOG.warn("Enumeration {} does not contain assigned name '{}' from {}", enumType, name, def);
+                }
+            }
+            for (String name : mapping.keySet()) {
+                if (!assignedNames.contains(name)) {
+                    LOG.warn("Enumeration {} contains assigned name '{}' not covered by {}", enumType, name, def);
+                }
             }
 
-            return new EnumerationCodec(enumType, builder.build());
+            return new EnumerationCodec(enumType, mapping);
         };
     }
 
     @Override
-    public Object deserialize(final Object input) {
-        Enum<?> value = yangValueToBinding.get(input);
-        checkArgument(value != null, "Invalid enumeration value %s. Valid values are %s", input,
-                yangValueToBinding.keySet());
+    public Enum<?> deserialize(final Object input) {
+        checkArgument(input instanceof String, "Input %s is not a String", input);
+        final Enum<?> value = nameToEnum.get(input);
+        checkArgument(value != null, "Invalid enumeration value %s. Valid values are %s", input, nameToEnum.keySet());
         return value;
     }
 
     @Override
-    public Object serialize(final Object input) {
-        checkArgument(getTypeClass().isInstance(input), "Input must be instance of %s", getTypeClass());
-        return yangValueToBinding.inverse().get(input);
+    public String serialize(final Object input) {
+        checkArgument(getTypeClass().isInstance(input), "Input %s is not a instance of %s", input, getTypeClass());
+        return requireNonNull(nameToEnum.inverse().get(input));
     }
 }
\ No newline at end of file
index 195cf01f66304ab2bc1a68577ffc72f0ab505910..9b05d53a542c594c2a7e9e3624194037deee9f29 100644 (file)
@@ -13,13 +13,24 @@ import static org.mockito.Mockito.mock;
 
 import com.google.common.collect.ImmutableList;
 import org.junit.Test;
+import org.opendaylight.yangtools.yang.binding.Enumeration;
 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
 
 public class EnumerationCodecTest {
 
-    private enum TestEnum {
-        ENUM
+    private enum TestEnum implements Enumeration {
+        ENUM;
+
+        @Override
+        public String getName() {
+            return "ENUM";
+        }
+
+        @Override
+        public int getIntValue() {
+            return 0;
+        }
     }
 
     @Test