Make GenericTableSchema.fromJson() a factory method
[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 import org.opendaylight.ovsdb.lib.schema.ColumnType.AtomicColumnType;
27
28 public abstract class TableSchema<E extends TableSchema<E>> {
29
30     private String name;
31     private Map<String, ColumnSchema> columns;
32
33     protected TableSchema(final String name) {
34         this.name = name;
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     protected void setName(final String name) {
108         this.name = name;
109     }
110
111     protected void setColumns(final Map<String, ColumnSchema> columns) {
112         this.columns = columns;
113     }
114
115     public TableUpdate<E> updatesFromJson(final JsonNode value) {
116         TableUpdate<E> tableUpdate = new TableUpdate<>();
117         Iterator<Entry<String, JsonNode>> fields = value.fields();
118         while (fields.hasNext()) {
119             Map.Entry<String, JsonNode> idOldNew = fields.next();
120             String uuid = idOldNew.getKey();
121
122             ObjectNode newObjectNode = (ObjectNode) idOldNew.getValue().get("new");
123             ObjectNode oldObjectNode = (ObjectNode) idOldNew.getValue().get("old");
124
125             Row<E> newRow = newObjectNode != null ? createRow(newObjectNode) : null;
126             Row<E> oldRow = oldObjectNode != null ? createRow(oldObjectNode) : null;
127
128             tableUpdate.addRow(new UUID(uuid), oldRow, newRow);
129         }
130         return tableUpdate;
131     }
132
133     public Row<E> createRow(final ObjectNode rowNode) {
134         List<Column<E, ?>> newColumns = new ArrayList<>();
135         for (Iterator<Map.Entry<String, JsonNode>> iter = rowNode.fields(); iter.hasNext();) {
136             Map.Entry<String, JsonNode> next = iter.next();
137             ColumnSchema<E, Object> schema = column(next.getKey(), Object.class);
138             /*
139              * Ideally the ColumnSchema shouldn't be null at this stage. But there can be cases in which
140              * the OVSDB manager Schema implementation might decide to include some "hidden" columns that
141              * are NOT reported in getSchema, but decide to report it in unfiltered monitor.
142              * Hence adding some safety checks around that.
143              */
144             if (schema != null) {
145                 Object value = schema.valueFromJson(next.getValue());
146                 newColumns.add(new Column<>(schema, value));
147             }
148         }
149         return new Row<>(this, newColumns);
150     }
151
152     public List<Row<E>> createRows(final JsonNode rowsNode) {
153         List<Row<E>> rows = new ArrayList<>();
154         for (JsonNode rowNode : rowsNode.get("rows")) {
155             rows.add(createRow((ObjectNode)rowNode));
156         }
157
158         return rows;
159     }
160
161     /*
162      * RFC 7047 Section 3.2 specifies 2 internally generated columns in each table
163      * namely _uuid and _version which are not exposed in get_schema call.
164      * Since these 2 columns are extremely useful for Mutate, update and select operations,
165      * the ColumnSchema for these 2 columns are manually populated.
166      *
167      * It is to be noted that these 2 columns are specified as part of the RFC7047 and not
168      * a specific Schema implementation detail & hence adding it by default in the Library
169      * for better application experience using the library.
170      */
171     public void populateInternallyGeneratedColumns() {
172         columns.put("_uuid", new ColumnSchema("_uuid", new AtomicColumnType(new UuidBaseType())));
173         columns.put("_version", new ColumnSchema("_version", new AtomicColumnType(new UuidBaseType())));
174     }
175 }