334a20415b5bc4c4c53b5b0011fc613a3596d140
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / yangtools / binding / data / codec / impl / BindingNormalizedNodeCodecRegistry.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.yangtools.binding.data.codec.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.cache.CacheBuilder;
14 import com.google.common.cache.CacheLoader;
15 import com.google.common.cache.LoadingCache;
16 import java.io.IOException;
17 import java.util.AbstractMap.SimpleEntry;
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.Map.Entry;
21 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
22 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeFactory;
23 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
24 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeWriterFactory;
25 import org.opendaylight.yangtools.binding.data.codec.gen.impl.DataObjectSerializerGenerator;
26 import org.opendaylight.yangtools.concepts.Delegator;
27 import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext;
28 import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext;
29 import org.opendaylight.yangtools.yang.binding.BindingStreamEventWriter;
30 import org.opendaylight.yangtools.yang.binding.DataContainer;
31 import org.opendaylight.yangtools.yang.binding.DataObject;
32 import org.opendaylight.yangtools.yang.binding.DataObjectSerializer;
33 import org.opendaylight.yangtools.yang.binding.DataObjectSerializerImplementation;
34 import org.opendaylight.yangtools.yang.binding.DataObjectSerializerRegistry;
35 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
36 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
37 import org.opendaylight.yangtools.yang.binding.Notification;
38 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
39 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
40 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
41 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
42 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
43 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
44 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
45 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
46 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
47 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
48 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
49 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
50 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
51 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
52 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56 public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerRegistry,
57         BindingCodecTreeFactory, BindingNormalizedNodeWriterFactory,
58         BindingNormalizedNodeSerializer {
59     private static final Logger LOG = LoggerFactory.getLogger(BindingNormalizedNodeCodecRegistry.class);
60
61     private final DataObjectSerializerGenerator generator;
62     private final LoadingCache<Class<? extends DataObject>, DataObjectSerializer> serializers;
63     private volatile BindingCodecContext codecContext;
64
65     public BindingNormalizedNodeCodecRegistry(final DataObjectSerializerGenerator generator) {
66         this.generator = Preconditions.checkNotNull(generator);
67         this.serializers = CacheBuilder.newBuilder().weakKeys().build(new GeneratorLoader());
68     }
69
70     @Override
71     public DataObjectSerializer getSerializer(final Class<? extends DataObject> type) {
72         return serializers.getUnchecked(type);
73     }
74
75     public BindingCodecTree getCodecContext() {
76         return codecContext;
77     }
78
79     public void onBindingRuntimeContextUpdated(final BindingRuntimeContext context) {
80         codecContext = create(context);
81         generator.onBindingRuntimeContextUpdated(context);
82     }
83
84
85     @Override
86     public YangInstanceIdentifier toYangInstanceIdentifier(final InstanceIdentifier<?> binding) {
87         return codecContext.getInstanceIdentifierCodec().serialize(binding);
88     }
89
90     @Override
91     public InstanceIdentifier<?> fromYangInstanceIdentifier(final YangInstanceIdentifier dom) {
92         return codecContext.getInstanceIdentifierCodec().deserialize(dom);
93     }
94
95     @Override
96     public <T extends DataObject> Entry<YangInstanceIdentifier,NormalizedNode<?,?>> toNormalizedNode(final InstanceIdentifier<T> path, final T data) {
97         final NormalizedNodeResult result = new NormalizedNodeResult();
98         // We create DOM stream writer which produces normalized nodes
99         final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
100
101         // We create Binding Stream Writer which translates from Binding to Normalized Nodes
102         final Entry<YangInstanceIdentifier, BindingStreamEventWriter> writeCtx = codecContext.newWriter(path, domWriter);
103
104         // We get serializer which reads binding data and uses Binding To Normalized Node writer to write result
105         try {
106             getSerializer(path.getTargetType()).serialize(data, writeCtx.getValue());
107         } catch (final IOException e) {
108             LOG.error("Unexpected failure while serializing path {} data {}", path, data, e);
109             throw new IllegalStateException("Failed to create normalized node", e);
110         }
111         return new SimpleEntry<YangInstanceIdentifier,NormalizedNode<?,?>>(writeCtx.getKey(),result.getResult());
112     }
113
114     @Override
115     public ContainerNode toNormalizedNodeNotification(final Notification data) {
116         final NormalizedNodeResult result = new NormalizedNodeResult();
117         // We create DOM stream writer which produces normalized nodes
118         final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
119         @SuppressWarnings({ "unchecked", "rawtypes" })
120         final Class<? extends DataObject> type = (Class) data.getImplementedInterface();
121         @SuppressWarnings({ "unchecked", "rawtypes" })
122         final BindingStreamEventWriter writer = newNotificationWriter((Class) type, domWriter);
123         try {
124             // FIXME: Should be cast to DataObject necessary?
125             getSerializer(type).serialize((DataObject) data, writer);
126         } catch (final IOException e) {
127             LOG.error("Unexpected failure while serializing data {}", data, e);
128             throw new IllegalStateException("Failed to create normalized node", e);
129         }
130         return (ContainerNode) result.getResult();
131
132     }
133
134     @Override
135     public ContainerNode toNormalizedNodeRpcData(final DataContainer data) {
136         final NormalizedNodeResult result = new NormalizedNodeResult();
137         // We create DOM stream writer which produces normalized nodes
138         final NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result);
139         @SuppressWarnings({ "unchecked", "rawtypes" })
140         final Class<? extends DataObject> type = (Class) data.getImplementedInterface();
141         final BindingStreamEventWriter writer = newRpcWriter(type, domWriter);
142         try {
143             // FIXME: Should be cast to DataObject necessary?
144             getSerializer(type).serialize((DataObject) data, writer);
145         } catch (final IOException e) {
146             LOG.error("Unexpected failure while serializing data {}", data, e);
147             throw new IllegalStateException("Failed to create normalized node", e);
148         }
149         return (ContainerNode) result.getResult();
150     }
151
152     private static boolean isBindingRepresentable(final NormalizedNode<?, ?> data) {
153         if (data instanceof ChoiceNode) {
154             return false;
155         }
156         if (data instanceof LeafNode<?>) {
157             return false;
158         }
159         if (data instanceof LeafSetNode) {
160             return false;
161         }
162         if( data instanceof LeafSetEntryNode<?>) {
163             return false;
164         }
165         if (data instanceof MapNode) {
166             return false;
167         }
168         if (data instanceof UnkeyedListNode) {
169             return false;
170         }
171
172         return true;
173     }
174
175     @Override
176     public Entry<InstanceIdentifier<?>, DataObject> fromNormalizedNode(final YangInstanceIdentifier path, final NormalizedNode<?, ?> data) {
177         if (!isBindingRepresentable(data)) {
178             return null;
179         }
180
181         final List<PathArgument> builder = new ArrayList<>();
182         final NodeCodecContext<?> codec = codecContext.getCodecContextNode(path, builder);
183         if (codec == null) {
184             if (data != null) {
185                 LOG.warn("Path {} does not have a binding equivalent, should have been caught earlier ({})", path, data.getClass());
186             }
187             return null;
188         }
189
190         final DataObject lazyObj = codec.deserialize(data);
191         final InstanceIdentifier<?> bindingPath = InstanceIdentifier.create(builder);
192         return new SimpleEntry<InstanceIdentifier<?>, DataObject>(bindingPath, lazyObj);
193     }
194
195     @Override
196     public Notification fromNormalizedNodeNotification(final SchemaPath path, final ContainerNode data) {
197         final NotificationCodecContext<?> codec = codecContext.getNotificationContext(path);
198         return codec.deserialize(data);
199     }
200
201     @Override
202     public DataObject fromNormalizedNodeRpcData(final SchemaPath path, final ContainerNode data) {
203         final ContainerNodeCodecContext<?> codec = codecContext.getRpcDataContext(path);
204         return codec.deserialize(data);
205     }
206
207    @Override
208     public Entry<YangInstanceIdentifier, BindingStreamEventWriter> newWriterAndIdentifier(final InstanceIdentifier<?> path, final NormalizedNodeStreamWriter domWriter) {
209         return codecContext.newWriter(path, domWriter);
210     }
211
212     @Override
213     public BindingStreamEventWriter newWriter(final InstanceIdentifier<?> path, final NormalizedNodeStreamWriter domWriter) {
214         return codecContext.newWriterWithoutIdentifier(path, domWriter);
215     }
216
217     @Override
218     public BindingStreamEventWriter newNotificationWriter(final Class<? extends Notification> notification,
219             final NormalizedNodeStreamWriter streamWriter) {
220         return codecContext.newNotificationWriter(notification, streamWriter);
221     }
222
223     @Override
224     public BindingStreamEventWriter newRpcWriter(final Class<? extends DataContainer> rpcInputOrOutput,
225             final NormalizedNodeStreamWriter streamWriter) {
226         return codecContext.newRpcWriter(rpcInputOrOutput,streamWriter);
227     }
228
229     public <T extends DataObject> Function<Optional<NormalizedNode<?, ?>>, Optional<T>>  deserializeFunction(final InstanceIdentifier<T> path) {
230         final DataObjectCodecContext<?,?> ctx = (DataObjectCodecContext<?,?>) codecContext.getCodecContextNode(path, null);
231         return new DeserializeFunction<T>(ctx);
232     }
233
234     @Override
235     public BindingCodecContext create(final BindingRuntimeContext context) {
236         return new BindingCodecContext(context, this);
237     }
238
239     @Override
240     public BindingCodecContext create(final SchemaContext context, final Class<?>... bindingClasses) {
241         final ModuleInfoBackedContext strategy = ModuleInfoBackedContext.create();
242         for (final Class<?> bindingCls : bindingClasses) {
243             try {
244                 strategy.registerModuleInfo(BindingReflections.getModuleInfo(bindingCls));
245             } catch (final Exception e) {
246                 throw new IllegalStateException(
247                         "Could not create BindingRuntimeContext from class " + bindingCls.getName(),
248                         e);
249             }
250         }
251         final BindingRuntimeContext runtimeCtx = BindingRuntimeContext.create(strategy, context);
252         return create(runtimeCtx);
253     }
254
255
256     private static final class DeserializeFunction<T> implements Function<Optional<NormalizedNode<?, ?>>, Optional<T>> {
257         private final DataObjectCodecContext<?,?> ctx;
258
259         DeserializeFunction(final DataObjectCodecContext<?,?> ctx) {
260             this.ctx = ctx;
261         }
262
263         @SuppressWarnings("unchecked")
264         @Override
265         public Optional<T> apply(final Optional<NormalizedNode<?, ?>> input) {
266             if (input.isPresent()) {
267                 return Optional.of((T) ctx.deserialize(input.get()));
268             }
269             return Optional.absent();
270         }
271     }
272
273     private final class GeneratorLoader extends CacheLoader<Class<? extends DataContainer>, DataObjectSerializer> {
274         @Override
275         public DataObjectSerializer load(final Class<? extends DataContainer> key) throws Exception {
276             final DataObjectSerializerImplementation prototype = generator.getSerializer(key);
277             return new DataObjectSerializerProxy(prototype);
278         }
279     }
280
281     private final class DataObjectSerializerProxy implements DataObjectSerializer, Delegator<DataObjectSerializerImplementation> {
282         private final DataObjectSerializerImplementation delegate;
283
284         DataObjectSerializerProxy(final DataObjectSerializerImplementation delegate) {
285             this.delegate = delegate;
286         }
287
288         @Override
289         public DataObjectSerializerImplementation getDelegate() {
290             return delegate;
291         }
292
293         @Override
294         public void serialize(final DataObject obj, final BindingStreamEventWriter stream) throws IOException {
295             delegate.serialize(BindingNormalizedNodeCodecRegistry.this, obj, stream);
296         }
297     }
298
299 }