LSSTApplications  18.1.0
LSSTDataManagementBasePackage
Dictionary.cc
Go to the documentation of this file.
1 /*
2  * LSST Data Management System
3  * Copyright 2008-2016 LSST Corporation.
4  *
5  * This product includes software developed by the
6  * LSST Project (http://www.lsst.org/).
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the LSST License Statement and
19  * the GNU General Public License along with this program. If not,
20  * see <http://www.lsstcorp.org/LegalNotices/>.
21  */
22 
23 /*
24  * Dictionary
25  */
28 // #include "lsst/pex/utils/Trace.h"
29 
30 #include <boost/regex.hpp>
31 
32 #include <memory>
33 #include <stdexcept>
34 #include <string>
35 #include <set>
36 
38 
39 namespace lsst {
40 namespace pex {
41 namespace policy {
42 
43 //@cond
44 
45 using namespace std;
46 
47 const string ValidationError::EMPTY;
48 
49 ValidationError::MsgLookup ValidationError::_errmsgs;
50 
51 POL_EXCEPT_VIRTFUNCS(lsst::pex::policy::ValidationError)
52 
53 void ValidationError::_loadMessages() {
54  _errmsgs[OK] = EMPTY;
55  _errmsgs[WRONG_TYPE] = "value has the incorrect type";
56  _errmsgs[MISSING_REQUIRED] = "no value available for required parameter";
57  _errmsgs[NOT_AN_ARRAY] = "value is not an array as required";
58  _errmsgs[ARRAY_TOO_SHORT] = "insufficient number of array values";
59  _errmsgs[TOO_FEW_VALUES] = "not enough values for parameter";
60  _errmsgs[TOO_MANY_VALUES] = "too many values provided for parameter";
61  _errmsgs[WRONG_OCCURRENCE_COUNT]="incorrect number of values for parameter";
62  _errmsgs[VALUE_DISALLOWED] = "value is not among defined set";
63  _errmsgs[VALUE_OUT_OF_RANGE] = "value is out of range";
64  _errmsgs[BAD_VALUE] = "illegal value";
65  _errmsgs[UNKNOWN_NAME] = "parameter name is unknown";
66  _errmsgs[BAD_DEFINITION] = "malformed definition";
67  _errmsgs[NOT_LOADED] = "file not loaded"
68  " -- call Policy.loadPolicyFiles() before validating";
69  _errmsgs[UNKNOWN_ERROR] = "unknown error";
70 }
71 
72 vector<string> ValidationError::getParamNames() const {
74  ParamLookup::const_iterator i;
75  for (i = _errors.begin(); i != _errors.end(); ++i)
76  result.push_back(i->first);
77  return result;
78 }
79 
80 string ValidationError::describe(string prefix) const {
82  list<string> names;
83  paramNames(names);
84  for (list<string>::const_iterator i = names.begin(); i != names.end(); ++i)
85  os << prefix << *i << ": "
86  << getErrorMessageFor((ErrorType)getErrors(*i)) << endl;
87  return os.str();
88 }
89 
90 char const* ValidationError::what(void) const throw() {
91  // static to avoid memory issue -- but a concurrency problem?
92  // copied from pex/exceptions/src/Exception.cc
93  static string buffer;
95  int n = getParamCount();
96  os << "Validation error";
97  if (n == 1) os << " (1 error)";
98  else if (n > 1) os << " (" << getParamCount() << " errors)";
99  if (getParamCount() == 0)
100  os << ": no errors" << "\n";
101  else {
102  os << ": \n" << describe(" * ");
103  }
104  buffer = os.str();
105  return buffer.c_str();
106 }
107 
108 ValidationError::~ValidationError() throw() { }
109 
111 // Definition
113 
114 Definition::~Definition() { }
115 
116 Policy::ValueType Definition::_determineType() const {
117  if (_policy->isString(Dictionary::KW_TYPE)) {
118  const string& type = _policy->getString(Dictionary::KW_TYPE);
120  try {
121  result = Policy::getTypeByName(type);
122  } catch(BadNameError&) {
123  throw LSST_EXCEPT
124  (DictionaryError, string("Unknown type: \"") + type + "\".");
125  }
126  if (result == Policy::FILE)
127  throw LSST_EXCEPT(DictionaryError, string("Illegal type: \"") + type
128  + "\"; use \"" + Policy::typeName[Policy::POLICY]
129  + "\" instead.");
130  else return result;
131  }
132  else if (_policy->exists(Dictionary::KW_TYPE)) {
133  throw LSST_EXCEPT
134  (DictionaryError, string("Expected string for \"type\"; found ")
135  + _policy->getTypeName(Dictionary::KW_TYPE) + " instead.");
136  }
137 
138  else return Policy::UNDEF;
139 }
140 
145 const string Definition::getDescription() const {
146  if (_policy->exists(Dictionary::KW_DESCRIPTION))
147  return _policy->getString(Dictionary::KW_DESCRIPTION);
148  else return "";
149 }
150 
155 const int Definition::getMaxOccurs() const {
156  if (_policy->exists(Dictionary::KW_MAX_OCCUR))
157  return _policy->getInt(Dictionary::KW_MAX_OCCUR);
158  else return -1;
159 }
160 
165 const int Definition::getMinOccurs() const {
166  if (_policy->exists(Dictionary::KW_MIN_OCCUR))
167  return _policy->getInt(Dictionary::KW_MIN_OCCUR);
168  else return 0;
169 }
170 
171 
172 /*
173  * the default value into the given policy
174  * @param policy the policy object update
175  * @param withName the name to look for the value under. This must be
176  * a non-hierarchical name.
177  * @exception ValidationError if the value does not conform to this definition.
178  */
179 void Definition::setDefaultIn(Policy& policy, const string& withName,
180  ValidationError* errs) const
181 {
182  if (! _policy->exists("default")) return;
183 /*
184  Policy::ValueType type = getType();
185  if (type == Policy::UNDEF)
186  type = _policy->getValueType("default");
187 */
188  Policy::ValueType type = _policy->getValueType("default");
189 
190  if (type == Policy::BOOL)
191  setDefaultIn<bool>(policy, withName, errs);
192  else if (type == Policy::INT)
193  setDefaultIn<int>(policy, withName, errs);
194  else if (type == Policy::DOUBLE)
195  setDefaultIn<double>(policy, withName, errs);
196  else if (type == Policy::STRING)
197  setDefaultIn<string>(policy, withName, errs);
198  else if (type == Policy::POLICY)
199  setDefaultIn<Policy::Ptr>(policy, withName, errs);
201  string("Programmer Error: Unknown type for \"")
202  + getPrefix() + withName + "\": "
203  + Policy::typeName[getType()]);
204 }
205 
206 /*
207  * confirm that a Policy parameter conforms to this definition
208  * @param policy the policy object to inspect
209  * @param name the name to look for the value under. If not given
210  * the name set in this definition will be used.
211  * @exception ValidationError if the value does not conform. The message
212  * should explain why.
213  */
214 void Definition::validate(const Policy& policy, const string& name,
215  ValidationError *errs) const
216 {
217  ValidationError ve(LSST_EXCEPT_HERE);
218  ValidationError *use = &ve;
219  if (errs != 0) use = errs;
220 
221  if (! policy.exists(name)) {
222  if (getMinOccurs() > 0)
223  use->addError(getPrefix() + name, ValidationError::MISSING_REQUIRED);
224  return;
225  }
226 
227  // What type is actually present in the policy?
228  Policy::ValueType type = policy.getValueType(name);
229 
230  switch (type) {
231  case Policy::BOOL:
232  validateBasic<bool>(name, policy, use);
233  break;
234 
235  case Policy::INT:
236  validateBasic<int>(name, policy, use);
237  break;
238 
239  case Policy::DOUBLE:
240  validateBasic<double>(name, policy, use);
241  break;
242 
243  case Policy::STRING:
244  validateBasic<string>(name, policy, use);
245  break;
246 
247  case Policy::POLICY:
248  validateBasic<Policy::ConstPtr>(name, policy, use);
249  validateRecurse(name, policy.getConstPolicyArray(name), use);
250  break;
251 
252  case Policy::FILE:
253  use->addError(getPrefix() + name, ValidationError::NOT_LOADED);
254  break;
255 
256  default:
258  string("Unknown type for \"") + getPrefix() + name
259  + "\": \"" + policy.getTypeName(name) + "\"");
260  }
261 
262  if (errs == 0 && ve.getParamCount() > 0) throw ve;
263 }
264 
272 void Definition::validateCount(const string& name, int count,
273  ValidationError *errs) const {
274  int max = getMaxOccurs(); // -1 means no limit / undefined
275  if (max >= 0 && count > max)
276  errs->addError(getPrefix() + name, ValidationError::TOO_MANY_VALUES);
277  if (count < getMinOccurs()) {
278  if (count == 0)
279  errs->addError(getPrefix() + name, ValidationError::MISSING_REQUIRED);
280  else if (count == 1)
281  errs->addError(getPrefix() + name, ValidationError::NOT_AN_ARRAY);
282  else
283  errs->addError(getPrefix() + name, ValidationError::ARRAY_TOO_SHORT);
284  }
285 }
286 
292 bool operator<(const Policy& a, const Policy& b) { return true; }
293 bool operator<(const Policy::ConstPtr& a, const Policy::ConstPtr& b) { return true; }
294 bool operator<(const Policy::FilePtr& a, const Policy::FilePtr& b) { return true; }
295 
296 void Definition::validateRecurse(const string& name,
298  ValidationError *errs) const
299 {
300  for (Policy::ConstPolicyPtrArray::const_iterator i = value.begin();
301  i != value.end(); ++i) {
302  Policy::ConstPtr p = *i;
303  validateRecurse(name, *p, errs);
304  }
305 }
306 
307 /* Validate a sub-policy using a sub-dictionary. */
308 void Definition::validateRecurse(const string& name, const Policy& value,
309  ValidationError *errs) const
310 {
311  if (_policy->exists(Dictionary::KW_DICT)) {
312  if (!_policy->isPolicy(Dictionary::KW_DICT))
313  throw LSST_EXCEPT
314  (DictionaryError, string("Wrong type for ") + getPrefix() + name
315  + " \"" + Dictionary::KW_DICT + "\": expected Policy, but found "
316  + _policy->getTypeName(Dictionary::KW_DICT) + ".");
317  else {
318  Dictionary subdict(*(_policy->getPolicy(Dictionary::KW_DICT)));
319  subdict.setPrefix(_prefix + name + ".");
320  subdict.validate(value, errs);
321  }
322  }
323  // is there an unresolved link here?
324  else if (_policy->exists(Dictionary::KW_DICT_FILE)) {
325  throw LSST_EXCEPT
326  (pexExcept::LogicError, _prefix + name
327  + "." + Dictionary::KW_DICT_FILE + " needs to be loaded with "
328  "Dictionary.loadPolicyFiles() before validating.");
329  }
330  // there's a policy type defined here, but no sub-definition (that is, no
331  // constraints -- it could be any policy), so anything is okay.
332  else { }
333 }
334 
335 // TODO: integrate all checks into check() rather than having them in validate()
336 template <class T>
337 void Definition::validateBasic(const string& name, const T& value,
338  int curcount, ValidationError *errs) const
339 {
340  ValidationError ve(LSST_EXCEPT_HERE);
341  ValidationError *use = &ve;
342  if (errs != 0) use = errs;
343 
344  // check if we're going to get too many
345  if (curcount >= 0) {
346  int maxOccurs = getMaxOccurs();
347  if (maxOccurs >= 0 && curcount + 1 > maxOccurs)
348  use->addError(getPrefix() + name, ValidationError::TOO_MANY_VALUES);
349  }
350 
351  if (getType() != Policy::UNDEF && getType() != Policy::getValueType<T>()) {
352  use->addError(getPrefix() + name, ValidationError::WRONG_TYPE);
353  }
354  else if (_policy->isPolicy(Dictionary::KW_ALLOWED)) {
355  Policy::PolicyPtrArray allowed
356  = _policy->getPolicyArray(Dictionary::KW_ALLOWED);
357 
358  T min, max;
359  bool minFound = false, maxFound = false;
360  set<T> allvals;
361  // iterate over the keys inside the "allowed" sub-policy
362  for (Policy::PolicyPtrArray::const_iterator it = allowed.begin();
363  it != allowed.end(); ++it)
364  {
365  Policy::Ptr a = *it;
366  if (a->exists(Dictionary::KW_MIN)) {
367  if (minFound) {
368  // Would like to catch this in Dictionary::check(), but that
369  // would require some fancy type-based logic.
370  throw LSST_EXCEPT
371  (DictionaryError,
372  string("Min value for ") + getPrefix() + name
373  + " already specified; additional value not allowed.");
374  }
375  try {
376  min = a->getValue<T>(Dictionary::KW_MIN);
377  minFound = true; // after min assign, in case of exceptions
378  } catch(TypeError& e) {
379  throw LSST_EXCEPT
380  (DictionaryError,
381  string("Wrong type for ") + getPrefix() + name
382  + " min value: expected " + getTypeName() + ", found "
383  + a->getTypeName(Dictionary::KW_MIN) + ".");
384  } catch(...) {
385  throw;
386  }
387  }
388  if (a->exists(Dictionary::KW_MAX)) {
389  if (maxFound)
390  throw LSST_EXCEPT
391  (DictionaryError,
392  string("Max value for ") + getPrefix() + name
393  + " already specified; additional value not allowed.");
394  try {
395  max = a->getValue<T>(Dictionary::KW_MAX);
396  maxFound = true; // after max assign, in case of exceptions
397  } catch(TypeError& e) {
398  throw LSST_EXCEPT
399  (DictionaryError,
400  string("Wrong type for ") + getPrefix() + name
401  + " max value: expected " + getTypeName() + ", found "
402  + a->getTypeName(Dictionary::KW_MAX) + ".");
403  }
404  }
405  if (a->exists(Dictionary::KW_VALUE)) {
406  const T& value = a->getValue<T>(Dictionary::KW_VALUE);
407 
408  allvals.insert(value);
409  // allvals.insert(a->getValue<T>(Dictionary::KW_VALUE));
410  vector<T> values = a->getValueArray<T>(Dictionary::KW_VALUE);
411  for (typename vector<T>::const_iterator vi = values.begin();
412  vi != values.end(); ++vi)
413  allvals.insert(*vi);
414  }
415  }
416 
417  if ((minFound && value < min) || (maxFound && max < value))
418  use->addError(getPrefix() + name, ValidationError::VALUE_OUT_OF_RANGE);
419 
420  if (allvals.size() > 0 && allvals.count(value) == 0)
421  use->addError(getPrefix() + name, ValidationError::VALUE_DISALLOWED);
422  }
423  if (errs == 0 && ve.getParamCount() > 0) throw ve;
424 }
425 
426 // instantiate to keep gcc 4.4 happy when compiling with opt=2
427 // (why? -- see LSST Trac ticket #1253)
428 template void Definition::validateBasic<Policy::Ptr>
429 (std::string const &, Policy::Ptr const &, int, ValidationError*) const;
430 
431 /*
432  * confirm that a Policy parameter name-value combination is consistent
433  * with this dictionary. This does not check occurrence compliance
434  * @param name the name of the parameter being checked
435  * @param value the value of the parameter to check.
436  * @exception ValidationError if the value does not conform. The message
437  * should explain why.
438  */
439 void Definition::validate(const string& name, bool value, int curcount,
440  ValidationError *errs) const
441 {
442  validateBasic<bool>(name, value, curcount, errs);
443 }
444 
445 void Definition::validate(const string& name, int value, int curcount,
446  ValidationError *errs) const
447 {
448  validateBasic<int>(name, value, curcount, errs);
449 }
450 
451 void Definition::validate(const string& name, double value, int curcount,
452  ValidationError *errs) const
453 {
454  validateBasic<double>(name, value, curcount, errs);
455 }
456 
457 void Definition::validate(const string& name, string value, int curcount,
458  ValidationError *errs) const
459 {
460  validateBasic<string>(name, value, curcount, errs);
461 }
462 
463 void Definition::validate(const string& name, const Policy& value,
464  int curcount, ValidationError *errs) const
465 {
466  validateBasic<Policy>(name, value, curcount, errs);
467  validateRecurse(name, value, errs);
468 }
469 
470 /*
471  * confirm that a Policy parameter name-array value combination is
472  * consistent with this dictionary. Unlike the scalar version,
473  * this does check occurrence compliance.
474  * @param name the name of the parameter being checked
475  * @param value the value of the parameter to check.
476  * @exception ValidationError if the value does not conform. The message
477  * should explain why.
478  */
479 void Definition::validate(const string& name, const Policy::BoolArray& value,
480  ValidationError *errs) const
481 {
482  validateBasic<bool>(name, value, errs);
483 }
484 
485 void Definition::validate(const string& name, const Policy::IntArray& value,
486  ValidationError *errs) const
487 {
488  validateBasic<int>(name, value, errs);
489 }
490 
491 void Definition::validate(const string& name, const Policy::DoubleArray& value,
492  ValidationError *errs) const
493 {
494  validateBasic<double>(name, value, errs);
495 }
496 void Definition::validate(const string& name, const Policy::StringArray& value,
497  ValidationError *errs) const
498 {
499  validateBasic<string>(name, value, errs);
500 }
501 
502 void Definition::validate(const string& name, const Policy::ConstPolicyPtrArray& value,
503  ValidationError *errs) const
504 {
505  validateBasic<Policy::ConstPtr>(name, value, errs);
506  validateRecurse(name, value, errs);
507 }
508 
509 void Definition::check() const {
510  static set<string> okayKeywords;
511  if (okayKeywords.size() == 0) {
512  okayKeywords.insert(Dictionary::KW_TYPE);
513  okayKeywords.insert(Dictionary::KW_DICT);
514  okayKeywords.insert(Dictionary::KW_DICT_FILE);
515  okayKeywords.insert(Policy::typeName[Policy::FILE]);
516  okayKeywords.insert(Dictionary::KW_MIN_OCCUR);
517  okayKeywords.insert(Dictionary::KW_MAX_OCCUR);
518  okayKeywords.insert(Dictionary::KW_MIN);
519  okayKeywords.insert(Dictionary::KW_MAX);
520  okayKeywords.insert(Dictionary::KW_ALLOWED);
521  okayKeywords.insert(Dictionary::KW_DESCRIPTION);
522  okayKeywords.insert(Dictionary::KW_DEFAULT);
523  }
524  Policy::StringArray terms = _policy->names(true);
525  for (Policy::StringArray::const_iterator i = terms.begin();
526  i != terms.end(); ++i)
527  {
528  if (okayKeywords.count(*i) == 1) continue;
529  else throw LSST_EXCEPT
530  (DictionaryError, string("Unknown Dictionary property found at ")
531  + _prefix + _name + ": " + *i);
532  }
533 }
534 
536 // Dictionary
538 
539 const char* Dictionary::KW_DICT = "dictionary";
540 const char* Dictionary::KW_DICT_FILE = "dictionaryFile";
541 const char* Dictionary::KW_TYPE = "type";
542 const char* Dictionary::KW_DESCRIPTION = "description";
543 const char* Dictionary::KW_DEFAULT = "default";
544 const char* Dictionary::KW_DEFINITIONS = "definitions";
545 const char* Dictionary::KW_CHILD_DEF = "childDefinition";
546 const char* Dictionary::KW_ALLOWED = "allowed";
547 const char* Dictionary::KW_MIN_OCCUR = "minOccurs";
548 const char* Dictionary::KW_MAX_OCCUR = "maxOccurs";
549 const char* Dictionary::KW_MIN = "min";
550 const char* Dictionary::KW_MAX = "max";
551 const char* Dictionary::KW_VALUE = "value";
552 
553 const boost::regex Dictionary::FIELDSEP_RE("\\.");
554 
555 /*
556  * load a dictionary from a file
557  */
558 Dictionary::Dictionary(const char *filePath) : Policy(filePath) {
559  if (!exists(KW_DEFINITIONS))
560  throw LSST_EXCEPT(pexExcept::RuntimeError, string(filePath)
561  + ": does not contain a Dictionary");
562  check();
563 }
564 Dictionary::Dictionary(const string& filePath) : Policy(filePath) {
565  if (!exists(KW_DEFINITIONS))
566  throw LSST_EXCEPT(pexExcept::RuntimeError, string(filePath)
567  + ": does not contain a Dictionary");
568  check();
569 }
570 Dictionary::Dictionary(const PolicyFile& filePath) : Policy(filePath) {
571  if (!exists(KW_DEFINITIONS))
572  throw LSST_EXCEPT(pexExcept::RuntimeError, filePath.getPath()
573  + ": does not contain a Dictionary");
574  check();
575 }
576 
577 /*
578  * return a definition for the named parameter. The caller is responsible
579  * for deleting the returned object. This is slightly more efficient than
580  * getDef().
581  * @param name the hierarchical name for the parameter
582  */
583 Definition* Dictionary::makeDef(const string& name) const {
584  Policy *p = const_cast<Dictionary*>(this);
585  Policy::Ptr sp; // sub-policy
586 
587  // split the name
588  boost::sregex_token_iterator it = boost::make_regex_token_iterator(name, FIELDSEP_RE, -1);
589  boost::sregex_token_iterator end;
590  string find;
591  bool isWildcard = false; // was the immediate parent a wildcard childDefinition?
592  while (it != end) {
593  find = *it;
594  if (! p->isPolicy(KW_DEFINITIONS))
595  throw LSST_EXCEPT(DictionaryError, "Definition for " + find
596  + " not found.");
597  sp = p->getPolicy(KW_DEFINITIONS);
598  if (sp->isPolicy(find)) {
599  sp = sp->getPolicy(find);
600  isWildcard = false; // update each time, to get only immediate parent
601  }
602  else if (sp->isPolicy(Dictionary::KW_CHILD_DEF)) {
603  if (sp->valueCount(Dictionary::KW_CHILD_DEF) > 1)
604  throw LSST_EXCEPT
605  (DictionaryError, string("Multiple ") + KW_CHILD_DEF
606  + "s found " + "that match " + getPrefix() + name + ".");
607  sp = sp->getPolicy(Dictionary::KW_CHILD_DEF);
608  isWildcard = true;
609  }
610  else throw LSST_EXCEPT(NameNotFound, find);
611  p = sp.get();
612  if (++it != end) {
613  if (! sp->isPolicy(Dictionary::KW_DICT))
614  throw LSST_EXCEPT(DictionaryError,
615  find + "." + KW_DICT + " not found.");
616  sp = sp->getPolicy(Dictionary::KW_DICT);
617  p = sp.get();
618  }
619  }
620  Definition* result = new Definition(name, sp);
621  result->setWildcard(isWildcard);
622  result->setPrefix(getPrefix());
623  return result;
624 }
625 
631 Policy::DictPtr Dictionary::getSubDictionary(const string& name) const {
632  string subname = string(KW_DEFINITIONS) + "." + name + ".dictionary";
633  if (!exists(subname)) throw LSST_EXCEPT
635  string("sub-policy \"") + subname + "\" not found.");
636  if (!isPolicy(subname)) throw LSST_EXCEPT
637  (DictionaryError, subname + " is a " + getTypeName(subname)
638  + " instead of a " + Policy::typeName[Policy::POLICY] + ".");
639  ConstPtr subpol = getPolicy(subname);
640  Policy::DictPtr result = std::make_shared<Dictionary>(*subpol);
641  result->setPrefix(_prefix + name + ".");
642  return result;
643 }
644 
645 int Dictionary::loadPolicyFiles(const boost::filesystem::path& repository, bool strict) {
646  int maxLevel = 16;
647  int result = 0;
648  // loop until we reach the leaves
649  for (int level = 0; level < maxLevel; ++level) {
650  // find the "dictionaryFile" parameters
651  list<string> params;
652  paramNames(params, false);
653  list<string> toRemove;
654  for (list<string>::const_iterator ni=params.begin(); ni != params.end(); ++ni) {
655  // loop over the keys that end with ".dictionaryFile"
656  static string endswith = string(".") + KW_DICT_FILE;
657  size_t p = ni->rfind(endswith);
658  if (p == ni->length()-endswith.length()) {
659  string parent = ni->substr(0, p);
660  Policy::Ptr defin = getPolicy(parent);
661 
662  // these will get dereferenced with the call to super method
663  if (isFile(*ni))
664  defin->set(Dictionary::KW_DICT, getFile(*ni));
665  else
666  defin->set(Dictionary::KW_DICT,
667  std::make_shared<PolicyFile>(getString(*ni)));
668 
669  toRemove.push_back(*ni);
670  }
671  }
672 
673  // if no new loads, then we've loaded everything
674  int newLoads = Policy::loadPolicyFiles(repository, strict);
675 
676  // remove obsolete dictionaryFile references, to prevent re-loading
677  for (list<string>::iterator i = toRemove.begin(); i != toRemove.end(); ++i)
678  remove(*i);
679 
680  if (newLoads == 0) {
681  check(); // validate self after everything is loaded
682  return result;
683  }
684  else result += newLoads;
685  }
686  throw LSST_EXCEPT
687  (DictionaryError, string("Exceeded recursion limit (")
688  + std::to_string(maxLevel)
689  + ") loading policy files; does this dictionary contain a circular"
690  " definition?");
691 }
692 
697 void Dictionary::check() const {
698  PolicyPtrArray defs = getValueArray<Policy::Ptr>(KW_DEFINITIONS);
699  if (defs.size() == 0)
700  throw LSST_EXCEPT(DictionaryError,
701  string("no \"") + KW_DEFINITIONS + "\" section found");
702  if (defs.size() > 1)
703  throw LSST_EXCEPT
704  (DictionaryError, string("expected a single \"") + KW_DEFINITIONS
705  + "\" section; found " + std::to_string(defs.size()));
706 
707  Policy::StringArray names = defs[0]->names(true);
708  for (Policy::StringArray::const_iterator i = names.begin();
709  i != names.end(); ++i)
710  {
711  std::unique_ptr<Definition> def(makeDef(*i));
712  def->check();
713 
714  if (hasSubDictionary(*i)) {
715  // if the subdict is a policy file, skip it -- it will have to be
716  // checked later, when it is loaded
717  ConstPtr subPol = defs[0]->getPolicy(*i);
718  if (subPol->getValueType(KW_DICT) != Policy::FILE)
719  getSubDictionary(*i)->check();
720  // TODO: test that loading a subdict from a reference gets re-checked
721  }
722  }
723 }
724 
725 /*
726  * validate a Policy against this Dictionary
727  */
728 void Dictionary::validate(const Policy& pol, ValidationError *errs) const {
729  ValidationError ve(LSST_EXCEPT_HERE);
730  ValidationError *use = &ve;
731  if (errs != 0) use = errs;
732  Policy::StringArray params = pol.names(true);
733 
734  // validate each item in policy
735  for (Policy::StringArray::const_iterator i = params.begin();
736  i != params.end(); ++i)
737  {
738  try {
739  std::unique_ptr<Definition> def(makeDef(*i));
740  def->validate(pol, *i, use);
741  }
742  catch (NameNotFound& e) {
743  use->addError(getPrefix() + *i, ValidationError::UNKNOWN_NAME);
744  }
745  }
746 
747  // check definitions of missing elements for required elements
748  Policy::ConstPtr defs = getDefinitions();
749  Policy::StringArray dn = defs->names(true);
750  for (Policy::StringArray::const_iterator i = dn.begin(); i != dn.end(); ++i) {
751  const string& name = *i;
752  if (!pol.exists(name)) { // item in dictionary, but not in policy
753  std::unique_ptr<Definition> def(makeDef(name));
754  if (name != Dictionary::KW_CHILD_DEF && def->getMinOccurs() > 0)
755  use->addError(getPrefix() + name,
756  ValidationError::MISSING_REQUIRED);
757  }
758  }
759 
760  if (errs == 0 && ve.getParamCount() > 0) throw ve;
761 }
762 
763 //@endcond
764 
765 } // namespace policy
766 } // namespace pex
767 } // namespace lsst
#define POL_EXCEPT_VIRTFUNCS(etn)
Definition: exceptions.h:40
definition of the PolicyFile class
std::vector< bool > BoolArray
Definition: Policy.h:178
std::shared_ptr< Dictionary > DictPtr
Definition: Policy.h:174
std::vector< std::string > StringArray
Definition: Policy.h:181
std::vector< double > DoubleArray
Definition: Policy.h:180
table::Key< int > b
T rfind(T... args)
T to_string(T... args)
py::object result
Definition: schema.cc:418
table::Key< int > a
STL namespace.
T end(T... args)
std::string prefix
Definition: SchemaMapper.cc:79
#define LSST_EXCEPT_HERE
For internal use; gathers the file, line, and function for a tracepoint.
Definition: Exception.h:40
std::vector< Ptr > PolicyPtrArray
Definition: Policy.h:182
std::shared_ptr< PolicyFile > FilePtr
Definition: Policy.h:176
int min
STL class.
std::vector< ConstPtr > ConstPolicyPtrArray
Definition: Policy.h:184
T push_back(T... args)
std::shared_ptr< const Policy > ConstPtr
Definition: Policy.h:173
A base class for image defects.
int loadPolicyFiles(bool strict=true)
Recursively replace all PolicyFile values with the contents of the files they refer to...
Definition: Policy.h:775
table::Key< int > type
Definition: Detector.cc:167
std::shared_ptr< Policy > Ptr
Definition: Policy.h:172
T str(T... args)
int max
Reports errors in the logical structure of the program.
Definition: Runtime.h:46
STL class.
T count(T... args)
T insert(T... args)
T find(T... args)
T size(T... args)
STL class.
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition: Exception.h:48
STL class.
STL class.
T begin(T... args)
T c_str(T... args)
static const char *const typeName[]
c-string forms for the supported value types.
Definition: Policy.h:205
definition of the Dictionary class
std::vector< int > IntArray
Definition: Policy.h:179
static ValueType getTypeByName(const std::string &name)
Given the human-readable name of a type ("bool", "int", "policy", etc), what is its ValueType (BOOL...
int end
std::ostream * os
Definition: Schema.cc:746
Reports errors that are due to events beyond the control of the program.
Definition: Runtime.h:104
ValueType
an enumeration for the supported policy types
Definition: Policy.h:189