--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.javav2.api.annotation;
+
+import com.google.common.annotations.Beta;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.opendaylight.mdsal.binding.javav2.spec.base.BaseIdentity;
+
+@Beta
+@Inherited
+@Documented
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RoutingContext {
+
+ Class<? extends BaseIdentity> value();
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.javav2.dom.adapter.extractor;
+
+import com.google.common.annotations.Beta;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import java.lang.reflect.Method;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.mdsal.binding.javav2.api.annotation.RoutingContext;
+import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Tree node reference extractor.
+ */
+@Beta
+public abstract class ContextReferenceExtractor {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ContextReferenceExtractor.class);
+ private static final String GET_VALUE_NAME = "getValue";
+
+ private static final ContextReferenceExtractor NULL_EXTRACTOR = new ContextReferenceExtractor() {
+
+ @Nullable
+ @Override
+ public InstanceIdentifier<? extends TreeNode> extract(final TreeNode obj) {
+ return null;
+ }
+ };
+
+ private static final LoadingCache<Class<?>, ContextReferenceExtractor> EXTRACTORS =
+ CacheBuilder.newBuilder().weakKeys().build(new CacheLoader<Class<?>, ContextReferenceExtractor>() {
+
+ @Nonnull
+ @Override
+ public ContextReferenceExtractor load(@Nonnull final Class<?> key) throws Exception {
+ return create(key);
+ }
+
+ private ContextReferenceExtractor create(final Class<?> key) {
+ final Method contextGetter = getContextGetter(key);
+ if (contextGetter == null) {
+ return NULL_EXTRACTOR;
+ }
+ final Class<?> returnType = contextGetter.getReturnType();
+ try {
+ if (InstanceIdentifier.class.isAssignableFrom(returnType)) {
+ return DirectGetterRouteContextExtractor.create(contextGetter);
+ }
+ final Method getValueMethod = findGetValueMethod(returnType);
+ if (getValueMethod != null) {
+ return GetValueRouteContextExtractor.create(contextGetter, getValueMethod);
+ } else {
+ LOG.warn("Class {} can not be used to determine context, falling back to NULL_EXTRACTOR.",
+ returnType);
+ }
+ } catch (final IllegalAccessException e) {
+ LOG.warn("Class {} does not conform to Binding Specification. Falling back to NULL_EXTRACTOR",
+ e);
+ }
+ return NULL_EXTRACTOR;
+ }
+
+ private Method getContextGetter(final Class<?> key) {
+ for (final Method method : key.getMethods()) {
+ if (method.getAnnotation(RoutingContext.class) != null) {
+ return method;
+ }
+ }
+ return null;
+ }
+ });
+
+ /**
+ * Extract context-reference (Instance Identifier) from Binding TreeNode.
+ *
+ * @param obj
+ * - TreeNode from which context reference should be extracted.
+ *
+ * @return Instance Identifier representing context reference or null, if tree node does not contain
+ * context reference.
+ */
+ @Nullable
+ public abstract InstanceIdentifier<? extends TreeNode> extract(TreeNode obj);
+
+ /**
+ * Method for return specific extractor of input object.
+ *
+ * @param obj
+ * - object for get specific extractor
+ * @return specific extractor
+ */
+ public static ContextReferenceExtractor from(final Class<?> obj) {
+ return EXTRACTORS.getUnchecked(obj);
+ }
+
+ private static Method findGetValueMethod(final Class<?> type) {
+ try {
+ final Method method = type.getMethod(GET_VALUE_NAME);
+ if (InstanceIdentifier.class.equals(method.getReturnType())) {
+ return method;
+ }
+ } catch (final NoSuchMethodException e) {
+ LOG.warn("Value class {} does not comform to Binding Specification.", type, e);
+ }
+ return null;
+ }
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.javav2.dom.adapter.extractor;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Throwables;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+
+@Beta
+final class DirectGetterRouteContextExtractor extends ContextReferenceExtractor {
+
+ private static final Lookup PUBLIC_LOOKUP = MethodHandles.publicLookup();
+
+ private final MethodHandle handle;
+
+ private DirectGetterRouteContextExtractor(final MethodHandle rawHandle) {
+ handle = rawHandle.asType(MethodType.methodType(InstanceIdentifier.class, TreeNode.class));
+ }
+
+ static ContextReferenceExtractor create(final Method getterMethod) throws IllegalAccessException {
+ final MethodHandle getterHandle = PUBLIC_LOOKUP.unreflect(getterMethod);
+ return new DirectGetterRouteContextExtractor(getterHandle);
+ }
+
+ @SuppressWarnings("checkstyle:IllegalCatch")
+ @Override
+ public InstanceIdentifier<? extends TreeNode> extract(final TreeNode obj) {
+ try {
+ return (InstanceIdentifier<? extends TreeNode>) handle.invokeExact(obj);
+ } catch (final Throwable e) {
+ throw Throwables.propagate(e);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.javav2.dom.adapter.extractor;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Throwables;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import org.opendaylight.mdsal.binding.javav2.spec.base.InstanceIdentifier;
+import org.opendaylight.mdsal.binding.javav2.spec.base.TreeNode;
+
+@Beta
+final class GetValueRouteContextExtractor extends ContextReferenceExtractor {
+
+ private static final Lookup PUBLIC_LOOKUP = MethodHandles.publicLookup();
+
+ private final MethodHandle contextHandle;
+ private final MethodHandle valueHandle;
+
+ private GetValueRouteContextExtractor(final MethodHandle rawContextHandle, final MethodHandle rawValueHandle) {
+ contextHandle = rawContextHandle.asType(MethodType.methodType(Object.class, TreeNode.class));
+ valueHandle = rawValueHandle.asType(MethodType.methodType(InstanceIdentifier.class, Object.class));
+ }
+
+ static ContextReferenceExtractor create(final Method contextGetter, final Method getValueMethod)
+ throws IllegalAccessException {
+ final MethodHandle rawContextHandle = PUBLIC_LOOKUP.unreflect(contextGetter);
+ final MethodHandle rawValueHandle = PUBLIC_LOOKUP.unreflect(getValueMethod);
+ return new GetValueRouteContextExtractor(rawContextHandle, rawValueHandle);
+ }
+
+ @SuppressWarnings("checkstyle:IllegalCatch")
+ @Override
+ public InstanceIdentifier<? extends TreeNode> extract(final TreeNode obj) {
+ try {
+ final Object ctx = contextHandle.invokeExact(obj);
+ if (ctx != null) {
+ return (InstanceIdentifier<? extends TreeNode>) valueHandle.invokeExact(ctx);
+ }
+ return null;
+ } catch (final Throwable e) {
+ throw Throwables.propagate(e);
+ }
+ }
+}
+