Move BindingMapping
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / gen / impl / DataNodeContainerSerializerSource.java
1 /*
2  * Copyright (c) 2014, 2015 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
9 package org.opendaylight.mdsal.binding.dom.codec.gen.impl;
10
11 import com.google.common.base.Preconditions;
12 import java.util.HashMap;
13 import java.util.Map;
14 import org.opendaylight.mdsal.binding.dom.codec.util.ChoiceDispatchSerializer;
15 import org.opendaylight.mdsal.binding.model.api.GeneratedType;
16 import org.opendaylight.mdsal.binding.model.api.MethodSignature;
17 import org.opendaylight.mdsal.binding.model.api.ParameterizedType;
18 import org.opendaylight.mdsal.binding.model.api.Type;
19 import org.opendaylight.mdsal.binding.spec.naming.BindingMapping;
20 import org.opendaylight.yangtools.yang.binding.BindingSerializer;
21 import org.opendaylight.yangtools.yang.binding.BindingStreamEventWriter;
22 import org.opendaylight.yangtools.yang.binding.DataObject;
23 import org.opendaylight.yangtools.yang.binding.DataObjectSerializerImplementation;
24 import org.opendaylight.yangtools.yang.binding.DataObjectSerializerRegistry;
25 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
29 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
34 import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
36 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 abstract class DataNodeContainerSerializerSource extends DataObjectSerializerSource {
41
42     private static final Logger LOG = LoggerFactory.getLogger(DataNodeContainerSerializerSource.class);
43
44     protected static final String INPUT = "_input";
45     private static final String CHOICE_PREFIX = "CHOICE_";
46
47     protected final DataNodeContainer schemaNode;
48     private final GeneratedType dtoType;
49
50     DataNodeContainerSerializerSource(final AbstractGenerator generator, final GeneratedType type,
51             final DataNodeContainer node) {
52         super(generator);
53         this.dtoType = Preconditions.checkNotNull(type);
54         this.schemaNode = Preconditions.checkNotNull(node);
55     }
56
57     /**
58      * Return the character sequence which should be used for start event.
59      *
60      * @return Start event character sequence
61      */
62     protected abstract CharSequence emitStartEvent();
63
64     @Override
65     protected CharSequence getSerializerBody() {
66         final StringBuilder sb = new StringBuilder();
67         sb.append("{\n");
68         sb.append(statement(assign(DataObjectSerializerRegistry.class.getName(), REGISTRY, "$1")));
69         sb.append(statement(assign(dtoType.getFullyQualifiedName(), INPUT,
70                 cast(dtoType.getFullyQualifiedName(), "$2"))));
71         sb.append(statement(assign(BindingStreamEventWriter.class.getName(), STREAM,
72             cast(BindingStreamEventWriter.class.getName(), "$3"))));
73         sb.append(statement(assign(BindingSerializer.class.getName(), SERIALIZER, null)));
74         sb.append("if (");
75         sb.append(STREAM);
76         sb.append(" instanceof ");
77         sb.append(BindingSerializer.class.getName());
78         sb.append(") {");
79         sb.append(statement(assign(SERIALIZER, cast(BindingSerializer.class.getName(), STREAM))));
80         sb.append('}');
81         sb.append(statement(emitStartEvent()));
82
83         emitBody(sb);
84         emitAfterBody(sb);
85         sb.append(statement(endNode()));
86         sb.append(statement("return null"));
87         sb.append('}');
88         return sb;
89     }
90
91     /**
92      * Allows for customization of emitting code, which is processed after
93      * normal DataNodeContainer body. Ideal for augmentations or others.
94      */
95     protected void emitAfterBody(final StringBuilder sb) {
96         // No-op
97     }
98
99     private static Map<String, Type> collectAllProperties(final GeneratedType type, final Map<String, Type> hashMap) {
100         for (final MethodSignature definition : type.getMethodDefinitions()) {
101             hashMap.put(definition.getName(), definition.getReturnType());
102         }
103         for (final Type parent : type.getImplements()) {
104             if (parent instanceof GeneratedType) {
105                 collectAllProperties((GeneratedType) parent, hashMap);
106             }
107         }
108         return hashMap;
109     }
110
111     private static String getGetterName(final DataSchemaNode node) {
112         final TypeDefinition<?> type;
113         if (node instanceof TypedDataSchemaNode) {
114             type = ((TypedDataSchemaNode) node).getType();
115         } else {
116             type = null;
117         }
118
119         final String prefix;
120         // Bug 8903: If it is a derived type of boolean or empty, not an inner type, then the return type
121         // of method would be the generated type of typedef not build-in types, so here it should be 'get'.
122         if ((type instanceof BooleanTypeDefinition || type instanceof EmptyTypeDefinition)
123                 && (type.getPath().equals(node.getPath()) || type.getBaseType() == null)) {
124             prefix = "is";
125         } else {
126             prefix = "get";
127         }
128         return prefix + BindingMapping.getGetterSuffix(node.getQName());
129     }
130
131     private void emitBody(final StringBuilder sb) {
132         final Map<String, Type> getterToType = collectAllProperties(dtoType, new HashMap<String, Type>());
133         for (final DataSchemaNode schemaChild : schemaNode.getChildNodes()) {
134             if (!schemaChild.isAugmenting()) {
135                 final String getter = getGetterName(schemaChild);
136                 final Type childType = getterToType.get(getter);
137                 if (childType == null) {
138                     // FIXME AnyXml nodes are ignored, since their type cannot be found in generated bindnig
139                     // Bug-706 https://bugs.opendaylight.org/show_bug.cgi?id=706
140                     if (schemaChild instanceof AnyXmlSchemaNode) {
141                         LOG.warn("Node {} will be ignored. AnyXml is not yet supported from binding aware code."
142                                 + "Binding Independent code can be used to serialize anyXml nodes.",
143                                 schemaChild.getPath());
144                         continue;
145                     }
146
147                     throw new IllegalStateException(
148                         String.format("Unable to find type for child node %s. Expected child nodes: %s",
149                             schemaChild.getPath(), getterToType));
150                 }
151                 emitChild(sb, getter, childType, schemaChild);
152             }
153         }
154     }
155
156     private void emitChild(final StringBuilder sb, final String getterName, final Type childType,
157             final DataSchemaNode schemaChild) {
158         sb.append(statement(assign(childType, getterName, cast(childType, invoke(INPUT, getterName)))));
159
160         sb.append("if (").append(getterName).append(" != null) {\n");
161         emitChildInner(sb, getterName, childType, schemaChild);
162         sb.append("}\n");
163     }
164
165     private void emitChildInner(final StringBuilder sb, final String getterName, final Type childType,
166             final DataSchemaNode child) {
167         if (child instanceof LeafSchemaNode) {
168             sb.append(statement(leafNode(child.getQName().getLocalName(), getterName)));
169         } else if (child instanceof AnyXmlSchemaNode) {
170             sb.append(statement(anyxmlNode(child.getQName().getLocalName(), getterName)));
171         } else if (child instanceof LeafListSchemaNode) {
172             final CharSequence startEvent;
173             if (((LeafListSchemaNode) child).isUserOrdered()) {
174                 startEvent = startOrderedLeafSet(child.getQName().getLocalName(),invoke(getterName, "size"));
175             } else {
176                 startEvent = startLeafSet(child.getQName().getLocalName(),invoke(getterName, "size"));
177             }
178             sb.append(statement(startEvent));
179             final Type valueType = ((ParameterizedType) childType).getActualTypeArguments()[0];
180             sb.append(forEach(getterName, valueType, statement(leafSetEntryNode(CURRENT))));
181             sb.append(statement(endNode()));
182         } else if (child instanceof ListSchemaNode) {
183             final Type valueType = ((ParameterizedType) childType).getActualTypeArguments()[0];
184             final ListSchemaNode casted = (ListSchemaNode) child;
185             emitList(sb, getterName, valueType, casted);
186         } else if (child instanceof ContainerSchemaNode) {
187             sb.append(tryToUseCacheElse(getterName,statement(staticInvokeEmitter(childType, getterName))));
188         } else if (child instanceof ChoiceSchemaNode) {
189             final String propertyName = CHOICE_PREFIX + childType.getName();
190             staticConstant(propertyName, DataObjectSerializerImplementation.class,
191                 ChoiceDispatchSerializer.from(loadClass(childType)));
192             sb.append(tryToUseCacheElse(getterName,statement(invoke(propertyName,
193                 StreamWriterGenerator.SERIALIZE_METHOD_NAME, REGISTRY, cast(DataObject.class.getName(), getterName),
194                 STREAM))));
195         }
196     }
197
198     private static StringBuilder tryToUseCacheElse(final String getterName, final CharSequence statement) {
199         final StringBuilder b = new StringBuilder();
200         b.append("if ( ");
201         b.append(SERIALIZER).append("== null || ");
202         b.append(invoke(SERIALIZER, "serialize", getterName)).append("== null");
203         b.append(") {");
204         b.append(statement);
205         b.append('}');
206         return b;
207     }
208
209     private void emitList(final StringBuilder sb, final String getterName, final Type valueType,
210             final ListSchemaNode child) {
211         final CharSequence startEvent;
212
213         sb.append(statement(assign("int", "_count", invoke(getterName, "size"))));
214         if (child.getKeyDefinition().isEmpty()) {
215             startEvent = startUnkeyedList(classReference(valueType), "_count");
216         } else if (child.isUserOrdered()) {
217             startEvent = startOrderedMapNode(classReference(valueType), "_count");
218         } else {
219             startEvent = startMapNode(classReference(valueType), "_count");
220         }
221         sb.append(statement(startEvent));
222         sb.append(forEach(getterName, valueType, tryToUseCacheElse(CURRENT, statement(staticInvokeEmitter(valueType,
223             CURRENT)))));
224         sb.append(statement(endNode()));
225     }
226 }