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