Eliminate use of the IllegalArgumentCodec
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / LeafNodeCodecContext.java
1 /*
2  * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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 static java.util.Objects.requireNonNull;
11
12 import java.util.Optional;
13 import org.eclipse.jdt.annotation.NonNull;
14 import org.opendaylight.mdsal.binding.dom.codec.api.BindingTypeObjectCodecTreeNode;
15 import org.opendaylight.yangtools.yang.binding.TypeObject;
16 import org.opendaylight.yangtools.yang.common.QName;
17 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
18 import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
19 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
20 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
21 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
22 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.Module;
24 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
25 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
26 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
27
28 class LeafNodeCodecContext extends ValueNodeCodecContext.WithCodec {
29     static final class OfTypeObject<T extends TypeObject> extends LeafNodeCodecContext
30             implements BindingTypeObjectCodecTreeNode<T> {
31         private final @NonNull Class<T> bindingClass;
32
33         OfTypeObject(final LeafSchemaNode schema, final ValueCodec<Object, Object> codec, final String getterName,
34                 final EffectiveModelContext schemaContext, final Class<T> bindingClass) {
35             super(schema, codec, getterName, schemaContext);
36             this.bindingClass = requireNonNull(bindingClass);
37         }
38
39         @Override
40         public Class<T> getBindingClass() {
41             return bindingClass;
42         }
43
44         @Override
45         public T deserialize(final NormalizedNode data) {
46             return bindingClass.cast(deserializeObject(data));
47         }
48
49         @Override
50         public NormalizedNode serialize(final T data) {
51             return ImmutableNodes.leafNode(getDomPathArgument(), getValueCodec().serialize(data));
52         }
53     }
54
55     LeafNodeCodecContext(final LeafSchemaNode schema, final ValueCodec<Object, Object> codec, final String getterName,
56             final EffectiveModelContext schemaContext) {
57         super(schema, codec, getterName, createDefaultObject(schema, codec, schemaContext));
58     }
59
60     static LeafNodeCodecContext of(final LeafSchemaNode schema, final ValueCodec<Object, Object> codec,
61             final String getterName, final Class<?> valueType, final EffectiveModelContext schemaContext) {
62         return TypeObject.class.isAssignableFrom(valueType)
63                 ? new OfTypeObject<>(schema, codec, getterName, schemaContext, valueType.asSubclass(TypeObject.class))
64                         : new LeafNodeCodecContext(schema, codec, getterName, schemaContext);
65     }
66
67     @Override
68     protected Object deserializeObject(final NormalizedNode normalizedNode) {
69         return normalizedNode != null ? getValueCodec().deserialize(normalizedNode.body()) : null;
70     }
71
72     private static Object createDefaultObject(final LeafSchemaNode schema, final ValueCodec<Object, Object> codec,
73             final EffectiveModelContext schemaContext) {
74         Optional<? extends Object> defaultValue = schema.getType().getDefaultValue();
75         TypeDefinition<?> type = schema.getType();
76         if (defaultValue.isPresent()) {
77             if (type instanceof IdentityrefTypeDefinition) {
78                 return qnameDomValueFromString(codec, schema, (String) defaultValue.get(), schemaContext);
79             }
80             return domValueFromString(codec, type, defaultValue.get());
81         }
82
83         while (type.getBaseType() != null && !type.getDefaultValue().isPresent()) {
84             type = type.getBaseType();
85         }
86
87         defaultValue = type.getDefaultValue();
88         if (defaultValue.isPresent()) {
89             if (type instanceof IdentityrefTypeDefinition) {
90                 return qnameDomValueFromString(codec, schema, (String) defaultValue.get(), schemaContext);
91             }
92             return domValueFromString(codec, type, defaultValue);
93         }
94         return null;
95     }
96
97     private static Object qnameDomValueFromString(final ValueCodec<Object, Object> codec, final DataSchemaNode schema,
98             final String defaultValue, final EffectiveModelContext schemaContext) {
99         int prefixEndIndex = defaultValue.indexOf(':');
100         QName qname;
101         if (prefixEndIndex != -1) {
102             String defaultValuePrefix = defaultValue.substring(0, prefixEndIndex);
103
104             Module module = schemaContext.findModule(schema.getQName().getModule()).get();
105             if (module.getPrefix().equals(defaultValuePrefix)) {
106                 qname = QName.create(module.getQNameModule(), defaultValue.substring(prefixEndIndex + 1));
107                 return codec.deserialize(qname);
108             }
109
110             for (ModuleImport moduleImport : module.getImports()) {
111                 if (moduleImport.getPrefix().equals(defaultValuePrefix)) {
112                     Module importedModule = schemaContext.findModule(moduleImport.getModuleName().getLocalName(),
113                         moduleImport.getRevision()).get();
114                     qname = QName.create(importedModule.getQNameModule(), defaultValue.substring(prefixEndIndex + 1));
115                     return codec.deserialize(qname);
116                 }
117             }
118             return null;
119         }
120
121         qname = QName.create(schema.getQName(), defaultValue);
122         return codec.deserialize(qname);
123     }
124
125     private static Object domValueFromString(final ValueCodec<Object, Object> codec, final TypeDefinition<?> type,
126             final Object defaultValue) {
127         TypeDefinitionAwareCodec<?, ?> typeDefAwareCodec = TypeDefinitionAwareCodec.from(type);
128         if (typeDefAwareCodec != null) {
129             Object castedDefaultValue = typeDefAwareCodec.deserialize((String) defaultValue);
130             return codec.deserialize(castedDefaultValue);
131         }
132         // FIXME: BUG-4647 Refactor / redesign this to throw hard error, once BUG-4638 is fixed and will provide proper
133         //                 getDefaultValue() implementation.
134         return null;
135     }
136 }