import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableBiMap;
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.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.Enumeration;
import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
final class EnumerationCodec extends ReflectionBasedCodec implements SchemaUnawareCodec {
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<Class<?>, @NonNull EnumerationCodec> CACHE = CacheBuilder.newBuilder().weakKeys()
+ .softValues().build();
private final ImmutableBiMap<String, Enum<?>> nameToEnum;
- EnumerationCodec(final Class<? extends Enum<?>> enumeration, final Map<String, Enum<?>> nameToEnum) {
+ private EnumerationCodec(final Class<? extends Enum<?>> enumeration, final Map<String, Enum<?>> nameToEnum) {
super(enumeration);
this.nameToEnum = ImmutableBiMap.copyOf(nameToEnum);
}
- static Callable<EnumerationCodec> loader(final Class<?> returnType, final EnumTypeDefinition def) {
- checkArgument(Enum.class.isAssignableFrom(returnType));
- @SuppressWarnings("unchecked")
- final Class<? extends Enum<?>> enumType = (Class<? extends Enum<?>>) returnType;
- return () -> {
+ static @NonNull EnumerationCodec of(final Class<?> returnType, final EnumTypeDefinition def)
+ throws ExecutionException {
+ return CACHE.get(returnType, () -> {
+ final Class<? extends Enum<?>> enumType = castType(returnType);
+
final Map<String, Enum<?>> mapping = Maps.uniqueIndex(Arrays.asList(enumType.getEnumConstants()),
value -> {
checkArgument(value instanceof Enumeration,
}
return new EnumerationCodec(enumType, mapping);
- };
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Class<? extends Enum<?>> castType(final Class<?> returnType) {
+ checkArgument(Enum.class.isAssignableFrom(returnType));
+ return (Class<? extends Enum<?>>) returnType;
}
@Override