Map identities to proper objects
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / IdentityCodec.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.mdsal.binding.dom.codec.impl;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.base.Throwables;
13 import com.google.common.cache.CacheBuilder;
14 import com.google.common.cache.CacheLoader;
15 import com.google.common.cache.LoadingCache;
16 import java.lang.reflect.Field;
17 import java.lang.reflect.Modifier;
18 import java.util.concurrent.ExecutionException;
19 import org.eclipse.jdt.annotation.NonNull;
20 import org.opendaylight.mdsal.binding.dom.codec.api.BindingIdentityCodec;
21 import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
22 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
23 import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
24 import org.opendaylight.yangtools.concepts.AbstractIllegalArgumentCodec;
25 import org.opendaylight.yangtools.yang.binding.BaseIdentity;
26 import org.opendaylight.yangtools.yang.common.QName;
27
28 final class IdentityCodec extends AbstractIllegalArgumentCodec<QName, BaseIdentity> implements BindingIdentityCodec {
29     private final LoadingCache<@NonNull QName, @NonNull BaseIdentity> values = CacheBuilder.newBuilder()
30         .build(new CacheLoader<>() {
31             @Override
32             public BaseIdentity load(final QName key) {
33                 final var clazz = context.getIdentityClass(key);
34                 final Field field;
35                 try {
36                     field = clazz.getField(BindingMapping.VALUE_STATIC_FIELD_NAME);
37                 } catch (NoSuchFieldException e) {
38                     throw new LinkageError(clazz + " does not define required field "
39                         + BindingMapping.VALUE_STATIC_FIELD_NAME, e);
40                 }
41                 if (!Modifier.isStatic(field.getModifiers())) {
42                     throw new LinkageError(field + " is not static");
43                 }
44
45                 final Object value;
46                 try {
47                     value = clazz.cast(field.get(null));
48                 } catch (IllegalAccessException e) {
49                     throw new LinkageError(field + " is not accesssible", e);
50                 }
51                 if (value == null) {
52                     throw new LinkageError(field + " is null");
53                 }
54                 try {
55                     return clazz.cast(value);
56                 } catch (ClassCastException e) {
57                     throw new LinkageError(field + " value " + value + " has illegal type", e);
58                 }
59             }
60         });
61
62     private final BindingRuntimeContext context;
63
64     IdentityCodec(final BindingRuntimeContext context) {
65         this.context = requireNonNull(context);
66     }
67
68     @Override
69     protected BaseIdentity deserializeImpl(final QName input) {
70         return toBinding(input);
71     }
72
73     @Override
74     protected QName serializeImpl(final BaseIdentity input) {
75         return fromBinding(input);
76     }
77
78     @Override
79     @SuppressWarnings("unchecked")
80     public <T extends BaseIdentity> T toBinding(final QName qname) {
81         try {
82             return (T) values.get(requireNonNull(qname));
83         } catch (ExecutionException e) {
84             Throwables.throwIfUnchecked(e.getCause());
85             throw new IllegalStateException("Unexpected error translating " + qname, e);
86         }
87     }
88
89     @Override
90     public QName fromBinding(final BaseIdentity bindingValue) {
91         return BindingReflections.getQName(bindingValue.implementedInterface());
92     }
93 }