From 733a6794a992eb298961de15ecb5dce2f20b5074 Mon Sep 17 00:00:00 2001 From: Jakub Toth Date: Thu, 8 Jun 2017 18:21:43 +0200 Subject: [PATCH] Binding2 runtime - Codecs impl - codecs - part3 Change-Id: Ice1f15d71425020e3002d408be6652e9032ca3cf Signed-off-by: Jakub Toth --- .../dom/codec/impl/IdentifiableItemCodec.java | 148 ++++++++++++++++++ .../javav2/dom/codec/impl/IdentityCodec.java | 47 ++++++ 2 files changed, 195 insertions(+) create mode 100644 binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/impl/IdentifiableItemCodec.java create mode 100644 binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/impl/IdentityCodec.java diff --git a/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/impl/IdentifiableItemCodec.java b/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/impl/IdentifiableItemCodec.java new file mode 100644 index 0000000000..adde743eef --- /dev/null +++ b/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/impl/IdentifiableItemCodec.java @@ -0,0 +1,148 @@ +/* + * 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.impl; + +import com.google.common.annotations.Beta; +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import org.opendaylight.mdsal.binding.javav2.dom.codec.impl.context.ValueContext; +import org.opendaylight.mdsal.binding.javav2.spec.base.IdentifiableItem; +import org.opendaylight.yangtools.concepts.Codec; +import org.opendaylight.yangtools.concepts.Identifier; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; + +/** + * Codec for serialize/deserialize list. + */ +@Beta +public final class IdentifiableItemCodec implements Codec> { + + private final Map keyValueContexts; + private final List keysInBindingOrder; + private final ListSchemaNode schema; + private final Class identifiable; + private final MethodHandle ctorInvoker; + private final MethodHandle ctor; + + /** + * Prepare constructor of binding object of list with resolving key values if exists. + * + * @param schema + * - list schema node + * @param keyClass + * - binding class of key + * @param identifiable + * - binding class of list + * @param keyValueContexts + * - key value contexts + */ + public IdentifiableItemCodec(final ListSchemaNode schema, final Class keyClass, + final Class identifiable, final Map keyValueContexts) { + this.schema = schema; + this.identifiable = identifiable; + + try { + ctor = MethodHandles.publicLookup().unreflectConstructor(getConstructor(keyClass)); + } catch (final IllegalAccessException e) { + throw new IllegalArgumentException("Missing construct in class " + keyClass); + } + final MethodHandle inv = MethodHandles.spreadInvoker(ctor.type(), 0); + this.ctorInvoker = inv.asType(inv.type().changeReturnType(Identifier.class)); + + /* + * We need to re-index to make sure we instantiate nodes in the order in which they are defined. + */ + final Map keys = new LinkedHashMap<>(); + for (final QName qname : schema.getKeyDefinition()) { + keys.put(qname, keyValueContexts.get(qname)); + } + this.keyValueContexts = ImmutableMap.copyOf(keys); + + /* + * When instantiating binding objects we need to specify constructor arguments in alphabetic order. We + * play a couple of tricks here to optimize CPU/memory trade-offs. + * + * We do not have to perform a sort if the source collection has less than two elements. + * + * We always perform an ImmutableList.copyOf(), as that will turn into a no-op if the source is + * already immutable. It will also produce optimized implementations for empty and singleton + * collections. + * + * BUG-2755: remove this if order is made declaration-order-dependent + */ + final List unsortedKeys = schema.getKeyDefinition(); + final List sortedKeys; + if (unsortedKeys.size() > 1) { + final List tmp = new ArrayList<>(unsortedKeys); + Collections.sort(tmp, (q1, q2) -> q1.getLocalName().compareToIgnoreCase(q2.getLocalName())); + sortedKeys = tmp; + } else { + sortedKeys = unsortedKeys; + } + + this.keysInBindingOrder = ImmutableList.copyOf(sortedKeys); + } + + @Override + public IdentifiableItem deserialize(final NodeIdentifierWithPredicates input) { + final Object[] bindingValues = new Object[keysInBindingOrder.size()]; + int offset = 0; + + for (final QName key : keysInBindingOrder) { + final Object yangValue = input.getKeyValues().get(key); + bindingValues[offset++] = keyValueContexts.get(key).deserialize(yangValue); + } + + final Identifier identifier; + try { + identifier = (Identifier) ctorInvoker.invokeExact(ctor, bindingValues); + } catch (final Throwable e) { + throw Throwables.propagate(e); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + final IdentifiableItem identifiableItem = new IdentifiableItem(identifiable, identifier); + return identifiableItem; + } + + @Override + public NodeIdentifierWithPredicates serialize(final IdentifiableItem input) { + final Object value = input.getKey(); + + final Map values = new LinkedHashMap<>(); + for (final Entry valueCtx : keyValueContexts.entrySet()) { + values.put(valueCtx.getKey(), valueCtx.getValue().getAndSerialize(value)); + } + return new NodeIdentifierWithPredicates(schema.getQName(), values); + } + + @SuppressWarnings("unchecked") + private static Constructor getConstructor(final Class clazz) { + for (@SuppressWarnings("rawtypes") + final Constructor constr : clazz.getConstructors()) { + final Class[] parameters = constr.getParameterTypes(); + if (!clazz.equals(parameters[0])) { + // It is not copy constructor; + return constr; + } + } + throw new IllegalArgumentException("Supplied class " + clazz + "does not have required constructor."); + } +} diff --git a/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/impl/IdentityCodec.java b/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/impl/IdentityCodec.java new file mode 100644 index 0000000000..8a0a4f8d70 --- /dev/null +++ b/binding2/mdsal-binding2-dom-codec/src/main/java/org/opendaylight/mdsal/binding/javav2/dom/codec/impl/IdentityCodec.java @@ -0,0 +1,47 @@ +/* + * 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.impl; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import org.opendaylight.mdsal.binding.javav2.runtime.context.BindingRuntimeContext; +import org.opendaylight.mdsal.binding.javav2.runtime.reflection.BindingReflections; +import org.opendaylight.mdsal.binding.javav2.spec.base.BaseIdentity; +import org.opendaylight.yangtools.concepts.Codec; +import org.opendaylight.yangtools.yang.common.QName; + +/** + * Codec for serialize/deserialize identity. + */ +@Beta +public final class IdentityCodec implements Codec> { + + private final BindingRuntimeContext context; + + /** + * Prepared binding runtime context for identity codec. + * + * @param context + * - binding runtime context + */ + public IdentityCodec(final BindingRuntimeContext context) { + this.context = Preconditions.checkNotNull(context); + } + + @Override + public Class deserialize(final QName input) { + Preconditions.checkArgument(input != null, "Input must not be null."); + return context.getIdentityClass(input); + } + + @Override + public QName serialize(final Class input) { + Preconditions.checkArgument(BaseIdentity.class.isAssignableFrom(input)); + return BindingReflections.findQName(input); + } +} -- 2.36.6