8ef687dd330e37b1ae86ea9213c318e08f7e52a5
[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.yangtools.yang.binding.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.TypedSchemaNode;
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, final DataNodeContainer node) {
51         super(generator);
52         this.dtoType = Preconditions.checkNotNull(type);
53         this.schemaNode = Preconditions.checkNotNull(node);
54     }
55
56     /**
57      * Return the character sequence which should be used for start event.
58      *
59      * @return Start event character sequence
60      */
61     protected abstract CharSequence emitStartEvent();
62
63     @Override
64     protected CharSequence getSerializerBody() {
65         final StringBuilder b = new StringBuilder();
66         b.append("{\n");
67         b.append(statement(assign(DataObjectSerializerRegistry.class.getName(), REGISTRY, "$1")));
68         b.append(statement(assign(dtoType.getFullyQualifiedName(), INPUT,
69                 cast(dtoType.getFullyQualifiedName(), "$2"))));
70         b.append(statement(assign(BindingStreamEventWriter.class.getName(), STREAM, cast(BindingStreamEventWriter.class.getName(), "$3"))));
71         b.append(statement(assign(BindingSerializer.class.getName(), SERIALIZER, null)));
72         b.append("if (");
73         b.append(STREAM);
74         b.append(" instanceof ");
75         b.append(BindingSerializer.class.getName());
76         b.append(") {");
77         b.append(statement(assign(SERIALIZER, cast(BindingSerializer.class.getName(), STREAM))));
78         b.append('}');
79         b.append(statement(emitStartEvent()));
80
81         emitBody(b);
82         emitAfterBody(b);
83         b.append(statement(endNode()));
84         b.append(statement("return null"));
85         b.append('}');
86         return b;
87     }
88
89     /**
90      * Allows for customization of emitting code, which is processed after
91      * normal DataNodeContainer body. Ideal for augmentations or others.
92      */
93     protected void emitAfterBody(final StringBuilder b) {
94         // No-op
95     }
96
97     private static Map<String, Type> collectAllProperties(final GeneratedType type, final Map<String, Type> hashMap) {
98         for (final MethodSignature definition : type.getMethodDefinitions()) {
99             hashMap.put(definition.getName(), definition.getReturnType());
100         }
101         for (final Type parent : type.getImplements()) {
102             if (parent instanceof GeneratedType) {
103                 collectAllProperties((GeneratedType) parent, hashMap);
104             }
105         }
106         return hashMap;
107     }
108
109     private static String getGetterName(final DataSchemaNode node) {
110         final TypeDefinition<?> type;
111         if (node instanceof TypedSchemaNode) {
112             type = ((TypedSchemaNode) node).getType();
113         } else {
114             type = null;
115         }
116
117         final String prefix;
118         if (type instanceof BooleanTypeDefinition || type instanceof EmptyTypeDefinition) {
119             prefix = "is";
120         } else {
121             prefix = "get";
122         }
123         return prefix + BindingMapping.getGetterSuffix(node.getQName());
124     }
125
126     private void emitBody(final StringBuilder b) {
127         final Map<String, Type> getterToType = collectAllProperties(dtoType, new HashMap<String, Type>());
128         for (final DataSchemaNode schemaChild : schemaNode.getChildNodes()) {
129             if (!schemaChild.isAugmenting()) {
130                 final String getter = getGetterName(schemaChild);
131                 final Type childType = getterToType.get(getter);
132                 if (childType == null) {
133                     // FIXME AnyXml nodes are ignored, since their type cannot be found in generated bindnig
134                     // Bug-706 https://bugs.opendaylight.org/show_bug.cgi?id=706
135                     if (schemaChild instanceof AnyXmlSchemaNode) {
136                         LOG.warn("Node {} will be ignored. AnyXml is not yet supported from binding aware code." +
137                                 "Binding Independent code can be used to serialize anyXml nodes.", schemaChild.getPath());
138                         continue;
139                     }
140
141                     throw new IllegalStateException(
142                         String.format("Unable to find type for child node %s. Expected child nodes: %s",
143                             schemaChild.getPath(), getterToType));
144                 }
145                 emitChild(b, getter, childType, schemaChild);
146             }
147         }
148     }
149
150     private void emitChild(final StringBuilder b, final String getterName, final Type childType,
151             final DataSchemaNode schemaChild) {
152         b.append(statement(assign(childType, getterName, cast(childType, invoke(INPUT, getterName)))));
153
154         b.append("if (").append(getterName).append(" != null) {\n");
155         emitChildInner(b, getterName, childType, schemaChild);
156         b.append("}\n");
157     }
158
159     private void emitChildInner(final StringBuilder b, final String getterName, final Type childType,
160             final DataSchemaNode child) {
161         if (child instanceof LeafSchemaNode) {
162             b.append(statement(leafNode(child.getQName().getLocalName(), getterName)));
163         } else if (child instanceof AnyXmlSchemaNode) {
164             b.append(statement(anyxmlNode(child.getQName().getLocalName(), getterName)));
165         } else if (child instanceof LeafListSchemaNode) {
166             final CharSequence startEvent;
167             if (((LeafListSchemaNode) child).isUserOrdered()) {
168                 startEvent = startOrderedLeafSet(child.getQName().getLocalName(),invoke(getterName, "size"));
169             } else {
170                 startEvent = startLeafSet(child.getQName().getLocalName(),invoke(getterName, "size"));
171             }
172             b.append(statement(startEvent));
173             final Type valueType = ((ParameterizedType) childType).getActualTypeArguments()[0];
174             b.append(forEach(getterName, valueType, statement(leafSetEntryNode(CURRENT))));
175             b.append(statement(endNode()));
176         } else if (child instanceof ListSchemaNode) {
177             final Type valueType = ((ParameterizedType) childType).getActualTypeArguments()[0];
178             final ListSchemaNode casted = (ListSchemaNode) child;
179             emitList(b, getterName, valueType, casted);
180         } else if (child instanceof ContainerSchemaNode) {
181             b.append(tryToUseCacheElse(getterName,statement(staticInvokeEmitter(childType, getterName))));
182         } else if (child instanceof ChoiceSchemaNode) {
183             final String propertyName = CHOICE_PREFIX + childType.getName();
184             staticConstant(propertyName, DataObjectSerializerImplementation.class, ChoiceDispatchSerializer.from(loadClass(childType)));
185             b.append(tryToUseCacheElse(getterName,statement(invoke(propertyName, StreamWriterGenerator.SERIALIZE_METHOD_NAME, REGISTRY, cast(DataObject.class.getName(),getterName), STREAM))));
186         }
187     }
188
189     private static StringBuilder tryToUseCacheElse(final String getterName, final CharSequence statement) {
190         final StringBuilder b = new StringBuilder();
191
192         b.append("if ( ");
193         b.append(SERIALIZER).append("== null || ");
194         b.append(invoke(SERIALIZER, "serialize", getterName)).append("== null");
195         b.append(") {");
196         b.append(statement);
197         b.append('}');
198         return b;
199     }
200
201     private void emitList(final StringBuilder b, final String getterName, final Type valueType,
202             final ListSchemaNode child) {
203         final CharSequence startEvent;
204
205         b.append(statement(assign("int", "_count", invoke(getterName, "size"))));
206         if (child.getKeyDefinition().isEmpty()) {
207             startEvent = startUnkeyedList(classReference(valueType), "_count");
208         } else if (child.isUserOrdered()) {
209             startEvent = startOrderedMapNode(classReference(valueType), "_count");
210         } else {
211             startEvent = startMapNode(classReference(valueType), "_count");
212         }
213         b.append(statement(startEvent));
214         b.append(forEach(getterName, valueType, tryToUseCacheElse(CURRENT,statement(staticInvokeEmitter(valueType, CURRENT)))));
215         b.append(statement(endNode()));
216     }
217 }