blob: b36b0e2a623602815349438c491196c0f5553fb4 [file] [log] [blame]
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Ray Milkeya4122362015-08-18 15:19:08 -070016package org.onosproject.net.config;
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -070017
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070018import com.fasterxml.jackson.databind.JsonNode;
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -070019import com.fasterxml.jackson.databind.ObjectMapper;
Brian O'Connorce2d8b52015-07-29 16:24:13 -070020import com.fasterxml.jackson.databind.node.ArrayNode;
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -070021import com.fasterxml.jackson.databind.node.ObjectNode;
Thomas Vachuskae2b7e7e2015-05-20 11:11:31 -070022import com.google.common.annotations.Beta;
Thomas Vachuskace0bbb32015-11-18 16:56:10 -080023import com.google.common.collect.ImmutableSet;
24import com.google.common.collect.Iterators;
Brian O'Connorce2d8b52015-07-29 16:24:13 -070025import com.google.common.collect.Lists;
Thomas Vachuskace0bbb32015-11-18 16:56:10 -080026import org.onlab.packet.IpAddress;
Jonathan Hart9eb45bb2016-02-12 10:59:11 -080027import org.onlab.packet.IpPrefix;
Thomas Vachuskace0bbb32015-11-18 16:56:10 -080028import org.onlab.packet.MacAddress;
Hyunsun Moon61b73e92016-05-10 18:05:57 -070029import org.onlab.packet.TpPort;
Jonathan Hart9eb45bb2016-02-12 10:59:11 -080030import org.onosproject.net.ConnectPoint;
Brian O'Connorce2d8b52015-07-29 16:24:13 -070031
32import java.util.Collection;
33import java.util.List;
Thomas Vachuskace0bbb32015-11-18 16:56:10 -080034import java.util.Set;
Brian O'Connorce2d8b52015-07-29 16:24:13 -070035import java.util.function.Function;
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -070036
37import static com.google.common.base.Preconditions.checkNotNull;
Charles Chan023a8982016-02-04 11:00:41 -080038import static com.google.common.base.Preconditions.checkState;
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -070039
40/**
41 * Base abstraction of a configuration facade for a specific subject. Derived
Thomas Vachuska96d55b12015-05-11 08:52:03 -070042 * classes should keep all state in the specified JSON tree as that is the
43 * only state that will be distributed or persisted; this class is merely
44 * a facade for interacting with a particular facet of configuration on a
45 * given subject.
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -070046 *
47 * @param <S> type of subject
48 */
Thomas Vachuskae2b7e7e2015-05-20 11:11:31 -070049@Beta
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -070050public abstract class Config<S> {
51
Jonathan Hart9eb45bb2016-02-12 10:59:11 -080052 private static final String TRUE_LITERAL = "true";
53 private static final String FALSE_LITERAL = "false";
54
Thomas Vachuska96d55b12015-05-11 08:52:03 -070055 protected S subject;
56 protected String key;
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070057
58 protected JsonNode node;
59 protected ObjectNode object;
60 protected ArrayNode array;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070061 protected ObjectMapper mapper;
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070062
Thomas Vachuska96d55b12015-05-11 08:52:03 -070063 protected ConfigApplyDelegate delegate;
64
65 /**
Thomas Vachuskace0bbb32015-11-18 16:56:10 -080066 * Indicator of whether a configuration JSON field is required.
67 */
68 public enum FieldPresence {
69 /**
70 * Signifies that config field is an optional one.
71 */
72 OPTIONAL,
73
74 /**
75 * Signifies that config field is mandatory.
76 */
77 MANDATORY
78 }
79
80 /**
Thomas Vachuska96d55b12015-05-11 08:52:03 -070081 * Initializes the configuration behaviour with necessary context.
82 *
83 * @param subject configuration subject
84 * @param key configuration key
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070085 * @param node JSON node where configuration data is stored
Thomas Vachuska96d55b12015-05-11 08:52:03 -070086 * @param mapper JSON object mapper
Charles Chan023a8982016-02-04 11:00:41 -080087 * @param delegate delegate context, or null for detached configs.
Thomas Vachuska96d55b12015-05-11 08:52:03 -070088 */
Charles Chan023a8982016-02-04 11:00:41 -080089 public final void init(S subject, String key, JsonNode node, ObjectMapper mapper,
Thomas Vachuska96d55b12015-05-11 08:52:03 -070090 ConfigApplyDelegate delegate) {
Charles Chan023a8982016-02-04 11:00:41 -080091 this.subject = checkNotNull(subject, "Subject cannot be null");
Thomas Vachuska96d55b12015-05-11 08:52:03 -070092 this.key = key;
Charles Chan023a8982016-02-04 11:00:41 -080093 this.node = checkNotNull(node, "Node cannot be null");
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070094 this.object = node instanceof ObjectNode ? (ObjectNode) node : null;
95 this.array = node instanceof ArrayNode ? (ArrayNode) node : null;
Charles Chan023a8982016-02-04 11:00:41 -080096 this.mapper = checkNotNull(mapper, "Mapper cannot be null");
97 this.delegate = delegate;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070098 }
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -070099
100 /**
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800101 * Indicates whether or not the backing JSON node contains valid data.
102 * <p>
103 * Default implementation returns true.
104 * Subclasses are expected to override this with their own validation.
Thomas Vachuska36008462016-01-07 15:38:20 -0800105 * Implementations are free to throw a RuntimeException if data is invalid.
106 * * </p>
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800107 *
108 * @return true if the data is valid; false otherwise
Thomas Vachuska36008462016-01-07 15:38:20 -0800109 * @throws RuntimeException if configuration is invalid or completely foobar
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800110 */
111 public boolean isValid() {
Thomas Vachuska36008462016-01-07 15:38:20 -0800112 // Derivatives should use the provided set of predicates to test
113 // validity of their fields, e.g.:
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800114 // isString(path)
115 // isBoolean(path)
116 // isNumber(path, [min, max])
117 // isDecimal(path, [min, max])
118 // isMacAddress(path)
119 // isIpAddress(path)
Jonathan Hart9eb45bb2016-02-12 10:59:11 -0800120 // isIpPrefix(path)
121 // isConnectPoint(path)
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800122 return true;
123 }
124
125 /**
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -0700126 * Returns the specific subject to which this configuration pertains.
127 *
128 * @return configuration subject
129 */
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700130 public S subject() {
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -0700131 return subject;
132 }
133
134 /**
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700135 * Returns the configuration key. This is primarily aimed for use in
136 * composite JSON trees in external representations and has no bearing on
137 * the internal behaviours.
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -0700138 *
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700139 * @return configuration key
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -0700140 */
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700141 public String key() {
142 return key;
143 }
144
145 /**
146 * Returns the JSON node that contains the configuration data.
147 *
148 * @return JSON node backing the configuration
149 */
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700150 public JsonNode node() {
Jonathan Harta8625482015-09-08 16:14:56 -0700151 return node;
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -0700152 }
153
154 /**
155 * Applies any configuration changes made via this configuration.
Charles Chan023a8982016-02-04 11:00:41 -0800156 *
157 * Not effective for detached configs.
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -0700158 */
159 public void apply() {
Charles Chan023a8982016-02-04 11:00:41 -0800160 checkState(delegate != null, "Cannot apply detached config");
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -0700161 delegate.onApply(this);
162 }
163
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700164 // Miscellaneous helpers for interacting with JSON
165
166 /**
167 * Gets the specified property as a string.
168 *
169 * @param name property name
170 * @param defaultValue default value if property not set
171 * @return property value or default value
172 */
173 protected String get(String name, String defaultValue) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700174 return object.path(name).asText(defaultValue);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700175 }
176
177 /**
178 * Sets the specified property as a string or clears it if null value given.
179 *
180 * @param name property name
181 * @param value new value or null to clear the property
182 * @return self
183 */
184 protected Config<S> setOrClear(String name, String value) {
185 if (value != null) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700186 object.put(name, value);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700187 } else {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700188 object.remove(name);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700189 }
190 return this;
191 }
192
193 /**
194 * Gets the specified property as a boolean.
195 *
196 * @param name property name
197 * @param defaultValue default value if property not set
198 * @return property value or default value
199 */
200 protected boolean get(String name, boolean defaultValue) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700201 return object.path(name).asBoolean(defaultValue);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700202 }
203
204 /**
HIGUCHI Yuta1d7c9cb2016-01-20 18:22:36 -0800205 * Clears the specified property.
206 *
207 * @param name property name
208 * @return self
209 */
210 protected Config<S> clear(String name) {
211 object.remove(name);
212 return this;
213 }
214
215 /**
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700216 * Sets the specified property as a boolean or clears it if null value given.
217 *
218 * @param name property name
219 * @param value new value or null to clear the property
220 * @return self
221 */
222 protected Config<S> setOrClear(String name, Boolean value) {
223 if (value != null) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700224 object.put(name, value.booleanValue());
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700225 } else {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700226 object.remove(name);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700227 }
228 return this;
229 }
230
231 /**
Jonathan Hart111b42b2015-07-14 13:28:05 -0700232 * Gets the specified property as an integer.
233 *
234 * @param name property name
235 * @param defaultValue default value if property not set
236 * @return property value or default value
237 */
238 protected int get(String name, int defaultValue) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700239 return object.path(name).asInt(defaultValue);
Jonathan Hart111b42b2015-07-14 13:28:05 -0700240 }
241
242 /**
243 * Sets the specified property as an integer or clears it if null value given.
244 *
245 * @param name property name
246 * @param value new value or null to clear the property
247 * @return self
248 */
249 protected Config<S> setOrClear(String name, Integer value) {
250 if (value != null) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700251 object.put(name, value.intValue());
Jonathan Hart111b42b2015-07-14 13:28:05 -0700252 } else {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700253 object.remove(name);
Jonathan Hart111b42b2015-07-14 13:28:05 -0700254 }
255 return this;
256 }
257
258 /**
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700259 * Gets the specified property as a long.
260 *
261 * @param name property name
262 * @param defaultValue default value if property not set
263 * @return property value or default value
264 */
265 protected long get(String name, long defaultValue) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700266 return object.path(name).asLong(defaultValue);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700267 }
268
269 /**
270 * Sets the specified property as a long or clears it if null value given.
271 *
272 * @param name property name
273 * @param value new value or null to clear the property
274 * @return self
275 */
276 protected Config<S> setOrClear(String name, Long value) {
277 if (value != null) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700278 object.put(name, value.longValue());
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700279 } else {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700280 object.remove(name);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700281 }
282 return this;
283 }
284
285 /**
286 * Gets the specified property as a double.
287 *
288 * @param name property name
289 * @param defaultValue default value if property not set
290 * @return property value or default value
291 */
292 protected double get(String name, double defaultValue) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700293 return object.path(name).asDouble(defaultValue);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700294 }
295
296 /**
297 * Sets the specified property as a double or clears it if null value given.
298 *
299 * @param name property name
300 * @param value new value or null to clear the property
301 * @return self
302 */
303 protected Config<S> setOrClear(String name, Double value) {
304 if (value != null) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700305 object.put(name, value.doubleValue());
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700306 } else {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700307 object.remove(name);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700308 }
309 return this;
310 }
311
312 /**
313 * Gets the specified property as an enum.
314 *
315 * @param name property name
316 * @param defaultValue default value if property not set
317 * @param enumClass the enum class
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700318 * @param <E> type of enum
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700319 * @return property value or default value
320 */
321 protected <E extends Enum<E>> E get(String name, E defaultValue, Class<E> enumClass) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700322 return Enum.valueOf(enumClass, object.path(name).asText(defaultValue.toString()));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700323 }
324
325 /**
326 * Sets the specified property as a double or clears it if null value given.
327 *
328 * @param name property name
329 * @param value new value or null to clear the property
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700330 * @param <E> type of enum
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700331 * @return self
332 */
333 protected <E extends Enum> Config<S> setOrClear(String name, E value) {
334 if (value != null) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700335 object.put(name, value.toString());
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700336 } else {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700337 object.remove(name);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700338 }
339 return this;
340 }
Jonathan Hart111b42b2015-07-14 13:28:05 -0700341
Brian O'Connorce2d8b52015-07-29 16:24:13 -0700342 /**
343 * Gets the specified array property as a list of items.
344 *
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700345 * @param name property name
Brian O'Connorce2d8b52015-07-29 16:24:13 -0700346 * @param function mapper from string to item
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700347 * @param <T> type of item
Brian O'Connorce2d8b52015-07-29 16:24:13 -0700348 * @return list of items
349 */
350 protected <T> List<T> getList(String name, Function<String, T> function) {
351 List<T> list = Lists.newArrayList();
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700352 ArrayNode arrayNode = (ArrayNode) object.path(name);
Brian O'Connorce2d8b52015-07-29 16:24:13 -0700353 arrayNode.forEach(i -> list.add(function.apply(i.asText())));
354 return list;
355 }
356
357 /**
Naoki Shiota399a0b32015-11-15 20:36:13 -0600358 * Gets the specified array property as a list of items.
359 *
360 * @param name property name
361 * @param function mapper from string to item
362 * @param defaultValue default value if property not set
363 * @param <T> type of item
364 * @return list of items
365 */
366 protected <T> List<T> getList(String name, Function<String, T> function, List<T> defaultValue) {
367 List<T> list = Lists.newArrayList();
368 JsonNode jsonNode = object.path(name);
369 if (jsonNode.isMissingNode()) {
370 return defaultValue;
371 }
372 ArrayNode arrayNode = (ArrayNode) jsonNode;
373 arrayNode.forEach(i -> list.add(function.apply(i.asText())));
374 return list;
375 }
376
377 /**
Brian O'Connorce2d8b52015-07-29 16:24:13 -0700378 * Sets the specified property as an array of items in a given collection or
379 * clears it if null is given.
380 *
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700381 * @param name propertyName
Brian O'Connorce2d8b52015-07-29 16:24:13 -0700382 * @param collection collection of items
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700383 * @param <T> type of items
Brian O'Connorce2d8b52015-07-29 16:24:13 -0700384 * @return self
385 */
386 protected <T> Config<S> setOrClear(String name, Collection<T> collection) {
387 if (collection == null) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700388 object.remove(name);
Brian O'Connorce2d8b52015-07-29 16:24:13 -0700389 } else {
390 ArrayNode arrayNode = mapper.createArrayNode();
391 collection.forEach(i -> arrayNode.add(i.toString()));
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700392 object.set(name, arrayNode);
Brian O'Connorce2d8b52015-07-29 16:24:13 -0700393 }
394 return this;
395 }
396
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800397 /**
398 * Indicates whether only the specified fields are present in the backing JSON.
399 *
400 * @param allowedFields allowed field names
Hyunsun Moon61b73e92016-05-10 18:05:57 -0700401 * @return true if only allowedFields are present; false otherwise
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800402 */
403 protected boolean hasOnlyFields(String... allowedFields) {
Jonathan Hart3a8896b2016-02-16 13:06:26 -0800404 return hasOnlyFields(object, allowedFields);
405 }
406
407 /**
408 * Indicates whether only the specified fields are present in a particular
409 * JSON object.
410 *
411 * @param node node whose fields to check
412 * @param allowedFields allowed field names
Hyunsun Moon61b73e92016-05-10 18:05:57 -0700413 * @return true if only allowedFields are present; false otherwise
Jonathan Hart3a8896b2016-02-16 13:06:26 -0800414 */
415 protected boolean hasOnlyFields(ObjectNode node, String... allowedFields) {
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800416 Set<String> fields = ImmutableSet.copyOf(allowedFields);
Jonathan Hart3a8896b2016-02-16 13:06:26 -0800417 return !Iterators.any(node.fieldNames(), f -> !fields.contains(f));
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800418 }
419
420 /**
Hyunsun Moon61b73e92016-05-10 18:05:57 -0700421 * Indicates whether all specified fields are present in the backing JSON.
422 *
423 * @param mandatoryFields mandatory field names
424 * @return true if all mandatory fields are present; false otherwise
425 */
426 protected boolean hasFields(String... mandatoryFields) {
427 return hasFields(object, mandatoryFields);
428 }
429
430 /**
431 * Indicates whether all specified fields are present in a particular
432 * JSON object.
433 *
434 * @param node node whose fields to check
435 * @param mandatoryFields mandatory field names
436 * @return true if all mandatory fields are present; false otherwise
437 */
438 protected boolean hasFields(ObjectNode node, String... mandatoryFields) {
439 Set<String> fields = ImmutableSet.copyOf(mandatoryFields);
440 return Iterators.all(fields.iterator(), f -> !node.path(f).isMissingNode());
441 }
442
443 /**
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800444 * Indicates whether the specified field holds a valid MAC address.
445 *
446 * @param field JSON field name
447 * @param presence specifies if field is optional or mandatory
448 * @return true if valid; false otherwise
449 * @throws IllegalArgumentException if field is present, but not valid MAC
450 */
451 protected boolean isMacAddress(String field, FieldPresence presence) {
452 JsonNode node = object.path(field);
453 return isValid(node, presence, node.isTextual() &&
454 MacAddress.valueOf(node.asText()) != null);
455 }
456
457 /**
Jonathan Hart9eb45bb2016-02-12 10:59:11 -0800458 * Indicates whether the specified field of a particular node holds a valid
459 * MAC address.
460 *
461 * @param objectNode JSON node
462 * @param field JSON field name
463 * @param presence specifies if field is optional or mandatory
464 * @return true if valid; false otherwise
465 * @throws IllegalArgumentException if field is present, but not valid MAC
466 */
467 protected boolean isMacAddress(ObjectNode objectNode, String field, FieldPresence presence) {
468 JsonNode node = objectNode.path(field);
469 return isValid(node, presence, node.isTextual() &&
470 MacAddress.valueOf(node.asText()) != null);
471 }
472
473 /**
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800474 * Indicates whether the specified field holds a valid IP address.
475 *
476 * @param field JSON field name
477 * @param presence specifies if field is optional or mandatory
478 * @return true if valid; false otherwise
479 * @throws IllegalArgumentException if field is present, but not valid IP
480 */
481 protected boolean isIpAddress(String field, FieldPresence presence) {
Jonathan Hart3a8896b2016-02-16 13:06:26 -0800482 return isIpAddress(object, field, presence);
483 }
484
485 /**
486 * Indicates whether the specified field of a particular node holds a valid
487 * IP address.
488 *
Jonathan Hart9eb45bb2016-02-12 10:59:11 -0800489 * @param objectNode node from whom to access the field
Jonathan Hart3a8896b2016-02-16 13:06:26 -0800490 * @param field JSON field name
491 * @param presence specifies if field is optional or mandatory
492 * @return true if valid; false otherwise
493 * @throws IllegalArgumentException if field is present, but not valid IP
494 */
Jonathan Hart9eb45bb2016-02-12 10:59:11 -0800495 protected boolean isIpAddress(ObjectNode objectNode, String field, FieldPresence presence) {
496 JsonNode node = objectNode.path(field);
497 return isValid(node, presence, node.isTextual() &&
498 IpAddress.valueOf(node.asText()) != null);
499 }
500
501 /**
502 * Indicates whether the specified field holds a valid IP prefix.
503 *
504 * @param field JSON field name
505 * @param presence specifies if field is optional or mandatory
506 * @return true if valid; false otherwise
507 * @throws IllegalArgumentException if field is present, but not valid IP
508 * prefix
509 */
510 protected boolean isIpPrefix(String field, FieldPresence presence) {
511 return isIpPrefix(object, field, presence);
512 }
513
514 /**
515 * Indicates whether the specified field of a particular node holds a valid
516 * IP prefix.
517 *
518 * @param objectNode node from whom to access the field
519 * @param field JSON field name
520 * @param presence specifies if field is optional or mandatory
521 * @return true if valid; false otherwise
522 * @throws IllegalArgumentException if field is present, but not valid IP
523 * prefix
524 */
525 protected boolean isIpPrefix(ObjectNode objectNode, String field, FieldPresence presence) {
526 JsonNode node = objectNode.path(field);
527 return isValid(node, presence, node.isTextual() &&
528 IpPrefix.valueOf(node.asText()) != null);
529 }
530
531 /**
Hyunsun Moon61b73e92016-05-10 18:05:57 -0700532 * Indicates whether the specified field holds a valid transport layer port.
533 *
534 * @param field JSON field name
535 * @param presence specifies if field is optional or mandatory
536 * @return true if valid; false otherwise
537 * @throws IllegalArgumentException if field is present, but not valid value
538 */
539 protected boolean isTpPort(String field, FieldPresence presence) {
540 return isTpPort(object, field, presence);
541 }
542
543 /**
544 * Indicates whether the specified field of a particular node holds a valid
545 * transport layer port.
546 *
547 * @param objectNode node from whom to access the field
548 * @param field JSON field name
549 * @param presence specifies if field is optional or mandatory
550 * @return true if valid; false otherwise
551 * @throws IllegalArgumentException if field is present, but not valid value
552 */
553 protected boolean isTpPort(ObjectNode objectNode, String field, FieldPresence presence) {
554 JsonNode node = objectNode.path(field);
555 return isValid(node, presence, node.isNumber() &&
556 TpPort.tpPort(node.asInt()) != null);
557 }
558
559 /**
Jonathan Hart9eb45bb2016-02-12 10:59:11 -0800560 * Indicates whether the specified field holds a valid connect point string.
561 *
562 * @param field JSON field name
563 * @param presence specifies if field is optional or mandatory
564 * @return true if valid; false otherwise
565 * @throws IllegalArgumentException if field is present, but not valid
566 * connect point string representation
567 */
568 protected boolean isConnectPoint(String field, FieldPresence presence) {
569 return isConnectPoint(object, field, presence);
570 }
571
572 /**
573 * Indicates whether the specified field of a particular node holds a valid
574 * connect point string.
575 *
576 * @param objectNode JSON node
577 * @param field JSON field name
578 * @param presence specifies if field is optional or mandatory
579 * @return true if valid; false otherwise
580 * @throws IllegalArgumentException if field is present, but not valid
581 * connect point string representation
582 */
583 protected boolean isConnectPoint(ObjectNode objectNode, String field, FieldPresence presence) {
584 JsonNode node = objectNode.path(field);
585 return isValid(node, presence, node.isTextual() &&
586 ConnectPoint.deviceConnectPoint(node.asText()) != null);
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800587 }
588
589 /**
590 * Indicates whether the specified field holds a valid string value.
591 *
592 * @param field JSON field name
593 * @param presence specifies if field is optional or mandatory
594 * @param pattern optional regex pattern
595 * @return true if valid; false otherwise
Jonathan Hart9eb45bb2016-02-12 10:59:11 -0800596 * @throws IllegalArgumentException if field is present, but not valid string
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800597 */
598 protected boolean isString(String field, FieldPresence presence, String... pattern) {
Jonathan Hart9eb45bb2016-02-12 10:59:11 -0800599 return isString(object, field, presence, pattern);
600 }
601
602 /**
603 * Indicates whether the specified field on a particular node holds a valid
604 * string value.
605 *
606 * @param objectNode JSON node
607 * @param field JSON field name
608 * @param presence specifies if field is optional or mandatory
609 * @param pattern optional regex pattern
610 * @return true if valid; false otherwise
611 * @throws IllegalArgumentException if field is present, but not valid string
612 */
613 protected boolean isString(ObjectNode objectNode, String field,
614 FieldPresence presence, String... pattern) {
615 JsonNode node = objectNode.path(field);
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800616 return isValid(node, presence, node.isTextual() &&
617 (pattern.length > 0 && node.asText().matches(pattern[0]) || pattern.length < 1));
618 }
619
620 /**
621 * Indicates whether the specified field holds a valid number.
622 *
623 * @param field JSON field name
624 * @param presence specifies if field is optional or mandatory
625 * @param minMax optional min/max values
626 * @return true if valid; false otherwise
627 * @throws IllegalArgumentException if field is present, but not valid
628 */
629 protected boolean isNumber(String field, FieldPresence presence, long... minMax) {
630 JsonNode node = object.path(field);
HIGUCHI Yuta1d7c9cb2016-01-20 18:22:36 -0800631 return isValid(node, presence, node.isNumber() &&
632 (minMax.length > 0 && minMax[0] <= node.asLong() || minMax.length < 1) &&
633 (minMax.length > 1 && minMax[1] > node.asLong() || minMax.length < 2));
634 }
635 /**
636 * Indicates whether the specified field holds a valid number.
637 *
638 * @param field JSON field name
639 * @param presence specifies if field is optional or mandatory
640 * @param minMax optional min/max values
641 * @return true if valid; false otherwise
642 * @throws IllegalArgumentException if field is present, but not valid
643 */
644 protected boolean isNumber(String field, FieldPresence presence, double... minMax) {
645 JsonNode node = object.path(field);
646 return isValid(node, presence, node.isNumber() &&
647 (minMax.length > 0 && minMax[0] <= node.asDouble() || minMax.length < 1) &&
648 (minMax.length > 1 && minMax[1] > node.asDouble() || minMax.length < 2));
649 }
650
651 /**
652 * Indicates whether the specified field holds a valid integer.
653 *
654 * @param field JSON field name
655 * @param presence specifies if field is optional or mandatory
656 * @param minMax optional min/max values
657 * @return true if valid; false otherwise
658 * @throws IllegalArgumentException if field is present, but not valid
659 */
660 protected boolean isIntegralNumber(String field, FieldPresence presence, long... minMax) {
Jonathan Hart9eb45bb2016-02-12 10:59:11 -0800661 return isIntegralNumber(object, field, presence, minMax);
662 }
663
664 /**
665 * Indicates whether the specified field of a particular node holds a valid
666 * integer.
667 *
668 * @param objectNode JSON node
669 * @param field JSON field name
670 * @param presence specifies if field is optional or mandatory
671 * @param minMax optional min/max values
672 * @return true if valid; false otherwise
673 * @throws IllegalArgumentException if field is present, but not valid
674 */
675 protected boolean isIntegralNumber(ObjectNode objectNode, String field,
676 FieldPresence presence, long... minMax) {
677 JsonNode node = objectNode.path(field);
678
679 return isValid(node, presence, n -> {
680 long number = (node.isIntegralNumber()) ? n.asLong() : Long.parseLong(n.asText());
681 return (minMax.length > 0 && minMax[0] <= number || minMax.length < 1) &&
682 (minMax.length > 1 && minMax[1] > number || minMax.length < 2);
683 });
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800684 }
685
686 /**
687 * Indicates whether the specified field holds a valid decimal number.
688 *
689 * @param field JSON field name
690 * @param presence specifies if field is optional or mandatory
691 * @param minMax optional min/max values
692 * @return true if valid; false otherwise
693 * @throws IllegalArgumentException if field is present, but not valid
694 */
695 protected boolean isDecimal(String field, FieldPresence presence, double... minMax) {
696 JsonNode node = object.path(field);
697 return isValid(node, presence, (node.isDouble() || node.isFloat()) &&
698 (minMax.length > 0 && minMax[0] <= node.asDouble() || minMax.length < 1) &&
699 (minMax.length > 1 && minMax[1] > node.asDouble() || minMax.length < 2));
700 }
701
702 /**
Thejaswi NK6f4ae1c2015-12-08 01:15:53 +0530703 * Indicates whether the specified field holds a valid boolean value.
704 *
705 * @param field JSON field name
706 * @param presence specifies if field is optional or mandatory
707 * @return true if valid; false otherwise
Thejaswi NK6f4ae1c2015-12-08 01:15:53 +0530708 */
709 protected boolean isBoolean(String field, FieldPresence presence) {
Jonathan Hart9eb45bb2016-02-12 10:59:11 -0800710 return isBoolean(object, field, presence);
711 }
712
713 /**
714 * Indicates whether the specified field of a particular node holds a valid
715 * boolean value.
716 *
717 * @param objectNode JSON object node
718 * @param field JSON field name
719 * @param presence specifies if field is optional or mandatory
720 * @return true if valid; false otherwise
721 */
722 protected boolean isBoolean(ObjectNode objectNode, String field, FieldPresence presence) {
723 JsonNode node = objectNode.path(field);
724 return isValid(node, presence, node.isBoolean() ||
725 (node.isTextual() && isBooleanString(node.asText())));
726 }
727
728 /**
729 * Indicates whether a string holds a boolean literal value.
730 *
731 * @param str string to test
732 * @return true if the string contains "true" or "false" (case insensitive),
733 * otherwise false
734 */
735 private boolean isBooleanString(String str) {
736 return str.equalsIgnoreCase(TRUE_LITERAL) || str.equalsIgnoreCase(FALSE_LITERAL);
Thejaswi NK6f4ae1c2015-12-08 01:15:53 +0530737 }
738
739 /**
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800740 * Indicates whether the node is present and of correct value or not
741 * mandatory and absent.
742 *
743 * @param node JSON node
744 * @param presence specifies if field is optional or mandatory
745 * @param correctValue true if the value is correct
746 * @return true if the field is as expected
747 */
748 private boolean isValid(JsonNode node, FieldPresence presence, boolean correctValue) {
Jonathan Hart9eb45bb2016-02-12 10:59:11 -0800749 return isValid(node, presence, n -> correctValue);
750 }
751
752 /**
753 * Indicates whether the node is present and of correct value or not
754 * mandatory and absent.
755 *
756 * @param node JSON node
757 * @param presence specified if field is optional or mandatory
758 * @param validationFunction function which can be used to verify if the
759 * node has the correct value
760 * @return true if the field is as expected
761 */
762 private boolean isValid(JsonNode node, FieldPresence presence,
763 Function<JsonNode, Boolean> validationFunction) {
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800764 boolean isMandatory = presence == FieldPresence.MANDATORY;
Jonathan Hart9eb45bb2016-02-12 10:59:11 -0800765 if (isMandatory && validationFunction.apply(node)) {
766 return true;
767 }
768
769 if (!isMandatory && (node.isNull() || node.isMissingNode())) {
770 return true;
771 }
772
773 return validationFunction.apply(node);
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800774 }
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -0700775}