b727e5317b087cb7784cb83be78e596dc397698e
[controller.git] / opendaylight / md-sal / sal-binding-broker / src / main / java / org / opendaylight / controller / md / sal / binding / impl / BindingToNormalizedNodeCodec.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.controller.md.sal.binding.impl;
9
10 import com.google.common.base.Function;
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.collect.ImmutableBiMap;
14 import java.lang.reflect.Method;
15 import java.util.AbstractMap.SimpleEntry;
16 import java.util.Iterator;
17 import java.util.Map;
18 import java.util.Map.Entry;
19 import javax.annotation.Nonnull;
20 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
21 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
22 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
23 import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTree;
24 import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeFactory;
25 import org.opendaylight.yangtools.binding.data.codec.api.BindingCodecTreeNode;
26 import org.opendaylight.yangtools.binding.data.codec.api.BindingNormalizedNodeSerializer;
27 import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
28 import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy;
29 import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext;
30 import org.opendaylight.yangtools.yang.binding.BindingMapping;
31 import org.opendaylight.yangtools.yang.binding.DataContainer;
32 import org.opendaylight.yangtools.yang.binding.DataObject;
33 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
34 import org.opendaylight.yangtools.yang.binding.Notification;
35 import org.opendaylight.yangtools.yang.binding.RpcService;
36 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
37 import org.opendaylight.yangtools.yang.common.QNameModule;
38 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
39 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
40 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
41 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
42 import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException;
43 import org.opendaylight.yangtools.yang.model.api.Module;
44 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
45 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
46 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
47 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
48
49 public class BindingToNormalizedNodeCodec implements BindingCodecTreeFactory, BindingNormalizedNodeSerializer, SchemaContextListener, AutoCloseable {
50
51     private final BindingNormalizedNodeCodecRegistry codecRegistry;
52     private DataNormalizer legacyToNormalized;
53     private final GeneratedClassLoadingStrategy classLoadingStrategy;
54     private BindingRuntimeContext runtimeContext;
55
56     public BindingToNormalizedNodeCodec(final GeneratedClassLoadingStrategy classLoadingStrategy,
57             final BindingNormalizedNodeCodecRegistry codecRegistry) {
58         this.classLoadingStrategy = classLoadingStrategy;
59         this.codecRegistry = codecRegistry;
60
61     }
62
63     public YangInstanceIdentifier toNormalized(final InstanceIdentifier<? extends DataObject> binding) {
64         return codecRegistry.toYangInstanceIdentifier(binding);
65     }
66
67     @Override
68     public YangInstanceIdentifier toYangInstanceIdentifier(final InstanceIdentifier<?> binding) {
69         return codecRegistry.toYangInstanceIdentifier(binding);
70     }
71
72     @Override
73     public <T extends DataObject> Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
74             final InstanceIdentifier<T> path, final T data) {
75         return codecRegistry.toNormalizedNode(path, data);
76     }
77
78     @SuppressWarnings({"unchecked", "rawtypes"})
79     public Entry<YangInstanceIdentifier, NormalizedNode<?, ?>> toNormalizedNode(
80             final Entry<InstanceIdentifier<? extends DataObject>, DataObject> binding) {
81         return toNormalizedNode((InstanceIdentifier) binding.getKey(),binding.getValue());
82     }
83
84     @Override
85     public Entry<InstanceIdentifier<?>, DataObject> fromNormalizedNode(final YangInstanceIdentifier path,
86             final NormalizedNode<?, ?> data) {
87         return codecRegistry.fromNormalizedNode(path, data);
88     }
89
90     @Override
91     public Notification fromNormalizedNodeNotification(final SchemaPath path, final ContainerNode data) {
92         return codecRegistry.fromNormalizedNodeNotification(path, data);
93     }
94
95     @Override
96     public DataObject fromNormalizedNodeRpcData(final SchemaPath path, final ContainerNode data) {
97         return codecRegistry.fromNormalizedNodeRpcData(path, data);
98     }
99
100     @Override
101     public InstanceIdentifier<?> fromYangInstanceIdentifier(final YangInstanceIdentifier dom) {
102         return codecRegistry.fromYangInstanceIdentifier(dom);
103     }
104
105     @Override
106     public ContainerNode toNormalizedNodeNotification(final Notification data) {
107         return codecRegistry.toNormalizedNodeNotification(data);
108     }
109
110     @Override
111     public ContainerNode toNormalizedNodeRpcData(final DataContainer data) {
112         return codecRegistry.toNormalizedNodeRpcData(data);
113     }
114
115     /**
116      *
117      * Returns a Binding-Aware instance identifier from normalized
118      * instance-identifier if it is possible to create representation.
119      *
120      * Returns Optional.absent for cases where target is mixin node except
121      * augmentation.
122      *
123      */
124     public Optional<InstanceIdentifier<? extends DataObject>> toBinding(final YangInstanceIdentifier normalized)
125                     throws DeserializationException {
126         try {
127             return Optional.<InstanceIdentifier<? extends DataObject>>fromNullable(codecRegistry.fromYangInstanceIdentifier(normalized));
128         } catch (final IllegalArgumentException e) {
129             return Optional.absent();
130         }
131     }
132
133     public DataNormalizer getDataNormalizer() {
134         return legacyToNormalized;
135     }
136
137     public Optional<Entry<InstanceIdentifier<? extends DataObject>, DataObject>> toBinding(
138             final @Nonnull Entry<YangInstanceIdentifier, ? extends NormalizedNode<?, ?>> normalized)
139                     throws DeserializationException {
140         try {
141             /*
142              * This cast is required, due to generics behaviour in openjdk / oracle javac
143              *
144              * InstanceIdentifier has definition InstanceIdentifier<T extends DataObject>,
145              * this means '?' is always  <? extends DataObject>. Eclipse compiler
146              * is able to determine this relationship and treats
147              * Entry<InstanceIdentifier<?>,DataObject> and Entry<InstanceIdentifier<? extends DataObject,DataObject>
148              * as assignable. However openjdk / oracle javac treats this two types
149              * as incompatible and issues a compile error.
150              *
151              * It is safe to  loose generic information and cast it to other generic signature.
152              *
153              */
154             @SuppressWarnings({ "unchecked", "rawtypes" })
155             final Entry<InstanceIdentifier<? extends DataObject>, DataObject> binding = Entry.class.cast(codecRegistry.fromNormalizedNode(normalized.getKey(), normalized.getValue()));
156             return Optional.fromNullable(binding);
157         } catch (final IllegalArgumentException e) {
158             return Optional.absent();
159         }
160     }
161
162     @Override
163     public void onGlobalContextUpdated(final SchemaContext arg0) {
164         legacyToNormalized = new DataNormalizer (arg0);
165         runtimeContext = BindingRuntimeContext.create(classLoadingStrategy, arg0);
166         codecRegistry.onBindingRuntimeContextUpdated(runtimeContext);
167     }
168
169     public <T extends DataObject> Function<Optional<NormalizedNode<?, ?>>, Optional<T>>  deserializeFunction(final InstanceIdentifier<T> path) {
170         return codecRegistry.deserializeFunction(path);
171     }
172
173     /**
174      * Returns an default object according to YANG schema for supplied path.
175      *
176      * @param path DOM Path
177      * @return Node with defaults set on.
178      */
179     public NormalizedNode<?, ?> getDefaultNodeFor(final YangInstanceIdentifier path) {
180         final Iterator<PathArgument> iterator = path.getPathArguments().iterator();
181         DataNormalizationOperation<?> currentOp = legacyToNormalized.getRootOperation();
182         while (iterator.hasNext()) {
183             final PathArgument currentArg = iterator.next();
184             try {
185                 currentOp = currentOp.getChild(currentArg);
186             } catch (final DataNormalizationException e) {
187                 throw new IllegalArgumentException(String.format("Invalid child encountered in path %s", path), e);
188             }
189         }
190         return currentOp.createDefault(path.getLastPathArgument());
191     }
192
193     public BindingNormalizedNodeCodecRegistry getCodecRegistry() {
194         return codecRegistry;
195     }
196
197     @Override
198     public void close() {
199         // NOOP Intentionally
200     }
201
202     public BindingNormalizedNodeCodecRegistry getCodecFactory() {
203         return codecRegistry;
204     }
205
206     // FIXME: This should be probably part of Binding Runtime context
207     public ImmutableBiMap<Method, SchemaPath> getRpcMethodToSchemaPath(final Class<? extends RpcService> key) {
208         final QNameModule moduleName = BindingReflections.getQNameModule(key);
209         final Module module = runtimeContext.getSchemaContext().findModuleByNamespaceAndRevision(moduleName.getNamespace(), moduleName.getRevision());
210         final ImmutableBiMap.Builder<Method, SchemaPath> ret = ImmutableBiMap.<Method, SchemaPath>builder();
211         try {
212             for (final RpcDefinition rpcDef : module.getRpcs()) {
213                 final Method method = findRpcMethod(key, rpcDef);
214                 ret.put(method, rpcDef.getPath());
215             }
216         } catch (final NoSuchMethodException e) {
217             throw new IllegalStateException("Rpc defined in model does not have representation in generated class.", e);
218         }
219         return ret.build();
220     }
221
222     private Method findRpcMethod(final Class<? extends RpcService> key, final RpcDefinition rpcDef) throws NoSuchMethodException {
223         final String methodName = BindingMapping.getMethodName(rpcDef.getQName());
224         if(rpcDef.getInput() != null) {
225             final Class<?> inputClz = runtimeContext.getClassForSchema(rpcDef.getInput());
226             return key.getMethod(methodName, inputClz);
227         }
228         return key.getMethod(methodName);
229     }
230
231     @Override
232     public BindingCodecTree create(final BindingRuntimeContext context) {
233         return codecRegistry.create(context);
234     }
235
236     @Override
237     public BindingCodecTree create(final SchemaContext context, final Class<?>... bindingClasses) {
238         return codecRegistry.create(context, bindingClasses);
239     }
240
241     @Nonnull protected Map.Entry<InstanceIdentifier<?>, BindingCodecTreeNode<?>> getSubtreeCodec(
242             final YangInstanceIdentifier domIdentifier) {
243
244         final BindingCodecTree currentCodecTree = codecRegistry.getCodecContext();
245         final InstanceIdentifier<?> bindingPath = codecRegistry.fromYangInstanceIdentifier(domIdentifier);
246         Preconditions.checkArgument(bindingPath != null);
247         /**
248          * If we are able to deserialize YANG instance identifier, getSubtreeCodec must
249          * return non-null value.
250          */
251         final BindingCodecTreeNode<?> codecContext = currentCodecTree.getSubtreeCodec(bindingPath);
252         return new SimpleEntry<InstanceIdentifier<?>, BindingCodecTreeNode<?>>(bindingPath, codecContext);
253     }
254
255 }