Rework TypedRowInvocationHandler invocation path
[ovsdb.git] / library / impl / src / main / java / org / opendaylight / ovsdb / lib / schema / typed / TypedRowInvocationHandler.java
1 /*
2  * Copyright © 2014, 2017 Red Hat, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.ovsdb.lib.schema.typed;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.collect.ImmutableMap;
13 import java.lang.reflect.InvocationHandler;
14 import java.lang.reflect.Method;
15 import java.util.Objects;
16 import org.eclipse.jdt.annotation.NonNull;
17 import org.eclipse.jdt.annotation.Nullable;
18 import org.opendaylight.ovsdb.lib.error.UnsupportedMethodException;
19 import org.opendaylight.ovsdb.lib.notation.Row;
20 import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
21 import org.opendaylight.ovsdb.lib.schema.typed.MethodDispatch.Invoker;
22
23 /*
24  * Theory of operation: we have a set of Invoker, which are indexed by method and point to implementations we should
25  * be invoking. This mapping is data-invariant, end hence we allow rebiding to a different row (which may not be null).
26  */
27 final class TypedRowInvocationHandler implements InvocationHandler {
28
29     private final @NonNull ImmutableMap<Method, Invoker> invokers;
30     private final @NonNull String tableName;
31     private final @Nullable Row<GenericTableSchema> row;
32
33     private TypedRowInvocationHandler(final @NonNull String tableName,
34             @NonNull final ImmutableMap<Method, Invoker> invokers, final Row<GenericTableSchema> row) {
35         this.tableName = requireNonNull(tableName);
36         this.invokers = requireNonNull(invokers);
37         this.row = row;
38     }
39
40     TypedRowInvocationHandler(final @NonNull String tableName, @NonNull final ImmutableMap<Method, Invoker> invokers) {
41         this(tableName, invokers, null);
42     }
43
44     TypedRowInvocationHandler bindToRow(final @Nullable Row<GenericTableSchema> newRow) {
45         return row == newRow ? this : new TypedRowInvocationHandler(tableName, invokers, newRow);
46     }
47
48     String getTableName() {
49         return tableName;
50     }
51
52     @Override
53     public Object invoke(final Object proxy, final Method method, final Object[] args) {
54         final Invoker invoker = invokers.get(method);
55         return invoker != null ? invoker.invokeMethod(row, proxy, args) : invokeObjectMethod(proxy, method, args);
56     }
57
58     // Split out to aid inlining
59     private Object invokeObjectMethod(final Object proxy, final Method method, final Object[] args) {
60         switch (method.getName()) {
61             case "hashCode":
62                 if (args == null || args.length == 0) {
63                     return row == null ? 0 : row.hashCode();
64                 }
65                 break;
66             case "equals":
67                 if (args != null && args.length == 1 && method.getParameterTypes()[0] == Object.class) {
68                     // We only run equality or our proxy and only when it is proxying a TypedBaseTable
69                     final Object obj = args[0];
70                     return proxy == obj || proxy.getClass().isInstance(obj) && obj instanceof TypedBaseTable
71                             && Objects.equals(row, ((TypedBaseTable<?>)obj).getRow());
72                 }
73                 break;
74             case "toString":
75                 if (args == null || args.length == 0) {
76                     return row == null ? tableName : tableName + " : " + row.toString();
77                 }
78                 break;
79             default:
80                 break;
81         }
82
83         throw new UnsupportedMethodException("Method not supported " + method.toString());
84     }
85 }