BUG-592: Rework instance identifier
[yangtools.git] / code-generator / binding-generator-impl / src / main / java / org / opendaylight / yangtools / sal / binding / generator / impl / InstanceIdentifierCodecImpl.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.sal.binding.generator.impl;
9
10 import com.google.common.base.Objects;
11 import com.google.common.collect.ImmutableList;
12 import java.util.ArrayList;
13 import java.util.Arrays;
14 import java.util.Collections;
15 import java.util.HashMap;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Map.Entry;
19 import java.util.Set;
20 import java.util.WeakHashMap;
21 import java.util.concurrent.ConcurrentHashMap;
22 import org.eclipse.xtext.xbase.lib.Functions.Function0;
23 import org.opendaylight.yangtools.concepts.Identifiable;
24 import org.opendaylight.yangtools.sal.binding.generator.impl.CodecTypeUtils;
25 import org.opendaylight.yangtools.yang.binding.Augmentation;
26 import org.opendaylight.yangtools.yang.binding.DataObject;
27 import org.opendaylight.yangtools.yang.binding.Identifier;
28 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
29 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
30 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
31 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
32 import org.opendaylight.yangtools.yang.common.QName;
33 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
34 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
35 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
36 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
37 import org.opendaylight.yangtools.yang.data.api.Node;
38 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
39 import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
40 import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry;
41 import org.opendaylight.yangtools.yang.data.impl.codec.IdentifierCodec;
42 import org.opendaylight.yangtools.yang.data.impl.codec.InstanceIdentifierCodec;
43 import org.opendaylight.yangtools.yang.data.impl.codec.ValueWithQName;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 public class InstanceIdentifierCodecImpl implements InstanceIdentifierCodec {
48     private static final Logger LOG = LoggerFactory.getLogger(InstanceIdentifierCodecImpl.class);
49
50     private final CodecRegistry codecRegistry;
51
52     private final Map<Class<?>, Map<List<QName>, Class<?>>> classToPreviousAugment = Collections
53             .synchronizedMap(new WeakHashMap<Class<?>, Map<List<QName>, Class<?>>>());
54
55     public InstanceIdentifierCodecImpl(final CodecRegistry registry) {
56         this.codecRegistry = registry;
57     }
58
59     @Override
60     public InstanceIdentifier<? extends Object> deserialize(
61             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier input) {
62         Class<?> baType = null;
63         List<org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument> biArgs = input.getPath();
64         List<QName> scannedPath = new ArrayList<>(biArgs.size());
65         List<InstanceIdentifier.PathArgument> baArgs = new ArrayList<InstanceIdentifier.PathArgument>(biArgs.size());
66         for (org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument biArg : biArgs) {
67
68             scannedPath.add(biArg.getNodeType());
69             org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument baArg = deserializePathArgument(
70                     biArg, scannedPath);
71             if (baArg != null) {
72                 baType = baArg.getType();
73             }
74             Map<List<QName>, Class<?>> injectAugment = classToPreviousAugment.get(baType);
75             if (injectAugment != null) {
76                 Class<? extends DataObject> augment = (Class<? extends DataObject>) injectAugment.get(scannedPath);
77                 if (augment != null) {
78                     baArgs.add(new Item(augment));
79                 }
80             }
81             baArgs.add(baArg);
82         }
83         InstanceIdentifier ret = InstanceIdentifier.create(baArgs);
84         LOG.debug("DOM Instance Identifier {} deserialized to {}", input, ret);
85         return ret;
86     }
87
88     private org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument _deserializePathArgument(
89             final NodeIdentifier argument, final List<QName> processedPath) {
90         final Class cls = codecRegistry.getClassForPath(processedPath);
91         Item<DataObject> item = new Item<>(cls);
92         return item;
93     }
94
95     private org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument _deserializePathArgument(
96             final NodeIdentifierWithPredicates argument, final List<QName> processedPath) {
97         final Class type = codecRegistry.getClassForPath(processedPath);
98         final IdentifierCodec codec = codecRegistry
99                 .<Identifiable<? extends Object>> getIdentifierCodecForIdentifiable(type);
100         CompositeNode _compositeNode = this.toCompositeNode(argument);
101         ValueWithQName<CompositeNode> deserialize = codec.deserialize(_compositeNode);
102         Object value = null;
103         if (deserialize != null) {
104             value = deserialize.getValue();
105         }
106         return CodecTypeUtils.newIdentifiableItem(type, value);
107     }
108
109     public CompositeNode toCompositeNode(NodeIdentifierWithPredicates predicates) {
110         Set<Map.Entry<QName, Object>> keyValues = predicates.getKeyValues().entrySet();
111         List<Node<?>> values = new ArrayList<>(keyValues.size());
112         for (Map.Entry<QName, Object> keyValue : keyValues) {
113             values.add(new SimpleNodeTOImpl<Object>(keyValue.getKey(), null, keyValue.getValue()));
114         }
115         return new CompositeNodeTOImpl(predicates.getNodeType(), null, values);
116     }
117
118     @Override
119     public org.opendaylight.yangtools.yang.data.api.InstanceIdentifier serialize(InstanceIdentifier<?> input) {
120         Class<?> previousAugmentation = null;
121         List<InstanceIdentifier.PathArgument> pathArgs = input.getPath();
122         QName previousQName = null;
123         List<PathArgument> components = new ArrayList<>(pathArgs.size());
124         List<QName> qnamePath = new ArrayList<>(pathArgs.size());
125         for (InstanceIdentifier.PathArgument baArg : pathArgs) {
126
127             if (!Augmentation.class.isAssignableFrom(baArg.getType())) {
128                 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument biArg = serializePathArgument(
129                         baArg, previousQName);
130                 previousQName = biArg.getNodeType();
131                 components.add(biArg);
132                 qnamePath.add(biArg.getNodeType());
133                 ImmutableList<QName> immutableList = ImmutableList.copyOf(qnamePath);
134                 codecRegistry.putPathToClass(immutableList, baArg.getType());
135                 if (previousAugmentation != null) {
136                     updateAugmentationInjection(baArg.getType(), immutableList, previousAugmentation);
137                 }
138                 previousAugmentation = null;
139             } else {
140                 previousQName = codecRegistry.getQNameForAugmentation(baArg.getType());
141                 previousAugmentation = baArg.getType();
142             }
143         }
144         org.opendaylight.yangtools.yang.data.api.InstanceIdentifier ret = new org.opendaylight.yangtools.yang.data.api.InstanceIdentifier(
145                 components);
146         LOG.debug("Binding Instance Identifier {} serialized to DOM InstanceIdentifier {}", input, ret);
147         return ret;
148     }
149
150     public Class<? extends Object> updateAugmentationInjection(Class<? extends DataObject> class1,
151             ImmutableList<QName> list, Class<?> augmentation) {
152         if (classToPreviousAugment.get(class1) == null) {
153             classToPreviousAugment.put(class1, new ConcurrentHashMap<List<QName>, Class<?>>());
154         }
155         return classToPreviousAugment.get(class1).put(list, augmentation);
156     }
157
158     private PathArgument _serializePathArgument(Item<?> argument, QName previousQname) {
159         Class<?> type = argument.getType();
160         QName qname = BindingReflections.findQName(type);
161         if (previousQname == null || (BindingReflections.isAugmentationChild(argument.getType()))) {
162             return new NodeIdentifier(qname);
163         }
164         return new NodeIdentifier(QName.create(previousQname, qname.getLocalName()));
165     }
166
167     private PathArgument _serializePathArgument(IdentifiableItem argument, QName previousQname) {
168         Map<QName, Object> predicates = new HashMap<>();
169         Class type = argument.getType();
170         IdentifierCodec<? extends Object> keyCodec = codecRegistry.getIdentifierCodecForIdentifiable(type);
171         QName qname = BindingReflections.findQName(type);
172         if (previousQname != null && !(BindingReflections.isAugmentationChild(argument.getType()))) {
173             qname = QName.create(previousQname, qname.getLocalName());
174         }
175         ValueWithQName combinedInput = new ValueWithQName(previousQname, argument.getKey());
176         CompositeNode compositeOutput = keyCodec.serialize(combinedInput);
177         for (Node<?> outputValue : compositeOutput.getValue()) {
178             predicates.put(outputValue.getNodeType(), outputValue.getValue());
179         }
180         if (previousQname == null) {
181             return new NodeIdentifierWithPredicates(qname, predicates);
182         }
183         return new NodeIdentifierWithPredicates(qname, predicates);
184     }
185
186     private org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument deserializePathArgument(
187             final PathArgument argument, final List<QName> processedPath) {
188         if (argument instanceof NodeIdentifier) {
189             return _deserializePathArgument((NodeIdentifier) argument, processedPath);
190         } else if (argument instanceof NodeIdentifierWithPredicates) {
191             return _deserializePathArgument((NodeIdentifierWithPredicates) argument, processedPath);
192         } else {
193             throw new IllegalArgumentException("Unhandled parameter types: "
194                     + Arrays.<Object> asList(argument, processedPath).toString());
195         }
196     }
197
198     private PathArgument serializePathArgument(
199             final org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument argument,
200             final QName previousQname) {
201         if (argument instanceof IdentifiableItem) {
202             return _serializePathArgument((IdentifiableItem) argument, previousQname);
203         } else if (argument instanceof Item) {
204             return _serializePathArgument((Item<?>) argument, previousQname);
205         } else {
206             throw new IllegalArgumentException("Unhandled parameter types: "
207                     + Arrays.<Object> asList(argument, previousQname).toString());
208         }
209     }
210
211 }