Bug 1385 : Adding support for toString, equals and hashCode for the Typed Interfaces.
[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     @Override
126     public int hashCode() {
127         final int prime = 31;
128         int result = 1;
129         result = prime * result
130                 + ((baseType == null) ? 0 : baseType.hashCode());
131         result = prime * result + (int) (max ^ (max >>> 32));
132         result = prime * result + (int) (min ^ (min >>> 32));
133         return result;
134     }
135
136     @Override
137     public boolean equals(Object obj) {
138         if (this == obj)
139             return true;
140         if (obj == null)
141             return false;
142         if (getClass() != obj.getClass())
143             return false;
144         ColumnType other = (ColumnType) obj;
145         if (baseType == null) {
146             if (other.baseType != null)
147                 return false;
148         } else if (!baseType.equals(other.baseType))
149             return false;
150         if (max != other.max)
151             return false;
152         if (min != other.min)
153             return false;
154         return true;
155     }
156
157     public static class AtomicColumnType extends ColumnType {
158         static final org.slf4j.Logger logger = LoggerFactory.getLogger(AtomicColumnType.class);
159         public AtomicColumnType() {
160         }
161
162         public AtomicColumnType(BaseType baseType1) {
163             super(baseType1);
164         }
165
166         @Override
167         public AtomicColumnType fromJsonNode(JsonNode json) {
168             if (json.isObject() && json.has("value")) {
169                 return null;
170             }
171             BaseType baseType = BaseType.fromJson(json, "key");
172
173             if (baseType != null) {
174
175                 AtomicColumnType atomicColumnType = new AtomicColumnType(baseType);
176
177                 JsonNode node = null;
178                 if ((node = json.get("min")) != null) {
179                     atomicColumnType.setMin(node.asLong());
180                 }
181
182                 if ((node = json.get("max")) != null) {
183                     if (node.isNumber()){
184                         atomicColumnType.setMax(node.asLong());
185                     } else if ("unlimited".equals(node.asText())) {
186                         atomicColumnType.setMax(Long.MAX_VALUE);
187                     }
188                 }
189                 return atomicColumnType;
190             }
191
192             return null;
193         }
194
195         @Override
196         public Object valueFromJson(JsonNode value) {
197             if (isMultiValued()) {
198                 OvsdbSet<Object> result = new OvsdbSet<Object>();
199                 if(value.isArray()) {
200                     if (value.size() == 2) {
201                         if (value.get(0).isTextual() && "set".equals(value.get(0).asText())) {
202                             for(JsonNode node: value.get(1)) {
203                                 result.add(getBaseType().toValue(node));
204                             }
205                         } else {
206                             result.add(getBaseType().toValue(value));
207                         }
208                     }
209                 } else {
210                     result.add(getBaseType().toValue(value));
211                 }
212                 return result;
213             } else {
214                 return getBaseType().toValue(value);
215             }
216         }
217
218         @Override
219         public void validate(Object value) {
220             this.baseType.validate(value);
221         }
222
223     }
224
225     public static class KeyValuedColumnType extends ColumnType {
226         BaseType keyType;
227
228         public BaseType getKeyType() {
229             return keyType;
230         }
231
232         public KeyValuedColumnType() {
233         }
234
235         public KeyValuedColumnType(BaseType keyType, BaseType valueType) {
236             super(valueType);
237             this.keyType = keyType;
238         }
239
240         @Override
241         public KeyValuedColumnType fromJsonNode(JsonNode json) {
242             if (json.isValueNode() || !json.has("value")) {
243                 return null;
244             }
245             BaseType keyType = BaseType.fromJson(json, "key");
246             BaseType valueType = BaseType.fromJson(json, "value");
247
248             KeyValuedColumnType keyValueColumnType = new KeyValuedColumnType(keyType, valueType);
249             JsonNode node = null;
250             if ((node = json.get("min")) != null) {
251                 keyValueColumnType.setMin(node.asLong());
252             }
253
254             if ((node = json.get("max")) != null) {
255                 if (node.isLong()){
256                     keyValueColumnType.setMax(node.asLong());
257                 } else if (node.isTextual() && "unlimited".equals(node.asText())) {
258                     keyValueColumnType.setMax(Long.MAX_VALUE);
259                 }
260             }
261
262             return keyValueColumnType;
263         }
264
265         @Override
266         public Object valueFromJson(JsonNode node) {
267             if (node.isArray()) {
268                 if (node.size() == 2) {
269                     if (node.get(0).isTextual() && "map".equals(node.get(0).asText())) {
270                         OvsdbMap<Object, Object> map = new OvsdbMap<Object, Object>();
271                         for (JsonNode pairNode : node.get(1)) {
272                             if (pairNode.isArray() && node.size() == 2) {
273                                 Object key = getKeyType().toValue(pairNode.get(0));
274                                 Object value = getBaseType().toValue(pairNode.get(1));
275                                 map.put(key, value);
276                             }
277                         }
278                         return map;
279                     } else if (node.size() == 0) {
280                         return null;
281                     }
282                 }
283             }
284             return null;
285         }
286
287         @Override
288         public void validate(Object value) {
289             this.baseType.validate(value);
290         }
291
292         @Override
293         public String toString() {
294             return "KeyValuedColumnType [keyType=" + keyType + " "+ super.toString() +"]";
295         }
296
297         @Override
298         public int hashCode() {
299             final int prime = 31;
300             int result = super.hashCode();
301             result = prime * result
302                     + ((keyType == null) ? 0 : keyType.hashCode());
303             return result;
304         }
305
306         @Override
307         public boolean equals(Object obj) {
308             if (this == obj)
309                 return true;
310             if (!super.equals(obj))
311                 return false;
312             if (getClass() != obj.getClass())
313                 return false;
314             KeyValuedColumnType other = (KeyValuedColumnType) obj;
315             if (keyType == null) {
316                 if (other.keyType != null)
317                     return false;
318             } else if (!keyType.equals(other.keyType))
319                 return false;
320             return true;
321         }
322     }
323 }