X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=binding%2Fmdsal-binding-dom-codec%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fmdsal%2Fbinding%2Fdom%2Fcodec%2Fimpl%2FEnumerationCodec.java;h=076ff1935f135b9a014c5c7ff1347ec14cd17393;hb=1a2d7e719b78cd564fe37578cab270372628d096;hp=ebe9c7d5d5c399d2f294a3c6da231138fb61da54;hpb=b1fb22967d05fa21efa4ff2b8a2202cf7a435e9a;p=mdsal.git diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/EnumerationCodec.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/EnumerationCodec.java index ebe9c7d5d5..076ff1935f 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/EnumerationCodec.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/EnumerationCodec.java @@ -8,58 +8,94 @@ 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.cache.Cache; +import com.google.common.cache.CacheBuilder; import com.google.common.collect.ImmutableBiMap; -import com.google.common.collect.ImmutableBiMap.Builder; -import java.util.concurrent.Callable; +import com.google.common.collect.Maps; +import java.util.Arrays; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; +import org.eclipse.jdt.annotation.NonNull; 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> yangValueToBinding; + private static final Logger LOG = LoggerFactory.getLogger(EnumerationCodec.class); + /* + * Use identity comparison for keys and allow classes to be GCd themselves. + * + * Since codecs can (and typically do) hold a direct or indirect strong reference to the class, they need to be also + * accessed via reference. Using a weak reference could be problematic, because the codec would quite often be only + * weakly reachable. We therefore use a soft reference, whose implementation guidance is suitable to our use case: + * + * "Virtual machine implementations are, however, encouraged to bias against clearing recently-created or + * recently-used soft references." + */ + private static final Cache, @NonNull EnumerationCodec> CACHE = CacheBuilder.newBuilder().weakKeys() + .softValues().build(); - EnumerationCodec(final Class> enumeration, final ImmutableBiMap> schema) { + private final ImmutableBiMap> nameToEnum; + + private EnumerationCodec(final Class> enumeration, final Map> nameToEnum) { super(enumeration); - yangValueToBinding = requireNonNull(schema); + this.nameToEnum = ImmutableBiMap.copyOf(nameToEnum); } - static Callable loader(final Class returnType, final EnumTypeDefinition enumSchema) { - checkArgument(Enum.class.isAssignableFrom(returnType)); - @SuppressWarnings({ "rawtypes", "unchecked" }) - final Class> enumType = (Class) returnType; - return () -> { - final BiMap identifierToYang = BindingMapping.mapEnumAssignedNames( - enumSchema.getValues().stream().map(EnumPair::getName).collect(Collectors.toList())).inverse(); + static @NonNull EnumerationCodec of(final Class returnType, final EnumTypeDefinition def) + throws ExecutionException { + return CACHE.get(returnType, () -> { + final Class> enumType = castType(returnType); - final Builder> 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); + final Map> 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(); + }); + + // Check if mapping is a bijection + final Set 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); + }); + } + + @SuppressWarnings("unchecked") + private static Class> castType(final Class returnType) { + checkArgument(Enum.class.isAssignableFrom(returnType)); + return (Class>) returnType; } @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