blob: 58219d2e74ff335ba945e553f1c2d8f39686b3b9 [file] [log] [blame]
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -07001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
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;
27import org.onlab.packet.MacAddress;
Brian O'Connorce2d8b52015-07-29 16:24:13 -070028
29import java.util.Collection;
30import java.util.List;
Thomas Vachuskace0bbb32015-11-18 16:56:10 -080031import java.util.Set;
Brian O'Connorce2d8b52015-07-29 16:24:13 -070032import java.util.function.Function;
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -070033
34import static com.google.common.base.Preconditions.checkNotNull;
Charles Chan023a8982016-02-04 11:00:41 -080035import static com.google.common.base.Preconditions.checkState;
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -070036
37/**
38 * Base abstraction of a configuration facade for a specific subject. Derived
Thomas Vachuska96d55b12015-05-11 08:52:03 -070039 * classes should keep all state in the specified JSON tree as that is the
40 * only state that will be distributed or persisted; this class is merely
41 * a facade for interacting with a particular facet of configuration on a
42 * given subject.
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -070043 *
44 * @param <S> type of subject
45 */
Thomas Vachuskae2b7e7e2015-05-20 11:11:31 -070046@Beta
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -070047public abstract class Config<S> {
48
Thomas Vachuska96d55b12015-05-11 08:52:03 -070049 protected S subject;
50 protected String key;
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070051
52 protected JsonNode node;
53 protected ObjectNode object;
54 protected ArrayNode array;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070055 protected ObjectMapper mapper;
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070056
Thomas Vachuska96d55b12015-05-11 08:52:03 -070057 protected ConfigApplyDelegate delegate;
58
59 /**
Thomas Vachuskace0bbb32015-11-18 16:56:10 -080060 * Indicator of whether a configuration JSON field is required.
61 */
62 public enum FieldPresence {
63 /**
64 * Signifies that config field is an optional one.
65 */
66 OPTIONAL,
67
68 /**
69 * Signifies that config field is mandatory.
70 */
71 MANDATORY
72 }
73
74 /**
Thomas Vachuska96d55b12015-05-11 08:52:03 -070075 * Initializes the configuration behaviour with necessary context.
76 *
77 * @param subject configuration subject
78 * @param key configuration key
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070079 * @param node JSON node where configuration data is stored
Thomas Vachuska96d55b12015-05-11 08:52:03 -070080 * @param mapper JSON object mapper
Charles Chan023a8982016-02-04 11:00:41 -080081 * @param delegate delegate context, or null for detached configs.
Thomas Vachuska96d55b12015-05-11 08:52:03 -070082 */
Charles Chan023a8982016-02-04 11:00:41 -080083 public final void init(S subject, String key, JsonNode node, ObjectMapper mapper,
Thomas Vachuska96d55b12015-05-11 08:52:03 -070084 ConfigApplyDelegate delegate) {
Charles Chan023a8982016-02-04 11:00:41 -080085 this.subject = checkNotNull(subject, "Subject cannot be null");
Thomas Vachuska96d55b12015-05-11 08:52:03 -070086 this.key = key;
Charles Chan023a8982016-02-04 11:00:41 -080087 this.node = checkNotNull(node, "Node cannot be null");
Thomas Vachuska0a400ea2015-09-04 11:25:03 -070088 this.object = node instanceof ObjectNode ? (ObjectNode) node : null;
89 this.array = node instanceof ArrayNode ? (ArrayNode) node : null;
Charles Chan023a8982016-02-04 11:00:41 -080090 this.mapper = checkNotNull(mapper, "Mapper cannot be null");
91 this.delegate = delegate;
Thomas Vachuska96d55b12015-05-11 08:52:03 -070092 }
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -070093
94 /**
Thomas Vachuskace0bbb32015-11-18 16:56:10 -080095 * Indicates whether or not the backing JSON node contains valid data.
96 * <p>
97 * Default implementation returns true.
98 * Subclasses are expected to override this with their own validation.
Thomas Vachuska36008462016-01-07 15:38:20 -080099 * Implementations are free to throw a RuntimeException if data is invalid.
100 * * </p>
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800101 *
102 * @return true if the data is valid; false otherwise
Thomas Vachuska36008462016-01-07 15:38:20 -0800103 * @throws RuntimeException if configuration is invalid or completely foobar
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800104 */
105 public boolean isValid() {
Thomas Vachuska36008462016-01-07 15:38:20 -0800106 // Derivatives should use the provided set of predicates to test
107 // validity of their fields, e.g.:
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800108 // isString(path)
109 // isBoolean(path)
110 // isNumber(path, [min, max])
111 // isDecimal(path, [min, max])
112 // isMacAddress(path)
113 // isIpAddress(path)
114 return true;
115 }
116
117 /**
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -0700118 * Returns the specific subject to which this configuration pertains.
119 *
120 * @return configuration subject
121 */
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700122 public S subject() {
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -0700123 return subject;
124 }
125
126 /**
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700127 * Returns the configuration key. This is primarily aimed for use in
128 * composite JSON trees in external representations and has no bearing on
129 * the internal behaviours.
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -0700130 *
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700131 * @return configuration key
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -0700132 */
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700133 public String key() {
134 return key;
135 }
136
137 /**
138 * Returns the JSON node that contains the configuration data.
139 *
140 * @return JSON node backing the configuration
141 */
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700142 public JsonNode node() {
Jonathan Harta8625482015-09-08 16:14:56 -0700143 return node;
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -0700144 }
145
146 /**
147 * Applies any configuration changes made via this configuration.
Charles Chan023a8982016-02-04 11:00:41 -0800148 *
149 * Not effective for detached configs.
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -0700150 */
151 public void apply() {
Charles Chan023a8982016-02-04 11:00:41 -0800152 checkState(delegate != null, "Cannot apply detached config");
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -0700153 delegate.onApply(this);
154 }
155
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700156 // Miscellaneous helpers for interacting with JSON
157
158 /**
159 * Gets the specified property as a string.
160 *
161 * @param name property name
162 * @param defaultValue default value if property not set
163 * @return property value or default value
164 */
165 protected String get(String name, String defaultValue) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700166 return object.path(name).asText(defaultValue);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700167 }
168
169 /**
170 * Sets the specified property as a string or clears it if null value given.
171 *
172 * @param name property name
173 * @param value new value or null to clear the property
174 * @return self
175 */
176 protected Config<S> setOrClear(String name, String value) {
177 if (value != null) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700178 object.put(name, value);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700179 } else {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700180 object.remove(name);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700181 }
182 return this;
183 }
184
185 /**
186 * Gets the specified property as a boolean.
187 *
188 * @param name property name
189 * @param defaultValue default value if property not set
190 * @return property value or default value
191 */
192 protected boolean get(String name, boolean defaultValue) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700193 return object.path(name).asBoolean(defaultValue);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700194 }
195
196 /**
HIGUCHI Yuta1d7c9cb2016-01-20 18:22:36 -0800197 * Clears the specified property.
198 *
199 * @param name property name
200 * @return self
201 */
202 protected Config<S> clear(String name) {
203 object.remove(name);
204 return this;
205 }
206
207 /**
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700208 * Sets the specified property as a boolean or clears it if null value given.
209 *
210 * @param name property name
211 * @param value new value or null to clear the property
212 * @return self
213 */
214 protected Config<S> setOrClear(String name, Boolean value) {
215 if (value != null) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700216 object.put(name, value.booleanValue());
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700217 } else {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700218 object.remove(name);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700219 }
220 return this;
221 }
222
223 /**
Jonathan Hart111b42b2015-07-14 13:28:05 -0700224 * Gets the specified property as an integer.
225 *
226 * @param name property name
227 * @param defaultValue default value if property not set
228 * @return property value or default value
229 */
230 protected int get(String name, int defaultValue) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700231 return object.path(name).asInt(defaultValue);
Jonathan Hart111b42b2015-07-14 13:28:05 -0700232 }
233
234 /**
235 * Sets the specified property as an integer or clears it if null value given.
236 *
237 * @param name property name
238 * @param value new value or null to clear the property
239 * @return self
240 */
241 protected Config<S> setOrClear(String name, Integer value) {
242 if (value != null) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700243 object.put(name, value.intValue());
Jonathan Hart111b42b2015-07-14 13:28:05 -0700244 } else {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700245 object.remove(name);
Jonathan Hart111b42b2015-07-14 13:28:05 -0700246 }
247 return this;
248 }
249
250 /**
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700251 * Gets the specified property as a long.
252 *
253 * @param name property name
254 * @param defaultValue default value if property not set
255 * @return property value or default value
256 */
257 protected long get(String name, long defaultValue) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700258 return object.path(name).asLong(defaultValue);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700259 }
260
261 /**
262 * Sets the specified property as a long or clears it if null value given.
263 *
264 * @param name property name
265 * @param value new value or null to clear the property
266 * @return self
267 */
268 protected Config<S> setOrClear(String name, Long value) {
269 if (value != null) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700270 object.put(name, value.longValue());
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700271 } else {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700272 object.remove(name);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700273 }
274 return this;
275 }
276
277 /**
278 * Gets the specified property as a double.
279 *
280 * @param name property name
281 * @param defaultValue default value if property not set
282 * @return property value or default value
283 */
284 protected double get(String name, double defaultValue) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700285 return object.path(name).asDouble(defaultValue);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700286 }
287
288 /**
289 * Sets the specified property as a double or clears it if null value given.
290 *
291 * @param name property name
292 * @param value new value or null to clear the property
293 * @return self
294 */
295 protected Config<S> setOrClear(String name, Double value) {
296 if (value != null) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700297 object.put(name, value.doubleValue());
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700298 } else {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700299 object.remove(name);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700300 }
301 return this;
302 }
303
304 /**
305 * Gets the specified property as an enum.
306 *
307 * @param name property name
308 * @param defaultValue default value if property not set
309 * @param enumClass the enum class
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700310 * @param <E> type of enum
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700311 * @return property value or default value
312 */
313 protected <E extends Enum<E>> E get(String name, E defaultValue, Class<E> enumClass) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700314 return Enum.valueOf(enumClass, object.path(name).asText(defaultValue.toString()));
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700315 }
316
317 /**
318 * Sets the specified property as a double or clears it if null value given.
319 *
320 * @param name property name
321 * @param value new value or null to clear the property
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700322 * @param <E> type of enum
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700323 * @return self
324 */
325 protected <E extends Enum> Config<S> setOrClear(String name, E value) {
326 if (value != null) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700327 object.put(name, value.toString());
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700328 } else {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700329 object.remove(name);
Thomas Vachuska96d55b12015-05-11 08:52:03 -0700330 }
331 return this;
332 }
Jonathan Hart111b42b2015-07-14 13:28:05 -0700333
Brian O'Connorce2d8b52015-07-29 16:24:13 -0700334 /**
335 * Gets the specified array property as a list of items.
336 *
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700337 * @param name property name
Brian O'Connorce2d8b52015-07-29 16:24:13 -0700338 * @param function mapper from string to item
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700339 * @param <T> type of item
Brian O'Connorce2d8b52015-07-29 16:24:13 -0700340 * @return list of items
341 */
342 protected <T> List<T> getList(String name, Function<String, T> function) {
343 List<T> list = Lists.newArrayList();
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700344 ArrayNode arrayNode = (ArrayNode) object.path(name);
Brian O'Connorce2d8b52015-07-29 16:24:13 -0700345 arrayNode.forEach(i -> list.add(function.apply(i.asText())));
346 return list;
347 }
348
349 /**
Naoki Shiota399a0b32015-11-15 20:36:13 -0600350 * Gets the specified array property as a list of items.
351 *
352 * @param name property name
353 * @param function mapper from string to item
354 * @param defaultValue default value if property not set
355 * @param <T> type of item
356 * @return list of items
357 */
358 protected <T> List<T> getList(String name, Function<String, T> function, List<T> defaultValue) {
359 List<T> list = Lists.newArrayList();
360 JsonNode jsonNode = object.path(name);
361 if (jsonNode.isMissingNode()) {
362 return defaultValue;
363 }
364 ArrayNode arrayNode = (ArrayNode) jsonNode;
365 arrayNode.forEach(i -> list.add(function.apply(i.asText())));
366 return list;
367 }
368
369 /**
Brian O'Connorce2d8b52015-07-29 16:24:13 -0700370 * Sets the specified property as an array of items in a given collection or
371 * clears it if null is given.
372 *
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700373 * @param name propertyName
Brian O'Connorce2d8b52015-07-29 16:24:13 -0700374 * @param collection collection of items
Thomas Vachuskad894b5d2015-07-30 11:59:07 -0700375 * @param <T> type of items
Brian O'Connorce2d8b52015-07-29 16:24:13 -0700376 * @return self
377 */
378 protected <T> Config<S> setOrClear(String name, Collection<T> collection) {
379 if (collection == null) {
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700380 object.remove(name);
Brian O'Connorce2d8b52015-07-29 16:24:13 -0700381 } else {
382 ArrayNode arrayNode = mapper.createArrayNode();
383 collection.forEach(i -> arrayNode.add(i.toString()));
Thomas Vachuska0a400ea2015-09-04 11:25:03 -0700384 object.set(name, arrayNode);
Brian O'Connorce2d8b52015-07-29 16:24:13 -0700385 }
386 return this;
387 }
388
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800389 /**
390 * Indicates whether only the specified fields are present in the backing JSON.
391 *
392 * @param allowedFields allowed field names
393 * @return true if all allowedFields are present; false otherwise
394 */
395 protected boolean hasOnlyFields(String... allowedFields) {
396 Set<String> fields = ImmutableSet.copyOf(allowedFields);
397 return !Iterators.any(object.fieldNames(), f -> !fields.contains(f));
398 }
399
400 /**
401 * Indicates whether the specified field holds a valid MAC address.
402 *
403 * @param field JSON field name
404 * @param presence specifies if field is optional or mandatory
405 * @return true if valid; false otherwise
406 * @throws IllegalArgumentException if field is present, but not valid MAC
407 */
408 protected boolean isMacAddress(String field, FieldPresence presence) {
409 JsonNode node = object.path(field);
410 return isValid(node, presence, node.isTextual() &&
411 MacAddress.valueOf(node.asText()) != null);
412 }
413
414 /**
415 * Indicates whether the specified field holds a valid IP address.
416 *
417 * @param field JSON field name
418 * @param presence specifies if field is optional or mandatory
419 * @return true if valid; false otherwise
420 * @throws IllegalArgumentException if field is present, but not valid IP
421 */
422 protected boolean isIpAddress(String field, FieldPresence presence) {
423 JsonNode node = object.path(field);
424 return isValid(node, presence, node.isTextual() &&
425 IpAddress.valueOf(node.asText()) != null);
426 }
427
428 /**
429 * Indicates whether the specified field holds a valid string value.
430 *
431 * @param field JSON field name
432 * @param presence specifies if field is optional or mandatory
433 * @param pattern optional regex pattern
434 * @return true if valid; false otherwise
435 * @throws IllegalArgumentException if field is present, but not valid MAC
436 */
437 protected boolean isString(String field, FieldPresence presence, String... pattern) {
438 JsonNode node = object.path(field);
439 return isValid(node, presence, node.isTextual() &&
440 (pattern.length > 0 && node.asText().matches(pattern[0]) || pattern.length < 1));
441 }
442
443 /**
444 * Indicates whether the specified field holds a valid number.
445 *
446 * @param field JSON field name
447 * @param presence specifies if field is optional or mandatory
448 * @param minMax optional min/max values
449 * @return true if valid; false otherwise
450 * @throws IllegalArgumentException if field is present, but not valid
451 */
452 protected boolean isNumber(String field, FieldPresence presence, long... minMax) {
453 JsonNode node = object.path(field);
HIGUCHI Yuta1d7c9cb2016-01-20 18:22:36 -0800454 return isValid(node, presence, node.isNumber() &&
455 (minMax.length > 0 && minMax[0] <= node.asLong() || minMax.length < 1) &&
456 (minMax.length > 1 && minMax[1] > node.asLong() || minMax.length < 2));
457 }
458 /**
459 * Indicates whether the specified field holds a valid number.
460 *
461 * @param field JSON field name
462 * @param presence specifies if field is optional or mandatory
463 * @param minMax optional min/max values
464 * @return true if valid; false otherwise
465 * @throws IllegalArgumentException if field is present, but not valid
466 */
467 protected boolean isNumber(String field, FieldPresence presence, double... minMax) {
468 JsonNode node = object.path(field);
469 return isValid(node, presence, node.isNumber() &&
470 (minMax.length > 0 && minMax[0] <= node.asDouble() || minMax.length < 1) &&
471 (minMax.length > 1 && minMax[1] > node.asDouble() || minMax.length < 2));
472 }
473
474 /**
475 * Indicates whether the specified field holds a valid integer.
476 *
477 * @param field JSON field name
478 * @param presence specifies if field is optional or mandatory
479 * @param minMax optional min/max values
480 * @return true if valid; false otherwise
481 * @throws IllegalArgumentException if field is present, but not valid
482 */
483 protected boolean isIntegralNumber(String field, FieldPresence presence, long... minMax) {
484 JsonNode node = object.path(field);
485 return isValid(node, presence, node.isIntegralNumber() &&
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800486 (minMax.length > 0 && minMax[0] <= node.asLong() || minMax.length < 1) &&
487 (minMax.length > 1 && minMax[1] > node.asLong() || minMax.length < 2));
488 }
489
490 /**
491 * Indicates whether the specified field holds a valid decimal number.
492 *
493 * @param field JSON field name
494 * @param presence specifies if field is optional or mandatory
495 * @param minMax optional min/max values
496 * @return true if valid; false otherwise
497 * @throws IllegalArgumentException if field is present, but not valid
498 */
499 protected boolean isDecimal(String field, FieldPresence presence, double... minMax) {
500 JsonNode node = object.path(field);
501 return isValid(node, presence, (node.isDouble() || node.isFloat()) &&
502 (minMax.length > 0 && minMax[0] <= node.asDouble() || minMax.length < 1) &&
503 (minMax.length > 1 && minMax[1] > node.asDouble() || minMax.length < 2));
504 }
505
506 /**
Thejaswi NK6f4ae1c2015-12-08 01:15:53 +0530507 * Indicates whether the specified field holds a valid boolean value.
508 *
509 * @param field JSON field name
510 * @param presence specifies if field is optional or mandatory
511 * @return true if valid; false otherwise
512 * @throws IllegalArgumentException if field is present, but not valid
513 */
514 protected boolean isBoolean(String field, FieldPresence presence) {
515 JsonNode node = object.path(field);
516 return isValid(node, presence, node.isBoolean());
517 }
518
519 /**
Thomas Vachuskace0bbb32015-11-18 16:56:10 -0800520 * Indicates whether the node is present and of correct value or not
521 * mandatory and absent.
522 *
523 * @param node JSON node
524 * @param presence specifies if field is optional or mandatory
525 * @param correctValue true if the value is correct
526 * @return true if the field is as expected
527 */
528 private boolean isValid(JsonNode node, FieldPresence presence, boolean correctValue) {
529 boolean isMandatory = presence == FieldPresence.MANDATORY;
530 return isMandatory && correctValue || !isMandatory && !node.isNull() || correctValue;
531 }
Thomas Vachuskaf0e1fae2015-04-24 00:51:51 -0700532}