2 * Copyright © 2014, 2017 Red Hat, Inc. and others. All rights reserved.
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
8 package org.opendaylight.ovsdb.lib.schema.typed;
10 import static java.util.Objects.requireNonNull;
12 import java.lang.reflect.InvocationHandler;
13 import java.lang.reflect.Method;
14 import java.util.Locale;
15 import java.util.Objects;
16 import org.opendaylight.ovsdb.lib.error.ColumnSchemaNotFoundException;
17 import org.opendaylight.ovsdb.lib.error.TableSchemaNotFoundException;
18 import org.opendaylight.ovsdb.lib.error.TyperException;
19 import org.opendaylight.ovsdb.lib.error.UnsupportedMethodException;
20 import org.opendaylight.ovsdb.lib.notation.Column;
21 import org.opendaylight.ovsdb.lib.notation.Row;
22 import org.opendaylight.ovsdb.lib.schema.ColumnSchema;
23 import org.opendaylight.ovsdb.lib.schema.DatabaseSchema;
24 import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
26 final class TypedRowInvocationHandler implements InvocationHandler {
27 private static final String GET_STARTS_WITH = "get";
28 private static final String SET_STARTS_WITH = "set";
29 private static final String GETCOLUMN_ENDS_WITH = "Column";
30 private static final String GETROW_ENDS_WITH = "Row";
32 private final Class<?> target;
33 private final DatabaseSchema dbSchema;
34 private final Row<GenericTableSchema> row;
36 TypedRowInvocationHandler(final Class<?> target, final DatabaseSchema dbSchema,
37 final Row<GenericTableSchema> row) {
38 this.target = requireNonNull(target);
39 this.dbSchema = requireNonNull(dbSchema);
43 private Object processGetData(final Method method) {
44 String columnName = getColumnName(method);
45 checkColumnSchemaVersion(dbSchema, method);
46 if (columnName == null) {
47 throw new TyperException("Error processing Getter : " + method.getName());
49 GenericTableSchema tableSchema = TyperUtils.getTableSchema(dbSchema, target);
50 if (tableSchema == null) {
51 String message = TableSchemaNotFoundException.createMessage(TypedReflections.getTableName(target),
53 throw new TableSchemaNotFoundException(message);
55 ColumnSchema<GenericTableSchema, Object> columnSchema =
56 TyperUtils.getColumnSchema(tableSchema, columnName, (Class<Object>) method.getReturnType());
57 if (columnSchema == null) {
58 String message = ColumnSchemaNotFoundException.createMessage(columnName, tableSchema.getName());
59 throw new ColumnSchemaNotFoundException(message);
61 if (row == null || row.getColumn(columnSchema) == null) {
64 return row.getColumn(columnSchema).getData();
67 private Object processGetRow() {
71 private Object processGetColumn(final Method method) {
72 String columnName = getColumnName(method);
73 checkColumnSchemaVersion(dbSchema, method);
74 if (columnName == null) {
75 throw new TyperException("Error processing GetColumn : " + method.getName());
77 GenericTableSchema tableSchema = TyperUtils.getTableSchema(dbSchema, target);
78 if (tableSchema == null) {
79 String message = TableSchemaNotFoundException.createMessage(TypedReflections.getTableName(target),
81 throw new TableSchemaNotFoundException(message);
83 ColumnSchema<GenericTableSchema, Object> columnSchema =
84 TyperUtils.getColumnSchema(tableSchema, columnName, (Class<Object>) method.getReturnType());
85 if (columnSchema == null) {
86 String message = ColumnSchemaNotFoundException.createMessage(columnName, tableSchema.getName());
87 throw new ColumnSchemaNotFoundException(message);
89 // When the row is null, that might indicate that the user maybe interested
90 // only in the ColumnSchema and not on the Data.
92 return new Column<>(columnSchema, null);
94 return row.getColumn(columnSchema);
97 private Object processSetData(final Object proxy, final Method method, final Object[] args) {
98 if (args == null || args.length != 1) {
99 throw new TyperException("Setter method : " + method.getName() + " requires 1 argument");
101 checkColumnSchemaVersion(dbSchema, method);
102 String columnName = getColumnName(method);
103 if (columnName == null) {
104 throw new TyperException("Unable to locate Column Name for " + method.getName());
106 GenericTableSchema tableSchema = TyperUtils.getTableSchema(dbSchema, target);
107 ColumnSchema<GenericTableSchema, Object> columnSchema =
108 TyperUtils.getColumnSchema(tableSchema, columnName, (Class<Object>) args[0].getClass());
109 Column<GenericTableSchema, Object> column =
110 new Column<>(columnSchema, args[0]);
111 row.addColumn(columnName, column);
115 private GenericTableSchema processGetTableSchema() {
116 return TyperUtils.getTableSchema(dbSchema, target);
119 private static Boolean isHashCodeMethod(final Method method, final Object[] args) {
120 return (args == null || args.length == 0) && method.getName().equals("hashCode");
123 private static Boolean isEqualsMethod(final Method method, final Object[] args) {
126 && method.getName().equals("equals")
127 && Object.class.equals(method.getParameterTypes()[0]);
130 private static Boolean isToStringMethod(final Method method, final Object[] args) {
131 return (args == null || args.length == 0) && method.getName().equals("toString");
135 public Object invoke(final Object proxy, final Method method, final Object[] args) throws Exception {
136 if (isGetTableSchema(method)) {
137 return processGetTableSchema();
138 } else if (isGetRow(method)) {
139 return processGetRow();
140 } else if (isSetData(method)) {
141 return processSetData(proxy, method, args);
142 } else if (isGetData(method)) {
143 return processGetData(method);
144 } else if (isGetColumn(method)) {
145 return processGetColumn(method);
146 } else if (isHashCodeMethod(method, args)) {
147 return processHashCode();
148 } else if (isEqualsMethod(method, args)) {
149 return proxy.getClass().isInstance(args[0]) && processEquals(args[0]);
150 } else if (isToStringMethod(method, args)) {
151 return processToString();
153 throw new UnsupportedMethodException("Method not supported " + method.toString());
156 private boolean processEquals(final Object obj) {
157 return obj instanceof TypedBaseTable && Objects.equals(row, ((TypedBaseTable<?>)obj).getRow());
160 private int processHashCode() {
161 return row == null ? 0 : row.hashCode();
164 private String processToString() {
165 final GenericTableSchema schema = processGetTableSchema();
166 final String tableName = schema != null ? schema.getName() : "";
167 return row == null ? tableName : tableName + " : " + row.toString();
170 private static boolean isGetColumn(final Method method) {
171 TypedColumn typedColumn = method.getAnnotation(TypedColumn.class);
172 if (typedColumn != null) {
173 return typedColumn.method().equals(MethodType.GETCOLUMN);
176 return method.getName().startsWith(GET_STARTS_WITH) && method.getName().endsWith(GETCOLUMN_ENDS_WITH);
179 private static boolean isGetData(final Method method) {
180 TypedColumn typedColumn = method.getAnnotation(TypedColumn.class);
181 if (typedColumn != null) {
182 return typedColumn.method().equals(MethodType.GETDATA);
185 return method.getName().startsWith(GET_STARTS_WITH) && !method.getName().endsWith(GETCOLUMN_ENDS_WITH);
188 private static boolean isGetRow(final Method method) {
189 TypedColumn typedColumn = method.getAnnotation(TypedColumn.class);
190 if (typedColumn != null) {
191 return typedColumn.method().equals(MethodType.GETROW);
194 return method.getName().startsWith(GET_STARTS_WITH) && method.getName().endsWith(GETROW_ENDS_WITH);
197 private static boolean isGetTableSchema(final Method method) {
198 TypedColumn typedColumn = method.getAnnotation(TypedColumn.class);
199 return typedColumn != null && typedColumn.method().equals(MethodType.GETTABLESCHEMA);
202 private static boolean isSetData(final Method method) {
203 TypedColumn typedColumn = method.getAnnotation(TypedColumn.class);
204 if (typedColumn != null) {
205 return typedColumn.method().equals(MethodType.SETDATA);
208 return method.getName().startsWith(SET_STARTS_WITH);
211 private static void checkColumnSchemaVersion(final DatabaseSchema dbSchema, final Method method) {
212 TyperUtils.checkVersion(dbSchema.getVersion(), TypedReflections.getColumnVersionRange(method));
215 private static String getColumnName(final Method method) {
216 TypedColumn typedColumn = method.getAnnotation(TypedColumn.class);
217 if (typedColumn != null) {
218 return typedColumn.name();
222 * Attempting to get the column name by parsing the method name with a following convention :
223 * 1. GETDATA : get<ColumnName>
224 * 2. SETDATA : set<ColumnName>
225 * 3. GETCOLUMN : get<ColumnName>Column
226 * where <ColumnName> is the name of the column that we are interested in.
228 int index = GET_STARTS_WITH.length();
229 if (isGetData(method) || isSetData(method)) {
230 return method.getName().substring(index, method.getName().length()).toLowerCase(Locale.ROOT);
231 } else if (isGetColumn(method)) {
232 return method.getName().substring(index, method.getName().indexOf(GETCOLUMN_ENDS_WITH,
233 index)).toLowerCase(Locale.ROOT);