Turn DatabaseSchema into an interface
[ovsdb.git] / library / impl / src / main / java / org / opendaylight / ovsdb / lib / schema / DatabaseSchemaImpl.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.google.common.annotations.VisibleForTesting;
14 import com.google.common.collect.ImmutableMap;
15 import com.google.common.collect.Maps;
16 import com.google.common.reflect.Invokable;
17 import java.lang.reflect.Constructor;
18 import java.lang.reflect.InvocationTargetException;
19 import java.util.HashMap;
20 import java.util.Iterator;
21 import java.util.Map;
22 import java.util.Set;
23 import org.opendaylight.ovsdb.lib.error.ParsingException;
24 import org.opendaylight.ovsdb.lib.notation.Version;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28 @VisibleForTesting
29 public class DatabaseSchemaImpl implements DatabaseSchema {
30     private static final Logger LOG = LoggerFactory.getLogger(DatabaseSchemaImpl.class);
31
32     private final String name;
33     private final Version version;
34     private final ImmutableMap<String, TableSchema> tables;
35
36     public DatabaseSchemaImpl(final String name, final Version version, final Map<String, TableSchema> tables) {
37         this.name = requireNonNull(name);
38         this.version = requireNonNull(version);
39         this.tables = ImmutableMap.copyOf(tables);
40     }
41
42     //todo : this needs to move to a custom factory
43     public static DatabaseSchema fromJson(final String dbName, final JsonNode json) {
44         if (!json.isObject() || !json.has("tables")) {
45             throw new ParsingException("bad DatabaseSchema root, expected \"tables\" as child but was not found");
46         }
47         if (!json.isObject() || !json.has("version")) {
48             throw new ParsingException("bad DatabaseSchema root, expected \"version\" as child but was not found");
49         }
50
51         Version dbVersion = Version.fromString(json.get("version").asText());
52
53         Map<String, TableSchema> tables = new HashMap<>();
54         for (Iterator<Map.Entry<String, JsonNode>> iter = json.get("tables").fields(); iter.hasNext(); ) {
55             Map.Entry<String, JsonNode> table = iter.next();
56             LOG.trace("Read schema for table[{}]:{}", table.getKey(), table.getValue());
57
58             //todo : this needs to done by a factory
59             tables.put(table.getKey(), GenericTableSchema.fromJson(table.getKey(), table.getValue()));
60         }
61
62         return new DatabaseSchemaImpl(dbName, dbVersion, tables);
63     }
64
65     @Override
66     public Set<String> getTables() {
67         return tables.keySet();
68     }
69
70     @Override
71     public boolean hasTable(final String table) {
72         return tables.containsKey(table);
73     }
74
75     @Override
76     public <E extends TableSchema<E>> E table(final String tableName, final Class<E> clazz) {
77         TableSchema<E> table = tables.get(tableName);
78
79         if (clazz.isInstance(table)) {
80             return clazz.cast(table);
81         }
82
83         return createTableSchema(clazz, table);
84     }
85
86     protected <E extends TableSchema<E>> E createTableSchema(final Class<E> clazz, final TableSchema<E> table) {
87         Constructor<E> declaredConstructor;
88         try {
89             declaredConstructor = clazz.getDeclaredConstructor(TableSchema.class);
90         } catch (NoSuchMethodException e) {
91             String message = String.format("Class %s does not have public constructor that accepts TableSchema object",
92                     clazz);
93             throw new IllegalArgumentException(message, e);
94         }
95         Invokable<E, E> invokable = Invokable.from(declaredConstructor);
96         try {
97             return invokable.invoke(null, table);
98         } catch (InvocationTargetException | IllegalAccessException e) {
99             String message = String.format("Not able to create instance of class %s using public constructor "
100                     + "that accepts TableSchema object", clazz);
101             throw new IllegalArgumentException(message, e);
102         }
103     }
104
105     @Override
106     public String getName() {
107         return name;
108     }
109
110     @Override
111     public Version getVersion() {
112         return version;
113     }
114
115     @Override
116     public DatabaseSchema withInternallyGeneratedColumns() {
117         return haveInternallyGeneratedColumns() ? this : new DatabaseSchemaImpl(name, version,
118             Maps.transformValues(tables, TableSchema::withInternallyGeneratedColumns));
119     }
120
121     protected final boolean haveInternallyGeneratedColumns() {
122         for (TableSchema tableSchema : tables.values()) {
123             if (!tableSchema.haveInternallyGeneratedColumns()) {
124                 return false;
125             }
126         }
127         return true;
128     }
129 }