2 * Copyright (c) 2014 Cisco Systems, Inc. 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.dom.codec.impl;
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;
17 import java.util.Map.Entry;
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.ChoiceNode;
27 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
29 import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils;
30 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
31 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
32 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
37 final class ChoiceNodeCodecContext<D extends DataObject> extends DataContainerCodecContext<D,ChoiceSchemaNode> {
38 private static final Logger LOG = LoggerFactory.getLogger(ChoiceNodeCodecContext.class);
39 private final ImmutableMap<YangInstanceIdentifier.PathArgument, DataContainerCodecPrototype<?>> byYangCaseChild;
40 private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byClass;
41 private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byCaseChildClass;
43 ChoiceNodeCodecContext(final DataContainerCodecPrototype<ChoiceSchemaNode> prototype) {
45 final Map<YangInstanceIdentifier.PathArgument, DataContainerCodecPrototype<?>> byYangCaseChildBuilder =
47 final Map<Class<?>, DataContainerCodecPrototype<?>> byClassBuilder = new HashMap<>();
48 final Map<Class<?>, DataContainerCodecPrototype<?>> byCaseChildClassBuilder = new HashMap<>();
49 final Set<Class<?>> potentialSubstitutions = new HashSet<>();
50 // Walks all cases for supplied choice in current runtime context
51 for (final Class<?> caze : factory().getRuntimeContext().getCases(getBindingClass())) {
52 // We try to load case using exact match thus name
53 // and original schema must equals
54 final DataContainerCodecPrototype<ChoiceCaseNode> cazeDef = loadCase(caze);
55 // If we have case definition, this case is instantiated
56 // at current location and thus is valid in context of parent choice
57 if (cazeDef != null) {
58 byClassBuilder.put(cazeDef.getBindingClass(), cazeDef);
59 // Updates collection of case children
60 @SuppressWarnings("unchecked")
61 final Class<? extends DataObject> cazeCls = (Class<? extends DataObject>) caze;
62 for (final Class<? extends DataObject> cazeChild : BindingReflections.getChildrenClasses(cazeCls)) {
63 byCaseChildClassBuilder.put(cazeChild, cazeDef);
65 // Updates collection of YANG instance identifier to case
66 for (final DataSchemaNode cazeChild : cazeDef.getSchema().getChildNodes()) {
67 if (cazeChild.isAugmenting()) {
68 final AugmentationSchema augment = SchemaUtils.findCorrespondingAugment(cazeDef.getSchema(),
70 if (augment != null) {
71 byYangCaseChildBuilder.put(SchemaUtils.getNodeIdentifierForAugmentation(augment), cazeDef);
75 byYangCaseChildBuilder.put(NodeIdentifier.create(cazeChild.getQName()), cazeDef);
79 * If case definition is not available, we store it for
80 * later check if it could be used as substitution of existing one.
82 potentialSubstitutions.add(caze);
86 final Map<Class<?>, DataContainerCodecPrototype<?>> bySubstitutionBuilder = new HashMap<>();
88 * Walks all cases which are not directly instantiated and
89 * tries to match them to instantiated cases - represent same data as instantiated case,
90 * only case name or schema path is different. This is required due property of
91 * binding specification, that if choice is in grouping schema path location is lost,
92 * and users may use incorrect case class using copy builders.
94 for (final Class<?> substitution : potentialSubstitutions) {
95 search: for (final Entry<Class<?>, DataContainerCodecPrototype<?>> real : byClassBuilder.entrySet()) {
96 if (BindingReflections.isSubstitutionFor(substitution, real.getKey())) {
97 bySubstitutionBuilder.put(substitution, real.getValue());
102 byClassBuilder.putAll(bySubstitutionBuilder);
103 byYangCaseChild = ImmutableMap.copyOf(byYangCaseChildBuilder);
104 byClass = ImmutableMap.copyOf(byClassBuilder);
105 byCaseChildClass = ImmutableMap.copyOf(byCaseChildClassBuilder);
108 @SuppressWarnings("unchecked")
110 public <C extends DataObject> DataContainerCodecContext<C, ?> streamChild(final Class<C> childClass) {
111 final DataContainerCodecPrototype<?> child = byClass.get(childClass);
112 return (DataContainerCodecContext<C, ?>) childNonNull(child, childClass, "Supplied class %s is not valid case",
116 @SuppressWarnings("unchecked")
118 public <C extends DataObject> Optional<DataContainerCodecContext<C, ?>> possibleStreamChild(
119 final Class<C> childClass) {
120 final DataContainerCodecPrototype<?> child = byClass.get(childClass);
122 return Optional.of((DataContainerCodecContext<C, ?>) child.get());
124 return Optional.absent();
127 Iterable<Class<?>> getCaseChildrenClasses() {
128 return byCaseChildClass.keySet();
131 protected DataContainerCodecPrototype<ChoiceCaseNode> loadCase(final Class<?> childClass) {
132 final Optional<ChoiceCaseNode> childSchema = factory().getRuntimeContext().getCaseSchemaDefinition(getSchema(),
134 if (childSchema.isPresent()) {
135 return DataContainerCodecPrototype.from(childClass, childSchema.get(), factory());
138 LOG.debug("Supplied class %s is not valid case in schema %s", childClass, getSchema());
143 public NodeCodecContext<?> yangPathArgumentChild(final YangInstanceIdentifier.PathArgument arg) {
144 final DataContainerCodecPrototype<?> cazeProto;
145 if (arg instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) {
146 cazeProto = byYangCaseChild.get(new NodeIdentifier(arg.getNodeType()));
148 cazeProto = byYangCaseChild.get(arg);
151 return childNonNull(cazeProto, arg,"Argument %s is not valid child of %s", arg, getSchema()).get()
152 .yangPathArgumentChild(arg);
155 @SuppressWarnings("unchecked")
158 public D deserialize(final NormalizedNode<?, ?> data) {
159 Preconditions.checkArgument(data instanceof ChoiceNode);
160 final NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>> casted =
161 (NormalizedNodeContainer<?, ?, NormalizedNode<?, ?>>) data;
162 final NormalizedNode<?, ?> first = Iterables.getFirst(casted.getValue(), null);
167 final DataContainerCodecPrototype<?> caze = byYangCaseChild.get(first.getIdentifier());
168 return (D) caze.get().deserialize(data);
171 DataContainerCodecContext<?, ?> getCazeByChildClass(final @Nonnull Class<? extends DataObject> type) {
172 final DataContainerCodecPrototype<?> protoCtx =
173 childNonNull(byCaseChildClass.get(type), type, "Class %s is not child of any cases for %s", type,
175 return protoCtx.get();
179 protected Object deserializeObject(final NormalizedNode<?, ?> normalizedNode) {
180 return deserialize(normalizedNode);
184 public PathArgument deserializePathArgument(final YangInstanceIdentifier.PathArgument arg) {
185 Preconditions.checkArgument(getDomPathArgument().equals(arg));
190 public YangInstanceIdentifier.PathArgument serializePathArgument(final PathArgument arg) {
191 // FIXME: check for null, since binding container is null.
192 return getDomPathArgument();