From: Jakub Toth Date: Tue, 6 Jun 2017 12:30:27 +0000 (+0200) Subject: Binding v2 DOM Codec - generator - SPI - part 1 X-Git-Tag: release/nitrogen~143 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=9672e88fee1170117f650b6146461bc4874b45d6;p=mdsal.git Binding v2 DOM Codec - generator - SPI - part 1 * 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 --- diff --git a/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/api/codecs/BindingTreeCodec.java b/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/api/codecs/BindingTreeCodec.java index a8962e23b7..98a665b27f 100644 --- a/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/api/codecs/BindingTreeCodec.java +++ b/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/api/codecs/BindingTreeCodec.java @@ -29,6 +29,7 @@ public interface BindingTreeCodec { * * @param path * - {@link InstanceIdentifier} path + * @param data type * @return subtree codec */ @Nullable diff --git a/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/api/codecs/BindingTreeNodeCodec.java b/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/api/codecs/BindingTreeNodeCodec.java index 1e1011798e..1bf09466cc 100644 --- a/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/api/codecs/BindingTreeNodeCodec.java +++ b/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/api/codecs/BindingTreeNodeCodec.java @@ -44,6 +44,7 @@ public interface BindingTreeNodeCodec extends BindingNormali * * @param childClass * - child class by Biding Stream navigation + * @param 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 extends BindingNormali * * @param childClass * - child class by Binding Stream navigation + * @param data type * @return context of child or Optional absent if supplied is not applicable * in context */ diff --git a/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/api/serializer/BindingNormalizedNodeSerializer.java b/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/api/serializer/BindingNormalizedNodeSerializer.java index b608864780..254279ab4b 100644 --- a/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/api/serializer/BindingNormalizedNodeSerializer.java +++ b/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/api/serializer/BindingNormalizedNodeSerializer.java @@ -72,6 +72,7 @@ public interface BindingNormalizedNodeSerializer { * - Binding Instance Identifier pointing to data * @param data * - representing Data Tree + * @param 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 index 0000000000..29799f9625 --- /dev/null +++ b/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/generator/spi/generator/AbstractGenerator.java @@ -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 index 0000000000..4924d3f161 --- /dev/null +++ b/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/generator/spi/generator/AbstractStreamWriterGenerator.java @@ -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, 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, 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 cls; + try { + cls = (Class) 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 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 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 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) () -> 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}. + *

+ * 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}. + *

+ * 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}. + *

+ * 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}. + *

+ * 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}. + *

+ * 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}. + *

+ * 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 index 0000000000..0d44cc4280 --- /dev/null +++ b/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/generator/spi/source/AbstractSource.java @@ -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 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 + * - type of constant + */ + final void staticConstant(final String name, final Class 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 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 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 index 0000000000..d6307d03b6 --- /dev/null +++ b/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/generator/spi/source/AbstractTreeNodeSerializerSource.java @@ -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> loadClass(final Type childType) { + try { + return (Class>) STRATEGY.loadClass(childType); + } catch (final ClassNotFoundException e) { + throw new IllegalStateException("Could not load referenced class ", e); + } + } + + /** + * Returns body of static serialize method. + * + *
    + *
  • {@link TreeNodeSerializerRegistry} - registry of serializers + *
  • {@link TreeNode} - node to be serialized + *
  • {@link BindingStreamEventWriter} - writer to which events should be + * serialized + *
+ * + * @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); + } +} + diff --git a/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/GeneratedClassLoadingStrategy.java b/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/GeneratedClassLoadingStrategy.java index 4a535ba831..5d81859126 100644 --- a/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/GeneratedClassLoadingStrategy.java +++ b/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/GeneratedClassLoadingStrategy.java @@ -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); + } + } }