5d8445e27935a889c22ff9cf075a0ce6cd5ec683
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / KeyedListNodeCodecContext.java
1 /*
2  * Copyright (c) 2014 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 import static org.opendaylight.mdsal.binding.spec.naming.BindingMapping.IDENTIFIABLE_KEY_NAME;
12
13 import com.google.common.collect.ImmutableMap;
14 import com.google.common.collect.ImmutableMap.Builder;
15 import java.lang.invoke.MethodHandle;
16 import java.lang.invoke.MethodHandles;
17 import java.lang.invoke.MethodType;
18 import java.lang.invoke.WrongMethodTypeException;
19 import java.lang.reflect.Method;
20 import java.util.List;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.opendaylight.yangtools.yang.binding.DataObject;
23 import org.opendaylight.yangtools.yang.binding.Identifiable;
24 import org.opendaylight.yangtools.yang.binding.Identifier;
25 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
26 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
27 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
29 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
31 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
32
33 abstract class KeyedListNodeCodecContext<D extends DataObject & Identifiable<?>> extends ListNodeCodecContext<D> {
34     private static final class Ordered<D extends DataObject & Identifiable<?>> extends KeyedListNodeCodecContext<D> {
35         Ordered(final DataContainerCodecPrototype<ListSchemaNode> prototype, final Method keyMethod,
36                 final IdentifiableItemCodec codec) {
37             super(prototype, keyMethod, codec);
38         }
39     }
40
41     private static final class Unordered<D extends DataObject & Identifiable<?>> extends KeyedListNodeCodecContext<D> {
42         private static final MethodType KEY_TYPE = MethodType.methodType(Object.class, DataObject.class);
43
44         private final MethodHandle keyHandle;
45
46         Unordered(final DataContainerCodecPrototype<ListSchemaNode> prototype, final Method keyMethod,
47                 final IdentifiableItemCodec codec) {
48             super(prototype, keyMethod, codec);
49
50             try {
51                 this.keyHandle = MethodHandles.publicLookup().unreflect(keyMethod).asType(KEY_TYPE);
52             } catch (IllegalAccessException | WrongMethodTypeException e) {
53                 throw new LinkageError("Failed to acquire method " + keyMethod, e);
54             }
55         }
56
57         @Override
58         Object fromMap(final MapNode map, final int size) {
59             // FIXME: MDSAL-539: Make this a lazily-populated map
60             final Builder<Object, D> builder = ImmutableMap.builderWithExpectedSize(size);
61             for (MapEntryNode node : map.getValue()) {
62                 final D entry = fromMapEntry(node);
63                 builder.put(getKey(entry), entry);
64             }
65             return builder.build();
66         }
67
68         @SuppressWarnings("checkstyle:illegalCatch")
69         private Object getKey(final D entry) {
70             try {
71                 return keyHandle.invokeExact(entry);
72             } catch (Throwable e) {
73                 throw new LinkageError("Failed to extract key from " + entry, e);
74             }
75         }
76     }
77
78     private final IdentifiableItemCodec codec;
79
80     KeyedListNodeCodecContext(final DataContainerCodecPrototype<ListSchemaNode> prototype,
81             final Method keyMethod, final IdentifiableItemCodec codec) {
82         super(prototype, keyMethod);
83         this.codec = requireNonNull(codec);
84     }
85
86     @SuppressWarnings("rawtypes")
87     static KeyedListNodeCodecContext create(final DataContainerCodecPrototype<ListSchemaNode> prototype) {
88         final Class<?> bindingClass = prototype.getBindingClass();
89         final Method keyMethod;
90         try {
91             keyMethod = bindingClass.getMethod(IDENTIFIABLE_KEY_NAME);
92         } catch (NoSuchMethodException e) {
93             throw new IllegalStateException("Required method not available", e);
94         }
95
96         final ListSchemaNode schema = prototype.getSchema();
97         final IdentifiableItemCodec codec = prototype.getFactory().getPathArgumentCodec(bindingClass, schema);
98         return schema.isUserOrdered() ? new Ordered<>(prototype, keyMethod, codec)
99                 : new Unordered<>(prototype, keyMethod, codec);
100     }
101
102     @Override
103     protected void addYangPathArgument(final InstanceIdentifier.PathArgument arg,
104             final List<YangInstanceIdentifier.PathArgument> builder) {
105         /*
106          * DOM Instance Identifier for list is always represent by two entries one for map and one for children. This
107          * is also true for wildcarded instance identifiers
108          */
109         if (builder == null) {
110             return;
111         }
112
113         super.addYangPathArgument(arg, builder);
114         if (arg instanceof IdentifiableItem) {
115             builder.add(codec.serialize((IdentifiableItem<?, ?>) arg));
116         } else {
117             // Adding wildcarded
118             super.addYangPathArgument(arg, builder);
119         }
120     }
121
122     @Override
123     protected InstanceIdentifier.PathArgument getBindingPathArgument(final YangInstanceIdentifier.PathArgument domArg) {
124         if (domArg instanceof NodeIdentifierWithPredicates) {
125             return codec.deserialize((NodeIdentifierWithPredicates) domArg);
126         }
127         return super.getBindingPathArgument(domArg);
128     }
129
130     @SuppressWarnings({ "rawtypes", "unchecked" })
131     NodeIdentifierWithPredicates serialize(final Identifier<?> key) {
132         return codec.serialize(IdentifiableItem.of((Class)getBindingClass(), (Identifier)key));
133     }
134
135     @NonNull Identifier<?> deserialize(final NodeIdentifierWithPredicates arg) {
136         return codec.deserializeIdentifier(arg);
137     }
138
139     @Override
140     public YangInstanceIdentifier.PathArgument serializePathArgument(final InstanceIdentifier.PathArgument arg) {
141         if (arg instanceof IdentifiableItem) {
142             return codec.serialize((IdentifiableItem<?, ?>) arg);
143         }
144         return super.serializePathArgument(arg);
145     }
146
147     @Override
148     public InstanceIdentifier.PathArgument deserializePathArgument(final YangInstanceIdentifier.PathArgument arg) {
149         if (arg instanceof NodeIdentifierWithPredicates) {
150             return codec.deserialize((NodeIdentifierWithPredicates) arg);
151         }
152         return super.deserializePathArgument(arg);
153     }
154 }