--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.javav2.dom.codec.generator.impl;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.generator.api.TreeNodeSerializerGenerator;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.generator.spi.generator.AbstractStreamWriterGenerator;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.generator.spi.source.AbstractAugmentableDataNodeContainerEmitterSource;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.generator.spi.source.AbstractDataNodeContainerSerializerSource;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.generator.spi.source.AbstractTreeNodeSerializerSource;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.serializer.AugmentableDispatchSerializer;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.serializer.ChoiceDispatchSerializer;
+import org.opendaylight.mdsal.binding.javav2.generator.impl.util.javassist.JavassistUtils;
+import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingStreamEventWriter;
+import org.opendaylight.mdsal.binding.javav2.spec.runtime.TreeNodeSerializerImplementation;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
+
+/**
+ * Concrete implementation of {@link AbstractStreamWriterGenerator} which in
+ * runtime generates classes implementing
+ * {@link TreeNodeSerializerImplementation} interface and are used to serialize
+ * Binding {@link TreeNode}.
+ *
+ * Actual implementation of codecs is done via static methods, which allows for
+ * static wiring of codecs. Choice codec and Augmentable codecs are static
+ * properties of parent codec and stateless implementations are used (
+ * {@link ChoiceDispatchSerializer}, {@link AugmentableDispatchSerializer},
+ * which uses registry to dispatch to concrete item codec.
+ *
+ */
+@Beta
+public class StreamWriterGenerator extends AbstractStreamWriterGenerator {
+
+ private static final String UNKNOWN_SIZE = BindingStreamEventWriter.class.getName() + ".UNKNOWN_SIZE";
+
+ private StreamWriterGenerator(final JavassistUtils utils, final Void ignore) {
+ super(utils);
+ }
+
+ /**
+ * Create a new instance backed by a specific {@link JavassistUtils}
+ * instance.
+ *
+ * @param utils
+ * JavassistUtils instance to use
+ * @return A new generator
+ */
+ public static TreeNodeSerializerGenerator create(final JavassistUtils utils) {
+ return new StreamWriterGenerator(utils, null);
+ }
+
+ @Override
+ protected AbstractTreeNodeSerializerSource generateContainerSerializer(final GeneratedType type,
+ final ContainerSchemaNode node) {
+
+ return new AbstractAugmentableDataNodeContainerEmitterSource(this, type, node) {
+ @Override
+ public CharSequence emitStartEvent() {
+ return startContainerNode(classReference(type), UNKNOWN_SIZE);
+ }
+ };
+ }
+
+ @Override
+ protected AbstractTreeNodeSerializerSource generateNotificationSerializer(final GeneratedType type,
+ final NotificationDefinition node) {
+
+ return new AbstractAugmentableDataNodeContainerEmitterSource(this, type, node) {
+ @Override
+ public CharSequence emitStartEvent() {
+ return startContainerNode(classReference(type), UNKNOWN_SIZE);
+ }
+ };
+ }
+
+ @Override
+ protected AbstractTreeNodeSerializerSource generateCaseSerializer(final GeneratedType type,
+ final ChoiceCaseNode node) {
+ return new AbstractAugmentableDataNodeContainerEmitterSource(this, type, node) {
+ @Override
+ public CharSequence emitStartEvent() {
+ return startCaseNode(classReference(type), UNKNOWN_SIZE);
+ }
+ };
+ }
+
+ @Override
+ protected AbstractTreeNodeSerializerSource generateUnkeyedListEntrySerializer(final GeneratedType type,
+ final ListSchemaNode node) {
+ return new AbstractAugmentableDataNodeContainerEmitterSource(this, type, node) {
+
+ @Override
+ public CharSequence emitStartEvent() {
+ return startUnkeyedListItem(UNKNOWN_SIZE);
+ }
+ };
+ }
+
+ @Override
+ protected AbstractTreeNodeSerializerSource generateSerializer(final GeneratedType type,
+ final AugmentationSchema schema) {
+ return new AbstractDataNodeContainerSerializerSource(this, type, schema) {
+
+ @Override
+ public CharSequence emitStartEvent() {
+ return startAugmentationNode(classReference(type));
+ }
+ };
+ }
+
+ @Override
+ protected AbstractTreeNodeSerializerSource generateMapEntrySerializer(final GeneratedType type,
+ final ListSchemaNode node) {
+ return new AbstractAugmentableDataNodeContainerEmitterSource(this, type, node) {
+ @Override
+ public CharSequence emitStartEvent() {
+ return startMapEntryNode(invoke(INPUT, "getKey"), UNKNOWN_SIZE);
+ }
+ };
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.javav2.dom.codec.generator.spi.source;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import java.util.HashMap;
+import java.util.Map;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.generator.impl.StreamWriterGenerator;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.generator.spi.generator.AbstractGenerator;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.serializer.ChoiceDispatchSerializer;
+import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifier;
+import org.opendaylight.mdsal.binding.javav2.generator.util.JavaIdentifierNormalizer;
+import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType;
+import org.opendaylight.mdsal.binding.javav2.model.api.MethodSignature;
+import org.opendaylight.mdsal.binding.javav2.model.api.ParameterizedType;
+import org.opendaylight.mdsal.binding.javav2.model.api.Type;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingSerializer;
+import org.opendaylight.mdsal.binding.javav2.spec.runtime.BindingStreamEventWriter;
+import org.opendaylight.mdsal.binding.javav2.spec.runtime.TreeNodeSerializerImplementation;
+import org.opendaylight.mdsal.binding.javav2.spec.runtime.TreeNodeSerializerRegistry;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypedSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Beta
+public abstract class AbstractDataNodeContainerSerializerSource extends AbstractTreeNodeSerializerSource {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractDataNodeContainerSerializerSource.class);
+
+ protected static final String INPUT = "_input";
+ private static final String CHOICE_PREFIX = "CHOICE_";
+
+ private final DataNodeContainer schemaNode;
+ private final GeneratedType dtoType;
+
+ public AbstractDataNodeContainerSerializerSource(final AbstractGenerator generator, final GeneratedType type,
+ final DataNodeContainer node) {
+ super(generator);
+ this.dtoType = Preconditions.checkNotNull(type);
+ this.schemaNode = Preconditions.checkNotNull(node);
+ }
+
+ /**
+ * Return the character sequence which should be used for start event.
+ *
+ * @return Start event character sequence
+ */
+ protected abstract CharSequence emitStartEvent();
+
+ @Override
+ public CharSequence getSerializerBody() {
+ final StringBuilder b = new StringBuilder();
+ b.append("{\n");
+ b.append(statement(assign(TreeNodeSerializerRegistry.class.getName(), REGISTRY, "$1")));
+ b.append(statement(assign(dtoType.getFullyQualifiedName(), INPUT,
+ cast(dtoType.getFullyQualifiedName(), "$2"))));
+ b.append(statement(assign(BindingStreamEventWriter.class.getName(), STREAM, cast(BindingStreamEventWriter.class.getName(), "$3"))));
+ b.append(statement(assign(BindingSerializer.class.getName(), SERIALIZER, null)));
+ b.append("if (");
+ b.append(STREAM);
+ b.append(" instanceof ");
+ b.append(BindingSerializer.class.getName());
+ b.append(") {");
+ b.append(statement(assign(SERIALIZER, cast(BindingSerializer.class.getName(), STREAM))));
+ b.append('}');
+ b.append(statement(emitStartEvent()));
+
+ emitBody(b);
+ emitAfterBody(b);
+ b.append(statement(endNode()));
+ b.append(statement("return null"));
+ b.append('}');
+ return b;
+ }
+
+ /**
+ * Allows for customization of emitting code, which is processed after
+ * normal DataNodeContainer body. Ideal for augmentations or others.
+ */
+ protected void emitAfterBody(final StringBuilder b) {
+ }
+
+ private static Map<String, Type> collectAllProperties(final GeneratedType type, final Map<String, Type> hashMap) {
+ for (final MethodSignature definition : type.getMethodDefinitions()) {
+ hashMap.put(definition.getName(), definition.getReturnType());
+ }
+ for (final Type parent : type.getImplements()) {
+ if (parent instanceof GeneratedType) {
+ collectAllProperties((GeneratedType) parent, hashMap);
+ }
+ }
+ return hashMap;
+ }
+
+ private static String getGetterName(final DataSchemaNode node) {
+ final TypeDefinition<?> type;
+ if (node instanceof TypedSchemaNode) {
+ type = ((TypedSchemaNode) node).getType();
+ } else {
+ type = null;
+ }
+
+ final String prefix;
+ if (type instanceof BooleanTypeDefinition || type instanceof EmptyTypeDefinition) {
+ prefix = "is";
+ } else {
+ prefix = "get";
+ }
+ return prefix + JavaIdentifierNormalizer.normalizeSpecificIdentifier(node.getQName().getLocalName(), JavaIdentifier.CLASS);
+ }
+
+ private void emitBody(final StringBuilder b) {
+ final Map<String, Type> getterToType = collectAllProperties(dtoType, new HashMap<String, Type>());
+ for (final DataSchemaNode schemaChild : schemaNode.getChildNodes()) {
+ if (!schemaChild.isAugmenting()) {
+ final String getter = getGetterName(schemaChild);
+ final Type childType = getterToType.get(getter);
+ if (childType == null) {
+ // FIXME AnyXml nodes are ignored, since their type cannot be found in generated bindnig
+ // Bug-706 https://bugs.opendaylight.org/show_bug.cgi?id=706
+ if (schemaChild instanceof AnyXmlSchemaNode) {
+ LOG.warn("Node {} will be ignored. AnyXml is not yet supported from binding aware code." +
+ "Binding Independent code can be used to serialize anyXml nodes.", schemaChild.getPath());
+ continue;
+ }
+
+ throw new IllegalStateException(
+ String.format("Unable to find type for child node %s. Expected child nodes: %s",
+ schemaChild.getPath(), getterToType));
+ }
+ emitChild(b, getter, childType, schemaChild);
+ }
+ }
+ }
+
+ private void emitChild(final StringBuilder b, final String getterName, final Type childType,
+ final DataSchemaNode schemaChild) {
+ b.append(statement(assign(childType, getterName, cast(childType, invoke(INPUT, getterName)))));
+
+ b.append("if (").append(getterName).append(" != null) {\n");
+ emitChildInner(b, getterName, childType, schemaChild);
+ b.append("}\n");
+ }
+
+ private void emitChildInner(final StringBuilder b, final String getterName, final Type childType,
+ final DataSchemaNode child) {
+ if (child instanceof LeafSchemaNode) {
+ b.append(statement(leafNode(child.getQName().getLocalName(), getterName)));
+ } else if (child instanceof AnyXmlSchemaNode) {
+ b.append(statement(anyxmlNode(child.getQName().getLocalName(), getterName)));
+ } else if (child instanceof LeafListSchemaNode) {
+ final CharSequence startEvent;
+ if (((LeafListSchemaNode) child).isUserOrdered()) {
+ startEvent = startOrderedLeafSet(child.getQName().getLocalName(),invoke(getterName, "size"));
+ } else {
+ startEvent = startLeafSet(child.getQName().getLocalName(),invoke(getterName, "size"));
+ }
+ b.append(statement(startEvent));
+ final Type valueType = ((ParameterizedType) childType).getActualTypeArguments()[0];
+ b.append(forEach(getterName, valueType, statement(leafSetEntryNode(CURRENT))));
+ b.append(statement(endNode()));
+ } else if (child instanceof ListSchemaNode) {
+ final Type valueType = ((ParameterizedType) childType).getActualTypeArguments()[0];
+ final ListSchemaNode casted = (ListSchemaNode) child;
+ emitList(b, getterName, valueType, casted);
+ } else if (child instanceof ContainerSchemaNode) {
+ b.append(tryToUseCacheElse(getterName,statement(staticInvokeEmitter(childType, getterName))));
+ } else if (child instanceof ChoiceSchemaNode) {
+ final String propertyName = CHOICE_PREFIX + childType.getName();
+ staticConstant(propertyName, TreeNodeSerializerImplementation.class, ChoiceDispatchSerializer.from(loadClass(childType)));
+ b.append(tryToUseCacheElse(getterName,statement(invoke(propertyName, StreamWriterGenerator.SERIALIZE_METHOD_NAME, REGISTRY, cast(TreeNode.class.getName(),getterName), STREAM))));
+ }
+ }
+
+ private static StringBuilder tryToUseCacheElse(final String getterName, final CharSequence statement) {
+ final StringBuilder b = new StringBuilder();
+
+ b.append("if ( ");
+ b.append(SERIALIZER).append("== null || ");
+ b.append(invoke(SERIALIZER, "serialize", getterName)).append("== null");
+ b.append(") {");
+ b.append(statement);
+ b.append('}');
+ return b;
+ }
+
+ private void emitList(final StringBuilder b, final String getterName, final Type valueType,
+ final ListSchemaNode child) {
+ final CharSequence startEvent;
+
+ b.append(statement(assign("int", "_count", invoke(getterName, "size"))));
+ if (child.getKeyDefinition().isEmpty()) {
+ startEvent = startUnkeyedList(classReference(valueType), "_count");
+ } else if (child.isUserOrdered()) {
+ startEvent = startOrderedMapNode(classReference(valueType), "_count");
+ } else {
+ startEvent = startMapNode(classReference(valueType), "_count");
+ }
+ b.append(statement(startEvent));
+ b.append(forEach(getterName, valueType, tryToUseCacheElse(CURRENT,statement(staticInvokeEmitter(valueType, CURRENT)))));
+ b.append(statement(endNode()));
+ }
+}
* - argument for invoking startContainerNode
* @return invoking startContainerNode method as String
*/
- static final CharSequence startContainerNode(final CharSequence type, final CharSequence expected) {
+ public static final CharSequence startContainerNode(final CharSequence type, final CharSequence expected) {
return invoke(STREAM, "startContainerNode", type, expected);
}
* - argument for invoking startUnkeyedListItem
* @return invoking startUnkeyedListItem method as String
*/
- static final CharSequence startUnkeyedListItem(final CharSequence expected) {
+ protected static final CharSequence startUnkeyedListItem(final CharSequence expected) {
return invoke(STREAM, "startUnkeyedListItem", expected);
}
* - argument for invoking startMapEntryNode
* @return invoking startMapEntryNode method as String
*/
- static final CharSequence startMapEntryNode(final CharSequence key, final CharSequence expected) {
+ protected static final CharSequence startMapEntryNode(final CharSequence key, final CharSequence expected) {
return invoke(STREAM, "startMapEntryNode", key, expected);
}
* - argument for invoking startAugmentationNode
* @return invoking startAugmentationNode method as String
*/
- static final CharSequence startAugmentationNode(final CharSequence key) {
+ protected static final CharSequence startAugmentationNode(final CharSequence key) {
return invoke(STREAM, "startAugmentationNode", key);
}
* - argument for invoking startCaseNode
* @return invoking startCaseNode method as String
*/
- static final CharSequence startCaseNode(final CharSequence localName, final CharSequence expected) {
+ protected static final CharSequence startCaseNode(final CharSequence localName, final CharSequence expected) {
return invoke(STREAM, "startCase", localName, expected);
}
* - type for referencing class
* @return referenced class of type
*/
- static final CharSequence classReference(final Type type) {
+ protected static final CharSequence classReference(final Type type) {
return type.getFullyQualifiedName() + ".class";
}