Merge 'origin/topic/schema' branch into merge-branch
[ovsdb.git] / library / src / main / java / org / opendaylight / ovsdb / lib / schema / ColumnType.java
1 /*
2  * Copyright (C) 2014 EBay Software Foundation
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  * Authors : Ashwin Raveendran
9  */
10 package org.opendaylight.ovsdb.lib.schema;
11
12 import org.opendaylight.ovsdb.lib.error.TyperException;
13 import org.opendaylight.ovsdb.lib.jsonrpc.JsonUtils;
14 import org.opendaylight.ovsdb.lib.notation.OvsdbMap;
15 import org.opendaylight.ovsdb.lib.notation.OvsdbSet;
16 import org.slf4j.LoggerFactory;
17
18 import com.fasterxml.jackson.databind.JsonNode;
19
20
21 public abstract class ColumnType {
22     BaseType baseType;
23     long min = 1;
24     long max = 1;
25
26     public long getMin() {
27         return min;
28     }
29
30     void setMin(long min) {
31         this.min = min;
32     }
33
34     public long getMax() {
35         return max;
36     }
37
38     void setMax(long max) {
39         this.max = max;
40     }
41
42     private static ColumnType columns[] = new ColumnType[]{
43             new AtomicColumnType(),
44             new KeyValuedColumnType()
45     };
46
47
48     public ColumnType() {
49
50     }
51
52     public ColumnType(BaseType baseType) {
53         this.baseType = baseType;
54     }
55
56     public BaseType getBaseType() {
57         return baseType;
58     }
59
60     /**
61             "type": {
62                 "key": {
63                      "maxInteger": 4294967295,
64                      "minInteger": 0,
65                      "type": "integer"
66                 },
67                 "min": 0,
68                 "value": {
69                     "type": "uuid",
70                     "refTable": "Queue"
71                  },
72                  "max": "unlimited"
73             }
74      * @param json
75      * @return
76      */
77     public static ColumnType fromJson(JsonNode json) {
78         for (ColumnType colType : columns) {
79             ColumnType columnType = colType.fromJsonNode(json);
80             if (null != columnType) {
81                 return columnType;
82             }
83         }
84         //todo move to speicfic typed exception
85         throw new TyperException(String.format("could not find the right column type %s",
86                 JsonUtils.prettyString(json)));
87     }
88
89
90     /**
91      * Creates a ColumnType from the JsonNode if the implementation  knows how to, returns null otherwise
92      *
93      * @param json the JSONNode object that needs to converted
94      * @return a valid SubType or Null (if the JsonNode does not represent
95      * the subtype)
96      */
97     protected abstract ColumnType fromJsonNode(JsonNode json);
98
99     /*
100      * Per RFC 7047, Section 3.2 <type> :
101      * If "min" or "max" is not specified, each defaults to 1.  If "max" is specified as "unlimited", then there is no specified maximum
102      * number of elements, although the implementation will enforce some limit.  After considering defaults, "min" must be exactly 0 or
103      * exactly 1, "max" must be at least 1, and "max" must be greater than or equal to "min".
104      *
105      * If "min" and "max" are both 1 and "value" is not specified, the
106      * type is the scalar type specified by "key".
107      */
108     public boolean isMultiValued() {
109         return this.min != this.max;
110     }
111
112     public abstract Object valueFromJson(JsonNode value);
113
114     public abstract void validate(Object value);
115
116     @Override
117     public String toString() {
118         return "ColumnType{" +
119                 "baseType=" + baseType +
120                 ", min=" + min +
121                 ", max=" + max +
122                 '}';
123     }
124
125     public static class AtomicColumnType extends ColumnType {
126         static final org.slf4j.Logger logger = LoggerFactory.getLogger(AtomicColumnType.class);
127         public AtomicColumnType() {
128         }
129
130         public AtomicColumnType(BaseType baseType1) {
131             super(baseType1);
132         }
133
134         @Override
135         public AtomicColumnType fromJsonNode(JsonNode json) {
136             if (json.isObject() && json.has("value")) {
137                 return null;
138             }
139             BaseType baseType = BaseType.fromJson(json, "key");
140
141             if (baseType != null) {
142
143                 AtomicColumnType atomicColumnType = new AtomicColumnType(baseType);
144
145                 JsonNode node = null;
146                 if ((node = json.get("min")) != null) {
147                     atomicColumnType.setMin(node.asLong());
148                 }
149
150                 if ((node = json.get("max")) != null) {
151                     if (node.isNumber()){
152                         atomicColumnType.setMax(node.asLong());
153                     } else if ("unlimited".equals(node.asText())) {
154                         atomicColumnType.setMax(Long.MAX_VALUE);
155                     }
156                 }
157                 return atomicColumnType;
158             }
159
160             return null;
161         }
162
163         @Override
164         public Object valueFromJson(JsonNode value) {
165             if (isMultiValued()) {
166                 OvsdbSet<Object> result = new OvsdbSet<Object>();
167                 if(value.isArray()) {
168                     if (value.size() == 2) {
169                         if (value.get(0).isTextual() && "set".equals(value.get(0).asText())) {
170                             for(JsonNode node: value.get(1)) {
171                                 result.add(getBaseType().toValue(node));
172                             }
173                         } else {
174                             result.add(getBaseType().toValue(value));
175                         }
176                     }
177                 } else {
178                     result.add(getBaseType().toValue(value));
179                 }
180                 return result;
181             } else {
182                 return getBaseType().toValue(value);
183             }
184         }
185
186         @Override
187         public void validate(Object value) {
188             this.baseType.validate(value);
189         }
190
191     }
192
193     public static class KeyValuedColumnType extends ColumnType {
194         BaseType keyType;
195
196         public BaseType getKeyType() {
197             return keyType;
198         }
199
200         public KeyValuedColumnType() {
201         }
202
203         public KeyValuedColumnType(BaseType keyType, BaseType valueType) {
204             super(valueType);
205             this.keyType = keyType;
206         }
207
208         @Override
209         public KeyValuedColumnType fromJsonNode(JsonNode json) {
210             if (json.isValueNode() || !json.has("value")) {
211                 return null;
212             }
213             BaseType keyType = BaseType.fromJson(json, "key");
214             BaseType valueType = BaseType.fromJson(json, "value");
215
216             KeyValuedColumnType keyValueColumnType = new KeyValuedColumnType(keyType, valueType);
217             JsonNode node = null;
218             if ((node = json.get("min")) != null) {
219                 keyValueColumnType.setMin(node.asLong());
220             }
221
222             if ((node = json.get("max")) != null) {
223                 if (node.isLong()){
224                     keyValueColumnType.setMax(node.asLong());
225                 } else if (node.isTextual() && "unlimited".equals(node.asText())) {
226                     keyValueColumnType.setMax(Long.MAX_VALUE);
227                 }
228             }
229
230             return keyValueColumnType;
231         }
232
233         @Override
234         public Object valueFromJson(JsonNode node) {
235             if (node.isArray()) {
236                 if (node.size() == 2) {
237                     if (node.get(0).isTextual() && "map".equals(node.get(0).asText())) {
238                         OvsdbMap<Object, Object> map = new OvsdbMap<Object, Object>();
239                         for (JsonNode pairNode : node.get(1)) {
240                             if (pairNode.isArray() && node.size() == 2) {
241                                 Object key = getKeyType().toValue(pairNode.get(0));
242                                 Object value = getBaseType().toValue(pairNode.get(1));
243                                 map.put(key, value);
244                             }
245                         }
246                         return map;
247                     } else if (node.size() == 0) {
248                         return null;
249                     }
250                 }
251             }
252             return null;
253         }
254
255         @Override
256         public void validate(Object value) {
257             this.baseType.validate(value);
258         }
259     }
260 }