NASA Astrobee Robot Software  0.19.1
Flight software for the Astrobee robots operating inside the International Space Station.
validation.h
Go to the documentation of this file.
1 /* Copyright (c) 2017, United States Government, as represented by the
2  * Administrator of the National Aeronautics and Space Administration.
3  *
4  * All rights reserved.
5  *
6  * The Astrobee platform is licensed under the Apache License, Version 2.0
7  * (the "License"); you may not use this file except in compliance with the
8  * License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15  * License for the specific language governing permissions and limitations
16  * under the License.
17  */
18 
19 #ifndef JSONLOADER_VALIDATION_H_
20 #define JSONLOADER_VALIDATION_H_
21 
22 #include <json/value.h>
23 
24 #include <string>
25 #include <vector>
26 
27 namespace jsonloader {
28 
29 // Make sure a field in an object is the right type, optionally require
30 // the field to be present.
31 class Field {
32  public:
33  Field(std::string const& name, Json::ValueType type, bool required = true);
34 
35  virtual bool Validate(Json::Value const& obj) const;
36 
37  protected:
38  std::string const& name() const noexcept;
39 
40  private:
41  std::string name_;
42  Json::ValueType type_;
43  bool required_;
44 };
45 
46 using Fields = std::vector<const Field*>;
47 
48 bool Validate(Json::Value const& obj, Fields const& fields);
49 
50 // Make sure a field in an object has the right value, optionally
51 // compare the strings case-sensitively.
52 class StringField : public Field {
53  public:
54  StringField(std::string const& name, std::string const& value,
55  bool case_sensitive = false, bool required = true);
56 
57  bool Validate(Json::Value const& obj) const override;
58 
59  private:
60  std::string value_;
61  bool case_sensitive_;
62 };
63 
64 // Compare to a sub-object in an array or object
65 class ObjectField : public Field {
66  public:
67  ObjectField(std::string const& name, Fields const& fields,
68  bool required = true);
69 
70  bool Validate(Json::Value const& obj) const override;
71 
72  private:
73  Fields fields_;
74 };
75 
76 // Make sure a field conforms to a certain set of values
77 // This doees case-insensitive comparisons.
78 class EnumField : public Field {
79  public:
80  using Values = std::vector<std::string>;
81 
82  EnumField(std::string const& name, Values const &values,
83  bool required = true);
84 
85  bool Validate(Json::Value const& obj) const override;
86 
87  private:
88  Values values_;
89 };
90 
91 // Make sure a field matches a specific value
92 template<typename T>
93 class ValueField : public Field {
94  static_assert(!std::is_same<std::string, T>::value,
95  "use StringField to compare strings");
96 
97  constexpr Json::ValueType GetType() {
98  static_assert(!(std::is_integral<T>::value &&
99  std::is_floating_point<T>::value),
100  "invalid type for ValueField");
101 
102  if (std::is_same<bool, T>::value) {
103  return Json::booleanValue;
104  } else if (std::is_floating_point<T>::value) {
105  // TODO(tfmorse): should probably warn the user this is a bad idea...
106  return Json::realValue;
107  } else if (std::is_integral<T>::value) {
108  return Json::intValue;
109  }
110  }
111 
112  public:
113  ValueField(std::string const& name, T const& value, bool required = true)
114  : Field(name, GetType(), required), value_(value)
115  { }
116 
117  bool Validate(Json::Value const& obj) const override {
118  if (!Field::Validate(obj)) {
119  return false;
120  }
121 
122  // Field is not required and not present
123  if (!obj.isMember(name())) {
124  return true;
125  }
126 
127  Json::Value wanted(value_);
128  Json::Value const& actual = obj[name()];
129 
130  if (wanted != actual) {
131  // Special case: jsoncpp will not decode integers as unsigned. Thus,
132  // if we're comparing against an unsigned int template type,
133  // we have to coerce them and compare :/
134  if (wanted.type() == Json::uintValue &&
135  actual.type() == Json::intValue &&
136  wanted.isConvertibleTo(Json::uintValue)) {
137  return wanted.asUInt() == actual.asUInt();
138  }
139 
140  return false;
141  }
142 
143  return true;
144  }
145 
146  private:
147  const T value_;
148 };
149 
152 
153 // Make sure a field is within the given range of values.
154 template<typename T>
155 class RangeField : public Field {
156  static_assert(std::is_arithmetic<T>::value, "Arithmetic type required");
157 
158  constexpr Json::ValueType GetType() {
159  return (std::is_floating_point<T>::value) ?
160  Json::realValue : Json::intValue;
161  }
162 
163  public:
164  RangeField(std::string const& name, T const& min, T const& max,
165  bool required = true)
166  : Field(name, GetType(), required), min_(min), max_(max)
167  { };
168 
169  bool Validate(Json::Value const& obj) const override {
170  if (!Field::Validate(obj)) {
171  return false;
172  }
173 
174  // Field is not required and not present
175  if (!obj.isMember(name())) {
176  return true;
177  }
178 
179  T val;
180  if (std::is_floating_point<T>::value) {
181  val = obj[name()].asFloat();
182  } else {
183  val = obj[name()].asInt();
184  }
185 
186  if (val < min_ || val > max_) {
187  return false;
188  }
189 
190  return true;
191  };
192 
193  private:
194  T min_, max_;
195 };
196 
197 using RangeFieldF = RangeField<float>; // helper type for floats
198 using RangeFieldI = RangeField<int>; // helper type for integers
199 
200 } // end namespace jsonloader
201 
202 #endif // JSONLOADER_VALIDATION_H_
203 
jsonloader::StringField::StringField
StringField(std::string const &name, std::string const &value, bool case_sensitive=false, bool required=true)
Definition: validation.cc:116
jsonloader::RangeField::RangeField
RangeField(std::string const &name, T const &min, T const &max, bool required=true)
Definition: validation.h:164
jsonloader::Field::Field
Field(std::string const &name, Json::ValueType type, bool required=true)
Definition: validation.cc:24
jsonloader::ObjectField::ObjectField
ObjectField(std::string const &name, Fields const &fields, bool required=true)
Definition: validation.cc:148
jsonloader::StringField
Definition: validation.h:52
jsonloader::Field::Validate
virtual bool Validate(Json::Value const &obj) const
Definition: validation.cc:53
jsonloader::ObjectField::Validate
bool Validate(Json::Value const &obj) const override
Definition: validation.cc:153
jsonloader::Field::name
std::string const & name() const noexcept
Definition: validation.cc:29
jsonloader::ObjectField
Definition: validation.h:65
jsonloader
Definition: command.h:28
gpio::Value
Value
Definition: GPIO.h:42
jsonloader::RangeField::Validate
bool Validate(Json::Value const &obj) const override
Definition: validation.h:169
jsonloader::EnumField::Validate
bool Validate(Json::Value const &obj) const override
Definition: validation.cc:178
jsonloader::RangeField
Definition: validation.h:155
jsonloader::Fields
std::vector< const Field * > Fields
Definition: validation.h:46
jsonloader::ValueField::Validate
bool Validate(Json::Value const &obj) const override
Definition: validation.h:117
jsonloader::ValueField
Definition: validation.h:93
jsonloader::EnumField::Values
std::vector< std::string > Values
Definition: validation.h:80
jsonloader::EnumField::EnumField
EnumField(std::string const &name, Values const &values, bool required=true)
Definition: validation.cc:173
jsonloader::ValueField::ValueField
ValueField(std::string const &name, T const &value, bool required=true)
Definition: validation.h:113
jsonloader::EnumField
Definition: validation.h:78
jsonloader::Field
Definition: validation.h:31
jsonloader::StringField::Validate
bool Validate(Json::Value const &obj) const override
Definition: validation.cc:122
jsonloader::Validate
bool Validate(Json::Value const &obj, Fields const &fields)
Definition: validation.cc:86