5240ecf6104ea600ad9cd092871b957362386657
[yangtools.git] / code-generator / binding-data-codec / src / main / java / org / opendaylight / yangtools / binding / data / codec / impl / ChoiceNodeCodecContext.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.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.collect.ImmutableMap;
13 import com.google.common.collect.Iterables;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.Map;
17 import java.util.Map.Entry;
18 import java.util.Set;
19 import javax.annotation.Nonnull;
20 import javax.annotation.Nullable;
21 import org.opendaylight.yangtools.yang.binding.DataObject;
22 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
23 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
26 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
27 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
28 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
29 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 final class ChoiceNodeCodecContext<D extends DataObject> extends DataContainerCodecContext<D,ChoiceSchemaNode> {
35     private static final Logger LOG = LoggerFactory.getLogger(ChoiceNodeCodecContext.class);
36     private final ImmutableMap<YangInstanceIdentifier.PathArgument, DataContainerCodecPrototype<?>> byYangCaseChild;
37     private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byClass;
38     private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byCaseChildClass;
39
40     public ChoiceNodeCodecContext(final DataContainerCodecPrototype<ChoiceSchemaNode> prototype) {
41         super(prototype);
42         Map<YangInstanceIdentifier.PathArgument, DataContainerCodecPrototype<?>> byYangCaseChildBuilder = new HashMap<>();
43         Map<Class<?>, DataContainerCodecPrototype<?>> byClassBuilder = new HashMap<>();
44         Map<Class<?>, DataContainerCodecPrototype<?>> byCaseChildClassBuilder = new HashMap<>();
45         Set<Class<?>> potentialSubstitutions = new HashSet<>();
46         // Walks all cases for supplied choice in current runtime context
47         for (Class<?> caze : factory().getRuntimeContext().getCases(getBindingClass())) {
48             // We try to load case using exact match thus name
49             // and original schema must equals
50             DataContainerCodecPrototype<ChoiceCaseNode> cazeDef = loadCase(caze);
51             // If we have case definition, this case is instantiated
52             // at current location and thus is valid in context of parent choice
53             if (cazeDef != null) {
54                 byClassBuilder.put(cazeDef.getBindingClass(), cazeDef);
55                 // Updates collection of case children
56                 @SuppressWarnings("unchecked")
57                 Class<? extends DataObject> cazeCls = (Class<? extends DataObject>) caze;
58                 for (Class<? extends DataObject> cazeChild : BindingReflections.getChildrenClasses(cazeCls)) {
59                     byCaseChildClassBuilder.put(cazeChild, cazeDef);
60                 }
61                 // Updates collection of YANG instance identifier to case
62                 for (DataSchemaNode cazeChild : cazeDef.getSchema().getChildNodes()) {
63                     byYangCaseChildBuilder.put(new NodeIdentifier(cazeChild.getQName()), cazeDef);
64                 }
65             } else {
66                 /*
67                  * If case definition is not available, we store it for
68                  * later check if it could be used as substitution of existing one.
69                  */
70                 potentialSubstitutions.add(caze);
71             }
72         }
73
74         Map<Class<?>, DataContainerCodecPrototype<?>> bySubstitutionBuilder = new HashMap<>();
75         /*
76          * Walks all cases which are not directly instantiated and
77          * tries to match them to instantiated cases - represent same data as instantiated case,
78          * only case name or schema path is different. This is required due property of
79          * binding specification, that if choice is in grouping schema path location is lost,
80          * and users may use incorrect case class using copy builders.
81          */
82         for(Class<?> substitution : potentialSubstitutions) {
83             search: for(Entry<Class<?>, DataContainerCodecPrototype<?>> real : byClassBuilder.entrySet()) {
84                 if(BindingReflections.isSubstitutionFor(substitution, real.getKey())) {
85                     bySubstitutionBuilder.put(substitution, real.getValue());
86                     break search;
87                 }
88             }
89         }
90         byClassBuilder.putAll(bySubstitutionBuilder);
91         byYangCaseChild = ImmutableMap.copyOf(byYangCaseChildBuilder);
92         byClass = ImmutableMap.copyOf(byClassBuilder);
93         byCaseChildClass = ImmutableMap.copyOf(byCaseChildClassBuilder);
94     }
95
96
97     @SuppressWarnings("unchecked")
98     @Override
99     public <DV extends DataObject> DataContainerCodecContext<DV, ?> streamChild(Class<DV> childClass) {
100         DataContainerCodecPrototype<?> child = byClass.get(childClass);
101         Preconditions.checkArgument(child != null,"Supplied class is not valid case",childClass);
102         return (DataContainerCodecContext<DV, ?>) child.get();
103     }
104
105
106     @SuppressWarnings("unchecked")
107     @Override
108     public <DV extends DataObject> Optional<DataContainerCodecContext<DV, ?>> possibleStreamChild(
109             Class<DV> childClass) {
110         DataContainerCodecPrototype<?> child = byClass.get(childClass);
111         if(child != null) {
112             return Optional.<DataContainerCodecContext<DV,?>>of((DataContainerCodecContext<DV, ?>) child.get());
113         }
114         return Optional.absent();
115     }
116
117     Iterable<Class<?>> getCaseChildrenClasses() {
118         return byCaseChildClass.keySet();
119     }
120
121     protected DataContainerCodecPrototype<ChoiceCaseNode> loadCase(final Class<?> childClass) {
122         Optional<ChoiceCaseNode> childSchema = factory().getRuntimeContext().getCaseSchemaDefinition(schema(), childClass);
123         if (childSchema.isPresent()) {
124             return DataContainerCodecPrototype.from(childClass, childSchema.get(), factory());
125         }
126
127         LOG.debug("Supplied class %s is not valid case in schema %s", childClass, schema());
128         return null;
129     }
130
131     @Override
132     public NodeCodecContext<?> yangPathArgumentChild(final YangInstanceIdentifier.PathArgument arg) {
133         DataContainerCodecPrototype<?> cazeProto = byYangCaseChild.get(arg);
134         Preconditions.checkArgument(cazeProto != null, "Argument %s is not valid child of %s", arg, schema());
135         return cazeProto.get().yangPathArgumentChild(arg);
136     }
137
138     @SuppressWarnings("unchecked")
139     @Override
140     public D deserialize(final NormalizedNode<?, ?> data) {
141         Preconditions
142                 .checkArgument(data instanceof org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode);
143         NormalizedNodeContainer<?, ?, NormalizedNode<?,?>> casted = (NormalizedNodeContainer<?, ?, NormalizedNode<?,?>>) data;
144         NormalizedNode<?, ?> first = Iterables.getFirst(casted.getValue(), null);
145
146         if (first == null) {
147             return null;
148         }
149         DataContainerCodecPrototype<?> caze = byYangCaseChild.get(first.getIdentifier());
150         return (D) caze.get().deserialize(data);
151     }
152
153     @Nullable DataContainerCodecContext<?,?> getCazeByChildClass(final @Nonnull Class<? extends DataObject> type) {
154         final DataContainerCodecPrototype<?> protoCtx = byCaseChildClass.get(type);
155         if(protoCtx != null) {
156             return protoCtx.get();
157         }
158         return null;
159     }
160
161     @Override
162     protected Object deserializeObject(NormalizedNode<?, ?> normalizedNode) {
163         return deserialize(normalizedNode);
164     }
165
166     @Override
167     public PathArgument deserializePathArgument(YangInstanceIdentifier.PathArgument arg) {
168         Preconditions.checkArgument(getDomPathArgument().equals(arg));
169         return null;
170     }
171
172     @Override
173     public YangInstanceIdentifier.PathArgument serializePathArgument(
174             PathArgument arg) {
175         // FIXME: check for null, since binding container is null.
176         return getDomPathArgument();
177     }
178
179 }