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.Collection;
13 import java.util.HashMap;
15 import org.opendaylight.mdsal.binding.javav2.dom.codec.generator.impl.StreamWriterGenerator;
16 import org.opendaylight.mdsal.binding.javav2.dom.codec.generator.spi.generator.AbstractGenerator;
17 import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.serializer.ChoiceDispatchSerializer;
18 import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifier;
19 import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifierNormalizer;
20 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType;
21 import org.opendaylight.mdsal.binding.javav2.model.api.MethodSignature;
22 import org.opendaylight.mdsal.binding.javav2.model.api.ParameterizedType;
23 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
24 import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
25 import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingSerializer;
26 import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingStreamEventWriter;
27 import org.opendaylight.mdsal.binding.javav2.spec.runtime.TreeNodeSerializerImplementation;
28 import org.opendaylight.mdsal.binding.javav2.spec.runtime.TreeNodeSerializerRegistry;
29 import org.opendaylight.yangtools.yang.common.QName;
30 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
34 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
39 import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
41 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
46 public abstract class AbstractDataNodeContainerSerializerSource extends AbstractTreeNodeSerializerSource {
48 private static final Logger LOG = LoggerFactory.getLogger(AbstractDataNodeContainerSerializerSource.class);
50 protected static final String INPUT = "_input";
51 private static final String CHOICE_PREFIX = "CHOICE_";
53 //Note: the field takes no effects by augmentation.
54 private final DataNodeContainer schemaNode;
55 private final GeneratedType dtoType;
57 public AbstractDataNodeContainerSerializerSource(final AbstractGenerator generator, final GeneratedType type,
58 final DataNodeContainer node) {
60 this.dtoType = Preconditions.checkNotNull(type);
61 this.schemaNode = Preconditions.checkNotNull(node);
65 * Return the character sequence which should be used for start event.
67 * @return Start event character sequence
69 protected abstract CharSequence emitStartEvent();
72 public CharSequence getSerializerBody() {
73 final StringBuilder builder = new StringBuilder();
74 builder.append("{\n");
75 builder.append(statement(assign(TreeNodeSerializerRegistry.class.getName(), REGISTRY, "$1")));
76 builder.append(statement(assign(dtoType.getFullyQualifiedName(), INPUT,
77 cast(dtoType.getFullyQualifiedName(), "$2"))));
78 builder.append(statement(assign(BindingStreamEventWriter.class.getName(), STREAM,
79 cast(BindingStreamEventWriter.class.getName(), "$3"))));
80 builder.append(statement(assign(BindingSerializer.class.getName(), SERIALIZER, null)));
81 builder.append("if (");
82 builder.append(STREAM);
83 builder.append(" instanceof ");
84 builder.append(BindingSerializer.class.getName());
85 builder.append(") {");
86 builder.append(statement(assign(SERIALIZER, cast(BindingSerializer.class.getName(), STREAM))));
88 builder.append(statement(emitStartEvent()));
91 emitAfterBody(builder);
92 builder.append(statement(endNode()));
93 builder.append(statement("return null"));
99 * Allows for customization of emitting code, which is processed after
100 * normal DataNodeContainer body. Ideal for augmentations or others.
102 protected void emitAfterBody(final StringBuilder builder) {
105 private static Map<String, Type> collectAllProperties(final GeneratedType type, final Map<String, Type> hashMap) {
106 for (final MethodSignature definition : type.getMethodDefinitions()) {
107 hashMap.put(definition.getName(), definition.getReturnType());
111 * According to binding v2 spec., uses nodes are processed as-if they are direct children
112 * of parent node, so we can't get properties from implements any more. Uses nodes are processed by invoking
113 * {@link org.opendaylight.mdsal.binding.javav2.generator.impl.GenHelperUtil#resolveDataSchemaNodes()} in which
114 * {@link org.opendaylight.mdsal.binding.javav2.generator.impl.GenHelperUtil#resolveDataSchemaNodesCheck()}
115 * allows them to be resolved.
121 private static String getGetterName(final DataSchemaNode node) {
122 final TypeDefinition<?> type;
123 if (node instanceof TypedDataSchemaNode) {
124 type = ((TypedDataSchemaNode) node).getType();
130 if (type instanceof BooleanTypeDefinition || type instanceof EmptyTypeDefinition) {
135 return prefix + JavaIdentifierNormalizer.normalizeSpecificIdentifier(node.getQName().getLocalName(),
136 JavaIdentifier.CLASS);
139 private boolean emitCheck(final DataSchemaNode schemaChild) {
140 if (schemaChild.isAugmenting()) {
141 QName root = schemaChild.getPath().getPathFromRoot().iterator().next();
142 return root.getModule().equals(schemaChild.getQName().getModule());
149 * Note: the method would be overrided by {@link AbstractAugmentSerializerSource#getChildNodes()},
150 * since all augmentation schema nodes of same target would be grouped into sort of one node,
151 * so call {@link AbstractAugmentSerializerSource#getChildNodes()} to get all these children
152 * nodes of same target augmentation schemas.
154 protected Collection<DataSchemaNode> getChildNodes() {
155 return schemaNode.getChildNodes();
158 private void emitBody(final StringBuilder builder) {
159 final Map<String, Type> getterToType = collectAllProperties(dtoType, new HashMap<String, Type>());
160 for (final DataSchemaNode schemaChild : getChildNodes()) {
162 * As before, it only emitted data nodes which were not added by uses or augment, now
163 * according to binding v2 specification, augment of the same module is same as inlining,
164 * all data node children should be processed as-if they were directly defined inside
167 if (emitCheck(schemaChild)) {
168 final String getter = getGetterName(schemaChild);
169 final Type childType = getterToType.get(getter);
170 if (childType == null) {
171 // FIXME AnyXml nodes are ignored, since their type cannot be found in generated bindnig
172 // Bug-706 https://bugs.opendaylight.org/show_bug.cgi?id=706
173 if (schemaChild instanceof AnyXmlSchemaNode) {
174 LOG.warn("Node {} will be ignored. AnyXml is not yet supported from binding aware code."
175 + "Binding Independent code can be used to serialize anyXml nodes.", schemaChild.getPath());
179 throw new IllegalStateException(
180 String.format("Unable to find type for child node %s. Expected child nodes: %s",
181 schemaChild.getPath(), getterToType));
183 emitChild(builder, getter, childType, schemaChild);
188 private void emitChild(final StringBuilder builder, final String getterName, final Type childType,
189 final DataSchemaNode schemaChild) {
190 builder.append(statement(assign(childType, getterName, cast(childType, invoke(INPUT, getterName)))));
192 builder.append("if (").append(getterName).append(" != null) {\n");
193 emitChildInner(builder, getterName, childType, schemaChild);
194 builder.append("}\n");
197 private void emitChildInner(final StringBuilder builder, final String getterName, final Type childType,
198 final DataSchemaNode child) {
199 if (child instanceof LeafSchemaNode) {
200 builder.append(statement(leafNode(child.getQName().getLocalName(), getterName)));
201 } else if (child instanceof AnyXmlSchemaNode) {
202 builder.append(statement(anyxmlNode(child.getQName().getLocalName(), getterName)));
203 } else if (child instanceof LeafListSchemaNode) {
204 final CharSequence startEvent;
205 if (((LeafListSchemaNode) child).isUserOrdered()) {
206 startEvent = startOrderedLeafSet(child.getQName().getLocalName(),
207 invoke(getterName, "size"));
209 startEvent = startLeafSet(child.getQName().getLocalName(),invoke(getterName, "size"));
211 builder.append(statement(startEvent));
212 final Type valueType = ((ParameterizedType) childType).getActualTypeArguments()[0];
213 builder.append(forEach(getterName, valueType, statement(leafSetEntryNode(CURRENT))));
214 builder.append(statement(endNode()));
215 } else if (child instanceof ListSchemaNode) {
216 final Type valueType = ((ParameterizedType) childType).getActualTypeArguments()[0];
217 final ListSchemaNode casted = (ListSchemaNode) child;
218 emitList(builder, getterName, valueType, casted);
219 } else if (child instanceof ContainerSchemaNode) {
220 builder.append(tryToUseCacheElse(getterName,statement(staticInvokeEmitter(childType, getterName))));
221 } else if (child instanceof ChoiceSchemaNode) {
222 final String propertyName = CHOICE_PREFIX + childType.getName();
223 staticConstant(propertyName, TreeNodeSerializerImplementation.class,
224 ChoiceDispatchSerializer.from(loadClass(childType)));
225 builder.append(tryToUseCacheElse(getterName,statement(invoke(propertyName,
226 StreamWriterGenerator.SERIALIZE_METHOD_NAME, REGISTRY, cast(TreeNode.class.getName(),getterName),
231 private static StringBuilder tryToUseCacheElse(final String getterName, final CharSequence statement) {
232 final StringBuilder b = new StringBuilder();
235 b.append(SERIALIZER).append("== null || ");
236 b.append(invoke(SERIALIZER, "serialize", getterName)).append("== null");
243 private void emitList(final StringBuilder builer, final String getterName, final Type valueType,
244 final ListSchemaNode child) {
245 final CharSequence startEvent;
247 builer.append(statement(assign("int", "_count", invoke(getterName, "size"))));
248 if (child.getKeyDefinition().isEmpty()) {
249 startEvent = startUnkeyedList(classReference(valueType), "_count");
250 } else if (child.isUserOrdered()) {
251 startEvent = startOrderedMapNode(classReference(valueType), "_count");
253 startEvent = startMapNode(classReference(valueType), "_count");
255 builer.append(statement(startEvent));
256 builer.append(forEach(getterName, valueType, tryToUseCacheElse(CURRENT,
257 statement(staticInvokeEmitter(valueType, CURRENT)))));
258 builer.append(statement(endNode()));