LSSTApplications  19.0.0+10,19.0.0+80,19.0.0-1-g20d9b18+31,19.0.0-1-g49a97f9+4,19.0.0-1-g8c57eb9+31,19.0.0-1-g9a028c0+13,19.0.0-1-ga72da6b+3,19.0.0-1-gb77924a+15,19.0.0-1-gbfe0924+66,19.0.0-1-gc3516c3,19.0.0-1-gefe1d0d+49,19.0.0-1-gf8cb8b4+3,19.0.0-14-g7511ce4+6,19.0.0-16-g3dc1a33c+6,19.0.0-17-g59f0e8a+4,19.0.0-17-g9c22e3c+9,19.0.0-18-g35bb99870+2,19.0.0-19-g2772d4a+9,19.0.0-2-g260436e+53,19.0.0-2-g31cdcee,19.0.0-2-g9675b69+3,19.0.0-2-gaa2795f,19.0.0-2-gbcc4de1,19.0.0-2-gd6f004e+6,19.0.0-2-gde8e5e3+5,19.0.0-2-gff6972b+15,19.0.0-21-ge306cd8,19.0.0-21-gf122e69+2,19.0.0-3-g2d43a51+2,19.0.0-3-gf3b1435+6,19.0.0-4-g56feb96,19.0.0-4-gac56cce+17,19.0.0-49-gce872c1+1,19.0.0-5-g66946eb+6,19.0.0-5-gd8897ba+6,19.0.0-51-gfc4a647e,19.0.0-7-g686a884+5,19.0.0-7-g886f805+5,19.0.0-8-g305ff64,w.2020.17
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] =
68  "file not loaded"
69  " -- call Policy.loadPolicyFiles() before validating";
70  _errmsgs[UNKNOWN_ERROR] = "unknown error";
71 }
72 
73 vector<string> ValidationError::getParamNames() const {
75  ParamLookup::const_iterator i;
76  for (i = _errors.begin(); i != _errors.end(); ++i) 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 << ": " << getErrorMessageFor((ErrorType)getErrors(*i)) << endl;
86  return os.str();
87 }
88 
89 char const* ValidationError::what(void) const throw() {
90  // static to avoid memory issue -- but a concurrency problem?
91  // copied from pex/exceptions/src/Exception.cc
92  static string buffer;
94  int n = getParamCount();
95  os << "Validation error";
96  if (n == 1)
97  os << " (1 error)";
98  else if (n > 1)
99  os << " (" << getParamCount() << " errors)";
100  if (getParamCount() == 0)
101  os << ": no errors"
102  << "\n";
103  else {
104  os << ": \n" << describe(" * ");
105  }
106  buffer = os.str();
107  return buffer.c_str();
108 }
109 
110 ValidationError::~ValidationError() throw() {}
111 
113 // Definition
115 
116 Definition::~Definition() {}
117 
118 Policy::ValueType Definition::_determineType() const {
119  if (_policy->isString(Dictionary::KW_TYPE)) {
120  const string& type = _policy->getString(Dictionary::KW_TYPE);
122  try {
123  result = Policy::getTypeByName(type);
124  } catch (BadNameError&) {
125  throw LSST_EXCEPT(DictionaryError, string("Unknown type: \"") + type + "\".");
126  }
127  if (result == Policy::FILE)
128  throw LSST_EXCEPT(DictionaryError, string("Illegal type: \"") + type + "\"; use \"" +
129  Policy::typeName[Policy::POLICY] + "\" instead.");
130  else
131  return result;
132  } else if (_policy->exists(Dictionary::KW_TYPE)) {
133  throw LSST_EXCEPT(DictionaryError, string("Expected string for \"type\"; found ") +
134  _policy->getTypeName(Dictionary::KW_TYPE) + " instead.");
135  }
136 
137  else
138  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
149  return "";
150 }
151 
156 const int Definition::getMaxOccurs() const {
157  if (_policy->exists(Dictionary::KW_MAX_OCCUR))
158  return _policy->getInt(Dictionary::KW_MAX_OCCUR);
159  else
160  return -1;
161 }
162 
167 const int Definition::getMinOccurs() const {
168  if (_policy->exists(Dictionary::KW_MIN_OCCUR))
169  return _policy->getInt(Dictionary::KW_MIN_OCCUR);
170  else
171  return 0;
172 }
173 
174 /*
175  * the default value into the given policy
176  * @param policy the policy object update
177  * @param withName the name to look for the value under. This must be
178  * a non-hierarchical name.
179  * @exception ValidationError if the value does not conform to this definition.
180  */
181 void Definition::setDefaultIn(Policy& policy, const string& withName, ValidationError* errs) const {
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);
200  else
201  throw LSST_EXCEPT(pexExcept::LogicError, 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, ValidationError* errs) const {
215  ValidationError ve(LSST_EXCEPT_HERE);
216  ValidationError* use = &ve;
217  if (errs != 0) use = errs;
218 
219  if (!policy.exists(name)) {
220  if (getMinOccurs() > 0) use->addError(getPrefix() + name, ValidationError::MISSING_REQUIRED);
221  return;
222  }
223 
224  // What type is actually present in the policy?
225  Policy::ValueType type = policy.getValueType(name);
226 
227  switch (type) {
228  case Policy::BOOL:
229  validateBasic<bool>(name, policy, use);
230  break;
231 
232  case Policy::INT:
233  validateBasic<int>(name, policy, use);
234  break;
235 
236  case Policy::DOUBLE:
237  validateBasic<double>(name, policy, use);
238  break;
239 
240  case Policy::STRING:
241  validateBasic<string>(name, policy, use);
242  break;
243 
244  case Policy::POLICY:
245  validateBasic<Policy::ConstPtr>(name, policy, use);
246  validateRecurse(name, policy.getConstPolicyArray(name), use);
247  break;
248 
249  case Policy::FILE:
250  use->addError(getPrefix() + name, ValidationError::NOT_LOADED);
251  break;
252 
253  default:
254  throw LSST_EXCEPT(pexExcept::LogicError, string("Unknown type for \"") + getPrefix() + name +
255  "\": \"" + policy.getTypeName(name) + "\"");
256  }
257 
258  if (errs == 0 && ve.getParamCount() > 0) throw ve;
259 }
260 
268 void Definition::validateCount(const string& name, int count, ValidationError* errs) const {
269  int max = getMaxOccurs(); // -1 means no limit / undefined
270  if (max >= 0 && count > max) errs->addError(getPrefix() + name, ValidationError::TOO_MANY_VALUES);
271  if (count < getMinOccurs()) {
272  if (count == 0)
273  errs->addError(getPrefix() + name, ValidationError::MISSING_REQUIRED);
274  else if (count == 1)
275  errs->addError(getPrefix() + name, ValidationError::NOT_AN_ARRAY);
276  else
277  errs->addError(getPrefix() + name, ValidationError::ARRAY_TOO_SHORT);
278  }
279 }
280 
286 bool operator<(const Policy& a, const Policy& b) { return true; }
287 bool operator<(const Policy::ConstPtr& a, const Policy::ConstPtr& b) { return true; }
288 bool operator<(const Policy::FilePtr& a, const Policy::FilePtr& b) { return true; }
289 
290 void Definition::validateRecurse(const string& name, Policy::ConstPolicyPtrArray value,
291  ValidationError* errs) const {
292  for (Policy::ConstPolicyPtrArray::const_iterator i = value.begin(); i != value.end(); ++i) {
293  Policy::ConstPtr p = *i;
294  validateRecurse(name, *p, errs);
295  }
296 }
297 
298 /* Validate a sub-policy using a sub-dictionary. */
299 void Definition::validateRecurse(const string& name, const Policy& value, ValidationError* errs) const {
300  if (_policy->exists(Dictionary::KW_DICT)) {
301  if (!_policy->isPolicy(Dictionary::KW_DICT))
302  throw LSST_EXCEPT(DictionaryError, string("Wrong type for ") + getPrefix() + name + " \"" +
303  Dictionary::KW_DICT +
304  "\": expected Policy, but found " +
305  _policy->getTypeName(Dictionary::KW_DICT) + ".");
306  else {
307  Dictionary subdict(*(_policy->getPolicy(Dictionary::KW_DICT)));
308  subdict.setPrefix(_prefix + name + ".");
309  subdict.validate(value, errs);
310  }
311  }
312  // is there an unresolved link here?
313  else if (_policy->exists(Dictionary::KW_DICT_FILE)) {
314  throw LSST_EXCEPT(pexExcept::LogicError, _prefix + name + "." + Dictionary::KW_DICT_FILE +
315  " needs to be loaded with "
316  "Dictionary.loadPolicyFiles() before validating.");
317  }
318  // there's a policy type defined here, but no sub-definition (that is, no
319  // constraints -- it could be any policy), so anything is okay.
320  else {
321  }
322 }
323 
324 // TODO: integrate all checks into check() rather than having them in validate()
325 template <class T>
326 void Definition::validateBasic(const string& name, const T& value, int curcount,
327  ValidationError* errs) const {
328  ValidationError ve(LSST_EXCEPT_HERE);
329  ValidationError* use = &ve;
330  if (errs != 0) use = errs;
331 
332  // check if we're going to get too many
333  if (curcount >= 0) {
334  int maxOccurs = getMaxOccurs();
335  if (maxOccurs >= 0 && curcount + 1 > maxOccurs)
336  use->addError(getPrefix() + name, ValidationError::TOO_MANY_VALUES);
337  }
338 
339  if (getType() != Policy::UNDEF && getType() != Policy::getValueType<T>()) {
340  use->addError(getPrefix() + name, ValidationError::WRONG_TYPE);
341  } else if (_policy->isPolicy(Dictionary::KW_ALLOWED)) {
342  Policy::PolicyPtrArray allowed = _policy->getPolicyArray(Dictionary::KW_ALLOWED);
343 
344  T min, max;
345  bool minFound = false, maxFound = false;
346  set<T> allvals;
347  // iterate over the keys inside the "allowed" sub-policy
348  for (Policy::PolicyPtrArray::const_iterator it = allowed.begin(); it != allowed.end(); ++it) {
349  Policy::Ptr a = *it;
350  if (a->exists(Dictionary::KW_MIN)) {
351  if (minFound) {
352  // Would like to catch this in Dictionary::check(), but that
353  // would require some fancy type-based logic.
354  throw LSST_EXCEPT(DictionaryError,
355  string("Min value for ") + getPrefix() + name +
356  " already specified; additional value not allowed.");
357  }
358  try {
359  min = a->getValue<T>(Dictionary::KW_MIN);
360  minFound = true; // after min assign, in case of exceptions
361  } catch (TypeError& e) {
362  throw LSST_EXCEPT(DictionaryError, string("Wrong type for ") + getPrefix() + name +
363  " min value: expected " + getTypeName() +
364  ", found " +
365  a->getTypeName(Dictionary::KW_MIN) + ".");
366  } catch (...) {
367  throw;
368  }
369  }
370  if (a->exists(Dictionary::KW_MAX)) {
371  if (maxFound)
372  throw LSST_EXCEPT(DictionaryError,
373  string("Max value for ") + getPrefix() + name +
374  " already specified; additional value not allowed.");
375  try {
376  max = a->getValue<T>(Dictionary::KW_MAX);
377  maxFound = true; // after max assign, in case of exceptions
378  } catch (TypeError& e) {
379  throw LSST_EXCEPT(DictionaryError, string("Wrong type for ") + getPrefix() + name +
380  " max value: expected " + getTypeName() +
381  ", found " +
382  a->getTypeName(Dictionary::KW_MAX) + ".");
383  }
384  }
385  if (a->exists(Dictionary::KW_VALUE)) {
386  const T& value = a->getValue<T>(Dictionary::KW_VALUE);
387 
388  allvals.insert(value);
389  // allvals.insert(a->getValue<T>(Dictionary::KW_VALUE));
390  vector<T> values = a->getValueArray<T>(Dictionary::KW_VALUE);
391  for (typename vector<T>::const_iterator vi = values.begin(); vi != values.end(); ++vi)
392  allvals.insert(*vi);
393  }
394  }
395 
396  if ((minFound && value < min) || (maxFound && max < value))
397  use->addError(getPrefix() + name, ValidationError::VALUE_OUT_OF_RANGE);
398 
399  if (allvals.size() > 0 && allvals.count(value) == 0)
400  use->addError(getPrefix() + name, ValidationError::VALUE_DISALLOWED);
401  }
402  if (errs == 0 && ve.getParamCount() > 0) throw ve;
403 }
404 
405 // instantiate to keep gcc 4.4 happy when compiling with opt=2
406 // (why? -- see LSST Trac ticket #1253)
407 template void Definition::validateBasic<Policy::Ptr>(std::string const&, Policy::Ptr const&, int,
408  ValidationError*) const;
409 
410 /*
411  * confirm that a Policy parameter name-value combination is consistent
412  * with this dictionary. This does not check occurrence compliance
413  * @param name the name of the parameter being checked
414  * @param value the value of the parameter to check.
415  * @exception ValidationError if the value does not conform. The message
416  * should explain why.
417  */
418 void Definition::validate(const string& name, bool value, int curcount, ValidationError* errs) const {
419  validateBasic<bool>(name, value, curcount, errs);
420 }
421 
422 void Definition::validate(const string& name, int value, int curcount, ValidationError* errs) const {
423  validateBasic<int>(name, value, curcount, errs);
424 }
425 
426 void Definition::validate(const string& name, double value, int curcount, ValidationError* errs) const {
427  validateBasic<double>(name, value, curcount, errs);
428 }
429 
430 void Definition::validate(const string& name, string value, int curcount, ValidationError* errs) const {
431  validateBasic<string>(name, value, curcount, errs);
432 }
433 
434 void Definition::validate(const string& name, const Policy& value, int curcount,
435  ValidationError* errs) const {
436  validateBasic<Policy>(name, value, curcount, errs);
437  validateRecurse(name, value, errs);
438 }
439 
440 /*
441  * confirm that a Policy parameter name-array value combination is
442  * consistent with this dictionary. Unlike the scalar version,
443  * this does check occurrence compliance.
444  * @param name the name of the parameter being checked
445  * @param value the value of the parameter to check.
446  * @exception ValidationError if the value does not conform. The message
447  * should explain why.
448  */
449 void Definition::validate(const string& name, const Policy::BoolArray& value, ValidationError* errs) const {
450  validateBasic<bool>(name, value, errs);
451 }
452 
453 void Definition::validate(const string& name, const Policy::IntArray& value, ValidationError* errs) const {
454  validateBasic<int>(name, value, errs);
455 }
456 
457 void Definition::validate(const string& name, const Policy::DoubleArray& value, ValidationError* errs) const {
458  validateBasic<double>(name, value, errs);
459 }
460 void Definition::validate(const string& name, const Policy::StringArray& value, ValidationError* errs) const {
461  validateBasic<string>(name, value, errs);
462 }
463 
464 void Definition::validate(const string& name, const Policy::ConstPolicyPtrArray& value,
465  ValidationError* errs) const {
466  validateBasic<Policy::ConstPtr>(name, value, errs);
467  validateRecurse(name, value, errs);
468 }
469 
470 void Definition::check() const {
471  static set<string> okayKeywords;
472  if (okayKeywords.size() == 0) {
473  okayKeywords.insert(Dictionary::KW_TYPE);
474  okayKeywords.insert(Dictionary::KW_DICT);
475  okayKeywords.insert(Dictionary::KW_DICT_FILE);
476  okayKeywords.insert(Policy::typeName[Policy::FILE]);
477  okayKeywords.insert(Dictionary::KW_MIN_OCCUR);
478  okayKeywords.insert(Dictionary::KW_MAX_OCCUR);
479  okayKeywords.insert(Dictionary::KW_MIN);
480  okayKeywords.insert(Dictionary::KW_MAX);
481  okayKeywords.insert(Dictionary::KW_ALLOWED);
482  okayKeywords.insert(Dictionary::KW_DESCRIPTION);
483  okayKeywords.insert(Dictionary::KW_DEFAULT);
484  }
485  Policy::StringArray terms = _policy->names(true);
486  for (Policy::StringArray::const_iterator i = terms.begin(); i != terms.end(); ++i) {
487  if (okayKeywords.count(*i) == 1)
488  continue;
489  else
490  throw LSST_EXCEPT(DictionaryError,
491  string("Unknown Dictionary property found at ") + _prefix + _name + ": " + *i);
492  }
493 }
494 
496 // Dictionary
498 
499 const char* Dictionary::KW_DICT = "dictionary";
500 const char* Dictionary::KW_DICT_FILE = "dictionaryFile";
501 const char* Dictionary::KW_TYPE = "type";
502 const char* Dictionary::KW_DESCRIPTION = "description";
503 const char* Dictionary::KW_DEFAULT = "default";
504 const char* Dictionary::KW_DEFINITIONS = "definitions";
505 const char* Dictionary::KW_CHILD_DEF = "childDefinition";
506 const char* Dictionary::KW_ALLOWED = "allowed";
507 const char* Dictionary::KW_MIN_OCCUR = "minOccurs";
508 const char* Dictionary::KW_MAX_OCCUR = "maxOccurs";
509 const char* Dictionary::KW_MIN = "min";
510 const char* Dictionary::KW_MAX = "max";
511 const char* Dictionary::KW_VALUE = "value";
512 
513 const boost::regex Dictionary::FIELDSEP_RE("\\.");
514 
515 /*
516  * load a dictionary from a file
517  */
518 Dictionary::Dictionary(const char* filePath) : Policy(filePath) {
519  if (!exists(KW_DEFINITIONS))
520  throw LSST_EXCEPT(pexExcept::RuntimeError, string(filePath) + ": does not contain a Dictionary");
521  check();
522 }
523 Dictionary::Dictionary(const string& filePath) : Policy(filePath) {
524  if (!exists(KW_DEFINITIONS))
525  throw LSST_EXCEPT(pexExcept::RuntimeError, string(filePath) + ": does not contain a Dictionary");
526  check();
527 }
528 Dictionary::Dictionary(const PolicyFile& filePath) : Policy(filePath) {
529  if (!exists(KW_DEFINITIONS))
530  throw LSST_EXCEPT(pexExcept::RuntimeError, filePath.getPath() + ": does not contain a Dictionary");
531  check();
532 }
533 
534 /*
535  * return a definition for the named parameter. The caller is responsible
536  * for deleting the returned object. This is slightly more efficient than
537  * getDef().
538  * @param name the hierarchical name for the parameter
539  */
540 Definition* Dictionary::makeDef(const string& name) const {
541  Policy* p = const_cast<Dictionary*>(this);
542  Policy::Ptr sp; // sub-policy
543 
544  // split the name
545  boost::sregex_token_iterator it = boost::make_regex_token_iterator(name, FIELDSEP_RE, -1);
546  boost::sregex_token_iterator end;
547  string find;
548  bool isWildcard = false; // was the immediate parent a wildcard childDefinition?
549  while (it != end) {
550  find = *it;
551  if (!p->isPolicy(KW_DEFINITIONS))
552  throw LSST_EXCEPT(DictionaryError, "Definition for " + find + " not found.");
553  sp = p->getPolicy(KW_DEFINITIONS);
554  if (sp->isPolicy(find)) {
555  sp = sp->getPolicy(find);
556  isWildcard = false; // update each time, to get only immediate parent
557  } else if (sp->isPolicy(Dictionary::KW_CHILD_DEF)) {
558  if (sp->valueCount(Dictionary::KW_CHILD_DEF) > 1)
559  throw LSST_EXCEPT(DictionaryError, string("Multiple ") + KW_CHILD_DEF + "s found " +
560  "that match " + getPrefix() + name + ".");
561  sp = sp->getPolicy(Dictionary::KW_CHILD_DEF);
562  isWildcard = true;
563  } else
564  throw LSST_EXCEPT(NameNotFound, find);
565  p = sp.get();
566  if (++it != end) {
567  if (!sp->isPolicy(Dictionary::KW_DICT))
568  throw LSST_EXCEPT(DictionaryError, find + "." + KW_DICT + " not found.");
569  sp = sp->getPolicy(Dictionary::KW_DICT);
570  p = sp.get();
571  }
572  }
573  Definition* result = new Definition(name, sp);
574  result->setWildcard(isWildcard);
575  result->setPrefix(getPrefix());
576  return result;
577 }
578 
584 Policy::DictPtr Dictionary::getSubDictionary(const string& name) const {
585  string subname = string(KW_DEFINITIONS) + "." + name + ".dictionary";
586  if (!exists(subname))
587  throw LSST_EXCEPT(pexExcept::LogicError, string("sub-policy \"") + subname + "\" not found.");
588  if (!isPolicy(subname))
589  throw LSST_EXCEPT(DictionaryError, subname + " is a " + getTypeName(subname) + " instead of a " +
591  ConstPtr subpol = getPolicy(subname);
592  Policy::DictPtr result = std::make_shared<Dictionary>(*subpol);
593  result->setPrefix(_prefix + name + ".");
594  return result;
595 }
596 
597 int Dictionary::loadPolicyFiles(const boost::filesystem::path& repository, bool strict) {
598  int maxLevel = 16;
599  int result = 0;
600  // loop until we reach the leaves
601  for (int level = 0; level < maxLevel; ++level) {
602  // find the "dictionaryFile" parameters
603  list<string> params;
604  paramNames(params, false);
605  list<string> toRemove;
606  for (list<string>::const_iterator ni = params.begin(); ni != params.end(); ++ni) {
607  // loop over the keys that end with ".dictionaryFile"
608  static string endswith = string(".") + KW_DICT_FILE;
609  size_t p = ni->rfind(endswith);
610  if (p == ni->length() - endswith.length()) {
611  string parent = ni->substr(0, p);
612  Policy::Ptr defin = getPolicy(parent);
613 
614  // these will get dereferenced with the call to super method
615  if (isFile(*ni))
616  defin->set(Dictionary::KW_DICT, getFile(*ni));
617  else
618  defin->set(Dictionary::KW_DICT, std::make_shared<PolicyFile>(getString(*ni)));
619 
620  toRemove.push_back(*ni);
621  }
622  }
623 
624  // if no new loads, then we've loaded everything
625  int newLoads = Policy::loadPolicyFiles(repository, strict);
626 
627  // remove obsolete dictionaryFile references, to prevent re-loading
628  for (list<string>::iterator i = toRemove.begin(); i != toRemove.end(); ++i) remove(*i);
629 
630  if (newLoads == 0) {
631  check(); // validate self after everything is loaded
632  return result;
633  } else
634  result += newLoads;
635  }
636  throw LSST_EXCEPT(DictionaryError,
637  string("Exceeded recursion limit (") + std::to_string(maxLevel) +
638  ") loading policy files; does this dictionary contain a circular"
639  " definition?");
640 }
641 
646 void Dictionary::check() const {
647  PolicyPtrArray defs = getValueArray<Policy::Ptr>(KW_DEFINITIONS);
648  if (defs.size() == 0)
649  throw LSST_EXCEPT(DictionaryError, string("no \"") + KW_DEFINITIONS + "\" section found");
650  if (defs.size() > 1)
651  throw LSST_EXCEPT(DictionaryError, string("expected a single \"") + KW_DEFINITIONS +
652  "\" section; found " + std::to_string(defs.size()));
653 
654  Policy::StringArray names = defs[0]->names(true);
655  for (Policy::StringArray::const_iterator i = names.begin(); i != names.end(); ++i) {
656  std::unique_ptr<Definition> def(makeDef(*i));
657  def->check();
658 
659  if (hasSubDictionary(*i)) {
660  // if the subdict is a policy file, skip it -- it will have to be
661  // checked later, when it is loaded
662  ConstPtr subPol = defs[0]->getPolicy(*i);
663  if (subPol->getValueType(KW_DICT) != Policy::FILE) getSubDictionary(*i)->check();
664  // TODO: test that loading a subdict from a reference gets re-checked
665  }
666  }
667 }
668 
669 /*
670  * validate a Policy against this Dictionary
671  */
672 void Dictionary::validate(const Policy& pol, ValidationError* errs) const {
673  ValidationError ve(LSST_EXCEPT_HERE);
674  ValidationError* use = &ve;
675  if (errs != 0) use = errs;
676  Policy::StringArray params = pol.names(true);
677 
678  // validate each item in policy
679  for (Policy::StringArray::const_iterator i = params.begin(); i != params.end(); ++i) {
680  try {
681  std::unique_ptr<Definition> def(makeDef(*i));
682  def->validate(pol, *i, use);
683  } catch (NameNotFound& e) {
684  use->addError(getPrefix() + *i, ValidationError::UNKNOWN_NAME);
685  }
686  }
687 
688  // check definitions of missing elements for required elements
689  Policy::ConstPtr defs = getDefinitions();
690  Policy::StringArray dn = defs->names(true);
691  for (Policy::StringArray::const_iterator i = dn.begin(); i != dn.end(); ++i) {
692  const string& name = *i;
693  if (!pol.exists(name)) { // item in dictionary, but not in policy
694  std::unique_ptr<Definition> def(makeDef(name));
695  if (name != Dictionary::KW_CHILD_DEF && def->getMinOccurs() > 0)
696  use->addError(getPrefix() + name, ValidationError::MISSING_REQUIRED);
697  }
698  }
699 
700  if (errs == 0 && ve.getParamCount() > 0) throw ve;
701 }
702 
703 //@endcond
704 
705 } // namespace policy
706 } // namespace pex
707 } // namespace lsst
std::vector< int > IntArray
Definition: Policy.h:176
std::shared_ptr< PolicyFile > FilePtr
Definition: Policy.h:173
std::shared_ptr< Policy > Ptr
Definition: Policy.h:169
std::vector< Ptr > PolicyPtrArray
Definition: Policy.h:179
definition of the PolicyFile class
table::Key< int > b
T rfind(T... args)
T to_string(T... args)
table::Key< int > a
STL namespace.
T end(T... args)
#define LSST_EXCEPT_HERE
For internal use; gathers the file, line, and function for a tracepoint.
Definition: Exception.h:40
static const char *const typeName[]
c-string forms for the supported value types.
Definition: Policy.h:193
std::shared_ptr< Dictionary > DictPtr
Definition: Policy.h:171
int min
STL class.
T push_back(T... args)
A base class for image defects.
table::Key< int > type
Definition: Detector.cc:163
std::vector< std::string > StringArray
Definition: Policy.h:178
T str(T... args)
int max
Reports errors in the logical structure of the program.
Definition: Runtime.h:46
STL class.
#define POL_EXCEPT_VIRTFUNCS(etn)
Definition: exceptions.h:40
T count(T... args)
T insert(T... args)
T find(T... args)
T size(T... args)
Definition: __init__.py:1
STL class.
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition: Exception.h:48
STL class.
STL class.
std::vector< double > DoubleArray
Definition: Policy.h:177
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...
definition of the Dictionary class
std::string prefix
Definition: SchemaMapper.cc:79
int loadPolicyFiles(bool strict=true)
Recursively replace all PolicyFile values with the contents of the files they refer to...
Definition: Policy.h:747
std::vector< bool > BoolArray
Definition: Policy.h:175
std::ostream * os
Definition: Schema.cc:746
py::object result
Definition: _schema.cc:429
int end
std::shared_ptr< const Policy > ConstPtr
Definition: Policy.h:170
std::vector< ConstPtr > ConstPolicyPtrArray
Definition: Policy.h:181
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:186