Rework TypedRowInvocationHandler invocation path
[ovsdb.git] / library / impl / src / main / java / org / opendaylight / ovsdb / lib / schema / typed / TypedDatabaseSchemaImpl.java
1 /*
2  * Copyright © 2019 PANTHEON.tech, s.r.o. 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.cache.CacheBuilder;
13 import com.google.common.cache.CacheLoader;
14 import com.google.common.cache.LoadingCache;
15 import com.google.common.reflect.Reflection;
16 import java.util.HashMap;
17 import java.util.Map;
18 import org.opendaylight.ovsdb.lib.message.TableUpdate;
19 import org.opendaylight.ovsdb.lib.message.TableUpdates;
20 import org.opendaylight.ovsdb.lib.notation.Row;
21 import org.opendaylight.ovsdb.lib.notation.UUID;
22 import org.opendaylight.ovsdb.lib.schema.DatabaseSchema;
23 import org.opendaylight.ovsdb.lib.schema.ForwardingDatabaseSchema;
24 import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
25
26 final class TypedDatabaseSchemaImpl extends ForwardingDatabaseSchema implements TypedDatabaseSchema {
27     private final LoadingCache<Class<?>, TypedRowInvocationHandler> handlers = CacheBuilder.newBuilder()
28             .weakKeys().weakValues().build(new CacheLoader<Class<?>, TypedRowInvocationHandler>() {
29                 @Override
30                 public TypedRowInvocationHandler load(final Class<?> key) {
31                     return MethodDispatch.forTarget(key).bindToSchema(TypedDatabaseSchemaImpl.this);
32                 }
33             });
34
35     private final DatabaseSchema delegate;
36
37     TypedDatabaseSchemaImpl(final DatabaseSchema delegate) {
38         this.delegate = requireNonNull(delegate);
39     }
40
41     @Override
42     protected DatabaseSchema delegate() {
43         return delegate;
44     }
45
46     @Override
47     public TypedDatabaseSchema withInternallyGeneratedColumns() {
48         final DatabaseSchema newDelegate = delegate.withInternallyGeneratedColumns();
49         return newDelegate == delegate ? this : new TypedDatabaseSchemaImpl(newDelegate);
50     }
51
52     @Override
53     public GenericTableSchema getTableSchema(final Class<?> klazz) {
54         return getTableSchema(TypedReflections.getTableName(klazz));
55     }
56
57     private GenericTableSchema getTableSchema(final String tableName) {
58         return table(tableName, GenericTableSchema.class);
59     }
60
61     @Override
62     public <T> T getTypedRowWrapper(final Class<T> klazz, final Row<GenericTableSchema> row) {
63         // Check validity of  of the parameter passed to getTypedRowWrapper:
64         // -  checks for a valid Database Schema matching the expected Database for a given table
65         // - checks for the presence of the Table in Database Schema.
66         final String dbName = TypedReflections.getTableDatabase(klazz);
67         if (dbName != null && !dbName.equalsIgnoreCase(getName())) {
68             return null;
69         }
70         TyperUtils.checkVersion(getVersion(), TypedReflections.getTableVersionRange(klazz));
71
72         TypedRowInvocationHandler handler = handlers.getUnchecked(klazz);
73         if (row != null) {
74             row.setTableSchema(getTableSchema(handler.getTableName()));
75             handler = handler.bindToRow(row);
76         }
77         return Reflection.newProxy(klazz, handler);
78     }
79
80     @Override
81     public <T> Map<UUID, T> extractRowsOld(final Class<T> klazz, final TableUpdates updates) {
82         Map<UUID,TableUpdate<GenericTableSchema>.RowUpdate<GenericTableSchema>> rowUpdates = extractRowUpdates(
83             requireNonNull(klazz), requireNonNull(updates));
84         Map<UUID,T> result = new HashMap<>();
85         for (TableUpdate<GenericTableSchema>.RowUpdate<GenericTableSchema> rowUpdate : rowUpdates.values()) {
86             if (rowUpdate != null && rowUpdate.getOld() != null) {
87                 Row<GenericTableSchema> row = rowUpdate.getOld();
88                 result.put(rowUpdate.getUuid(), getTypedRowWrapper(klazz, row));
89             }
90         }
91         return result;
92     }
93
94     @Override
95     public <T> Map<UUID, T> extractRowsUpdated(final Class<T> klazz, final TableUpdates updates) {
96         final Map<UUID,TableUpdate<GenericTableSchema>.RowUpdate<GenericTableSchema>> rowUpdates =
97                 extractRowUpdates(klazz, updates);
98         final Map<UUID,T> result = new HashMap<>();
99         for (TableUpdate<GenericTableSchema>.RowUpdate<GenericTableSchema> rowUpdate : rowUpdates.values()) {
100             if (rowUpdate != null && rowUpdate.getNew() != null) {
101                 Row<GenericTableSchema> row = rowUpdate.getNew();
102                 result.put(rowUpdate.getUuid(), getTypedRowWrapper(klazz, row));
103             }
104         }
105         return result;
106     }
107
108     @Override
109     public <T> Map<UUID, T> extractRowsRemoved(final Class<T> klazz, final TableUpdates updates) {
110         final Map<UUID, TableUpdate<GenericTableSchema>.RowUpdate<GenericTableSchema>> rowUpdates =
111                 extractRowUpdates(klazz, updates);
112         final Map<UUID, T> result = new HashMap<>();
113         for (TableUpdate<GenericTableSchema>.RowUpdate<GenericTableSchema> rowUpdate : rowUpdates.values()) {
114             if (rowUpdate != null && rowUpdate.getNew() == null && rowUpdate.getOld() != null) {
115                 Row<GenericTableSchema> row = rowUpdate.getOld();
116                 result.put(rowUpdate.getUuid(), getTypedRowWrapper(klazz, row));
117             }
118         }
119         return result;
120     }
121
122     /**
123      * This method extracts all RowUpdates of Class&lt;T&gt; klazz from a TableUpdates that correspond to rows of type
124      * klazz. Example:
125      * <code>
126      * Map&lt;UUID,TableUpdate&lt;GenericTableSchema&gt;.RowUpdate&lt;GenericTableSchema&gt;&gt; updatedBridges =
127      *     extractRowsUpdates(Bridge.class,updates,dbSchema)
128      * </code>
129      *
130      * @param klazz Class for row type to be extracted
131      * @param updates TableUpdates from which to extract rowUpdates
132      * @return Map&lt;UUID,TableUpdate&lt;GenericTableSchema&gt;.RowUpdate&lt;GenericTableSchema&gt;&gt;
133      *     for the type of things being sought
134      */
135     private Map<UUID,TableUpdate<GenericTableSchema>.RowUpdate<GenericTableSchema>> extractRowUpdates(
136             final Class<?> klazz, final TableUpdates updates) {
137         TableUpdate<GenericTableSchema> update = updates.getUpdate(table(TypedReflections.getTableName(klazz),
138             GenericTableSchema.class));
139         Map<UUID, TableUpdate<GenericTableSchema>.RowUpdate<GenericTableSchema>> result = new HashMap<>();
140         if (update != null) {
141             Map<UUID, TableUpdate<GenericTableSchema>.RowUpdate<GenericTableSchema>> rows = update.getRows();
142             if (rows != null) {
143                 result = rows;
144             }
145         }
146         return result;
147     }
148 }