93ea3b8b0b9f67b45fa64a1e7697c63a4bb2702e
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / LeafNodeCodecContext.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.mdsal.binding.dom.codec.impl;
9
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.collect.ImmutableCollection;
13 import java.lang.reflect.Method;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.List;
17 import java.util.Set;
18 import javax.annotation.Nullable;
19 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeNode;
20 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeCachingCodec;
21 import org.opendaylight.yangtools.concepts.Codec;
22 import org.opendaylight.yangtools.yang.binding.DataObject;
23 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
24 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
25 import org.opendaylight.yangtools.yang.common.QName;
26 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
27 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
29 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
30 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
32 import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
33 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.Module;
36 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
37 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
38 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
39 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
40
41 final class LeafNodeCodecContext<D extends DataObject> extends NodeCodecContext<D> implements NodeContextSupplier {
42
43     private final YangInstanceIdentifier.PathArgument yangIdentifier;
44     private final Codec<Object, Object> valueCodec;
45     private final Method getter;
46     private final DataSchemaNode schema;
47     private final Object defaultObject;
48
49     LeafNodeCodecContext(final DataSchemaNode schema, final Codec<Object, Object> codec, final Method getter,
50                 final SchemaContext schemaContext) {
51         this.yangIdentifier = new YangInstanceIdentifier.NodeIdentifier(schema.getQName());
52         this.valueCodec = Preconditions.checkNotNull(codec);
53         this.getter = getter;
54         this.schema = Preconditions.checkNotNull(schema);
55
56         this.defaultObject = createDefaultObject(schema, valueCodec, schemaContext);
57     }
58
59     private static Object createDefaultObject(final DataSchemaNode schema, final Codec<Object, Object> codec,
60                                               final SchemaContext schemaContext) {
61         if (schema instanceof LeafSchemaNode) {
62             Object defaultValue = ((LeafSchemaNode) schema).getDefault();
63             TypeDefinition<?> type = ((LeafSchemaNode) schema).getType();
64             if (defaultValue != null) {
65                 if (type instanceof IdentityrefTypeDefinition) {
66                     return qnameDomValueFromString(codec, schema, (String) defaultValue, schemaContext);
67                 }
68                 return domValueFromString(codec, type, defaultValue);
69             } else {
70                 while (type.getBaseType() != null && type.getDefaultValue() == null) {
71                     type = type.getBaseType();
72                 }
73
74                 defaultValue = type.getDefaultValue();
75                 if (defaultValue != null) {
76                     if (type instanceof IdentityrefTypeDefinition) {
77                         return qnameDomValueFromString(codec, schema, (String) defaultValue, schemaContext);
78                     }
79                     return domValueFromString(codec, type, defaultValue);
80                 }
81             }
82         }
83         return null;
84     }
85
86     private static Object qnameDomValueFromString(final Codec<Object, Object> codec, final DataSchemaNode schema,
87                                                   final String defaultValue, final SchemaContext schemaContext) {
88         int prefixEndIndex = defaultValue.indexOf(':');
89         QName qname;
90         if (prefixEndIndex != -1) {
91             String defaultValuePrefix = defaultValue.substring(0, prefixEndIndex);
92
93             Module module = schemaContext.findModuleByNamespaceAndRevision(schema.getQName().getNamespace(),
94                     schema.getQName().getRevision());
95
96             if (module.getPrefix().equals(defaultValuePrefix)) {
97                 qname = QName.create(module.getQNameModule(), defaultValue.substring(prefixEndIndex + 1));
98                 return codec.deserialize(qname);
99             } else {
100                 Set<ModuleImport> imports = module.getImports();
101                 for (ModuleImport moduleImport : imports) {
102                     if (moduleImport.getPrefix().equals(defaultValuePrefix)) {
103                         Module importedModule = schemaContext.findModuleByName(moduleImport.getModuleName(),
104                                 moduleImport.getRevision());
105                         qname = QName.create(importedModule.getQNameModule(), defaultValue.substring(prefixEndIndex + 1));
106                         return codec.deserialize(qname);
107                     }
108                 }
109                 return null;
110             }
111         }
112
113         qname = QName.create(schema.getQName(), defaultValue);
114         return codec.deserialize(qname);
115     }
116
117     private static Object domValueFromString(final Codec<Object, Object> codec, final TypeDefinition<?> type,
118     final Object defaultValue) {
119         TypeDefinitionAwareCodec<?, ?> typeDefAwareCodec = TypeDefinitionAwareCodec.from(type);
120         if (typeDefAwareCodec != null) {
121             Object castedDefaultValue = typeDefAwareCodec.deserialize((String) defaultValue);
122             return codec.deserialize(castedDefaultValue);
123         }
124         // FIXME: BUG-4647 Refactor / redesign this to throw hard error,
125         // once BUG-4638 is fixed and will provide proper getDefaultValue implementation.
126         return null;
127     }
128
129     @Override
130     protected YangInstanceIdentifier.PathArgument getDomPathArgument() {
131         return yangIdentifier;
132     }
133
134     protected Codec<Object, Object> getValueCodec() {
135         return valueCodec;
136     }
137
138     @Override
139     public D deserialize(final NormalizedNode<?, ?> normalizedNode) {
140         throw new UnsupportedOperationException("Leaf can not be deserialized to DataObject");
141     }
142
143     @Override
144     public NodeCodecContext<?> get() {
145         return this;
146     }
147
148     final Method getGetter() {
149         return getter;
150     }
151
152     @Override
153     public BindingCodecTreeNode<?> bindingPathArgumentChild(final PathArgument arg,
154             final List<YangInstanceIdentifier.PathArgument> builder) {
155         throw new IllegalArgumentException("Leaf does not have children");
156     }
157
158     @Override
159     public BindingNormalizedNodeCachingCodec<D> createCachingCodec(
160             final ImmutableCollection<Class<? extends DataObject>> cacheSpecifier) {
161         throw new UnsupportedOperationException("Leaves does not support caching codec.");
162     }
163
164     @Override
165     public Class<D> getBindingClass() {
166         throw new UnsupportedOperationException("Leaf does not have DataObject representation");
167     }
168
169     @Override
170     public NormalizedNode<?, ?> serialize(final D data) {
171         throw new UnsupportedOperationException("Separete serialization of leaf node is not supported.");
172     }
173
174     @Override
175     public void writeAsNormalizedNode(final D data, final NormalizedNodeStreamWriter writer) {
176         throw new UnsupportedOperationException("Separete serialization of leaf node is not supported.");
177     }
178
179     @Override
180     public <E extends DataObject> BindingCodecTreeNode<E> streamChild(final Class<E> childClass) {
181         throw new IllegalArgumentException("Leaf does not have children");
182     }
183
184     @Override
185     public <E extends DataObject> Optional<? extends BindingCodecTreeNode<E>> possibleStreamChild(
186             final Class<E> childClass) {
187         throw new IllegalArgumentException("Leaf does not have children");
188     }
189
190     @Override
191     public BindingCodecTreeNode<?> yangPathArgumentChild(final YangInstanceIdentifier.PathArgument child) {
192         throw new IllegalArgumentException("Leaf does not have children");
193     }
194
195     @Override
196     protected Object deserializeObject(final NormalizedNode<?, ?> normalizedNode) {
197         if (normalizedNode instanceof LeafNode<?>) {
198             return valueCodec.deserialize(normalizedNode.getValue());
199         }
200         if (normalizedNode instanceof LeafSetNode<?>) {
201             @SuppressWarnings("unchecked")
202             final Collection<LeafSetEntryNode<Object>> domValues = ((LeafSetNode<Object>) normalizedNode).getValue();
203             final List<Object> result = new ArrayList<>(domValues.size());
204             for (final LeafSetEntryNode<Object> valueNode : domValues) {
205                 result.add(valueCodec.deserialize(valueNode.getValue()));
206             }
207             return result;
208         }
209         return null;
210     }
211
212     @Override
213     public InstanceIdentifier.PathArgument deserializePathArgument(final YangInstanceIdentifier.PathArgument arg) {
214         Preconditions.checkArgument(getDomPathArgument().equals(arg));
215         return null;
216     }
217
218     @Override
219     public YangInstanceIdentifier.PathArgument serializePathArgument(final InstanceIdentifier.PathArgument arg) {
220         return getDomPathArgument();
221     }
222
223     @Override
224     public DataSchemaNode getSchema() {
225         return schema;
226     }
227
228     /**
229      * Return the default value object.
230      *
231      * @return The default value object, or null if the default value is not defined.
232      */
233     @Nullable Object defaultObject() {
234         return defaultObject;
235     }
236 }