2 * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.binding.javav2.dom.codec.generator.spi.source;
10 import com.google.common.annotations.Beta;
11 import com.google.common.base.Preconditions;
12 import java.util.HashMap;
14 import org.opendaylight.mdsal.binding.javav2.dom.codec.generator.impl.StreamWriterGenerator;
15 import org.opendaylight.mdsal.binding.javav2.dom.codec.generator.spi.generator.AbstractGenerator;
16 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.serializer.ChoiceDispatchSerializer;
17 import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifier;
18 import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifierNormalizer;
19 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType;
20 import org.opendaylight.mdsal.binding.javav2.model.api.MethodSignature;
21 import org.opendaylight.mdsal.binding.javav2.model.api.ParameterizedType;
22 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
23 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
24 import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingSerializer;
25 import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingStreamEventWriter;
26 import org.opendaylight.mdsal.binding.javav2.spec.runtime.TreeNodeSerializerImplementation;
27 import org.opendaylight.mdsal.binding.javav2.spec.runtime.TreeNodeSerializerRegistry;
28 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
32 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
37 import org.opendaylight.yangtools.yang.model.api.TypedSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
39 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
44 public abstract class AbstractDataNodeContainerSerializerSource extends AbstractTreeNodeSerializerSource {
46 private static final Logger LOG = LoggerFactory.getLogger(AbstractDataNodeContainerSerializerSource.class);
48 protected static final String INPUT = "_input";
49 private static final String CHOICE_PREFIX = "CHOICE_";
51 private final DataNodeContainer schemaNode;
52 private final GeneratedType dtoType;
54 public AbstractDataNodeContainerSerializerSource(final AbstractGenerator generator, final GeneratedType type,
55 final DataNodeContainer node) {
57 this.dtoType = Preconditions.checkNotNull(type);
58 this.schemaNode = Preconditions.checkNotNull(node);
62 * Return the character sequence which should be used for start event.
64 * @return Start event character sequence
66 protected abstract CharSequence emitStartEvent();
69 public CharSequence getSerializerBody() {
70 final StringBuilder b = new StringBuilder();
72 b.append(statement(assign(TreeNodeSerializerRegistry.class.getName(), REGISTRY, "$1")));
73 b.append(statement(assign(dtoType.getFullyQualifiedName(), INPUT,
74 cast(dtoType.getFullyQualifiedName(), "$2"))));
75 b.append(statement(assign(BindingStreamEventWriter.class.getName(), STREAM, cast(BindingStreamEventWriter.class.getName(), "$3"))));
76 b.append(statement(assign(BindingSerializer.class.getName(), SERIALIZER, null)));
79 b.append(" instanceof ");
80 b.append(BindingSerializer.class.getName());
82 b.append(statement(assign(SERIALIZER, cast(BindingSerializer.class.getName(), STREAM))));
84 b.append(statement(emitStartEvent()));
88 b.append(statement(endNode()));
89 b.append(statement("return null"));
95 * Allows for customization of emitting code, which is processed after
96 * normal DataNodeContainer body. Ideal for augmentations or others.
98 protected void emitAfterBody(final StringBuilder b) {
101 private static Map<String, Type> collectAllProperties(final GeneratedType type, final Map<String, Type> hashMap) {
102 for (final MethodSignature definition : type.getMethodDefinitions()) {
103 hashMap.put(definition.getName(), definition.getReturnType());
105 for (final Type parent : type.getImplements()) {
106 if (parent instanceof GeneratedType) {
107 collectAllProperties((GeneratedType) parent, hashMap);
113 private static String getGetterName(final DataSchemaNode node) {
114 final TypeDefinition<?> type;
115 if (node instanceof TypedSchemaNode) {
116 type = ((TypedSchemaNode) node).getType();
122 if (type instanceof BooleanTypeDefinition || type instanceof EmptyTypeDefinition) {
127 return prefix + JavaIdentifierNormalizer.normalizeSpecificIdentifier(node.getQName().getLocalName(), JavaIdentifier.CLASS);
130 private void emitBody(final StringBuilder b) {
131 final Map<String, Type> getterToType = collectAllProperties(dtoType, new HashMap<String, Type>());
132 for (final DataSchemaNode schemaChild : schemaNode.getChildNodes()) {
133 if (!schemaChild.isAugmenting()) {
134 final String getter = getGetterName(schemaChild);
135 final Type childType = getterToType.get(getter);
136 if (childType == null) {
137 // FIXME AnyXml nodes are ignored, since their type cannot be found in generated bindnig
138 // Bug-706 https://bugs.opendaylight.org/show_bug.cgi?id=706
139 if (schemaChild instanceof AnyXmlSchemaNode) {
140 LOG.warn("Node {} will be ignored. AnyXml is not yet supported from binding aware code." +
141 "Binding Independent code can be used to serialize anyXml nodes.", schemaChild.getPath());
145 throw new IllegalStateException(
146 String.format("Unable to find type for child node %s. Expected child nodes: %s",
147 schemaChild.getPath(), getterToType));
149 emitChild(b, getter, childType, schemaChild);
154 private void emitChild(final StringBuilder b, final String getterName, final Type childType,
155 final DataSchemaNode schemaChild) {
156 b.append(statement(assign(childType, getterName, cast(childType, invoke(INPUT, getterName)))));
158 b.append("if (").append(getterName).append(" != null) {\n");
159 emitChildInner(b, getterName, childType, schemaChild);
163 private void emitChildInner(final StringBuilder b, final String getterName, final Type childType,
164 final DataSchemaNode child) {
165 if (child instanceof LeafSchemaNode) {
166 b.append(statement(leafNode(child.getQName().getLocalName(), getterName)));
167 } else if (child instanceof AnyXmlSchemaNode) {
168 b.append(statement(anyxmlNode(child.getQName().getLocalName(), getterName)));
169 } else if (child instanceof LeafListSchemaNode) {
170 final CharSequence startEvent;
171 if (((LeafListSchemaNode) child).isUserOrdered()) {
172 startEvent = startOrderedLeafSet(child.getQName().getLocalName(),invoke(getterName, "size"));
174 startEvent = startLeafSet(child.getQName().getLocalName(),invoke(getterName, "size"));
176 b.append(statement(startEvent));
177 final Type valueType = ((ParameterizedType) childType).getActualTypeArguments()[0];
178 b.append(forEach(getterName, valueType, statement(leafSetEntryNode(CURRENT))));
179 b.append(statement(endNode()));
180 } else if (child instanceof ListSchemaNode) {
181 final Type valueType = ((ParameterizedType) childType).getActualTypeArguments()[0];
182 final ListSchemaNode casted = (ListSchemaNode) child;
183 emitList(b, getterName, valueType, casted);
184 } else if (child instanceof ContainerSchemaNode) {
185 b.append(tryToUseCacheElse(getterName,statement(staticInvokeEmitter(childType, getterName))));
186 } else if (child instanceof ChoiceSchemaNode) {
187 final String propertyName = CHOICE_PREFIX + childType.getName();
188 staticConstant(propertyName, TreeNodeSerializerImplementation.class, ChoiceDispatchSerializer.from(loadClass(childType)));
189 b.append(tryToUseCacheElse(getterName,statement(invoke(propertyName, StreamWriterGenerator.SERIALIZE_METHOD_NAME, REGISTRY, cast(TreeNode.class.getName(),getterName), STREAM))));
193 private static StringBuilder tryToUseCacheElse(final String getterName, final CharSequence statement) {
194 final StringBuilder b = new StringBuilder();
197 b.append(SERIALIZER).append("== null || ");
198 b.append(invoke(SERIALIZER, "serialize", getterName)).append("== null");
205 private void emitList(final StringBuilder b, final String getterName, final Type valueType,
206 final ListSchemaNode child) {
207 final CharSequence startEvent;
209 b.append(statement(assign("int", "_count", invoke(getterName, "size"))));
210 if (child.getKeyDefinition().isEmpty()) {
211 startEvent = startUnkeyedList(classReference(valueType), "_count");
212 } else if (child.isUserOrdered()) {
213 startEvent = startOrderedMapNode(classReference(valueType), "_count");
215 startEvent = startMapNode(classReference(valueType), "_count");
217 b.append(statement(startEvent));
218 b.append(forEach(getterName, valueType, tryToUseCacheElse(CURRENT,statement(staticInvokeEmitter(valueType, CURRENT)))));
219 b.append(statement(endNode()));