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.yangtools.binding.data.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.util.BindingReflections;
23 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
24 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
25 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
26 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
27 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
28 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
33 final class ChoiceNodeCodecContext extends DataContainerCodecContext<ChoiceSchemaNode> {
34 private static final Logger LOG = LoggerFactory.getLogger(ChoiceNodeCodecContext.class);
35 private final ImmutableMap<YangInstanceIdentifier.PathArgument, DataContainerCodecPrototype<?>> byYangCaseChild;
36 private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byClass;
37 private final ImmutableMap<Class<?>, DataContainerCodecPrototype<?>> byCaseChildClass;
39 public ChoiceNodeCodecContext(final DataContainerCodecPrototype<ChoiceSchemaNode> prototype) {
41 Map<YangInstanceIdentifier.PathArgument, DataContainerCodecPrototype<?>> byYangCaseChildBuilder = new HashMap<>();
42 Map<Class<?>, DataContainerCodecPrototype<?>> byClassBuilder = new HashMap<>();
43 Map<Class<?>, DataContainerCodecPrototype<?>> byCaseChildClassBuilder = new HashMap<>();
44 Set<Class<?>> potentialSubstitutions = new HashSet<>();
45 // Walks all cases for supplied choice in current runtime context
46 for (Class<?> caze : factory().getRuntimeContext().getCases(bindingClass())) {
47 // We try to load case using exact match thus name
48 // and original schema must equals
49 DataContainerCodecPrototype<ChoiceCaseNode> cazeDef = loadCase(caze);
50 // If we have case definition, this case is instantiated
51 // at current location and thus is valid in context of parent choice
52 if (cazeDef != null) {
53 byClassBuilder.put(cazeDef.getBindingClass(), cazeDef);
54 // Updates collection of case children
55 for (Class<? extends DataObject> cazeChild : BindingReflections.getChildrenClasses((Class<? extends DataObject>) caze)) {
56 byCaseChildClassBuilder.put(cazeChild, cazeDef);
58 // Updates collection of YANG instance identifier to case
59 for (DataSchemaNode cazeChild : cazeDef.getSchema().getChildNodes()) {
60 byYangCaseChildBuilder.put(new NodeIdentifier(cazeChild.getQName()), cazeDef);
64 * If case definition is not available, we store it for
65 * later check if it could be used as substitution of existing one.
67 potentialSubstitutions.add(caze);
71 Map<Class<?>, DataContainerCodecPrototype<?>> bySubstitutionBuilder = new HashMap<>();
73 * Walks all cases which are not directly instantiated and
74 * tries to match them to instantiated cases - represent same data as instantiated case,
75 * only case name or schema path is different. This is required due property of
76 * binding specification, that if choice is in grouping schema path location is lost,
77 * and users may use incorrect case class using copy builders.
79 for(Class<?> substitution : potentialSubstitutions) {
80 search: for(Entry<Class<?>, DataContainerCodecPrototype<?>> real : byClassBuilder.entrySet()) {
81 if(BindingReflections.isSubstitutionFor(substitution, real.getKey())) {
82 bySubstitutionBuilder.put(substitution, real.getValue());
87 byClassBuilder.putAll(bySubstitutionBuilder);
88 byYangCaseChild = ImmutableMap.copyOf(byYangCaseChildBuilder);
89 byClass = ImmutableMap.copyOf(byClassBuilder);
90 byCaseChildClass = ImmutableMap.copyOf(byCaseChildClassBuilder);
94 protected DataContainerCodecContext<?> getStreamChild(final Class<?> childClass) {
95 DataContainerCodecPrototype<?> child = byClass.get(childClass);
96 Preconditions.checkArgument(child != null,"Supplied class is not valid case",childClass);
101 protected Optional<DataContainerCodecContext<?>> getPossibleStreamChild(final Class<?> childClass) {
102 DataContainerCodecPrototype<?> child = byClass.get(childClass);
104 return Optional.<DataContainerCodecContext<?>>of(child.get());
106 return Optional.absent();
109 Iterable<Class<?>> getCaseChildrenClasses() {
110 return byCaseChildClass.keySet();
113 protected DataContainerCodecPrototype<ChoiceCaseNode> loadCase(final Class<?> childClass) {
114 Optional<ChoiceCaseNode> childSchema = factory().getRuntimeContext().getCaseSchemaDefinition(schema(), childClass);
115 if (childSchema.isPresent()) {
116 return DataContainerCodecPrototype.from(childClass, childSchema.get(), factory());
119 LOG.debug("Supplied class %s is not valid case in schema %s", childClass, schema());
124 protected NodeCodecContext getYangIdentifierChild(final YangInstanceIdentifier.PathArgument arg) {
125 DataContainerCodecPrototype<?> cazeProto = byYangCaseChild.get(arg);
126 Preconditions.checkArgument(cazeProto != null, "Argument %s is not valid child of %s", arg, schema());
127 return cazeProto.get().getYangIdentifierChild(arg);
131 protected Object dataFromNormalizedNode(final NormalizedNode<?, ?> data) {
133 .checkArgument(data instanceof org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode);
134 NormalizedNodeContainer<?, ?, NormalizedNode<?,?>> casted = (NormalizedNodeContainer<?, ?, NormalizedNode<?,?>>) data;
135 NormalizedNode<?, ?> first = Iterables.getFirst(casted.getValue(), null);
140 DataContainerCodecPrototype<?> caze = byYangCaseChild.get(first.getIdentifier());
141 return caze.get().dataFromNormalizedNode(data);
144 public @Nullable DataContainerCodecContext<?> getCazeByChildClass(final @Nonnull Class<? extends DataObject> type) {
145 final DataContainerCodecPrototype<?> protoCtx = byCaseChildClass.get(type);
146 if(protoCtx != null) {
147 return protoCtx.get();