lsst.pex.policy  14.0-1-g4b114ac+14
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 {
73  vector<string> result;
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 {
81  ostringstream os;
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;
94  ostringstream os;
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);
119  Policy::ValueType result;
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 (!getType() == Policy::POLICY) // should have checked this at a higher level
312  throw LSST_EXCEPT
313  (pexExcept::LogicError, string("Wrong type: expected ")
314  + Policy::typeName[Policy::POLICY] + " for " + getPrefix() + name
315  + " but found " + getTypeName() + ". // recurse if we have a sub-definition");
316  // recurse if we have a sub-definition
317  else if (_policy->exists(Dictionary::KW_DICT)) {
318  if (!_policy->isPolicy(Dictionary::KW_DICT))
319  throw LSST_EXCEPT
320  (DictionaryError, string("Wrong type for ") + getPrefix() + name
321  + " \"" + Dictionary::KW_DICT + "\": expected Policy, but found "
322  + _policy->getTypeName(Dictionary::KW_DICT) + ".");
323  else {
324  Dictionary subdict(*(_policy->getPolicy(Dictionary::KW_DICT)));
325  subdict.setPrefix(_prefix + name + ".");
326  subdict.validate(value, errs);
327  }
328  }
329  // is there an unresolved link here?
330  else if (_policy->exists(Dictionary::KW_DICT_FILE)) {
331  throw LSST_EXCEPT
332  (pexExcept::LogicError, _prefix + name
333  + "." + Dictionary::KW_DICT_FILE + " needs to be loaded with "
334  "Dictionary.loadPolicyFiles() before validating.");
335  }
336  // there's a policy type defined here, but no sub-definition (that is, no
337  // constraints -- it could be any policy), so anything is okay.
338  else { }
339 }
340 
341 // TODO: integrate all checks into check() rather than having them in validate()
342 template <class T>
343 void Definition::validateBasic(const string& name, const T& value,
344  int curcount, ValidationError *errs) const
345 {
346  ValidationError ve(LSST_EXCEPT_HERE);
347  ValidationError *use = &ve;
348  if (errs != 0) use = errs;
349 
350  // check if we're going to get too many
351  if (curcount >= 0) {
352  int maxOccurs = getMaxOccurs();
353  if (maxOccurs >= 0 && curcount + 1 > maxOccurs)
354  use->addError(getPrefix() + name, ValidationError::TOO_MANY_VALUES);
355  }
356 
357  if (getType() != Policy::UNDEF && getType() != Policy::getValueType<T>()) {
358  use->addError(getPrefix() + name, ValidationError::WRONG_TYPE);
359  }
360  else if (_policy->isPolicy(Dictionary::KW_ALLOWED)) {
361  Policy::PolicyPtrArray allowed
362  = _policy->getPolicyArray(Dictionary::KW_ALLOWED);
363 
364  T min, max;
365  bool minFound = false, maxFound = false;
366  set<T> allvals;
367  // iterate over the keys inside the "allowed" sub-policy
368  for (Policy::PolicyPtrArray::const_iterator it = allowed.begin();
369  it != allowed.end(); ++it)
370  {
371  Policy::Ptr a = *it;
372  if (a->exists(Dictionary::KW_MIN)) {
373  if (minFound) {
374  // Would like to catch this in Dictionary::check(), but that
375  // would require some fancy type-based logic.
376  throw LSST_EXCEPT
377  (DictionaryError,
378  string("Min value for ") + getPrefix() + name
379  + " already specified; additional value not allowed.");
380  }
381  try {
382  min = a->getValue<T>(Dictionary::KW_MIN);
383  minFound = true; // after min assign, in case of exceptions
384  } catch(TypeError& e) {
385  throw LSST_EXCEPT
386  (DictionaryError,
387  string("Wrong type for ") + getPrefix() + name
388  + " min value: expected " + getTypeName() + ", found "
389  + a->getTypeName(Dictionary::KW_MIN) + ".");
390  } catch(...) {
391  throw;
392  }
393  }
394  if (a->exists(Dictionary::KW_MAX)) {
395  if (maxFound)
396  throw LSST_EXCEPT
397  (DictionaryError,
398  string("Max value for ") + getPrefix() + name
399  + " already specified; additional value not allowed.");
400  try {
401  max = a->getValue<T>(Dictionary::KW_MAX);
402  maxFound = true; // after max assign, in case of exceptions
403  } catch(TypeError& e) {
404  throw LSST_EXCEPT
405  (DictionaryError,
406  string("Wrong type for ") + getPrefix() + name
407  + " max value: expected " + getTypeName() + ", found "
408  + a->getTypeName(Dictionary::KW_MAX) + ".");
409  }
410  }
411  if (a->exists(Dictionary::KW_VALUE)) {
412  const T& value = a->getValue<T>(Dictionary::KW_VALUE);
413 
414  allvals.insert(value);
415  // allvals.insert(a->getValue<T>(Dictionary::KW_VALUE));
416  vector<T> values = a->getValueArray<T>(Dictionary::KW_VALUE);
417  for (typename vector<T>::const_iterator vi = values.begin();
418  vi != values.end(); ++vi)
419  allvals.insert(*vi);
420  }
421  }
422 
423  if ((minFound && value < min) || (maxFound && max < value))
424  use->addError(getPrefix() + name, ValidationError::VALUE_OUT_OF_RANGE);
425 
426  if (allvals.size() > 0 && allvals.count(value) == 0)
427  use->addError(getPrefix() + name, ValidationError::VALUE_DISALLOWED);
428  }
429  if (errs == 0 && ve.getParamCount() > 0) throw ve;
430 }
431 
432 // instantiate to keep gcc 4.4 happy when compiling with opt=2
433 // (why? -- see LSST Trac ticket #1253)
434 template void Definition::validateBasic<Policy::Ptr>
435 (std::string const &, Policy::Ptr const &, int, ValidationError*) const;
436 
437 /*
438  * confirm that a Policy parameter name-value combination is consistent
439  * with this dictionary. This does not check occurrence compliance
440  * @param name the name of the parameter being checked
441  * @param value the value of the parameter to check.
442  * @exception ValidationError if the value does not conform. The message
443  * should explain why.
444  */
445 void Definition::validate(const string& name, bool value, int curcount,
446  ValidationError *errs) const
447 {
448  validateBasic<bool>(name, value, curcount, errs);
449 }
450 
451 void Definition::validate(const string& name, int value, int curcount,
452  ValidationError *errs) const
453 {
454  validateBasic<int>(name, value, curcount, errs);
455 }
456 
457 void Definition::validate(const string& name, double value, int curcount,
458  ValidationError *errs) const
459 {
460  validateBasic<double>(name, value, curcount, errs);
461 }
462 
463 void Definition::validate(const string& name, string value, int curcount,
464  ValidationError *errs) const
465 {
466  validateBasic<string>(name, value, curcount, errs);
467 }
468 
469 void Definition::validate(const string& name, const Policy& value,
470  int curcount, ValidationError *errs) const
471 {
472  validateBasic<Policy>(name, value, curcount, errs);
473  validateRecurse(name, value, errs);
474 }
475 
476 /*
477  * confirm that a Policy parameter name-array value combination is
478  * consistent with this dictionary. Unlike the scalar version,
479  * this does check occurrence compliance.
480  * @param name the name of the parameter being checked
481  * @param value the value of the parameter to check.
482  * @exception ValidationError if the value does not conform. The message
483  * should explain why.
484  */
485 void Definition::validate(const string& name, const Policy::BoolArray& value,
486  ValidationError *errs) const
487 {
488  validateBasic<bool>(name, value, errs);
489 }
490 
491 void Definition::validate(const string& name, const Policy::IntArray& value,
492  ValidationError *errs) const
493 {
494  validateBasic<int>(name, value, errs);
495 }
496 
497 void Definition::validate(const string& name, const Policy::DoubleArray& value,
498  ValidationError *errs) const
499 {
500  validateBasic<double>(name, value, errs);
501 }
502 void Definition::validate(const string& name, const Policy::StringArray& value,
503  ValidationError *errs) const
504 {
505  validateBasic<string>(name, value, errs);
506 }
507 
508 void Definition::validate(const string& name, const Policy::ConstPolicyPtrArray& value,
509  ValidationError *errs) const
510 {
511  validateBasic<Policy::ConstPtr>(name, value, errs);
512  validateRecurse(name, value, errs);
513 }
514 
515 void Definition::check() const {
516  static set<string> okayKeywords;
517  if (okayKeywords.size() == 0) {
518  okayKeywords.insert(Dictionary::KW_TYPE);
519  okayKeywords.insert(Dictionary::KW_DICT);
520  okayKeywords.insert(Dictionary::KW_DICT_FILE);
521  okayKeywords.insert(Policy::typeName[Policy::FILE]);
522  okayKeywords.insert(Dictionary::KW_MIN_OCCUR);
523  okayKeywords.insert(Dictionary::KW_MAX_OCCUR);
524  okayKeywords.insert(Dictionary::KW_MIN);
525  okayKeywords.insert(Dictionary::KW_MAX);
526  okayKeywords.insert(Dictionary::KW_ALLOWED);
527  okayKeywords.insert(Dictionary::KW_DESCRIPTION);
528  okayKeywords.insert(Dictionary::KW_DEFAULT);
529  }
530  Policy::StringArray terms = _policy->names(true);
531  for (Policy::StringArray::const_iterator i = terms.begin();
532  i != terms.end(); ++i)
533  {
534  if (okayKeywords.count(*i) == 1) continue;
535  else throw LSST_EXCEPT
536  (DictionaryError, string("Unknown Dictionary property found at ")
537  + _prefix + _name + ": " + *i);
538  }
539 }
540 
542 // Dictionary
544 
545 const char* Dictionary::KW_DICT = "dictionary";
546 const char* Dictionary::KW_DICT_FILE = "dictionaryFile";
547 const char* Dictionary::KW_TYPE = "type";
548 const char* Dictionary::KW_DESCRIPTION = "description";
549 const char* Dictionary::KW_DEFAULT = "default";
550 const char* Dictionary::KW_DEFINITIONS = "definitions";
551 const char* Dictionary::KW_CHILD_DEF = "childDefinition";
552 const char* Dictionary::KW_ALLOWED = "allowed";
553 const char* Dictionary::KW_MIN_OCCUR = "minOccurs";
554 const char* Dictionary::KW_MAX_OCCUR = "maxOccurs";
555 const char* Dictionary::KW_MIN = "min";
556 const char* Dictionary::KW_MAX = "max";
557 const char* Dictionary::KW_VALUE = "value";
558 
559 const boost::regex Dictionary::FIELDSEP_RE("\\.");
560 
561 /*
562  * load a dictionary from a file
563  */
564 Dictionary::Dictionary(const char *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 string& filePath) : Policy(filePath) {
571  if (!exists(KW_DEFINITIONS))
572  throw LSST_EXCEPT(pexExcept::RuntimeError, string(filePath)
573  + ": does not contain a Dictionary");
574  check();
575 }
576 Dictionary::Dictionary(const PolicyFile& filePath) : Policy(filePath) {
577  if (!exists(KW_DEFINITIONS))
578  throw LSST_EXCEPT(pexExcept::RuntimeError, filePath.getPath()
579  + ": does not contain a Dictionary");
580  check();
581 }
582 
583 /*
584  * return a definition for the named parameter. The caller is responsible
585  * for deleting the returned object. This is slightly more efficient than
586  * getDef().
587  * @param name the hierarchical name for the parameter
588  */
589 Definition* Dictionary::makeDef(const string& name) const {
590  Policy *p = const_cast<Dictionary*>(this);
591  Policy::Ptr sp; // sub-policy
592 
593  // split the name
594  boost::sregex_token_iterator it = boost::make_regex_token_iterator(name, FIELDSEP_RE, -1);
595  boost::sregex_token_iterator end;
596  string find;
597  bool isWildcard = false; // was the immediate parent a wildcard childDefinition?
598  while (it != end) {
599  find = *it;
600  if (! p->isPolicy(KW_DEFINITIONS))
601  throw LSST_EXCEPT(DictionaryError, "Definition for " + find
602  + " not found.");
603  sp = p->getPolicy(KW_DEFINITIONS);
604  if (sp->isPolicy(find)) {
605  sp = sp->getPolicy(find);
606  isWildcard = false; // update each time, to get only immediate parent
607  }
608  else if (sp->isPolicy(Dictionary::KW_CHILD_DEF)) {
609  if (sp->valueCount(Dictionary::KW_CHILD_DEF) > 1)
610  throw LSST_EXCEPT
611  (DictionaryError, string("Multiple ") + KW_CHILD_DEF
612  + "s found " + "that match " + getPrefix() + name + ".");
613  sp = sp->getPolicy(Dictionary::KW_CHILD_DEF);
614  isWildcard = true;
615  }
616  else throw LSST_EXCEPT(NameNotFound, find);
617  p = sp.get();
618  if (++it != end) {
619  if (! sp->isPolicy(Dictionary::KW_DICT))
620  throw LSST_EXCEPT(DictionaryError,
621  find + "." + KW_DICT + " not found.");
622  sp = sp->getPolicy(Dictionary::KW_DICT);
623  p = sp.get();
624  }
625  }
626  Definition* result = new Definition(name, sp);
627  result->setWildcard(isWildcard);
628  result->setPrefix(getPrefix());
629  return result;
630 }
631 
637 Policy::DictPtr Dictionary::getSubDictionary(const string& name) const {
638  string subname = string(KW_DEFINITIONS) + "." + name + ".dictionary";
639  if (!exists(subname)) throw LSST_EXCEPT
641  string("sub-policy \"") + subname + "\" not found.");
642  if (!isPolicy(subname)) throw LSST_EXCEPT
643  (DictionaryError, subname + " is a " + getTypeName(subname)
644  + " instead of a " + Policy::typeName[Policy::POLICY] + ".");
645  ConstPtr subpol = getPolicy(subname);
646  Policy::DictPtr result = std::make_shared<Dictionary>(*subpol);
647  result->setPrefix(_prefix + name + ".");
648  return result;
649 }
650 
651 int Dictionary::loadPolicyFiles(const boost::filesystem::path& repository, bool strict) {
652  int maxLevel = 16;
653  int result = 0;
654  // loop until we reach the leaves
655  for (int level = 0; level < maxLevel; ++level) {
656  // find the "dictionaryFile" parameters
657  list<string> params;
658  paramNames(params, false);
659  list<string> toRemove;
660  for (list<string>::const_iterator ni=params.begin(); ni != params.end(); ++ni) {
661  // loop over the keys that end with ".dictionaryFile"
662  static string endswith = string(".") + KW_DICT_FILE;
663  size_t p = ni->rfind(endswith);
664  if (p == ni->length()-endswith.length()) {
665  string parent = ni->substr(0, p);
666  Policy::Ptr defin = getPolicy(parent);
667 
668  // these will get dereferenced with the call to super method
669  if (isFile(*ni))
670  defin->set(Dictionary::KW_DICT, getFile(*ni));
671  else
672  defin->set(Dictionary::KW_DICT,
673  std::make_shared<PolicyFile>(getString(*ni)));
674 
675  toRemove.push_back(*ni);
676  }
677  }
678 
679  // if no new loads, then we've loaded everything
680  int newLoads = Policy::loadPolicyFiles(repository, strict);
681 
682  // remove obsolete dictionaryFile references, to prevent re-loading
683  for (list<string>::iterator i = toRemove.begin(); i != toRemove.end(); ++i)
684  remove(*i);
685 
686  if (newLoads == 0) {
687  check(); // validate self after everything is loaded
688  return result;
689  }
690  else result += newLoads;
691  }
692  throw LSST_EXCEPT
693  (DictionaryError, string("Exceeded recursion limit (")
694  + std::to_string(maxLevel)
695  + ") loading policy files; does this dictionary contain a circular"
696  " definition?");
697 }
698 
703 void Dictionary::check() const {
704  PolicyPtrArray defs = getValueArray<Policy::Ptr>(KW_DEFINITIONS);
705  if (defs.size() == 0)
706  throw LSST_EXCEPT(DictionaryError,
707  string("no \"") + KW_DEFINITIONS + "\" section found");
708  if (defs.size() > 1)
709  throw LSST_EXCEPT
710  (DictionaryError, string("expected a single \"") + KW_DEFINITIONS
711  + "\" section; found " + std::to_string(defs.size()));
712 
713  Policy::StringArray names = defs[0]->names(true);
714  for (Policy::StringArray::const_iterator i = names.begin();
715  i != names.end(); ++i)
716  {
717  std::unique_ptr<Definition> def(makeDef(*i));
718  def->check();
719 
720  if (hasSubDictionary(*i)) {
721  // if the subdict is a policy file, skip it -- it will have to be
722  // checked later, when it is loaded
723  ConstPtr subPol = defs[0]->getPolicy(*i);
724  if (subPol->getValueType(KW_DICT) != Policy::FILE)
725  getSubDictionary(*i)->check();
726  // TODO: test that loading a subdict from a reference gets re-checked
727  }
728  }
729 }
730 
731 /*
732  * validate a Policy against this Dictionary
733  */
734 void Dictionary::validate(const Policy& pol, ValidationError *errs) const {
735  ValidationError ve(LSST_EXCEPT_HERE);
736  ValidationError *use = &ve;
737  if (errs != 0) use = errs;
738  Policy::StringArray params = pol.names(true);
739 
740  // validate each item in policy
741  for (Policy::StringArray::const_iterator i = params.begin();
742  i != params.end(); ++i)
743  {
744  try {
745  std::unique_ptr<Definition> def(makeDef(*i));
746  def->validate(pol, *i, use);
747  }
748  catch (NameNotFound& e) {
749  use->addError(getPrefix() + *i, ValidationError::UNKNOWN_NAME);
750  }
751  }
752 
753  // check definitions of missing elements for required elements
754  Policy::ConstPtr defs = getDefinitions();
755  Policy::StringArray dn = defs->names(true);
756  for (Policy::StringArray::const_iterator i = dn.begin(); i != dn.end(); ++i) {
757  const string& name = *i;
758  if (!pol.exists(name)) { // item in dictionary, but not in policy
759  std::unique_ptr<Definition> def(makeDef(name));
760  if (name != Dictionary::KW_CHILD_DEF && def->getMinOccurs() > 0)
761  use->addError(getPrefix() + name,
762  ValidationError::MISSING_REQUIRED);
763  }
764  }
765 
766  if (errs == 0 && ve.getParamCount() > 0) throw ve;
767 }
768 
769 //@endcond
770 
771 } // namespace policy
772 } // namespace pex
773 } // namespace lsst
std::vector< int > IntArray
Definition: Policy.h:179
std::shared_ptr< PolicyFile > FilePtr
Definition: Policy.h:176
std::shared_ptr< Policy > Ptr
Definition: Policy.h:172
std::vector< Ptr > PolicyPtrArray
Definition: Policy.h:182
definition of the PolicyFile class
T rfind(T... args)
T to_string(T... args)
STL namespace.
T end(T... args)
static const char *const typeName[]
c-string forms for the supported value types.
Definition: Policy.h:205
std::shared_ptr< Dictionary > DictPtr
Definition: Policy.h:174
STL class.
T min(T... args)
T push_back(T... args)
bool exists() const
return true if the file exists.
Definition: PolicyFile.h:181
std::vector< std::string > StringArray
Definition: Policy.h:181
PolicyFile(const std::string &filepath, const SupportedFormats::Ptr &fmts=defaultFormats)
create a Policy file that points a file with given path.
T str(T... args)
STL class.
T max(T... args)
T count(T... args)
T insert(T... args)
T find(T... args)
T size(T... args)
STL class.
#define LSST_EXCEPT(type,...)
STL class.
STL class.
std::vector< double > DoubleArray
Definition: Policy.h:180
T begin(T... args)
T c_str(T... args)
static ValueType getTypeByName(const std::string &name)
Given the human-readable name of a type ("bool", "int", "policy", etc), what is its ValueType (BOOL...
#define POL_EXCEPT_VIRTFUNCS(etn)
Definition: exceptions.h:39
definition of the Dictionary class
int loadPolicyFiles(bool strict=true)
Recursively replace all PolicyFile values with the contents of the files they refer to...
Definition: Policy.h:775
std::vector< bool > BoolArray
Definition: Policy.h:178
std::shared_ptr< const Policy > ConstPtr
Definition: Policy.h:173
std::vector< ConstPtr > ConstPolicyPtrArray
Definition: Policy.h:184
ValueType
an enumeration for the supported policy types
Definition: Policy.h:189