2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.mdsal.binding.dom.codec.impl;
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.cache.Cache;
14 import com.google.common.cache.CacheBuilder;
15 import com.google.common.collect.ImmutableBiMap;
16 import com.google.common.collect.Maps;
17 import java.util.Arrays;
20 import java.util.concurrent.ExecutionException;
21 import java.util.stream.Collectors;
22 import org.eclipse.jdt.annotation.NonNull;
23 import org.opendaylight.yangtools.yang.binding.EnumTypeObject;
24 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
25 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
29 final class EnumerationCodec extends SchemaUnawareCodec {
30 private static final Logger LOG = LoggerFactory.getLogger(EnumerationCodec.class);
32 * Use identity comparison for keys and allow classes to be GCd themselves.
34 * Since codecs can (and typically do) hold a direct or indirect strong reference to the class, they need to be also
35 * accessed via reference. Using a weak reference could be problematic, because the codec would quite often be only
36 * weakly reachable. We therefore use a soft reference, whose implementation guidance is suitable to our use case:
38 * "Virtual machine implementations are, however, encouraged to bias against clearing recently-created or
39 * recently-used soft references."
41 private static final Cache<Class<?>, @NonNull EnumerationCodec> CACHE = CacheBuilder.newBuilder().weakKeys()
42 .softValues().build();
44 private final ImmutableBiMap<String, Enum<?>> nameToEnum;
45 private final Class<? extends Enum<?>> enumClass;
47 private EnumerationCodec(final Class<? extends Enum<?>> enumClass, final Map<String, Enum<?>> nameToEnum) {
48 this.enumClass = requireNonNull(enumClass);
49 this.nameToEnum = ImmutableBiMap.copyOf(nameToEnum);
52 static @NonNull EnumerationCodec of(final Class<?> returnType, final EnumTypeDefinition def)
53 throws ExecutionException {
54 return CACHE.get(returnType, () -> {
55 final Class<? extends Enum<?>> enumType = castType(returnType);
57 final Map<String, Enum<?>> mapping = Maps.uniqueIndex(Arrays.asList(enumType.getEnumConstants()),
59 checkArgument(value instanceof EnumTypeObject,
60 "Enumeration constant %s.%s is not implementing EnumTypeObject", enumType.getName(), value);
61 return ((EnumTypeObject) value).getName();
64 // Check if mapping is a bijection
65 final Set<String> assignedNames = def.getValues().stream().map(EnumPair::getName)
66 .collect(Collectors.toSet());
67 for (String name : assignedNames) {
68 if (!mapping.containsKey(name)) {
69 LOG.warn("Enumeration {} does not contain assigned name '{}' from {}", enumType, name, def);
72 for (String name : mapping.keySet()) {
73 if (!assignedNames.contains(name)) {
74 LOG.warn("Enumeration {} contains assigned name '{}' not covered by {}", enumType, name, def);
78 return new EnumerationCodec(enumType, mapping);
82 @SuppressWarnings("unchecked")
83 private static Class<? extends Enum<?>> castType(final Class<?> returnType) {
84 checkArgument(Enum.class.isAssignableFrom(returnType));
85 return (Class<? extends Enum<?>>) returnType;
89 protected Enum<?> deserializeImpl(final Object input) {
90 checkArgument(input instanceof String, "Input %s is not a String", input);
91 final Enum<?> value = nameToEnum.get(input);
92 checkArgument(value != null, "Invalid enumeration value %s. Valid values are %s", input, nameToEnum.keySet());
97 protected String serializeImpl(final Object input) {
98 checkArgument(enumClass.isInstance(input), "Input %s is not a instance of %s", input, enumClass);
99 // FIXME: verifyNotNull here
100 return requireNonNull(nameToEnum.inverse().get(input));