package org.opendaylight.ovsdb.lib.notation;
+import com.google.common.collect.Range;
import com.google.errorprone.annotations.Var;
/**
public class Version implements Comparable<Version> {
private static final String FORMAT = "(\\d+)\\.(\\d+)\\.(\\d+)";
- private int major;
- private int minor;
- private int patch;
+ private final int major;
+ private final int minor;
+ private final int patch;
- public Version(int major, int minor, int patch) {
+ public Version(final int major, final int minor, final int patch) {
this.major = major;
this.minor = minor;
this.patch = patch;
public static final Version NULL = new Version(0,0,0);
public static final String NULL_VERSION_STRING = "0.0.0";
- public static Version fromString(String version) {
+ public static Version fromString(final String version) {
final int firstDot = version.indexOf('.');
final int secondDot = version.indexOf('.', firstDot + 1);
if (firstDot == -1 || secondDot == -1) {
* has identified this as the top #3 (!) memory allocator overall - 1 GB avoidable String.
* @author Michael Vorburger.ch
*/
- private static int parse(String string, int start, int end) {
+ private static int parse(final String string, final int start, final int end) {
@Var int result = 0;
for (int i = start; i < end && i < string.length(); i++) {
char character = string.charAt(i);
return major;
}
- public void setMajor(int major) {
- this.major = major;
- }
-
public int getMinor() {
return minor;
}
- public void setMinor(int minor) {
- this.minor = minor;
- }
-
public int getPatch() {
return patch;
}
- public void setPatch(int patch) {
- this.patch = patch;
- }
-
-
// ToDo: While format is X.X.X semantics are schema dependent.
// Therefore we should allow equals to be overridden by the schema
@Override
- public boolean equals(Object object) {
- if (this == object) {
+ public boolean equals(final Object obj) {
+ if (this == obj) {
return true;
}
- if (object == null || getClass() != object.getClass()) {
+ if (obj == null || getClass() != obj.getClass()) {
return false;
}
- Version version = (Version) object;
-
- if (major != version.major) {
- return false;
- }
- if (minor != version.minor) {
- return false;
- }
- if (patch != version.patch) {
- return false;
- }
-
- return true;
+ final Version other = (Version) obj;
+ return major == other.major && minor == other.minor && patch == other.patch;
}
@Override
// ToDo: While format is X.X.X semantics are schema dependent
// Therefore we should allow compareTo to be overridden by the schema
@Override
- public int compareTo(Version version) {
+ public int compareTo(final Version version) {
if (this.equals(version)) {
return 0;
}
// must be less than
return -1;
}
+
+ public static Range<Version> createRangeOf(final Version from, final Version to) {
+ if (from == null || Version.NULL.equals(from)) {
+ return to == null || Version.NULL.equals(to) ? Range.all() : Range.atMost(to);
+ }
+ return to == null || Version.NULL.equals(to) ? Range.atLeast(from) : Range.closed(from, to);
+ }
}
--- /dev/null
+/*
+ * Copyright © 2019 PANTHEON.tech, 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.ovsdb.lib.schema.typed;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.Range;
+import java.lang.reflect.Method;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.ovsdb.lib.notation.Version;
+
+/**
+ * Utilities for extracting annotation information at runtime.
+ */
+public final class TypedReflections {
+ private static final LoadingCache<Method, Range<Version>> COLUMN_VERSIONS = CacheBuilder.newBuilder().weakKeys()
+ .build(new CacheLoader<Method, Range<Version>>() {
+ @Override
+ public Range<Version> load(final Method key) {
+ final TypedColumn typedColumn = key.getAnnotation(TypedColumn.class);
+ return typedColumn == null ? Range.all()
+ : createVersionRange(typedColumn.fromVersion(), typedColumn.untilVersion());
+ }
+ });
+ private static final LoadingCache<Class<?>, Range<Version>> TABLE_VERSIONS = CacheBuilder.newBuilder().weakKeys()
+ .build(new CacheLoader<Class<?>, Range<Version>>() {
+ @Override
+ public Range<Version> load(final Class<?> key) {
+ final TypedTable typedTable = key.getAnnotation(TypedTable.class);
+ return typedTable == null ? Range.all()
+ : createVersionRange(typedTable.fromVersion(), typedTable.untilVersion());
+ }
+ });
+
+
+ private TypedReflections() {
+
+ }
+
+ public static @Nullable String getTableDatabase(final Class<?> type) {
+ // Pure reflection metadata access -- no need to cache this
+ final TypedTable typedTable = type.getAnnotation(TypedTable.class);
+ return typedTable != null ? typedTable.database() : null;
+ }
+
+ public static @NonNull String getTableName(final Class<?> type) {
+ // Pure reflection metadata access -- no need to cache this
+ final TypedTable typedTable = type.getAnnotation(TypedTable.class);
+ return typedTable != null ? typedTable.name() : type.getSimpleName();
+ }
+
+ public static @NonNull Range<Version> getTableVersionRange(final Class<?> type) {
+ // Involves String -> Version conversion, use a cache
+ return TABLE_VERSIONS.getUnchecked(type);
+ }
+
+ public static @NonNull Range<Version> getColumnVersionRange(final Method method) {
+ // Involves String -> Version conversion, use a cache
+ return COLUMN_VERSIONS.getUnchecked(method);
+ }
+
+ static Range<Version> createVersionRange(final String from, final String until) {
+ return Version.createRangeOf(from == null ? Version.NULL : Version.fromString(from),
+ until == null ? Version.NULL : Version.fromString(until));
+ }
+}
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;
// Prevent instantiating a utility class
}
- private static <T> String getTableName(Class<T> klazz) {
- TypedTable typedTable = klazz.getAnnotation(TypedTable.class);
- if (typedTable != null) {
- return typedTable.name();
- }
- return klazz.getSimpleName();
- }
-
/**
* Retrieve the table schema for the given table in the given database schema.
*
* using their {@link TypedTable} annotation, if they have one, or by name.
* @return the table schema.
*/
- public static GenericTableSchema getTableSchema(DatabaseSchema dbSchema, Class<?> klazz) {
- String tableName = getTableName(klazz);
- return dbSchema.table(tableName, GenericTableSchema.class);
+ public static GenericTableSchema getTableSchema(final DatabaseSchema dbSchema, final Class<?> klazz) {
+ return dbSchema.table(TypedReflections.getTableName(klazz), GenericTableSchema.class);
}
- public static ColumnSchema<GenericTableSchema, Object>
- getColumnSchema(GenericTableSchema tableSchema, String columnName, Class<Object> metaClass) {
+ public static ColumnSchema<GenericTableSchema, Object> getColumnSchema(final GenericTableSchema tableSchema,
+ final String columnName, final Class<Object> metaClass) {
return tableSchema.column(columnName, metaClass);
}
@SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
justification = "https://github.com/spotbugs/spotbugs/issues/811")
- private static String getColumnName(Method method) {
+ private static String getColumnName(final Method method) {
TypedColumn typedColumn = method.getAnnotation(TypedColumn.class);
if (typedColumn != null) {
return typedColumn.name();
@SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
justification = "https://github.com/spotbugs/spotbugs/issues/811")
- private static boolean isGetTableSchema(Method method) {
+ 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(Method method) {
+ 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(Method method) {
+ 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(Method method) {
+ 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(Method method) {
+ 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);
}
- public static Version getColumnFromVersion(Method method) {
- TypedColumn typedColumn = method.getAnnotation(TypedColumn.class);
- if (typedColumn != null) {
- return Version.fromString(typedColumn.fromVersion());
- }
- return Version.NULL;
- }
-
- public static <T> Version getTableFromVersion(final Class<T> klazz) {
- TypedTable typedTable = klazz.getAnnotation(TypedTable.class);
- if (typedTable != null) {
- return Version.fromString(typedTable.fromVersion());
- }
- return Version.NULL;
- }
-
- public static Version getColumnUntilVersion(Method method) {
- TypedColumn typedColumn = method.getAnnotation(TypedColumn.class);
- if (typedColumn != null) {
- return Version.fromString(typedColumn.untilVersion());
- }
- return Version.NULL;
- }
-
- public static <T> Version getTableUntilVersion(final Class<T> klazz) {
- TypedTable typedTable = klazz.getAnnotation(TypedTable.class);
- if (typedTable != null) {
- return Version.fromString(typedTable.untilVersion());
- }
- return Version.NULL;
- }
-
/**
* 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
* @param klazz Typed Class that represents a Table
* @return true if valid, false otherwise
*/
- private static <T> boolean isValid(DatabaseSchema dbSchema, final Class<T> klazz) {
+ private static <T> boolean isValid(final DatabaseSchema dbSchema, final Class<T> klazz) {
if (dbSchema == null) {
return false;
}
- TypedTable typedTable = klazz.getAnnotation(TypedTable.class);
- if (typedTable != null && !dbSchema.getName().equalsIgnoreCase(typedTable.database())) {
+ final String dbName = TypedReflections.getTableDatabase(klazz);
+ if (dbName != null && !dbSchema.getName().equalsIgnoreCase(dbName)) {
return false;
}
@SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
justification = "https://github.com/spotbugs/spotbugs/issues/811")
- private static void checkColumnSchemaVersion(DatabaseSchema dbSchema, Method method) {
- Version fromVersion = getColumnFromVersion(method);
- Version untilVersion = getColumnUntilVersion(method);
- Version schemaVersion = dbSchema.getVersion();
- checkVersion(schemaVersion, fromVersion, untilVersion);
+ private static void checkColumnSchemaVersion(final DatabaseSchema dbSchema, final Method method) {
+ checkVersion(dbSchema.getVersion(), TypedReflections.getColumnVersionRange(method));
}
- private static <T> void checkTableSchemaVersion(DatabaseSchema dbSchema, Class<T> klazz) {
- Version fromVersion = getTableFromVersion(klazz);
- Version untilVersion = getTableUntilVersion(klazz);
- Version schemaVersion = dbSchema.getVersion();
- checkVersion(schemaVersion, fromVersion, untilVersion);
+ private static <T> void checkTableSchemaVersion(final DatabaseSchema dbSchema, final Class<T> klazz) {
+ checkVersion(dbSchema.getVersion(), TypedReflections.getTableVersionRange(klazz));
}
- private static void checkVersion(Version schemaVersion, Version fromVersion, Version untilVersion) {
- if (!fromVersion.equals(Version.NULL) && schemaVersion.compareTo(fromVersion) < 0 || !untilVersion.equals(
- Version.NULL) && schemaVersion.compareTo(untilVersion) > 0) {
- throw new SchemaVersionMismatchException(schemaVersion, fromVersion, untilVersion);
+ @VisibleForTesting
+ static void checkVersion(final Version schemaVersion, final Range<Version> range) {
+ if (!range.contains(schemaVersion)) {
+ throw new SchemaVersionMismatchException(schemaVersion,
+ range.hasLowerBound() ? range.lowerEndpoint() : Version.NULL,
+ range.hasUpperBound() ? range.upperEndpoint() : Version.NULL);
}
}
row.setTableSchema(getTableSchema(dbSchema, klazz));
}
return Reflection.newProxy(klazz, new InvocationHandler() {
- private Object processGetData(Method method) {
+ private Object processGetData(final Method method) {
String columnName = getColumnName(method);
checkColumnSchemaVersion(dbSchema, method);
if (columnName == null) {
}
GenericTableSchema tableSchema = getTableSchema(dbSchema, klazz);
if (tableSchema == null) {
- String message =
- TableSchemaNotFoundException.createMessage(getTableName(klazz), dbSchema.getName());
+ String message = TableSchemaNotFoundException.createMessage(TypedReflections.getTableName(klazz),
+ dbSchema.getName());
throw new TableSchemaNotFoundException(message);
}
ColumnSchema<GenericTableSchema, Object> columnSchema =
return row;
}
- private Object processGetColumn(Method method) {
+ private Object processGetColumn(final Method method) {
String columnName = getColumnName(method);
checkColumnSchemaVersion(dbSchema, method);
if (columnName == null) {
}
GenericTableSchema tableSchema = getTableSchema(dbSchema, klazz);
if (tableSchema == null) {
- String message =
- TableSchemaNotFoundException.createMessage(getTableName(klazz), dbSchema.getName());
+ String message = TableSchemaNotFoundException.createMessage(TypedReflections.getTableName(klazz),
+ dbSchema.getName());
throw new TableSchemaNotFoundException(message);
}
ColumnSchema<GenericTableSchema, Object> columnSchema =
return row.getColumn(columnSchema);
}
- private Object processSetData(Object proxy, Method method, Object[] args) {
+ 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");
}
return getTableSchema(dbSchema, klazz);
}
- private Boolean isHashCodeMethod(Method method, Object[] args) {
+ private Boolean isHashCodeMethod(final Method method, final Object[] args) {
return (args == null || args.length == 0) && method.getName().equals("hashCode");
}
- private Boolean isEqualsMethod(Method method, Object[] args) {
+ 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(Method method, Object[] args) {
+ private Boolean isToStringMethod(final Method method, final Object[] args) {
return (args == null || args.length == 0) && method.getName().equals("toString");
}
@Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
+ public Object invoke(final Object proxy, final Method method, final Object[] args) throws Exception {
if (isGetTableSchema(method)) {
return processGetTableSchema();
} else if (isGetRow(method)) {
@Override
@SuppressFBWarnings({"EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS", "EQ_UNUSUAL"})
- public boolean equals(Object obj) {
+ public boolean equals(final Object obj) {
if (!(obj instanceof TypedBaseTable)) {
return false;
}
return Objects.equal(row, typedRowObj.getRow());
}
- @Override public int hashCode() {
+ @Override
+ public int hashCode() {
if (row == null) {
return 0;
}
return row.hashCode();
}
- @Override public String toString() {
+ @Override
+ public String toString() {
String tableName;
TableSchema<?> schema = (TableSchema<?>)processGetTableSchema();
if (schema != null) {
* @param dbSchema Dbschema for the TableUpdates
* @return Map<UUID,T> for the type of things being sought
*/
- public static <T> Map<UUID,T> extractRowsUpdated(Class<T> klazz,TableUpdates updates,DatabaseSchema dbSchema) {
+ public static <T> Map<UUID,T> extractRowsUpdated(final Class<T> klazz, final TableUpdates updates,
+ final DatabaseSchema dbSchema) {
Preconditions.checkNotNull(klazz);
Preconditions.checkNotNull(updates);
Preconditions.checkNotNull(dbSchema);
* @param dbSchema Dbschema for the TableUpdates
* @return Map<UUID,T> for the type of things being sought
*/
- public static <T> Map<UUID, T> extractRowsOld(Class<T> klazz, TableUpdates updates, DatabaseSchema dbSchema) {
+ public static <T> Map<UUID, T> extractRowsOld(final Class<T> klazz, final TableUpdates updates,
+ final DatabaseSchema dbSchema) {
Preconditions.checkNotNull(klazz);
Preconditions.checkNotNull(updates);
Preconditions.checkNotNull(dbSchema);
* @param dbSchema Dbschema for the TableUpdates
* @return Map<UUID,T> for the type of things being sought
*/
- public static <T> Map<UUID,T> extractRowsRemoved(Class<T> klazz,TableUpdates updates,DatabaseSchema dbSchema) {
+ public static <T> Map<UUID,T> extractRowsRemoved(final Class<T> klazz, final TableUpdates updates,
+ final DatabaseSchema dbSchema) {
Preconditions.checkNotNull(klazz);
Preconditions.checkNotNull(updates);
Preconditions.checkNotNull(dbSchema);
* for the type of things being sought
*/
public static Map<UUID,TableUpdate<GenericTableSchema>.RowUpdate<GenericTableSchema>>
- extractRowUpdates(Class<?> klazz,TableUpdates updates,DatabaseSchema dbSchema) {
+ extractRowUpdates(final Class<?> klazz,final TableUpdates updates,final DatabaseSchema dbSchema) {
Preconditions.checkNotNull(klazz);
Preconditions.checkNotNull(updates);
Preconditions.checkNotNull(dbSchema);
}
return result;
}
-
}
import static org.junit.Assert.assertEquals;
import com.google.common.collect.ImmutableMap;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
+import com.google.common.collect.Range;
import java.util.Collections;
-import org.junit.Assert;
import org.junit.Test;
import org.opendaylight.ovsdb.lib.error.SchemaVersionMismatchException;
import org.opendaylight.ovsdb.lib.notation.Version;
import org.opendaylight.ovsdb.lib.schema.DatabaseSchema;
import org.opendaylight.ovsdb.lib.schema.DatabaseSchemaImpl;
import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* Test class for {@link TyperUtils}.
*/
public class TyperUtilsTest {
- private static final Logger LOG = LoggerFactory.getLogger(TyperUtilsTest.class);
-
@TypedTable(name = "TestTypedTable", database = "Open_vSwitch")
private class TestTypedTable {
}
/**
- * Test that {@link TyperUtils#checkVersion(Version, Version, Version)} detects an old version. (The aim here isn't
+ * Test that {@link TyperUtils#checkVersion(Version, Range)} detects an old version. (The aim here isn't
* to test {@link Version#compareTo(Version)}, that should be done in
* {@link org.opendaylight.ovsdb.lib.notation.VersionTest}).
*/
}
/**
- * Test that {@link TyperUtils#checkVersion(Version, Version, Version)} detects a new version.
+ * Test that {@link TyperUtils#checkVersion(Version, Range)} detects a new version.
*/
@Test(expected = SchemaVersionMismatchException.class)
public void testCheckNewVersionFails() throws SchemaVersionMismatchException {
}
/**
- * Test that {@link TyperUtils#checkVersion(Version, Version, Version)} accepts null boundaries.
+ * Test that {@link TyperUtils#checkVersion(Version, Range)} accepts null boundaries.
*/
@Test
public void testCheckNullVersionsSucceed() throws SchemaVersionMismatchException {
}
/**
- * Test that {@link TyperUtils#checkVersion(Version, Version, Version)} accepts the lower boundary version.
+ * Test that {@link TyperUtils#checkVersion(Version, Range)} accepts the lower boundary version.
*/
@Test
public void testCheckLowerVersionBoundarySucceeds() throws SchemaVersionMismatchException {
}
/**
- * Test that {@link TyperUtils#checkVersion(Version, Version, Version)} accepts the upper boundary version.
+ * Test that {@link TyperUtils#checkVersion(Version, Range)} accepts the upper boundary version.
*/
@Test
public void testCheckUpperVersionBoundarySucceeds() throws SchemaVersionMismatchException {
}
/**
- * Test that {@link TyperUtils#checkVersion(Version, Version, Version)} accepts both boundary versions.
+ * Test that {@link TyperUtils#checkVersion(Version, Range)} accepts both boundary versions.
*/
@Test
public void testCheckSingleVersionBoundarySucceeds() throws SchemaVersionMismatchException {
}
/**
- * Test that {@link TyperUtils#checkVersion(Version, Version, Version)} accepts a version within the boundaries
+ * Test that {@link TyperUtils#checkVersion(Version, Range)} accepts a version within the boundaries
* (strictly).
*/
@Test
}
/**
- * Call {@link TyperUtils#checkVersion(Version, Version, Version)}.
+ * Call {@link TyperUtils#checkVersion(Version, Range)}.
*
* @param schema The schema version (to be checked).
* @param from The minimum supported version.
* @param to The maximum supported version.
* @throws SchemaVersionMismatchException if the schema version isn't supported.
*/
- // We extract the real cause, which “loses” the original cause, but that’s fine
- @SuppressWarnings("checkstyle:AvoidHidingCauseException")
private static void callCheckVersion(final Version schema, final Version from, final Version to) {
- try {
- Method method =
- TyperUtils.class.getDeclaredMethod("checkVersion", Version.class, Version.class, Version.class);
- method.setAccessible(true);
- method.invoke(TyperUtils.class, schema, from, to);
- } catch (NoSuchMethodException e) {
- LOG.error("Can't find TyperUtils::checkVersion(), TyperUtilsTest::callCheckVersion() may be obsolete", e);
- } catch (IllegalAccessException e) {
- LOG.error("Error invoking TyperUtils::checkVersion(), please check TyperUtilsTest::callCheckVersion()", e);
- } catch (InvocationTargetException e) {
- final Throwable cause = e.getCause();
- if (cause instanceof SchemaVersionMismatchException) {
- throw (SchemaVersionMismatchException) cause;
- }
- LOG.error("Unexpected exception thrown by TyperUtils::checkVersion()", cause);
- Assert.fail("Unexpected exception thrown by TyperUtils::checkVersion()");
- }
+ TyperUtils.checkVersion(schema, Version.createRangeOf(from, to));
}
}