2 * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context;
10 import com.google.common.annotations.Beta;
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import com.google.common.collect.ImmutableMap;
14 import com.google.common.collect.Iterables;
15 import java.util.HashMap;
16 import java.util.HashSet;
18 import java.util.Map.Entry;
20 import javax.annotation.Nonnull;
21 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.base.DataContainerCodecContext;
22 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.base.DataContainerCodecPrototype;
23 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.base.NodeCodecContext;
24 import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections;
25 import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
26 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeArgument;
27 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
33 import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils;
34 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
35 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
36 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
42 * Context for prototype of choice node codec.
48 public class ChoiceNodeCodecContext<D extends TreeNode> extends DataContainerCodecContext<D, ChoiceSchemaNode> {
50 private static final Logger LOG = LoggerFactory.getLogger(ChoiceNodeCodecContext.class);
52 private final ImmutableMap<YangInstanceIdentifier.PathArgument, DataContainerCodecPrototype<?>> byYangCaseChild;
53 private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byClass;
54 private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byCaseChildClass;
57 * Prepare context for choice node from prototype and all case children of choice class.
60 * - codec prototype of choice node
62 public ChoiceNodeCodecContext(final DataContainerCodecPrototype<ChoiceSchemaNode> prototype) {
64 final Map<YangInstanceIdentifier.PathArgument, DataContainerCodecPrototype<?>> byYangCaseChildBuilder =
66 final Map<Class<?>, DataContainerCodecPrototype<?>> byClassBuilder = new HashMap<>();
67 final Map<Class<?>, DataContainerCodecPrototype<?>> byCaseChildClassBuilder = new HashMap<>();
68 final Set<Class<?>> potentialSubstitutions = new HashSet<>();
69 // Walks all cases for supplied choice in current runtime context
70 for (final Class<?> caze : factory().getRuntimeContext().getCases(getBindingClass())) {
71 // We try to load case using exact match thus name
72 // and original schema must equals
73 final DataContainerCodecPrototype<ChoiceCaseNode> cazeDef = loadCase(caze);
74 // If we have case definition, this case is instantiated
75 // at current location and thus is valid in context of parent choice
76 if (cazeDef != null) {
77 byClassBuilder.put(cazeDef.getBindingClass(), cazeDef);
78 // Updates collection of case children
79 @SuppressWarnings("unchecked")
80 final Class<? extends Instantiable<?>> cazeCls = (Class<? extends Instantiable<?>>) caze;
81 for (final Class<? extends TreeNode> cazeChild : BindingReflections.getChildrenClasses(cazeCls)) {
82 byCaseChildClassBuilder.put(cazeChild, cazeDef);
84 // Updates collection of YANG instance identifier to case
85 for (final DataSchemaNode cazeChild : cazeDef.getSchema().getChildNodes()) {
86 if (cazeChild.isAugmenting()) {
87 final AugmentationSchema augment =
88 SchemaUtils.findCorrespondingAugment(cazeDef.getSchema(), cazeChild);
89 if (augment != null) {
90 byYangCaseChildBuilder.put(SchemaUtils.getNodeIdentifierForAugmentation(augment), cazeDef);
94 byYangCaseChildBuilder.put(NodeIdentifier.create(cazeChild.getQName()), cazeDef);
98 * If case definition is not available, we store it for later check if it could be used as
99 * substitution of existing one.
101 potentialSubstitutions.add(caze);
105 final Map<Class<?>, DataContainerCodecPrototype<?>> bySubstitutionBuilder = new HashMap<>();
107 * Walks all cases which are not directly instantiated and tries to match them to instantiated cases -
108 * represent same data as instantiated case, only case name or schema path is different. This is
109 * required due property of binding specification, that if choice is in grouping schema path location
110 * is lost, and users may use incorrect case class using copy builders.
112 for (final Class<?> substitution : potentialSubstitutions) {
113 for (final Entry<Class<?>, DataContainerCodecPrototype<?>> real : byClassBuilder.entrySet()) {
114 if (BindingReflections.isSubstitutionFor(substitution, real.getKey())) {
115 bySubstitutionBuilder.put(substitution, real.getValue());
120 byClassBuilder.putAll(bySubstitutionBuilder);
121 byYangCaseChild = ImmutableMap.copyOf(byYangCaseChildBuilder);
122 byClass = ImmutableMap.copyOf(byClassBuilder);
123 byCaseChildClass = ImmutableMap.copyOf(byCaseChildClassBuilder);
126 @SuppressWarnings("unchecked")
129 public <C extends TreeNode> DataContainerCodecContext<C, ?> streamChild(@Nonnull final Class<C> childClass) {
130 final DataContainerCodecPrototype<?> child = byClass.get(childClass);
131 return (DataContainerCodecContext<C,
132 ?>) childNonNull(child, childClass, "Supplied class %s is not valid case", childClass).get();
135 @SuppressWarnings("unchecked")
137 public <C extends TreeNode> Optional<DataContainerCodecContext<C, ?>>
138 possibleStreamChild(@Nonnull final Class<C> childClass) {
139 final DataContainerCodecPrototype<?> child = byClass.get(childClass);
141 return Optional.of((DataContainerCodecContext<C, ?>) child.get());
143 return Optional.absent();
146 Iterable<Class<?>> getCaseChildrenClasses() {
147 return byCaseChildClass.keySet();
150 private DataContainerCodecPrototype<ChoiceCaseNode> loadCase(final Class<?> childClass) {
151 final Optional<ChoiceCaseNode> childSchema =
152 factory().getRuntimeContext().getCaseSchemaDefinition(getSchema(), childClass);
153 if (childSchema.isPresent()) {
154 return DataContainerCodecPrototype.from(childClass, childSchema.get(), factory());
157 LOG.debug("Supplied class %s is not valid case in schema %s", childClass, getSchema());
163 public NodeCodecContext<?> yangPathArgumentChild(final YangInstanceIdentifier.PathArgument arg) {
164 final DataContainerCodecPrototype<?> cazeProto;
165 if (arg instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) {
166 cazeProto = byYangCaseChild.get(new NodeIdentifier(arg.getNodeType()));
168 cazeProto = byYangCaseChild.get(arg);
171 return childNonNull(cazeProto, arg, "Argument %s is not valid child of %s", arg, getSchema()).get()
172 .yangPathArgumentChild(arg);
175 @SuppressWarnings("unchecked")
178 public D deserialize(@Nonnull final NormalizedNode<?, ?> data) {
179 Preconditions.checkArgument(data instanceof ChoiceNode);
180 final NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>> casted =
181 (NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>>) data;
182 final NormalizedNode<?, ?> first = Iterables.getFirst(casted.getValue(), null);
187 final DataContainerCodecPrototype<?> caze = byYangCaseChild.get(first.getIdentifier());
188 return (D) caze.get().deserialize(data);
192 protected Object deserializeObject(final NormalizedNode<?, ?> normalizedNode) {
193 return deserialize(normalizedNode);
198 public TreeArgument<?> deserializePathArgument(@Nonnull final YangInstanceIdentifier.PathArgument arg) {
199 Preconditions.checkArgument(getDomPathArgument().equals(arg));
205 public YangInstanceIdentifier.PathArgument serializePathArgument(@Nonnull final TreeArgument<?> arg) {
206 return getDomPathArgument();