Bug 1869: Fixed binding-data-codec to work with empty type
[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.BindingStreamEventWriter;
19 import org.opendaylight.yangtools.yang.binding.DataObject;
20 import org.opendaylight.yangtools.yang.binding.DataObjectSerializerImplementation;
21 import org.opendaylight.yangtools.yang.binding.DataObjectSerializerRegistry;
22 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
24 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
26 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
31 import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
32 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
33
34 abstract class DataNodeContainerSerializerSource extends DataObjectSerializerSource {
35
36     protected static final String INPUT = "_input";
37     private static final String CHOICE_PREFIX = "CHOICE_";
38
39     protected final DataNodeContainer schemaNode;
40     private final GeneratedType dtoType;
41
42     DataNodeContainerSerializerSource(final AbstractGenerator generator, final GeneratedType type, final DataNodeContainer node) {
43         super(generator);
44         this.dtoType = Preconditions.checkNotNull(type);
45         this.schemaNode = Preconditions.checkNotNull(node);
46     }
47
48     /**
49      * Return the character sequence which should be used for start event.
50      *
51      * @return Start event character sequence
52      */
53     protected abstract CharSequence emitStartEvent();
54
55     @Override
56     protected CharSequence getSerializerBody() {
57         StringBuilder b = new StringBuilder();
58         b.append("{\n");
59         b.append(statement(assign(DataObjectSerializerRegistry.class.getName(), REGISTRY, "$1")));
60         b.append(statement(assign(dtoType.getFullyQualifiedName(), INPUT,
61                 cast(dtoType.getFullyQualifiedName(), "$2"))));
62         b.append(statement(assign(BindingStreamEventWriter.class.getName(), STREAM, cast(BindingStreamEventWriter.class.getName(), "$3"))));
63         b.append(statement(emitStartEvent()));
64
65         emitBody(b);
66         emitAfterBody(b);
67         b.append(statement(endNode()));
68         b.append(statement("return null"));
69         b.append('}');
70         return b;
71     }
72
73     /**
74      * Allows for customization of emitting code, which is processed after
75      * normal DataNodeContainer body. Ideal for augmentations or others.
76      */
77     protected void emitAfterBody(final StringBuilder b) {
78         // No-op
79     }
80
81     private static Map<String, Type> collectAllProperties(final GeneratedType type, final Map<String, Type> hashMap) {
82         for (MethodSignature definition : type.getMethodDefinitions()) {
83             hashMap.put(definition.getName(), definition.getReturnType());
84         }
85         for (Type parent : type.getImplements()) {
86             if (parent instanceof GeneratedType) {
87                 collectAllProperties((GeneratedType) parent, hashMap);
88             }
89         }
90         return hashMap;
91     }
92
93     private static final String getGetterName(final DataSchemaNode node) {
94         final TypeDefinition<?> type ;
95         if (node instanceof LeafSchemaNode) {
96             type = ((LeafSchemaNode) node).getType();
97         } else if(node instanceof LeafListSchemaNode) {
98             type = ((LeafListSchemaNode) node).getType();
99         } else {
100             type = null;
101         }
102         String prefix = "get";
103         if(type != null) {
104             TypeDefinition<?> rootType = type;
105             while (rootType.getBaseType() != null) {
106                 rootType = rootType.getBaseType();
107             }
108             if(rootType instanceof BooleanTypeDefinition || rootType instanceof EmptyTypeDefinition) {
109                 prefix = "is";
110             }
111         }
112
113         return prefix + BindingMapping.getClassName(node.getQName().getLocalName());
114     }
115
116     private void emitBody(final StringBuilder b) {
117         Map<String, Type> getterToType = collectAllProperties(dtoType, new HashMap<String, Type>());
118         for (DataSchemaNode schemaChild : schemaNode.getChildNodes()) {
119             if (!schemaChild.isAugmenting()) {
120                 String getter = getGetterName(schemaChild);
121                 Type childType = getterToType.get(getter);
122                 emitChild(b, getter, childType, schemaChild);
123             }
124         }
125     }
126
127     private void emitChild(final StringBuilder b, final String getterName, final Type childType,
128             final DataSchemaNode schemaChild) {
129         b.append(statement(assign(childType, getterName, cast(childType, invoke(INPUT, getterName)))));
130
131         b.append("if (").append(getterName).append(" != null) {\n");
132         emitChildInner(b, getterName, childType, schemaChild);
133         b.append("}\n");
134     }
135
136     private void emitChildInner(final StringBuilder b, final String getterName, final Type childType,
137             final DataSchemaNode child) {
138         if (child instanceof LeafSchemaNode) {
139             b.append(statement(leafNode(child.getQName().getLocalName(), getterName)));
140         } else if (child instanceof AnyXmlSchemaNode) {
141             b.append(statement(anyxmlNode(child.getQName().getLocalName(), getterName)));
142         } else if (child instanceof LeafListSchemaNode) {
143             b.append(statement(startLeafSet(child.getQName().getLocalName(),invoke(getterName, "size"))));
144             Type valueType = ((ParameterizedType) childType).getActualTypeArguments()[0];
145             b.append(forEach(getterName, valueType, statement(leafSetEntryNode(CURRENT))));
146             b.append(statement(endNode()));
147         } else if (child instanceof ListSchemaNode) {
148             Type valueType = ((ParameterizedType) childType).getActualTypeArguments()[0];
149             ListSchemaNode casted = (ListSchemaNode) child;
150             emitList(b, getterName, valueType, casted);
151         } else if (child instanceof ContainerSchemaNode) {
152             b.append(statement(staticInvokeEmitter(childType, getterName)));
153         } else if (child instanceof ChoiceNode) {
154             String propertyName = CHOICE_PREFIX + childType.getName();
155             staticConstant(propertyName, DataObjectSerializerImplementation.class, ChoiceDispatchSerializer.from(loadClass(childType)));
156             b.append(statement(invoke(propertyName, StreamWriterGenerator.SERIALIZE_METHOD_NAME, REGISTRY, cast(DataObject.class.getName(),getterName), STREAM)));
157         }
158     }
159
160     private void emitList(final StringBuilder b, final String getterName, final Type valueType,
161             final ListSchemaNode child) {
162         final CharSequence startEvent;
163
164         b.append(statement(assign("int", "_count", invoke(getterName, "size"))));
165         if (child.getKeyDefinition().isEmpty()) {
166             startEvent = startUnkeyedList(classReference(valueType), "_count");
167         } else if (child.isUserOrdered()) {
168             startEvent = startOrderedMapNode(classReference(valueType), "_count");
169         } else {
170             startEvent = startMapNode(classReference(valueType), "_count");
171         }
172         b.append(statement(startEvent));
173         b.append(forEach(getterName, valueType, statement(staticInvokeEmitter(valueType, CURRENT))));
174         b.append(statement(endNode()));
175     }
176 }