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