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