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 throw new TableSchemaNotFoundException(TypedReflections.getTableName(target), dbSchema.getName());
53 ColumnSchema<GenericTableSchema, Object> columnSchema =
54 TyperUtils.getColumnSchema(tableSchema, columnName, (Class<Object>) method.getReturnType());
55 if (columnSchema == null) {
56 throw new ColumnSchemaNotFoundException(columnName, tableSchema.getName());
58 if (row == null || row.getColumn(columnSchema) == null) {
61 return row.getColumn(columnSchema).getData();
64 private Object processGetRow() {
68 private Object processGetColumn(final Method method) {
69 String columnName = getColumnName(method);
70 checkColumnSchemaVersion(dbSchema, method);
71 if (columnName == null) {
72 throw new TyperException("Error processing GetColumn : " + method.getName());
74 GenericTableSchema tableSchema = TyperUtils.getTableSchema(dbSchema, target);
75 if (tableSchema == null) {
76 throw new TableSchemaNotFoundException(TypedReflections.getTableName(target), dbSchema.getName());
78 ColumnSchema<GenericTableSchema, Object> columnSchema =
79 TyperUtils.getColumnSchema(tableSchema, columnName, (Class<Object>) method.getReturnType());
80 if (columnSchema == null) {
81 throw new ColumnSchemaNotFoundException(columnName, tableSchema.getName());
83 // When the row is null, that might indicate that the user maybe interested
84 // only in the ColumnSchema and not on the Data.
86 return new Column<>(columnSchema, null);
88 return row.getColumn(columnSchema);
91 private Object processSetData(final Object proxy, final Method method, final Object[] args) {
92 if (args == null || args.length != 1) {
93 throw new TyperException("Setter method : " + method.getName() + " requires 1 argument");
95 checkColumnSchemaVersion(dbSchema, method);
96 String columnName = getColumnName(method);
97 if (columnName == null) {
98 throw new TyperException("Unable to locate Column Name for " + method.getName());
100 GenericTableSchema tableSchema = TyperUtils.getTableSchema(dbSchema, target);
101 ColumnSchema<GenericTableSchema, Object> columnSchema =
102 TyperUtils.getColumnSchema(tableSchema, columnName, (Class<Object>) args[0].getClass());
103 Column<GenericTableSchema, Object> column =
104 new Column<>(columnSchema, args[0]);
105 row.addColumn(columnName, column);
109 private GenericTableSchema processGetTableSchema() {
110 return TyperUtils.getTableSchema(dbSchema, target);
113 private static Boolean isHashCodeMethod(final Method method, final Object[] args) {
114 return (args == null || args.length == 0) && method.getName().equals("hashCode");
117 private static Boolean isEqualsMethod(final Method method, final Object[] args) {
120 && method.getName().equals("equals")
121 && Object.class.equals(method.getParameterTypes()[0]);
124 private static Boolean isToStringMethod(final Method method, final Object[] args) {
125 return (args == null || args.length == 0) && method.getName().equals("toString");
129 public Object invoke(final Object proxy, final Method method, final Object[] args) throws Exception {
130 if (isGetTableSchema(method)) {
131 return processGetTableSchema();
132 } else if (isGetRow(method)) {
133 return processGetRow();
134 } else if (isSetData(method)) {
135 return processSetData(proxy, method, args);
136 } else if (isGetData(method)) {
137 return processGetData(method);
138 } else if (isGetColumn(method)) {
139 return processGetColumn(method);
140 } else if (isHashCodeMethod(method, args)) {
141 return processHashCode();
142 } else if (isEqualsMethod(method, args)) {
143 return proxy.getClass().isInstance(args[0]) && processEquals(args[0]);
144 } else if (isToStringMethod(method, args)) {
145 return processToString();
147 throw new UnsupportedMethodException("Method not supported " + method.toString());
150 private boolean processEquals(final Object obj) {
151 return obj instanceof TypedBaseTable && Objects.equals(row, ((TypedBaseTable<?>)obj).getRow());
154 private int processHashCode() {
155 return row == null ? 0 : row.hashCode();
158 private String processToString() {
159 final GenericTableSchema schema = processGetTableSchema();
160 final String tableName = schema != null ? schema.getName() : "";
161 return row == null ? tableName : tableName + " : " + row.toString();
164 private static boolean isGetColumn(final Method method) {
165 TypedColumn typedColumn = method.getAnnotation(TypedColumn.class);
166 if (typedColumn != null) {
167 return typedColumn.method().equals(MethodType.GETCOLUMN);
170 return method.getName().startsWith(GET_STARTS_WITH) && method.getName().endsWith(GETCOLUMN_ENDS_WITH);
173 private static boolean isGetData(final Method method) {
174 TypedColumn typedColumn = method.getAnnotation(TypedColumn.class);
175 if (typedColumn != null) {
176 return typedColumn.method().equals(MethodType.GETDATA);
179 return method.getName().startsWith(GET_STARTS_WITH) && !method.getName().endsWith(GETCOLUMN_ENDS_WITH);
182 private static boolean isGetRow(final Method method) {
183 TypedColumn typedColumn = method.getAnnotation(TypedColumn.class);
184 if (typedColumn != null) {
185 return typedColumn.method().equals(MethodType.GETROW);
188 return method.getName().startsWith(GET_STARTS_WITH) && method.getName().endsWith(GETROW_ENDS_WITH);
191 private static boolean isGetTableSchema(final Method method) {
192 TypedColumn typedColumn = method.getAnnotation(TypedColumn.class);
193 return typedColumn != null && typedColumn.method().equals(MethodType.GETTABLESCHEMA);
196 private static boolean isSetData(final Method method) {
197 TypedColumn typedColumn = method.getAnnotation(TypedColumn.class);
198 if (typedColumn != null) {
199 return typedColumn.method().equals(MethodType.SETDATA);
202 return method.getName().startsWith(SET_STARTS_WITH);
205 private static void checkColumnSchemaVersion(final DatabaseSchema dbSchema, final Method method) {
206 TyperUtils.checkVersion(dbSchema.getVersion(), TypedReflections.getColumnVersionRange(method));
209 private static String getColumnName(final Method method) {
210 TypedColumn typedColumn = method.getAnnotation(TypedColumn.class);
211 if (typedColumn != null) {
212 return typedColumn.name();
216 * Attempting to get the column name by parsing the method name with a following convention :
217 * 1. GETDATA : get<ColumnName>
218 * 2. SETDATA : set<ColumnName>
219 * 3. GETCOLUMN : get<ColumnName>Column
220 * where <ColumnName> is the name of the column that we are interested in.
222 int index = GET_STARTS_WITH.length();
223 if (isGetData(method) || isSetData(method)) {
224 return method.getName().substring(index, method.getName().length()).toLowerCase(Locale.ROOT);
225 } else if (isGetColumn(method)) {
226 return method.getName().substring(index, method.getName().indexOf(GETCOLUMN_ENDS_WITH,
227 index)).toLowerCase(Locale.ROOT);