Binding v2 DOM Codec - generator - SPI - part 1 27/58327/14
authorJakub Toth <jakub.toth@pantheon.tech>
Tue, 6 Jun 2017 12:30:27 +0000 (14:30 +0200)
committerRobert Varga <nite@hq.sk>
Fri, 9 Jun 2017 14:42:50 +0000 (14:42 +0000)
 * prepare base SPI parts of generating serializers for writers
 * add missing method to GeneratedClassLoadingStrategy
   * resolving class from class loader according to fully qualified
     name
 * checkstyle issues solved

Change-Id: I55aa8286c8d02c69db2af3534076c13f443d7544
Signed-off-by: Jakub Toth <jakub.toth@pantheon.tech>
binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/api/codecs/BindingTreeCodec.java
binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/api/codecs/BindingTreeNodeCodec.java
binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/api/serializer/BindingNormalizedNodeSerializer.java
binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/generator/spi/generator/AbstractGenerator.java [new file with mode: 0644]
binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/generator/spi/generator/AbstractStreamWriterGenerator.java [new file with mode: 0644]
binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/generator/spi/source/AbstractSource.java [new file with mode: 0644]
binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/generator/spi/source/AbstractTreeNodeSerializerSource.java [new file with mode: 0644]
binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/GeneratedClassLoadingStrategy.java

index a8962e23b7c9a624805fff8bac6052e738237f17..98a665b27fa6916a7244d80805cbb3d426bff74a 100644 (file)
@@ -29,6 +29,7 @@ public interface BindingTreeCodec {
      *
      * @param path
      *            - {@link InstanceIdentifier} path
+     * @param <T> data type
      * @return subtree codec
      */
     @Nullable
index 1e1011798e0ebdc4a5904e1f4c4def23b5261e66..1bf09466cc760d3068b746f2a6dca84d7421214e 100644 (file)
@@ -44,6 +44,7 @@ public interface BindingTreeNodeCodec<T extends TreeNode> extends BindingNormali
      *
      * @param childClass
      *            - child class by Biding Stream navigation
+     * @param <E> data type
      * @return context of child
      * @throws IllegalArgumentException
      *             - if supplied child class is not valid in specified context
@@ -64,6 +65,7 @@ public interface BindingTreeNodeCodec<T extends TreeNode> extends BindingNormali
      *
      * @param childClass
      *            - child class by Binding Stream navigation
+     * @param <E> data type
      * @return context of child or Optional absent if supplied is not applicable
      *         in context
      */
index b608864780fc68723e493d044dba39c9e0a40b1e..254279ab4bf7fce2b3abbf4c0e342f0b7477fdb1 100644 (file)
@@ -72,6 +72,7 @@ public interface BindingNormalizedNodeSerializer {
      *            - Binding Instance Identifier pointing to data
      * @param data
      *            - representing Data Tree
+     * @param <T> data type
      * @return NormalizedNode representation
      * @throws IllegalArgumentException
      *             - if supplied Instance Identifier is not valid.
diff --git a/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/generator/spi/generator/AbstractGenerator.java b/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/generator/spi/generator/AbstractGenerator.java
new file mode 100644 (file)
index 0000000..29799f9
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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.generator;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Base class for sharing the loading capability.
+ */
+@Beta
+public abstract class AbstractGenerator {
+
+    /**
+     * Ensure that the serializer class for specified class is loaded and return
+     * its name.
+     *
+     * @param cls
+     *            - data tree class
+     * @return serializer class name
+     */
+    public abstract String loadSerializerFor(final Class<?> cls);
+}
diff --git a/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/generator/spi/generator/AbstractStreamWriterGenerator.java b/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/generator/spi/generator/AbstractStreamWriterGenerator.java
new file mode 100644 (file)
index 0000000..4924d3f
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * 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.generator;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Supplier;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Map.Entry;
+import javassist.CannotCompileException;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.CtMethod;
+import javassist.Modifier;
+import javassist.NotFoundException;
+import javax.annotation.Nonnull;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.generator.api.TreeNodeSerializerGenerator;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.generator.impl.StaticBindingProperty;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.generator.impl.TreeNodeSerializerPrototype;
+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.generator.impl.util.BindingRuntimeContext;
+import org.opendaylight.mdsal.binding.javav2.generator.impl.util.javassist.JavassistUtils;
+import org.opendaylight.mdsal.binding.javav2.generator.util.Types;
+import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType;
+import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
+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.mdsal.binding.javav2.spec.runtime.TreeNodeSerializerRegistry;
+import org.opendaylight.mdsal.binding.javav2.spec.util.BindingReflections;
+import org.opendaylight.yangtools.util.ClassLoaderUtils;
+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;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Beta
+public abstract class AbstractStreamWriterGenerator extends AbstractGenerator implements TreeNodeSerializerGenerator {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractStreamWriterGenerator.class);
+
+    public static final String SERIALIZE_METHOD_NAME = "serialize";
+    protected static final AugmentableDispatchSerializer AUGMENTABLE = new AugmentableDispatchSerializer();
+    private static final Field FIELD_MODIFIERS;
+
+    private final LoadingCache<Class<?>, TreeNodeSerializerImplementation> implementations;
+    private final CtClass[] serializeArguments;
+    private final JavassistUtils javassist;
+
+    private BindingRuntimeContext context;
+
+    static {
+        /*
+         * Cache reflection access to field modifiers field. We need this to set
+         * fix the static declared fields to final once we initialize them. If
+         * we cannot get access, that's fine, too.
+         */
+        Field f = null;
+        try {
+            f = Field.class.getDeclaredField("modifiers");
+            f.setAccessible(true);
+        } catch (NoSuchFieldException | SecurityException e) {
+            LOG.warn("Could not get Field modifiers field, serializers run at decreased efficiency", e);
+        }
+
+        FIELD_MODIFIERS = f;
+    }
+
+    protected AbstractStreamWriterGenerator(final JavassistUtils utils) {
+        super();
+        this.javassist = Preconditions.checkNotNull(utils, "JavassistUtils instance is required.");
+        this.serializeArguments = new CtClass[] { javassist.asCtClass(TreeNodeSerializerRegistry.class),
+                javassist.asCtClass(TreeNode.class), javassist.asCtClass(BindingStreamEventWriter.class), };
+        javassist.appendClassLoaderIfMissing(TreeNodeSerializerPrototype.class.getClassLoader());
+        this.implementations = CacheBuilder.newBuilder().weakKeys().build(new SerializerImplementationLoader());
+    }
+
+    @Override
+    public final TreeNodeSerializerImplementation getSerializer(final Class<?> type) {
+        return implementations.getUnchecked(type);
+    }
+
+    @Override
+    public final void onBindingRuntimeContextUpdated(final BindingRuntimeContext runtime) {
+        this.context = runtime;
+    }
+
+    @Override
+    public final String loadSerializerFor(final Class<?> cls) {
+        return implementations.getUnchecked(cls).getClass().getName();
+    }
+
+    private final class SerializerImplementationLoader extends CacheLoader<Class<?>, TreeNodeSerializerImplementation> {
+
+        private static final String GETINSTANCE_METHOD_NAME = "getInstance";
+        private static final String SERIALIZER_SUFFIX = "$StreamWriter";
+
+        private String getSerializerName(final Class<?> type) {
+            return type.getName() + SERIALIZER_SUFFIX;
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public TreeNodeSerializerImplementation load(@Nonnull final Class<?> type) throws Exception {
+            Preconditions.checkArgument(BindingReflections.isBindingClass(type));
+            Preconditions.checkArgument(Instantiable.class.isAssignableFrom(type),
+                    "Instantiable is not assingnable from %s from classloader %s.", type, type.getClassLoader());
+
+            final String serializerName = getSerializerName(type);
+
+            Class<? extends TreeNodeSerializerImplementation> cls;
+            try {
+                cls = (Class<? extends TreeNodeSerializerImplementation>) ClassLoaderUtils
+                        .loadClass(type.getClassLoader(), serializerName);
+            } catch (final ClassNotFoundException e) {
+                cls = generateSerializer(type, serializerName);
+            }
+
+            final TreeNodeSerializerImplementation obj =
+                    (TreeNodeSerializerImplementation) cls.getDeclaredMethod(GETINSTANCE_METHOD_NAME).invoke(null);
+            LOG.debug("Loaded serializer {} for class {}", obj, type);
+            return obj;
+        }
+
+        @SuppressWarnings("unchecked")
+        private Class<? extends TreeNodeSerializerImplementation> generateSerializer(final Class<?> type,
+                final String serializerName)
+                throws CannotCompileException, IllegalAccessException, IllegalArgumentException,
+                InvocationTargetException, NoSuchMethodException, SecurityException, NoSuchFieldException {
+            final AbstractTreeNodeSerializerSource source = generateEmitterSource(type, serializerName);
+            final CtClass poolClass = generateEmitter0(type, source, serializerName);
+            final Class<? extends TreeNodeSerializerImplementation> cls =
+                    poolClass.toClass(type.getClassLoader(), type.getProtectionDomain());
+
+            /*
+             * Due to OSGi class loader rules we cannot initialize the fields
+             * during construction, as the initializer expressions do not see
+             * our implementation classes. This should be almost as good as
+             * that, as we are resetting the fields to final before ever leaking
+             * the class.
+             */
+            for (final StaticBindingProperty constant : source.getStaticConstants()) {
+                final Field field = cls.getDeclaredField(constant.getName());
+                field.setAccessible(true);
+                field.set(null, constant.getValue());
+
+                if (FIELD_MODIFIERS != null) {
+                    FIELD_MODIFIERS.setInt(field, field.getModifiers() | Modifier.FINAL);
+                }
+            }
+
+            return cls;
+        }
+    }
+
+    private AbstractTreeNodeSerializerSource generateEmitterSource(final Class<?> type, final String serializerName) {
+        Types.typeForClass(type);
+        javassist.appendClassLoaderIfMissing(type.getClassLoader());
+        final Entry<GeneratedType, Object> typeWithSchema = context.getTypeWithSchema(type);
+        final GeneratedType generatedType = typeWithSchema.getKey();
+        final Object schema = typeWithSchema.getValue();
+
+        final AbstractTreeNodeSerializerSource source;
+        if (schema instanceof ContainerSchemaNode) {
+            source = generateContainerSerializer(generatedType, (ContainerSchemaNode) schema);
+        } else if (schema instanceof ListSchemaNode) {
+            final ListSchemaNode casted = (ListSchemaNode) schema;
+            if (casted.getKeyDefinition().isEmpty()) {
+                source = generateUnkeyedListEntrySerializer(generatedType, casted);
+            } else {
+                source = generateMapEntrySerializer(generatedType, casted);
+            }
+        } else if (schema instanceof AugmentationSchema) {
+            source = generateSerializer(generatedType, (AugmentationSchema) schema);
+        } else if (schema instanceof ChoiceCaseNode) {
+            source = generateCaseSerializer(generatedType, (ChoiceCaseNode) schema);
+        } else if (schema instanceof NotificationDefinition) {
+            source = generateNotificationSerializer(generatedType, (NotificationDefinition) schema);
+        } else {
+            throw new UnsupportedOperationException("Schema type " + schema.getClass() + " is not supported");
+        }
+        return source;
+    }
+
+    private CtClass generateEmitter0(final Class<?> type, final AbstractTreeNodeSerializerSource source,
+            final String serializerName) {
+        final CtClass product;
+
+        /*
+         * getSerializerBody() has side effects, such as loading classes and
+         * codecs, it should be run in model class loader in order to correctly
+         * reference load child classes.
+         *
+         * Furthermore the fact that getSerializedBody() can trigger other code
+         * generation to happen, we need to take care of this before calling
+         * instantiatePrototype(), as that will call our customizer with the
+         * lock held, hence any code generation will end up being blocked on the
+         * javassist lock.
+         */
+        final String body = ClassLoaderUtils.withClassLoader(type.getClassLoader(),
+                (Supplier<String>) () -> source.getSerializerBody().toString());
+
+        try {
+            product = javassist.instantiatePrototype(TreeNodeSerializerPrototype.class.getName(), serializerName,
+                    cls -> {
+                        // Generate any static fields
+                        for (final StaticBindingProperty def : source.getStaticConstants()) {
+                            final CtField field = new CtField(javassist.asCtClass(def.getType()), def.getName(), cls);
+                            field.setModifiers(Modifier.PRIVATE + Modifier.STATIC);
+                            cls.addField(field);
+                        }
+
+                        // Replace serialize() -- may reference static fields
+                        final CtMethod serializeTo = cls.getDeclaredMethod(SERIALIZE_METHOD_NAME, serializeArguments);
+                        serializeTo.setBody(body);
+
+                        // The prototype is not visible, so we need to take care
+                        // of that
+                        cls.setModifiers(Modifier.setPublic(cls.getModifiers()));
+                    });
+        } catch (final NotFoundException e) {
+            LOG.error("Failed to instatiate serializer {}", source, e);
+            throw new LinkageError("Unexpected instantation problem: serializer prototype not found", e);
+        }
+        return product;
+    }
+
+    /**
+     * Generates serializer source code for supplied container node, which will
+     * read supplied binding type and invoke proper methods on supplied
+     * {@link BindingStreamEventWriter}.
+     * <p>
+     * Implementation is required to recursively invoke events for all reachable
+     * binding objects.
+     *
+     * @param type - binding type of container
+     * @param node - schema of container
+     * @return source for container node writer
+     */
+    protected abstract AbstractTreeNodeSerializerSource generateContainerSerializer(GeneratedType type,
+            ContainerSchemaNode node);
+
+    /**
+     * Generates serializer source for supplied case node, which will read
+     * supplied binding type and invoke proper methods on supplied
+     * {@link BindingStreamEventWriter}.
+     * <p>
+     * Implementation is required to recursively invoke events for all reachable
+     * binding objects.
+     *
+     * @param type - binding type of case
+     * @param node - schema of case
+     * @return source for case node writer
+     */
+    protected abstract AbstractTreeNodeSerializerSource generateCaseSerializer(GeneratedType type, ChoiceCaseNode node);
+
+    /**
+     * Generates serializer source for supplied list node, which will read
+     * supplied binding type and invoke proper methods on supplied
+     * {@link BindingStreamEventWriter}.
+     * <p>
+     * Implementation is required to recursively invoke events for all reachable
+     * binding objects.
+     *
+     * @param type - binding type of list
+     * @param node - schema of list
+     * @return source for list node writer
+     */
+    protected abstract AbstractTreeNodeSerializerSource generateMapEntrySerializer(GeneratedType type, ListSchemaNode node);
+
+    /**
+     * Generates serializer source for supplied list node, which will read
+     * supplied binding type and invoke proper methods on supplied
+     * {@link BindingStreamEventWriter}.
+     * <p>
+     * Implementation is required to recursively invoke events for all reachable
+     * binding objects.
+     *
+     * @param type - binding type of list
+     * @param node - schema of list
+     * @return source for list node writer
+     */
+    protected abstract AbstractTreeNodeSerializerSource generateUnkeyedListEntrySerializer(GeneratedType type,
+            ListSchemaNode node);
+
+    /**
+     * Generates serializer source for supplied augmentation node, which will
+     * read supplied binding type and invoke proper methods on supplied
+     * {@link BindingStreamEventWriter}.
+     * <p>
+     * Implementation is required to recursively invoke events for all reachable
+     * binding objects.
+     *
+     * @param type - binding type of augmentation
+     * @param schema - schema of augmentation
+     * @return source for augmentation node writer
+     */
+    protected abstract AbstractTreeNodeSerializerSource generateSerializer(GeneratedType type, AugmentationSchema schema);
+
+    /**
+     * Generates serializer source for notification node, which will read
+     * supplied binding type and invoke proper methods on supplied
+     * {@link BindingStreamEventWriter}.
+     * <p>
+     * Implementation is required to recursively invoke events for all reachable
+     * binding objects.
+     *
+     * @param type - binding type of notification
+     * @param node - schema of notification
+     * @return source for notification node writer
+     */
+    protected abstract AbstractTreeNodeSerializerSource generateNotificationSerializer(GeneratedType type,
+            NotificationDefinition node);
+
+}
diff --git a/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/generator/spi/source/AbstractSource.java b/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/generator/spi/source/AbstractSource.java
new file mode 100644 (file)
index 0000000..0d44cc4
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * 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.collect.Iterators;
+import com.google.common.collect.UnmodifiableIterator;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.generator.impl.StaticBindingProperty;
+import org.opendaylight.mdsal.binding.javav2.model.api.Type;
+
+/**
+ * Base class for preparing types and constants for writer.
+ *
+ */
+@Beta
+abstract class AbstractSource {
+
+    private final Set<StaticBindingProperty> staticConstants = new HashSet<>();
+
+    /**
+     * Create new static constant of specific type with value.
+     *
+     * @param name
+     *            - name of constant
+     * @param type
+     *            - specific type of constant
+     * @param value
+     *            - value of constant
+     * @param <T>
+     *            - type of constant
+     */
+    final <T> void staticConstant(final String name, final Class<T> type, final T value) {
+        this.staticConstants.add(new StaticBindingProperty(name, type, value));
+    }
+
+    /**
+     * Get set of static constants.
+     *
+     * @return unmodifiable view of set of static constants
+     */
+    public final Set<StaticBindingProperty> getStaticConstants() {
+        return Collections.unmodifiableSet(this.staticConstants);
+    }
+
+    /**
+     * Prepare common part of invoke of method on object.
+     *
+     * @param object
+     *            - object for invoke method
+     * @param methodName
+     *            - method name to be invoked
+     * @return base part of invoking method on object as String
+     */
+    private static StringBuilder prepareCommonInvokePart(final CharSequence object, final String methodName) {
+        final StringBuilder sb = new StringBuilder();
+        if (object != null) {
+            sb.append(object);
+            sb.append('.');
+        }
+        return sb.append(methodName).append('(');
+    }
+
+    /**
+     * Prepare invoking method on object with an argument.
+     *
+     * @param object
+     *            - object for invoke method
+     * @param methodName
+     *            - method name to be invoked
+     * @param arg
+     *            - argument of method
+     * @return invoking method on object with an argument as String
+     */
+    static final CharSequence invoke(final CharSequence object, final String methodName, final Object arg) {
+        return prepareCommonInvokePart(object, methodName).append(arg).append(')');
+    }
+
+    /**
+     * Prepare invoking method on object with more arguments.
+     *
+     * @param object
+     *            - object for invoke method
+     * @param methodName
+     *            - method name to be invoked
+     * @param args
+     *            - arguments of method
+     * @return invoking method on object with more arguments as String
+     */
+    static final CharSequence invoke(final CharSequence object, final String methodName,
+            final Object... args) {
+        final StringBuilder sb = prepareCommonInvokePart(object, methodName);
+
+        final UnmodifiableIterator<Object> iterator = Iterators.forArray(args);
+        while (iterator.hasNext()) {
+            sb.append(iterator.next());
+            if (iterator.hasNext()) {
+                sb.append(',');
+            }
+        }
+        return sb.append(')');
+    }
+
+    /**
+     * Assign of value to variable.
+     *
+     * @param var
+     *            - name of variable
+     * @param value
+     *            - value of variable
+     * @return assigned value to variable as char sequence
+     */
+    static final CharSequence assign(final String var, final CharSequence value) {
+        return assign((String) null, var, value);
+    }
+
+    /**
+     * Assign of value to variable of specific type.
+     *
+     * @param type
+     *            - specific type of value
+     * @param var
+     *            - name of variable
+     * @param value
+     *            - value of variable
+     * @return assigned value to variable of specific type as char sequence, if
+     *         type is null then there is not added type to final string of
+     *         assigned value
+     */
+    static final CharSequence assign(final String type, final String var, final CharSequence value) {
+        final StringBuilder sb = new StringBuilder();
+        if (type != null) {
+            sb.append(type);
+            sb.append(' ');
+        }
+        return sb.append(var).append(" = ").append(value);
+    }
+
+    /**
+     * Assign of value to variable of specific type.
+     *
+     * @param type
+     *            - specific type of value
+     * @param var
+     *            - name of variable
+     * @param value
+     *            - value of variable
+     * @return assigned value to variable of specific type as char sequence, if
+     *         type is null then there is not added type to final string of
+     *         assigned value
+     */
+    static final CharSequence assign(final Type type, final String var, final CharSequence value) {
+        return assign(type.getFullyQualifiedName(), var, value);
+    }
+
+    /**
+     * Cast value to specific type.
+     *
+     * @param type
+     *            - specific type
+     * @param value
+     *            - value for cast
+     * @return casted value to specifc type as char sequence
+     */
+    static final CharSequence cast(final Type type, final CharSequence value) {
+        return cast(type.getFullyQualifiedName(), value);
+    }
+
+    /**
+     * Create loop through iterable object with specific body.
+     *
+     * @param iterable
+     *            - iterable object
+     * @param iteratorName
+     *            - name of iterator variable of iterable object
+     * @param valueType
+     *            - type of iterable item
+     * @param valueName
+     *            - name of variable of iterable item
+     * @param body
+     *            - specific body for porcess of iterable item
+     * @return loop through iterable object with specific body as String
+     */
+    static final CharSequence forEach(final String iterable, final String iteratorName,
+            final String valueType, final String valueName, final CharSequence body) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append(statement(assign(java.util.Iterator.class.getName(), iteratorName, invoke(iterable, "iterator"))));
+        sb.append("while (").append(invoke(iteratorName, "hasNext")).append(") {\n");
+        sb.append(statement(assign(valueType, valueName, cast(valueType, invoke(iteratorName, "next")))));
+        sb.append(body);
+        return sb.append("\n}\n");
+    }
+
+    /**
+     * Cast value to specific type.
+     *
+     * @param type
+     *            - specific type
+     * @param value
+     *            - value for cast
+     * @return casted value to specifc type as char sequence
+     */
+    static final CharSequence cast(final String type, final CharSequence value) {
+        return "((" + type + ") " + value + ')';
+    }
+
+    /**
+     * Create new Java statement.
+     *
+     * @param statement
+     *            - input for creating new Java statement
+     * @return java statement
+     */
+    static final CharSequence statement(final CharSequence statement) {
+        return new StringBuilder(statement).append(";\n");
+    }
+}
diff --git a/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/generator/spi/source/AbstractTreeNodeSerializerSource.java b/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/generator/spi/source/AbstractTreeNodeSerializerSource.java
new file mode 100644 (file)
index 0000000..d6307d0
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * 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 org.opendaylight.mdsal.binding.javav2.dom.codec.generator.spi.generator.AbstractGenerator;
+import org.opendaylight.mdsal.binding.javav2.dom.codec.generator.spi.generator.AbstractStreamWriterGenerator;
+import org.opendaylight.mdsal.binding.javav2.generator.api.ClassLoadingStrategy;
+import org.opendaylight.mdsal.binding.javav2.generator.impl.GeneratedClassLoadingStrategy;
+import org.opendaylight.mdsal.binding.javav2.model.api.Type;
+import org.opendaylight.mdsal.binding.javav2.spec.base.Instantiable;
+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.TreeNodeSerializerRegistry;
+
+@Beta
+public abstract class AbstractTreeNodeSerializerSource extends AbstractSource {
+
+    private static final ClassLoadingStrategy STRATEGY = GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy();
+
+    static final String SERIALIZER = "_serializer";
+    static final String STREAM = "_stream";
+    static final String ITERATOR = "_iterator";
+    static final String CURRENT = "_current";
+    static final String REGISTRY = "_registry";
+
+    private final AbstractGenerator generator;
+
+    /**
+     * Set up generator.
+     *
+     * @param generator
+     *            -parent generator
+     */
+    AbstractTreeNodeSerializerSource(final AbstractGenerator generator) {
+        this.generator = Preconditions.checkNotNull(generator);
+    }
+
+    @SuppressWarnings("unchecked")
+    Class<? extends Instantiable<?>> loadClass(final Type childType) {
+        try {
+            return (Class<? extends Instantiable<?>>) STRATEGY.loadClass(childType);
+        } catch (final ClassNotFoundException e) {
+            throw new IllegalStateException("Could not load referenced class ", e);
+        }
+    }
+
+    /**
+     * Returns body of static serialize method.
+     *
+     * <ul>
+     * <li>{@link TreeNodeSerializerRegistry} - registry of serializers
+     * <li>{@link TreeNode} - node to be serialized
+     * <li>{@link BindingStreamEventWriter} - writer to which events should be
+     * serialized
+     * </ul>
+     *
+     * @return valid javassist code describing static serialization body
+     */
+    public abstract CharSequence getSerializerBody();
+
+    /**
+     * Invoking leafNode method of stream with arguments local name and value.
+     *
+     * @param localName
+     *            - argument for invoking leafNode
+     * @param value
+     *            - argument for invoking leafNode
+     * @return invoking leafNode method as String
+     */
+    static final CharSequence leafNode(final String localName, final CharSequence value) {
+        return invoke(STREAM, "leafNode", escape(localName), value);
+    }
+
+    /**
+     * Invoking startLeafSet method of stream with arguments local name and
+     * expected.
+     *
+     * @param localName
+     *            - argument for invoking startLeafSet
+     * @param expected
+     *            - argument for invoking startLeafSet
+     * @return invoking startLeafSet method as String
+     */
+    static final CharSequence startLeafSet(final String localName, final CharSequence expected) {
+        return invoke(STREAM, "startLeafSet", escape(localName), expected);
+    }
+
+    /**
+     * Invoking startOrderedLeafSet method of stream with arguments localname
+     * and expected.
+     *
+     * @param localName
+     *            - argument for invoking startOrderedLeafSet
+     * @param expected
+     *            - argument for invoking startOrderedLeafSet
+     * @return invoking startOrderedLeafSet method as String
+     */
+    static final CharSequence startOrderedLeafSet(final String localName, final CharSequence expected) {
+        return invoke(STREAM, "startOrderedLeafSet", escape(localName), expected);
+    }
+
+    /**
+     * Bound local name by quotes.
+     *
+     * @param localName
+     *            - to be bounded
+     * @return bounded local name
+     */
+    static final CharSequence escape(final String localName) {
+        return '"' + localName + '"';
+    }
+
+    /**
+     * Invoking leafSetEntryNode method of stream with argument value.
+     *
+     * @param value
+     *            - argument for invoking leafSetEntryNode
+     * @return invoking leafSetEntryNode method as String
+     */
+    static final CharSequence leafSetEntryNode(final CharSequence value) {
+        return invoke(STREAM, "leafSetEntryNode", value);
+    }
+
+    /**
+     * Invoking startContainerNode method of stream with arguments type and
+     * expected.
+     *
+     * @param type
+     *            - argument for invoking startContainerNode
+     * @param expected
+     *            - argument for invoking startContainerNode
+     * @return invoking startContainerNode method as String
+     */
+    static final CharSequence startContainerNode(final CharSequence type, final CharSequence expected) {
+        return invoke(STREAM, "startContainerNode", type, expected);
+    }
+
+    /**
+     * Invoking startUnkeyedList method of stream with arguments type and
+     * expected.
+     *
+     * @param type
+     *            - argument for invoking startUnkeyedList
+     * @param expected
+     *            - argument for invoking startUnkeyedList
+     * @return invoking startUnkeyedList method as String
+     */
+    static final CharSequence startUnkeyedList(final CharSequence type, final CharSequence expected) {
+        return invoke(STREAM, "startUnkeyedList", type, expected);
+    }
+
+    /**
+     * Invoking startUnkeyedListItem of stream with argument expected.
+     *
+     * @param expected
+     *            - argument for invoking startUnkeyedListItem
+     * @return invoking startUnkeyedListItem method as String
+     */
+    static final CharSequence startUnkeyedListItem(final CharSequence expected) {
+        return invoke(STREAM, "startUnkeyedListItem", expected);
+    }
+
+    /**
+     * Invoking startMapNode method of stream with arguments type and expected.
+     *
+     * @param type
+     *            - argument for invoking startMapNode
+     * @param expected
+     *            - argument for invoking startMapNode
+     * @return invoking startMapNode method as String
+     */
+    static final CharSequence startMapNode(final CharSequence type, final CharSequence expected) {
+        return invoke(STREAM, "startMapNode", type, expected);
+    }
+
+    /**
+     * Invoking startOrderedMapNode method of stream with arguments type and
+     * expected.
+     *
+     * @param type
+     *            - argument for invoking startOrderedMapNode
+     * @param expected
+     *            - argument for invoking startOrderedMapNode
+     * @return invoking startOrderedMapNode method as String
+     */
+    static final CharSequence startOrderedMapNode(final CharSequence type, final CharSequence expected) {
+        return invoke(STREAM, "startOrderedMapNode", type, expected);
+    }
+
+    /**
+     * Invoking startMapEntryNode method of stream with arguments key and
+     * expected.
+     *
+     * @param key
+     *            - argument for invoking startMapEntryNode
+     * @param expected
+     *            - argument for invoking startMapEntryNode
+     * @return invoking startMapEntryNode method as String
+     */
+    static final CharSequence startMapEntryNode(final CharSequence key, final CharSequence expected) {
+        return invoke(STREAM, "startMapEntryNode", key, expected);
+    }
+
+    /**
+     * Invoking startAugmentationNode of stream with argument key.
+     *
+     * @param key
+     *            - argument for invoking startAugmentationNode
+     * @return invoking startAugmentationNode method as String
+     */
+    static final CharSequence startAugmentationNode(final CharSequence key) {
+        return invoke(STREAM, "startAugmentationNode", key);
+    }
+
+    /**
+     * Invoking startChoiceNode method of stream with arguments localname and
+     * expected.
+     *
+     * @param localName
+     *            - argument for invoking startChoiceNode
+     * @param expected
+     *            - argument for invoking startChoiceNode
+     * @return invoking startChoiceNode method as String
+     */
+    static final CharSequence startChoiceNode(final CharSequence localName, final CharSequence expected) {
+        return invoke(STREAM, "startChoiceNode", localName, expected);
+    }
+
+    /**
+     * Invoking startCaseNode method of stream with arguments localname and
+     * expected.
+     *
+     * @param localName
+     *            - argument for invoking startCaseNode
+     * @param expected
+     *            - argument for invoking startCaseNode
+     * @return invoking startCaseNode method as String
+     */
+    static final CharSequence startCaseNode(final CharSequence localName, final CharSequence expected) {
+        return invoke(STREAM, "startCase", localName, expected);
+    }
+
+    /**
+     * Invoking anyxmlNode method of stream with arguments name and
+     * value.
+     *
+     * @param name
+     *            - argument for invoking anyxmlNode
+     * @param value
+     *            - argument for invoking anyxmlNode
+     * @return invoking anyxmlNode method as String
+     */
+    static final CharSequence anyxmlNode(final String name, final String value)
+            throws IllegalArgumentException {
+        return invoke(STREAM, "anyxmlNode", escape(name), name);
+    }
+
+    /**
+     * Invoking anydataNode method of stream with arguments name and
+     * value.
+     *
+     * @param name
+     *            - argument for invoking anydataNode
+     * @param value
+     *            - argument for invoking anydataNode
+     * @return invoking anydataNode method as String
+     */
+    static final CharSequence anydataNode(final String name, final String value)
+            throws IllegalArgumentException {
+        return invoke(STREAM, "anydataNode", escape(name), name);
+    }
+
+    /**
+     * Invoking endNode method of stream without any arguments.
+     *
+     * @return invoking andNode method as String
+     */
+    static final CharSequence endNode() {
+        return invoke(STREAM, "endNode");
+    }
+
+    /**
+     * Prepare loop through iterable object with specific body.
+     *
+     * @param iterable
+     *            - name of iterable object
+     * @param valueType
+     *            - type of iterate objects
+     * @param body
+     *            - specific body of loop
+     * @return loop through iterable object as String
+     */
+    static final CharSequence forEach(final String iterable, final Type valueType, final CharSequence body) {
+        return forEach(iterable, ITERATOR, valueType.getFullyQualifiedName(), CURRENT, body);
+    }
+
+    /**
+     * Returns class reference for type.
+     *
+     * @param type
+     *            - type for referencing class
+     * @return referenced class of type
+     */
+    static final CharSequence classReference(final Type type) {
+        return type.getFullyQualifiedName() + ".class";
+    }
+
+    /**
+     * After getting class of childType from class loader, prepare invoking of
+     * serialize() method from instance of reached class of childType with
+     * arguments {@link #REGISTRY}, name and {@link #STREAM}.
+     *
+     * @param childType
+     *            - type of child for getting class from classloader
+     * @param name
+     *            - argument for invoking serialize method of instance childType
+     *            class
+     * @return invoking serialize method with specific arguments as String
+     */
+    final CharSequence staticInvokeEmitter(final Type childType, final String name) {
+        final Class<?> cls;
+        try {
+            cls = STRATEGY.loadClass(childType);
+        } catch (final ClassNotFoundException e) {
+            throw new IllegalStateException("Failed to invoke emitter", e);
+        }
+
+        final String className = this.generator.loadSerializerFor(cls) + ".getInstance()";
+        return invoke(className, AbstractStreamWriterGenerator.SERIALIZE_METHOD_NAME, REGISTRY, name, STREAM);
+    }
+}
+
index 4a535ba831731356a57f6dfac9b3b79292f5aba8..5d8185912663b5fc4dcb3f9da4d483d0c4e34492 100644 (file)
@@ -11,15 +11,29 @@ package org.opendaylight.mdsal.binding.javav2.generator.impl;
 import com.google.common.annotations.Beta;
 import org.opendaylight.mdsal.binding.javav2.generator.api.ClassLoadingStrategy;
 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
+import org.opendaylight.yangtools.util.ClassLoaderUtils;
 
 @Beta
 public abstract class GeneratedClassLoadingStrategy implements ClassLoadingStrategy {
 
+    private static final GeneratedClassLoadingStrategy TCCL_STRATEGY = new TCCLClassLoadingStrategy();
+
     @Override
-    public Class<?> loadClass(Type type) throws ClassNotFoundException {
+    public Class<?> loadClass(final Type type) throws ClassNotFoundException {
         return loadClass(type.getFullyQualifiedName());
     }
 
     @Override
     public abstract Class<?> loadClass(String fqcn) throws ClassNotFoundException;
+
+    public static ClassLoadingStrategy getTCCLClassLoadingStrategy() {
+        return TCCL_STRATEGY;
+    }
+
+    private static final class TCCLClassLoadingStrategy extends GeneratedClassLoadingStrategy {
+        @Override
+        public Class<?> loadClass(final String fullyQualifiedName) throws ClassNotFoundException {
+            return ClassLoaderUtils.loadClassWithTCCL(fullyQualifiedName);
+        }
+    }
 }