SSAGES  0.9.3
Software Suite for Advanced General Ensemble Simulations
ObjectRequirement.h
1 
26 #pragma once
27 
28 #include <iostream>
29 #include <algorithm>
30 #include <list>
31 #include <map>
32 #include <numeric>
33 #include "Requirement.h"
34 #include "StringRequirement.h"
35 #include "IntegerRequirement.h"
36 #include "NumberRequirement.h"
37 #include "DependencyRequirement.h"
38 #include "RequirementLoader.h"
39 
40 namespace Json
41 {
43 
47  {
48  private:
50  std::map<std::string, std::unique_ptr<Requirement>> properties_;
51 
53  std::map<std::string, std::unique_ptr<Requirement>> patternProps_;
54 
57 
59  std::unique_ptr<DependencyRequirement> dependency_;
60 
61  std::vector<std::string> required_;
62  bool moreProps_;
63  bool setMin_;
64  bool setMax_;
65  unsigned int min_;
66  unsigned int max_;
67 
68  public:
72  moreProps_(true), setMin_(false), setMax_(false), min_(0), max_(0)
73  {}
74 
77  {
78  properties_.clear();
79 
80  patternProps_.clear();
81 
82  extended_.clear();
83  }
84 
86  virtual void ClearErrors() override
87  {
88  for(auto& c : properties_)
89  c.second->ClearErrors();
90 
91  for(auto& c : patternProps_)
92  c.second->ClearErrors();
93 
94  for(auto& c : extended_)
95  c->ClearErrors();
96 
97  if(dependency_ != nullptr)
98  dependency_->ClearErrors();
99 
101  }
102 
104  virtual void ClearNotices() override
105  {
106  for(auto& c : properties_)
107  c.second->ClearNotices();
108 
109  for(auto& c : patternProps_)
110  c.second->ClearNotices();
111 
112  for(auto& c : extended_)
113  c->ClearNotices();
114 
115  if(dependency_ != nullptr)
116  dependency_->ClearNotices();
117 
119  }
120 
122  virtual void Reset() override
123  {
124  ClearErrors();
125  ClearNotices();
126 
127  properties_.clear();
128  patternProps_.clear();
129 
130  moreProps_ = true;
131  setMin_ = setMax_ = false;
132  min_ = max_ = 0;
133  required_.clear();
134  dependency_.reset();
135  }
136 
138 
142  virtual void Parse(Value json, const std::string& path) override
143  {
144  Reset();
145  RequirementLoader loader;
146 
147  // Additional properties.
148  if(json.isMember("additionalProperties"))
149  {
150  if(json["additionalProperties"].isBool())
151  moreProps_ = json["additionalProperties"].asBool();
152  else if(json["additionalProperties"].isObject())
153  json["properties"]["additionalProperties"] = json["additionalProperties"];
154  }
155 
156  // properties.
157  if(json.isMember("properties") && json["properties"].isObject())
158  {
159  auto& props = json["properties"];
160  auto names = props.getMemberNames();
161  int i = 0;
162  for(auto& prop : props)
163  {
164  if(auto property = loader.LoadRequirement(prop))
165  {
166  properties_[names[i]] = std::move(property);
167  properties_[names[i]]->Parse(prop, path + "/" + names[i]);
168  }
169 
170  ++i;
171  }
172  }
173 
174  // Pattern properties. TODO: eliminate redundant code!!
175  if(json.isMember("patternProperties") && json["patternProperties"].isObject())
176  {
177  auto& props = json["patternProperties"];
178  auto names = props.getMemberNames();
179  int i = 0;
180  for(auto& prop : props)
181  {
182  if(prop.isObject())
183  {
184  if(auto property = loader.LoadRequirement(prop))
185  {
186  patternProps_[names[i]] = std::move(property);
187  patternProps_[names[i]]->Parse(prop, path + "/" + names[i]);
188  }
189  }
190 
191  ++i;
192  }
193  }
194 
195  // Required properties.
196  if(json.isMember("required") && json["required"].isArray())
197  {
198  for(auto& requirement : json["required"])
199  required_.push_back(requirement.asString());
200  }
201 
202  // Min property count.
203  if(json.isMember("minProperties") && json["minProperties"].isUInt())
204  {
205  setMin_ = true;
206  min_ = json["minProperties"].asInt();
207  }
208 
209  // Max property count.
210  if(json.isMember("maxProperties") && json["maxProperties"].isUInt())
211  {
212  setMax_ = true;
213  max_ = json["maxProperties"].asInt();
214  }
215 
216  // Dependencies
217  if(json.isMember("dependencies") && json["dependencies"].isObject())
218  {
219  dependency_ = std::unique_ptr<DependencyRequirement>(new DependencyRequirement());
220  dependency_->Parse(json["dependencies"], path);
221  }
222 
223  // Extended properties.
224  for(auto& prop : json)
225  {
226  if(auto req = loader.LoadExtended(prop))
227  {
228  extended_.push_back(std::move(req));
229  extended_.back()->Parse(prop, path);
230  }
231  }
232  }
233 
235 
239  virtual void Validate(const Value& json, const std::string& path) override
240  {
241  if(!json.isObject())
242  {
243  PushError(path + ": Must be of type \"object\"");
244  return;
245  }
246 
247  if(setMin_ && json.size() < min_)
248  PushError(path + ": Object must contain at least " + std::to_string(min_) + " properties");
249 
250  if(setMax_ && json.size() > max_)
251  PushError(path + ": Object must contain at most " + std::to_string(max_) + " properties");
252 
253 
254  // Check dependencies.
255  if(dependency_ != nullptr)
256  {
257  dependency_->Validate(json, path);
258  if(dependency_->HasErrors())
259  for(const auto& error : dependency_->GetErrors())
260  PushError(error);
261  if(dependency_->HasNotices())
262  for(const auto& notice : dependency_->GetNotices())
263  PushNotice(notice);
264  }
265 
266  // Copy so we can pop items off the list.
267  auto rprops = required_;
268 
269  auto names = json.getMemberNames();
270  int i = 0;
271  for(auto& prop : json)
272  {
273  Requirement* requirement = nullptr;
274  auto it = properties_.find(names[i]);
275  if(it != properties_.end())
276  requirement = it->second.get();
277  else if(patternProps_.size() != 0)
278  {
279  for(auto& pattern : patternProps_)
280  {
281  auto regex = std::regex(pattern.first, std::regex::ECMAScript);
282  if(std::regex_search(names[i], regex))
283  requirement = pattern.second.get();
284  }
285  }
286 
287  if(!requirement && properties_.find("additionalProperties") != properties_.end())
288  requirement = properties_["additionalProperties"].get();
289 
290  if(requirement)
291  {
292  requirement->Validate(prop, path + "/" + names[i]);
293  if(requirement->HasErrors())
294  for(const auto& error : requirement->GetErrors())
295  PushError(error);
296  if(requirement->HasNotices())
297  for(const auto& notice : requirement->GetNotices())
298  PushNotice(notice);
299  }
300  else if(!moreProps_)
301  PushError(path + ": Invalid property \"" + names[i] + "\" specified");
302 
303  rprops.erase(std::remove(rprops.begin(), rprops.end(),names[i]),rprops.end());
304  ++i;
305  }
306 
307  if(required_.size() && rprops.size() != 0)
308  {
309  std::string msg = std::accumulate(rprops.begin(), rprops.end(), std::string(),
310  [](const std::string& a, const std::string& b) -> std::string {
311  return a + (a.length() > 0 ? ", " : "") + b;
312  });
313  PushError(path + ": Missing properties: " + msg);
314  }
315 
316  // Validate extended.
317  for(auto& requirement : extended_)
318  {
319  requirement->Validate(json, path);
320  if(requirement->HasErrors())
321  for(const auto& error : requirement->GetErrors())
322  PushError(error);
323  if(requirement->HasNotices())
324  for(const auto& notice : requirement->GetNotices())
325  PushNotice(notice);
326  }
327  }
328  };
329 }
Requires dependencies to be met.
Requirements on an object.
unsigned int max_
Upper bound.
RequireList extended_
List of requirements.
virtual void Parse(Value json, const std::string &path) override
Parse JSON value to generate Requirement(s).
virtual void Reset() override
Reset Requirement.
virtual void ClearNotices() override
Clear notices on all Requirements.
std::map< std::string, std::unique_ptr< Requirement > > patternProps_
Map of patterns the object needs to match.
ObjectRequirement()
Constructor.
std::vector< std::string > required_
List of requirements.
std::map< std::string, std::unique_ptr< Requirement > > properties_
Map of properties the object needs to have.
unsigned int min_
Lower bound.
virtual void Validate(const Value &json, const std::string &path) override
Validate JSON value.
virtual void ClearErrors() override
Clear errors on all Requirements.
std::unique_ptr< DependencyRequirement > dependency_
Dependency requirement.
bool setMax_
If True upper bound is active.
bool moreProps_
If True, more properties need to be set.
bool setMin_
If True lower bound is active.
Helper class to load Requirement.
std::unique_ptr< Requirement > LoadExtended(const Value &json)
Extended Requirement loader.
std::unique_ptr< Requirement > LoadRequirement(const Value &json)
Load specific requirement.
Requirements on input files.
Definition: Requirement.h:40
void PushNotice(const std::string &notice)
Add message to list of notices.
Definition: Requirement.h:62
std::vector< std::string > GetNotices()
Get list of notices.
Definition: Requirement.h:107
std::vector< std::string > GetErrors()
Get list of error messages.
Definition: Requirement.h:92
virtual void ClearNotices()
Clear list of notice messages.
Definition: Requirement.h:110
virtual void Validate(const Value &json, const std::string &path)=0
Validate that JSON value meets requirements.
virtual void ClearErrors()
Clear list of error messages.
Definition: Requirement.h:95
virtual bool HasNotices()
Check if notices have been queued.
Definition: Requirement.h:101
void PushError(const std::string &error)
Add error to list of error messages.
Definition: Requirement.h:53
bool HasErrors()
Check if errors have occured.
Definition: Requirement.h:86
std::vector< std::unique_ptr< Requirement > > RequireList
List of Requirements.
Definition: Requirement.h:117