Bug 1439, Bug 1443 Binding Codec NormalizedNodeWriter support
[yangtools.git] / code-generator / binding-data-codec / src / main / java / org / opendaylight / yangtools / binding / data / codec / impl / DataObjectCodecContext.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.Preconditions;
11 import com.google.common.collect.ImmutableMap;
12 import com.google.common.collect.Iterables;
13
14 import java.util.List;
15 import java.util.Map.Entry;
16
17 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
18 import org.opendaylight.yangtools.sal.binding.generator.api.ClassLoadingStrategy;
19 import org.opendaylight.yangtools.sal.binding.model.api.Type;
20 import org.opendaylight.yangtools.yang.binding.Augmentation;
21 import org.opendaylight.yangtools.yang.binding.DataObject;
22 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
23 import org.opendaylight.yangtools.yang.common.QName;
24 import org.opendaylight.yangtools.yang.common.QNameModule;
25 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
27 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
28 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
29 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
30 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
31 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
32 import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
33
34 abstract class DataObjectCodecContext<T extends DataNodeContainer> extends DataContainerCodecContext<T> {
35
36     protected final ImmutableMap<String, LeafNodeCodecContext> leafChild;
37     protected final ImmutableMap<Type, Entry<Type, Type>> choiceCaseChildren;
38     protected final ImmutableMap<AugmentationIdentifier, Type> augIdentifierToType;
39
40     protected DataObjectCodecContext(final Class<?> cls, final QNameModule namespace, final T nodeSchema,
41             final CodecContextFactory loader) {
42         super(cls, namespace, nodeSchema, loader);
43         this.leafChild = loader.getLeafNodes(cls, nodeSchema);
44         this.choiceCaseChildren = factory.getRuntimeContext().getChoiceCaseChildren(schema);
45         this.augIdentifierToType = factory.getRuntimeContext().getAvailableAugmentationTypes(nodeSchema);
46     }
47
48     @Override
49     protected DataContainerCodecContext<?> getIdentifierChild(final InstanceIdentifier.PathArgument arg,
50             final List<YangInstanceIdentifier.PathArgument> builder) {
51         if (choiceCaseChildren.isEmpty()) {
52             return super.getIdentifierChild(arg, builder);
53         }
54         // Lookup in choiceCase
55         Class<? extends DataObject> argument = arg.getType();
56         ReferencedTypeImpl ref = new ReferencedTypeImpl(argument.getPackage().getName(), argument.getSimpleName());
57         Entry<Type, Type> cazeId = choiceCaseChildren.get(ref);
58         if (cazeId == null) {
59             return super.getIdentifierChild(arg, builder);
60         }
61         ClassLoadingStrategy loader = factory.getRuntimeContext().getStrategy();
62         try {
63             Class<?> choice = loader.loadClass(cazeId.getKey());
64             Class<?> caze = loader.loadClass(cazeId.getValue());
65             ChoiceNodeCodecContext choiceNode = (ChoiceNodeCodecContext) getStreamChild(choice);
66             choiceNode.addYangPathArgument(arg, builder);
67             CaseNodeCodecContext cazeNode = (CaseNodeCodecContext) choiceNode.getStreamChild(caze);
68             cazeNode.addYangPathArgument(arg, builder);
69             return cazeNode.getIdentifierChild(arg, builder);
70
71         } catch (ClassNotFoundException e) {
72             throw new IllegalArgumentException("Required class not found.", e);
73         }
74
75     }
76
77     @Override
78     protected NodeCodecContext getYangIdentifierChild(final YangInstanceIdentifier.PathArgument arg) {
79         if (arg instanceof YangInstanceIdentifier.AugmentationIdentifier) {
80             return getChildByAugmentationIdentifier((YangInstanceIdentifier.AugmentationIdentifier) arg);
81         }
82
83         QName childQName = arg.getNodeType();
84         DataSchemaNode childSchema = schema.getDataChildByName(childQName);
85         Preconditions.checkArgument(childSchema != null, "Argument %s is not valid child of %s", arg, schema);
86         if (childSchema instanceof DataNodeContainer || childSchema instanceof ChoiceNode) {
87             Class<?> childCls = factory.getRuntimeContext().getClassForSchema(childSchema);
88             DataContainerCodecContext<?> childNode = getStreamChild(childCls);
89             return childNode;
90         } else {
91             return getLeafChild(childQName.getLocalName());
92         }
93     }
94
95     protected NodeCodecContext getChildByAugmentationIdentifier(final YangInstanceIdentifier.AugmentationIdentifier arg) {
96         final Type augType = augIdentifierToType.get(arg);
97         try {
98             Class<?> augClass = factory.getRuntimeContext().getStrategy().loadClass(augType);
99             return getStreamChild(augClass);
100         } catch (ClassNotFoundException e) {
101             throw new IllegalStateException("Unable to load referenced augmentation.", e);
102         }
103     }
104
105     protected final LeafNodeCodecContext getLeafChild(final String name) {
106         final LeafNodeCodecContext value = leafChild.get(name);
107         Preconditions.checkArgument(value != null, "Leaf %s is not valid for %s", name, bindingClass);
108         return value;
109     }
110
111     @Override
112     protected DataContainerCodecContext<?> loadChild(final Class<?> childClass) {
113         if (Augmentation.class.isAssignableFrom(childClass)) {
114             return loadAugmentation(childClass);
115         }
116
117         DataSchemaNode origDef = factory.getRuntimeContext().getSchemaDefinition(childClass);
118         // Direct instantiation or use in same module in which grouping
119         // was defined.
120         DataSchemaNode sameName = schema.getDataChildByName(origDef.getQName());
121         final DataSchemaNode childSchema;
122         if (sameName != null) {
123             // Exactly same schema node
124             if (origDef.equals(sameName)) {
125                 childSchema = sameName;
126                 // We check if instantiated node was added via uses
127                 // statement and is an instantiation of same grouping
128             } else if (origDef.equals(SchemaNodeUtils.getRootOriginalIfPossible(sameName))) {
129                 childSchema = sameName;
130             } else {
131                 // Node has same name, but clearly is different
132                 childSchema = null;
133             }
134         } else {
135             // We are looking for instantiation via uses in other module
136             QName instantiedName = QName.create(namespace, origDef.getQName().getLocalName());
137             DataSchemaNode potential = schema.getDataChildByName(instantiedName);
138             // We check if it is really instantiated from same
139             // definition
140             // as class was derived
141             if (potential != null && origDef.equals(SchemaNodeUtils.getRootOriginalIfPossible(potential))) {
142                 childSchema = potential;
143             } else {
144                 childSchema = null;
145             }
146         }
147         Preconditions
148         .checkArgument(childSchema != null, "Node %s does not have child named %s", schema, childClass);
149         return DataContainerCodecContext.from(childClass, childSchema, factory);
150     }
151
152     @SuppressWarnings("rawtypes")
153     private AugmentationNode loadAugmentation(final Class childClass) {
154         Preconditions.checkArgument(schema instanceof AugmentationTarget);
155         @SuppressWarnings("unchecked")
156         Entry<AugmentationIdentifier, AugmentationSchema> augSchema = factory.getRuntimeContext()
157                 .getResolvedAugmentationSchema(schema, childClass);
158         QNameModule namespace = Iterables.getFirst(augSchema.getKey().getPossibleChildNames(), null).getModule();
159         return new AugmentationNode(childClass, namespace, augSchema.getKey(), augSchema.getValue(), factory);
160     }
161
162 }