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