From 0d6be18102aeb3031144c98e05a52d0c17a3afee Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Sun, 1 Dec 2019 11:33:02 +0100 Subject: [PATCH] Extract TypedRowInvocationHandler The InvocationHandler used in TyperUtils has significant functionality, which we want to further extend/optimize. Split it out into its own class. Change-Id: I6406150799d805b2c2e0867a37205f54ec1c10bd Signed-off-by: Robert Varga --- .../typed/TypedRowInvocationHandler.java | 238 ++++++++++++++++++ .../ovsdb/lib/schema/typed/TyperUtils.java | 238 +----------------- 2 files changed, 241 insertions(+), 235 deletions(-) create mode 100644 library/impl/src/main/java/org/opendaylight/ovsdb/lib/schema/typed/TypedRowInvocationHandler.java diff --git a/library/impl/src/main/java/org/opendaylight/ovsdb/lib/schema/typed/TypedRowInvocationHandler.java b/library/impl/src/main/java/org/opendaylight/ovsdb/lib/schema/typed/TypedRowInvocationHandler.java new file mode 100644 index 000000000..27f1d4f44 --- /dev/null +++ b/library/impl/src/main/java/org/opendaylight/ovsdb/lib/schema/typed/TypedRowInvocationHandler.java @@ -0,0 +1,238 @@ +/* + * Copyright © 2014, 2017 Red Hat, Inc. 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.ovsdb.lib.schema.typed; + +import static java.util.Objects.requireNonNull; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.util.Locale; +import java.util.Objects; +import org.opendaylight.ovsdb.lib.error.ColumnSchemaNotFoundException; +import org.opendaylight.ovsdb.lib.error.TableSchemaNotFoundException; +import org.opendaylight.ovsdb.lib.error.TyperException; +import org.opendaylight.ovsdb.lib.error.UnsupportedMethodException; +import org.opendaylight.ovsdb.lib.notation.Column; +import org.opendaylight.ovsdb.lib.notation.Row; +import org.opendaylight.ovsdb.lib.schema.ColumnSchema; +import org.opendaylight.ovsdb.lib.schema.DatabaseSchema; +import org.opendaylight.ovsdb.lib.schema.GenericTableSchema; + +final class TypedRowInvocationHandler implements InvocationHandler { + private static final String GET_STARTS_WITH = "get"; + private static final String SET_STARTS_WITH = "set"; + private static final String GETCOLUMN_ENDS_WITH = "Column"; + private static final String GETROW_ENDS_WITH = "Row"; + + private final Class target; + private final DatabaseSchema dbSchema; + private final Row row; + + TypedRowInvocationHandler(final Class target, final DatabaseSchema dbSchema, + final Row row) { + this.target = requireNonNull(target); + this.dbSchema = requireNonNull(dbSchema); + this.row = row; + } + + private Object processGetData(final Method method) { + String columnName = getColumnName(method); + checkColumnSchemaVersion(dbSchema, method); + if (columnName == null) { + throw new TyperException("Error processing Getter : " + method.getName()); + } + GenericTableSchema tableSchema = TyperUtils.getTableSchema(dbSchema, target); + if (tableSchema == null) { + String message = TableSchemaNotFoundException.createMessage(TypedReflections.getTableName(target), + dbSchema.getName()); + throw new TableSchemaNotFoundException(message); + } + ColumnSchema columnSchema = + TyperUtils.getColumnSchema(tableSchema, columnName, (Class) method.getReturnType()); + if (columnSchema == null) { + String message = ColumnSchemaNotFoundException.createMessage(columnName, tableSchema.getName()); + throw new ColumnSchemaNotFoundException(message); + } + if (row == null || row.getColumn(columnSchema) == null) { + return null; + } + return row.getColumn(columnSchema).getData(); + } + + private Object processGetRow() { + return row; + } + + private Object processGetColumn(final Method method) { + String columnName = getColumnName(method); + checkColumnSchemaVersion(dbSchema, method); + if (columnName == null) { + throw new TyperException("Error processing GetColumn : " + method.getName()); + } + GenericTableSchema tableSchema = TyperUtils.getTableSchema(dbSchema, target); + if (tableSchema == null) { + String message = TableSchemaNotFoundException.createMessage(TypedReflections.getTableName(target), + dbSchema.getName()); + throw new TableSchemaNotFoundException(message); + } + ColumnSchema columnSchema = + TyperUtils.getColumnSchema(tableSchema, columnName, (Class) method.getReturnType()); + if (columnSchema == null) { + String message = ColumnSchemaNotFoundException.createMessage(columnName, tableSchema.getName()); + throw new ColumnSchemaNotFoundException(message); + } + // When the row is null, that might indicate that the user maybe interested + // only in the ColumnSchema and not on the Data. + if (row == null) { + return new Column<>(columnSchema, null); + } + return row.getColumn(columnSchema); + } + + private Object processSetData(final Object proxy, final Method method, final Object[] args) { + if (args == null || args.length != 1) { + throw new TyperException("Setter method : " + method.getName() + " requires 1 argument"); + } + checkColumnSchemaVersion(dbSchema, method); + String columnName = getColumnName(method); + if (columnName == null) { + throw new TyperException("Unable to locate Column Name for " + method.getName()); + } + GenericTableSchema tableSchema = TyperUtils.getTableSchema(dbSchema, target); + ColumnSchema columnSchema = + TyperUtils.getColumnSchema(tableSchema, columnName, (Class) args[0].getClass()); + Column column = + new Column<>(columnSchema, args[0]); + row.addColumn(columnName, column); + return proxy; + } + + private GenericTableSchema processGetTableSchema() { + return TyperUtils.getTableSchema(dbSchema, target); + } + + private static Boolean isHashCodeMethod(final Method method, final Object[] args) { + return (args == null || args.length == 0) && method.getName().equals("hashCode"); + } + + private static Boolean isEqualsMethod(final Method method, final Object[] args) { + return args != null + && args.length == 1 + && method.getName().equals("equals") + && Object.class.equals(method.getParameterTypes()[0]); + } + + private static Boolean isToStringMethod(final Method method, final Object[] args) { + return (args == null || args.length == 0) && method.getName().equals("toString"); + } + + @Override + public Object invoke(final Object proxy, final Method method, final Object[] args) throws Exception { + if (isGetTableSchema(method)) { + return processGetTableSchema(); + } else if (isGetRow(method)) { + return processGetRow(); + } else if (isSetData(method)) { + return processSetData(proxy, method, args); + } else if (isGetData(method)) { + return processGetData(method); + } else if (isGetColumn(method)) { + return processGetColumn(method); + } else if (isHashCodeMethod(method, args)) { + return processHashCode(); + } else if (isEqualsMethod(method, args)) { + return proxy.getClass().isInstance(args[0]) && processEquals(args[0]); + } else if (isToStringMethod(method, args)) { + return processToString(); + } + throw new UnsupportedMethodException("Method not supported " + method.toString()); + } + + private boolean processEquals(final Object obj) { + return obj instanceof TypedBaseTable && Objects.equals(row, ((TypedBaseTable)obj).getRow()); + } + + private int processHashCode() { + return row == null ? 0 : row.hashCode(); + } + + private String processToString() { + final GenericTableSchema schema = processGetTableSchema(); + final String tableName = schema != null ? schema.getName() : ""; + return row == null ? tableName : tableName + " : " + row.toString(); + } + + private static boolean isGetColumn(final Method method) { + TypedColumn typedColumn = method.getAnnotation(TypedColumn.class); + if (typedColumn != null) { + return typedColumn.method().equals(MethodType.GETCOLUMN); + } + + return method.getName().startsWith(GET_STARTS_WITH) && method.getName().endsWith(GETCOLUMN_ENDS_WITH); + } + + private static boolean isGetData(final Method method) { + TypedColumn typedColumn = method.getAnnotation(TypedColumn.class); + if (typedColumn != null) { + return typedColumn.method().equals(MethodType.GETDATA); + } + + return method.getName().startsWith(GET_STARTS_WITH) && !method.getName().endsWith(GETCOLUMN_ENDS_WITH); + } + + private static boolean isGetRow(final Method method) { + TypedColumn typedColumn = method.getAnnotation(TypedColumn.class); + if (typedColumn != null) { + return typedColumn.method().equals(MethodType.GETROW); + } + + return method.getName().startsWith(GET_STARTS_WITH) && method.getName().endsWith(GETROW_ENDS_WITH); + } + + private static boolean isGetTableSchema(final Method method) { + TypedColumn typedColumn = method.getAnnotation(TypedColumn.class); + return typedColumn != null && typedColumn.method().equals(MethodType.GETTABLESCHEMA); + } + + private static boolean isSetData(final Method method) { + TypedColumn typedColumn = method.getAnnotation(TypedColumn.class); + if (typedColumn != null) { + return typedColumn.method().equals(MethodType.SETDATA); + } + + return method.getName().startsWith(SET_STARTS_WITH); + } + + private static void checkColumnSchemaVersion(final DatabaseSchema dbSchema, final Method method) { + TyperUtils.checkVersion(dbSchema.getVersion(), TypedReflections.getColumnVersionRange(method)); + } + + private static String getColumnName(final Method method) { + TypedColumn typedColumn = method.getAnnotation(TypedColumn.class); + if (typedColumn != null) { + return typedColumn.name(); + } + + /* + * Attempting to get the column name by parsing the method name with a following convention : + * 1. GETDATA : get + * 2. SETDATA : set + * 3. GETCOLUMN : getColumn + * where is the name of the column that we are interested in. + */ + int index = GET_STARTS_WITH.length(); + if (isGetData(method) || isSetData(method)) { + return method.getName().substring(index, method.getName().length()).toLowerCase(Locale.ROOT); + } else if (isGetColumn(method)) { + return method.getName().substring(index, method.getName().indexOf(GETCOLUMN_ENDS_WITH, + index)).toLowerCase(Locale.ROOT); + } + + return null; + } +} diff --git a/library/impl/src/main/java/org/opendaylight/ovsdb/lib/schema/typed/TyperUtils.java b/library/impl/src/main/java/org/opendaylight/ovsdb/lib/schema/typed/TyperUtils.java index 284e3c676..0e2029c3a 100644 --- a/library/impl/src/main/java/org/opendaylight/ovsdb/lib/schema/typed/TyperUtils.java +++ b/library/impl/src/main/java/org/opendaylight/ovsdb/lib/schema/typed/TyperUtils.java @@ -5,28 +5,16 @@ * 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.ovsdb.lib.schema.typed; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.collect.Range; import com.google.common.reflect.Reflection; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; import java.util.HashMap; -import java.util.Locale; import java.util.Map; -import org.opendaylight.ovsdb.lib.error.ColumnSchemaNotFoundException; import org.opendaylight.ovsdb.lib.error.SchemaVersionMismatchException; -import org.opendaylight.ovsdb.lib.error.TableSchemaNotFoundException; -import org.opendaylight.ovsdb.lib.error.TyperException; -import org.opendaylight.ovsdb.lib.error.UnsupportedMethodException; import org.opendaylight.ovsdb.lib.message.TableUpdate; import org.opendaylight.ovsdb.lib.message.TableUpdates; -import org.opendaylight.ovsdb.lib.notation.Column; import org.opendaylight.ovsdb.lib.notation.Row; import org.opendaylight.ovsdb.lib.notation.UUID; import org.opendaylight.ovsdb.lib.notation.Version; @@ -38,12 +26,6 @@ import org.opendaylight.ovsdb.lib.schema.GenericTableSchema; * Utility methods for typed OVSDB schema data. */ public final class TyperUtils { - - private static final String GET_STARTS_WITH = "get"; - private static final String SET_STARTS_WITH = "set"; - private static final String GETCOLUMN_ENDS_WITH = "Column"; - private static final String GETROW_ENDS_WITH = "Row"; - private TyperUtils() { // Prevent instantiating a utility class } @@ -65,77 +47,6 @@ public final class TyperUtils { return tableSchema.column(columnName, metaClass); } - @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", - justification = "https://github.com/spotbugs/spotbugs/issues/811") - private static String getColumnName(final Method method) { - TypedColumn typedColumn = method.getAnnotation(TypedColumn.class); - if (typedColumn != null) { - return typedColumn.name(); - } - - /* - * Attempting to get the column name by parsing the method name with a following convention : - * 1. GETDATA : get - * 2. SETDATA : set - * 3. GETCOLUMN : getColumn - * where is the name of the column that we are interested in. - */ - int index = GET_STARTS_WITH.length(); - if (isGetData(method) || isSetData(method)) { - return method.getName().substring(index, method.getName().length()).toLowerCase(Locale.ROOT); - } else if (isGetColumn(method)) { - return method.getName().substring(index, method.getName().indexOf(GETCOLUMN_ENDS_WITH, - index)).toLowerCase(Locale.ROOT); - } - - return null; - } - - @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", - justification = "https://github.com/spotbugs/spotbugs/issues/811") - private static boolean isGetTableSchema(final Method method) { - TypedColumn typedColumn = method.getAnnotation(TypedColumn.class); - return typedColumn != null && typedColumn.method().equals(MethodType.GETTABLESCHEMA); - } - - @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", - justification = "https://github.com/spotbugs/spotbugs/issues/811") - private static boolean isGetRow(final Method method) { - TypedColumn typedColumn = method.getAnnotation(TypedColumn.class); - if (typedColumn != null) { - return typedColumn.method().equals(MethodType.GETROW); - } - - return method.getName().startsWith(GET_STARTS_WITH) && method.getName().endsWith(GETROW_ENDS_WITH); - } - - private static boolean isGetColumn(final Method method) { - TypedColumn typedColumn = method.getAnnotation(TypedColumn.class); - if (typedColumn != null) { - return typedColumn.method().equals(MethodType.GETCOLUMN); - } - - return method.getName().startsWith(GET_STARTS_WITH) && method.getName().endsWith(GETCOLUMN_ENDS_WITH); - } - - private static boolean isGetData(final Method method) { - TypedColumn typedColumn = method.getAnnotation(TypedColumn.class); - if (typedColumn != null) { - return typedColumn.method().equals(MethodType.GETDATA); - } - - return method.getName().startsWith(GET_STARTS_WITH) && !method.getName().endsWith(GETCOLUMN_ENDS_WITH); - } - - private static boolean isSetData(final Method method) { - TypedColumn typedColumn = method.getAnnotation(TypedColumn.class); - if (typedColumn != null) { - return typedColumn.method().equals(MethodType.SETDATA); - } - - return method.getName().startsWith(SET_STARTS_WITH); - } - /** * Method that checks validity of the parameter passed to getTypedRowWrapper. * This method checks for a valid Database Schema matching the expected Database for a given table @@ -146,31 +57,15 @@ public final class TyperUtils { * @return true if valid, false otherwise */ private static boolean isValid(final DatabaseSchema dbSchema, final Class klazz) { - if (dbSchema == null) { - return false; - } - final String dbName = TypedReflections.getTableDatabase(klazz); if (dbName != null && !dbSchema.getName().equalsIgnoreCase(dbName)) { return false; } - checkTableSchemaVersion(dbSchema, klazz); - - return true; - } - - @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", - justification = "https://github.com/spotbugs/spotbugs/issues/811") - private static void checkColumnSchemaVersion(final DatabaseSchema dbSchema, final Method method) { - checkVersion(dbSchema.getVersion(), TypedReflections.getColumnVersionRange(method)); - } - - private static void checkTableSchemaVersion(final DatabaseSchema dbSchema, final Class klazz) { checkVersion(dbSchema.getVersion(), TypedReflections.getTableVersionRange(klazz)); + return true; } - @VisibleForTesting static void checkVersion(final Version schemaVersion, final Range range) { if (!range.contains(schemaVersion)) { throw new SchemaVersionMismatchException(schemaVersion, @@ -221,140 +116,13 @@ public final class TyperUtils { */ public static T getTypedRowWrapper(final DatabaseSchema dbSchema, final Class klazz, final Row row) { - if (!isValid(dbSchema, klazz)) { + if (dbSchema == null || !isValid(dbSchema, klazz)) { return null; } if (row != null) { row.setTableSchema(getTableSchema(dbSchema, klazz)); } - return Reflection.newProxy(klazz, new InvocationHandler() { - private Object processGetData(final Method method) { - String columnName = getColumnName(method); - checkColumnSchemaVersion(dbSchema, method); - if (columnName == null) { - throw new TyperException("Error processing Getter : " + method.getName()); - } - GenericTableSchema tableSchema = getTableSchema(dbSchema, klazz); - if (tableSchema == null) { - String message = TableSchemaNotFoundException.createMessage(TypedReflections.getTableName(klazz), - dbSchema.getName()); - throw new TableSchemaNotFoundException(message); - } - ColumnSchema columnSchema = - getColumnSchema(tableSchema, columnName, (Class) method.getReturnType()); - if (columnSchema == null) { - String message = ColumnSchemaNotFoundException.createMessage(columnName, tableSchema.getName()); - throw new ColumnSchemaNotFoundException(message); - } - if (row == null || row.getColumn(columnSchema) == null) { - return null; - } - return row.getColumn(columnSchema).getData(); - } - - private Object processGetRow() { - return row; - } - - private Object processGetColumn(final Method method) { - String columnName = getColumnName(method); - checkColumnSchemaVersion(dbSchema, method); - if (columnName == null) { - throw new TyperException("Error processing GetColumn : " + method.getName()); - } - GenericTableSchema tableSchema = getTableSchema(dbSchema, klazz); - if (tableSchema == null) { - String message = TableSchemaNotFoundException.createMessage(TypedReflections.getTableName(klazz), - dbSchema.getName()); - throw new TableSchemaNotFoundException(message); - } - ColumnSchema columnSchema = - getColumnSchema(tableSchema, columnName, (Class) method.getReturnType()); - if (columnSchema == null) { - String message = ColumnSchemaNotFoundException.createMessage(columnName, tableSchema.getName()); - throw new ColumnSchemaNotFoundException(message); - } - // When the row is null, that might indicate that the user maybe interested - // only in the ColumnSchema and not on the Data. - if (row == null) { - return new Column<>(columnSchema, null); - } - return row.getColumn(columnSchema); - } - - private Object processSetData(final Object proxy, final Method method, final Object[] args) { - if (args == null || args.length != 1) { - throw new TyperException("Setter method : " + method.getName() + " requires 1 argument"); - } - checkColumnSchemaVersion(dbSchema, method); - String columnName = getColumnName(method); - if (columnName == null) { - throw new TyperException("Unable to locate Column Name for " + method.getName()); - } - GenericTableSchema tableSchema = getTableSchema(dbSchema, klazz); - ColumnSchema columnSchema = - getColumnSchema(tableSchema, columnName, (Class) args[0].getClass()); - Column column = - new Column<>(columnSchema, args[0]); - row.addColumn(columnName, column); - return proxy; - } - - private GenericTableSchema processGetTableSchema() { - return dbSchema == null ? null : getTableSchema(dbSchema, klazz); - } - - private Boolean isHashCodeMethod(final Method method, final Object[] args) { - return (args == null || args.length == 0) && method.getName().equals("hashCode"); - } - - private Boolean isEqualsMethod(final Method method, final Object[] args) { - return args != null - && args.length == 1 - && method.getName().equals("equals") - && Object.class.equals(method.getParameterTypes()[0]); - } - - private Boolean isToStringMethod(final Method method, final Object[] args) { - return (args == null || args.length == 0) && method.getName().equals("toString"); - } - - @Override - public Object invoke(final Object proxy, final Method method, final Object[] args) throws Exception { - if (isGetTableSchema(method)) { - return processGetTableSchema(); - } else if (isGetRow(method)) { - return processGetRow(); - } else if (isSetData(method)) { - return processSetData(proxy, method, args); - } else if (isGetData(method)) { - return processGetData(method); - } else if (isGetColumn(method)) { - return processGetColumn(method); - } else if (isHashCodeMethod(method, args)) { - return processHashCode(); - } else if (isEqualsMethod(method, args)) { - return proxy.getClass().isInstance(args[0]) && processEquals(args[0]); - } else if (isToStringMethod(method, args)) { - return processToString(); - } - throw new UnsupportedMethodException("Method not supported " + method.toString()); - } - - private boolean processEquals(final Object obj) { - return obj instanceof TypedBaseTable && Objects.equal(row, ((TypedBaseTable)obj).getRow()); - } - - private int processHashCode() { - return row == null ? 0 : row.hashCode(); - } - - private String processToString() { - final GenericTableSchema schema = processGetTableSchema(); - final String tableName = schema != null ? schema.getName() : ""; - return row == null ? tableName : tableName + " : " + row.toString(); - } - }); + return Reflection.newProxy(klazz, new TypedRowInvocationHandler(klazz, dbSchema, row)); } /** -- 2.36.6