Refactor ColumnType
[ovsdb.git] / library / impl / src / main / java / org / opendaylight / ovsdb / lib / schema / TableSchema.java
1 /*
2  * Copyright (c) 2014, 2015 EBay Software Foundation 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;
9
10 import com.fasterxml.jackson.databind.JsonNode;
11 import com.fasterxml.jackson.databind.node.ObjectNode;
12 import java.lang.reflect.Constructor;
13 import java.lang.reflect.InvocationTargetException;
14 import java.util.ArrayList;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Map.Entry;
19 import java.util.Set;
20 import org.opendaylight.ovsdb.lib.message.TableUpdate;
21 import org.opendaylight.ovsdb.lib.notation.Column;
22 import org.opendaylight.ovsdb.lib.notation.Row;
23 import org.opendaylight.ovsdb.lib.notation.UUID;
24 import org.opendaylight.ovsdb.lib.operations.Insert;
25 import org.opendaylight.ovsdb.lib.schema.BaseType.UuidBaseType;
26
27 public abstract class TableSchema<E extends TableSchema<E>> {
28
29     private String name;
30     private Map<String, ColumnSchema> columns;
31
32     protected TableSchema(final String name) {
33         this.name = name;
34     }
35
36     protected TableSchema(final String name, final Map<String, ColumnSchema> columns) {
37         this.name = name;
38         this.columns = columns;
39     }
40
41     public Set<String> getColumns() {
42         return this.columns.keySet();
43     }
44
45     public Map<String, ColumnSchema> getColumnSchemas() {
46         return columns;
47     }
48
49     public boolean hasColumn(final String column) {
50         return this.getColumns().contains(column);
51     }
52
53     public ColumnType getColumnType(final String column) {
54         return this.columns.get(column).getType();
55     }
56
57     public <E extends TableSchema<E>> E as(final Class<E> clazz) {
58         try {
59             Constructor<E> instance = clazz.getConstructor(TableSchema.class);
60             return instance.newInstance(this);
61         } catch (InstantiationException | IllegalAccessException
62                 | InvocationTargetException | NoSuchMethodException e) {
63             throw new RuntimeException("exception constructing instance of clazz " + clazz, e);
64         }
65     }
66
67     public Insert<E> insert() {
68         return new Insert<>(this);
69     }
70
71     public <D> ColumnSchema<E, Set<D>> multiValuedColumn(final String column, final Class<D> type) {
72         //todo exception handling
73
74         ColumnSchema<E, Set<D>> columnSchema = columns.get(column);
75         columnSchema.validateType(type);
76         return columnSchema;
77     }
78
79     public <K,V> ColumnSchema<E, Map<K,V>> multiValuedColumn(final String column, final Class<K> keyType,
80             final Class<V> valueType) {
81         //todo exception handling
82
83         ColumnSchema<E, Map<K, V>> columnSchema = columns.get(column);
84         columnSchema.validateType(valueType);
85         return columnSchema;
86     }
87
88     public <D> ColumnSchema<E, D> column(final String column, final Class<D> type) {
89         //todo exception handling
90
91         ColumnSchema<E, D> columnSchema = columns.get(column);
92         if (columnSchema != null) {
93             columnSchema.validateType(type);
94         }
95         return columnSchema;
96     }
97
98     public ColumnSchema column(final String column) {
99         return this.columns.get(column);
100     }
101
102     public String getName() {
103         return name;
104     }
105
106     protected void setName(final String name) {
107         this.name = name;
108     }
109
110     protected void setColumns(final Map<String, ColumnSchema> columns) {
111         this.columns = columns;
112     }
113
114     public TableUpdate<E> updatesFromJson(final JsonNode value) {
115         TableUpdate<E> tableUpdate = new TableUpdate<>();
116         Iterator<Entry<String, JsonNode>> fields = value.fields();
117         while (fields.hasNext()) {
118             Map.Entry<String, JsonNode> idOldNew = fields.next();
119             String uuid = idOldNew.getKey();
120
121             ObjectNode newObjectNode = (ObjectNode) idOldNew.getValue().get("new");
122             ObjectNode oldObjectNode = (ObjectNode) idOldNew.getValue().get("old");
123
124             Row<E> newRow = newObjectNode != null ? createRow(newObjectNode) : null;
125             Row<E> oldRow = oldObjectNode != null ? createRow(oldObjectNode) : null;
126
127             tableUpdate.addRow(new UUID(uuid), oldRow, newRow);
128         }
129         return tableUpdate;
130     }
131
132     public Row<E> createRow(final ObjectNode rowNode) {
133         List<Column<E, ?>> newColumns = new ArrayList<>();
134         for (Iterator<Map.Entry<String, JsonNode>> iter = rowNode.fields(); iter.hasNext();) {
135             Map.Entry<String, JsonNode> next = iter.next();
136             ColumnSchema<E, Object> schema = column(next.getKey(), Object.class);
137             /*
138              * Ideally the ColumnSchema shouldn't be null at this stage. But there can be cases in which
139              * the OVSDB manager Schema implementation might decide to include some "hidden" columns that
140              * are NOT reported in getSchema, but decide to report it in unfiltered monitor.
141              * Hence adding some safety checks around that.
142              */
143             if (schema != null) {
144                 Object value = schema.valueFromJson(next.getValue());
145                 newColumns.add(new Column<>(schema, value));
146             }
147         }
148         return new Row<>(this, newColumns);
149     }
150
151     public List<Row<E>> createRows(final JsonNode rowsNode) {
152         List<Row<E>> rows = new ArrayList<>();
153         for (JsonNode rowNode : rowsNode.get("rows")) {
154             rows.add(createRow((ObjectNode)rowNode));
155         }
156
157         return rows;
158     }
159
160     /*
161      * RFC 7047 Section 3.2 specifies 2 internally generated columns in each table
162      * namely _uuid and _version which are not exposed in get_schema call.
163      * Since these 2 columns are extremely useful for Mutate, update and select operations,
164      * the ColumnSchema for these 2 columns are manually populated.
165      *
166      * It is to be noted that these 2 columns are specified as part of the RFC7047 and not
167      * a specific Schema implementation detail & hence adding it by default in the Library
168      * for better application experience using the library.
169      */
170     public void populateInternallyGeneratedColumns() {
171         columns.put("_uuid", new ColumnSchema("_uuid", new AtomicColumnType(new UuidBaseType())));
172         columns.put("_version", new ColumnSchema("_version", new AtomicColumnType(new UuidBaseType())));
173     }
174 }