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