30 #include <boost/scoped_ptr.hpp>
31 #include <boost/make_shared.hpp>
32 #include <boost/lexical_cast.hpp>
33 #include <boost/regex.hpp>
40 namespace pexExcept = lsst::pex::exceptions;
50 const string ValidationError::EMPTY;
52 ValidationError::MsgLookup ValidationError::_errmsgs;
56 void ValidationError::_loadMessages() {
58 _errmsgs[WRONG_TYPE] =
"value has the incorrect type";
59 _errmsgs[MISSING_REQUIRED] =
"no value available for required parameter";
60 _errmsgs[NOT_AN_ARRAY] =
"value is not an array as required";
61 _errmsgs[ARRAY_TOO_SHORT] =
"insufficient number of array values";
62 _errmsgs[TOO_FEW_VALUES] =
"not enough values for parameter";
63 _errmsgs[TOO_MANY_VALUES] =
"too many values provided for parameter";
64 _errmsgs[WRONG_OCCURRENCE_COUNT]=
"incorrect number of values for parameter";
65 _errmsgs[VALUE_DISALLOWED] =
"value is not among defined set";
66 _errmsgs[VALUE_OUT_OF_RANGE] =
"value is out of range";
67 _errmsgs[BAD_VALUE] =
"illegal value";
68 _errmsgs[UNKNOWN_NAME] =
"parameter name is unknown";
69 _errmsgs[BAD_DEFINITION] =
"malformed definition";
70 _errmsgs[NOT_LOADED] =
"file not loaded"
71 " -- call Policy.loadPolicyFiles() before validating";
72 _errmsgs[UNKNOWN_ERROR] =
"unknown error";
75 vector<string> ValidationError::getParamNames()
const {
76 vector<string> result;
77 ParamLookup::const_iterator i;
78 for (i = _errors.begin(); i != _errors.end(); ++i)
79 result.push_back(i->first);
83 string ValidationError::describe(
string prefix)
const {
87 for (list<string>::const_iterator i = names.begin(); i != names.end(); ++i)
88 os << prefix << *i <<
": "
89 << getErrorMessageFor((ErrorType)getErrors(*i)) << endl;
93 char const* ValidationError::what(
void)
const throw() {
98 int n = getParamCount();
99 os <<
"Validation error";
100 if (n == 1) os <<
" (1 error)";
101 else if (n > 1) os <<
" (" << getParamCount() <<
" errors)";
102 if (getParamCount() == 0)
103 os <<
": no errors" <<
"\n";
105 os <<
": \n" << describe(
" * ");
108 return buffer.c_str();
111 ValidationError::~ValidationError() throw() { }
117 Definition::~Definition() { }
120 if (_policy->isString(Dictionary::KW_TYPE)) {
121 const string& type = _policy->getString(Dictionary::KW_TYPE);
125 }
catch(BadNameError&) {
127 (DictionaryError,
string(
"Unknown type: \"") + type +
"\".");
130 throw LSST_EXCEPT(DictionaryError,
string(
"Illegal type: \"") + type
135 else if (_policy->exists(Dictionary::KW_TYPE)) {
137 (DictionaryError,
string(
"Expected string for \"type\"; found ")
138 + _policy->getTypeName(Dictionary::KW_TYPE) +
" instead.");
148 const string Definition::getDescription()
const {
149 if (_policy->exists(Dictionary::KW_DESCRIPTION))
150 return _policy->getString(Dictionary::KW_DESCRIPTION);
158 const int Definition::getMaxOccurs()
const {
159 if (_policy->exists(Dictionary::KW_MAX_OCCUR))
160 return _policy->getInt(Dictionary::KW_MAX_OCCUR);
168 const int Definition::getMinOccurs()
const {
169 if (_policy->exists(Dictionary::KW_MIN_OCCUR))
170 return _policy->getInt(Dictionary::KW_MIN_OCCUR);
182 void Definition::setDefaultIn(Policy& policy,
const string& withName,
183 ValidationError* errs)
const
185 if (! _policy->exists(
"default"))
return;
194 setDefaultIn<bool>(policy, withName, errs);
196 setDefaultIn<int>(policy, withName, errs);
198 setDefaultIn<double>(policy, withName, errs);
200 setDefaultIn<string>(policy, withName, errs);
202 setDefaultIn<Policy::Ptr>(policy, withName, errs);
204 string(
"Programmer Error: Unknown type for \"")
205 + getPrefix() + withName +
"\": "
217 void Definition::validate(
const Policy& policy,
const string&
name,
218 ValidationError *errs)
const
221 ValidationError *use = &ve;
222 if (errs != 0) use = errs;
224 if (! policy.exists(name)) {
225 if (getMinOccurs() > 0)
226 use->addError(getPrefix() + name, ValidationError::MISSING_REQUIRED);
235 validateBasic<bool>(
name, policy, use);
239 validateBasic<int>(
name, policy, use);
243 validateBasic<double>(
name, policy, use);
247 validateBasic<string>(
name, policy, use);
251 validateBasic<Policy::ConstPtr>(
name, policy, use);
252 validateRecurse(name, policy.getConstPolicyArray(name), use);
256 use->addError(getPrefix() + name, ValidationError::NOT_LOADED);
261 string(
"Unknown type for \"") + getPrefix() + name
262 +
"\": \"" + policy.getTypeName(name) +
"\"");
265 if (errs == 0 && ve.getParamCount() > 0)
throw ve;
275 void Definition::validateCount(
const string& name,
int count,
276 ValidationError *errs)
const {
277 int max = getMaxOccurs();
278 if (max >= 0 && count > max)
279 errs->addError(getPrefix() + name, ValidationError::TOO_MANY_VALUES);
280 if (count < getMinOccurs()) {
282 errs->addError(getPrefix() + name, ValidationError::MISSING_REQUIRED);
284 errs->addError(getPrefix() + name, ValidationError::NOT_AN_ARRAY);
286 errs->addError(getPrefix() + name, ValidationError::ARRAY_TOO_SHORT);
295 bool operator<(
const Policy& a,
const Policy&
b) {
return true; }
299 void Definition::validateRecurse(
const string& name,
301 ValidationError *errs)
const
303 for (Policy::ConstPolicyPtrArray::const_iterator i = value.begin();
304 i != value.end(); ++i) {
306 validateRecurse(name, *p, errs);
311 void Definition::validateRecurse(
const string& name,
const Policy& value,
312 ValidationError *errs)
const
316 (pexExcept::LogicError,
string(
"Wrong type: expected ")
318 +
" but found " + getTypeName() +
". // recurse if we have a sub-definition");
320 else if (_policy->exists(Dictionary::KW_DICT)) {
321 if (!_policy->isPolicy(Dictionary::KW_DICT))
323 (DictionaryError,
string(
"Wrong type for ") + getPrefix() + name
324 +
" \"" + Dictionary::KW_DICT +
"\": expected Policy, but found "
325 + _policy->getTypeName(Dictionary::KW_DICT) +
".");
327 Dictionary subdict(*(_policy->getPolicy(Dictionary::KW_DICT)));
328 subdict.setPrefix(_prefix + name +
".");
329 subdict.validate(value, errs);
333 else if (_policy->exists(Dictionary::KW_DICT_FILE)) {
335 (pexExcept::LogicError, _prefix + name
336 +
"." + Dictionary::KW_DICT_FILE +
" needs to be loaded with "
337 "Dictionary.loadPolicyFiles() before validating.");
346 void Definition::validateBasic(
const string& name,
const T& value,
347 int curcount, ValidationError *errs)
const
350 ValidationError *use = &ve;
351 if (errs != 0) use = errs;
355 int maxOccurs = getMaxOccurs();
356 if (maxOccurs >= 0 && curcount + 1 > maxOccurs)
357 use->addError(getPrefix() + name, ValidationError::TOO_MANY_VALUES);
360 if (getType() !=
Policy::UNDEF && getType() != Policy::getValueType<T>()) {
361 use->addError(getPrefix() + name, ValidationError::WRONG_TYPE);
363 else if (_policy->isPolicy(Dictionary::KW_ALLOWED)) {
365 = _policy->getPolicyArray(Dictionary::KW_ALLOWED);
368 bool minFound =
false, maxFound =
false;
371 for (Policy::PolicyPtrArray::const_iterator it = allowed.begin();
372 it != allowed.end(); ++it)
375 if (a->exists(Dictionary::KW_MIN)) {
381 string(
"Min value for ") + getPrefix() + name
382 +
" (" + boost::lexical_cast<string>(min)
383 +
") already specified; additional value not allowed.");
386 min = a->getValue<T>(Dictionary::KW_MIN);
388 }
catch(TypeError& e) {
391 string(
"Wrong type for ") + getPrefix() + name
392 +
" min value: expected " + getTypeName() +
", found "
393 + a->getTypeName(Dictionary::KW_MIN) +
".");
398 if (a->exists(Dictionary::KW_MAX)) {
402 string(
"Max value for ") + getPrefix() + name
403 +
" (" + boost::lexical_cast<string>(max)
404 +
") already specified; additional value not allowed.");
406 max = a->getValue<T>(Dictionary::KW_MAX);
408 }
catch(TypeError& e) {
411 string(
"Wrong type for ") + getPrefix() + name
412 +
" max value: expected " + getTypeName() +
", found "
413 + a->getTypeName(Dictionary::KW_MAX) +
".");
416 if (a->exists(Dictionary::KW_VALUE)) {
417 const T& value = a->getValue<T>(Dictionary::KW_VALUE);
419 allvals.insert(value);
421 vector<T> values = a->getValueArray<T>(Dictionary::KW_VALUE);
422 for (
typename vector<T>::const_iterator vi = values.begin();
423 vi != values.end(); ++vi)
428 if ((minFound && value < min) || (maxFound && max < value))
429 use->addError(getPrefix() + name, ValidationError::VALUE_OUT_OF_RANGE);
431 if (allvals.size() > 0 && allvals.count(value) == 0)
432 use->addError(getPrefix() + name, ValidationError::VALUE_DISALLOWED);
434 if (errs == 0 && ve.getParamCount() > 0)
throw ve;
439 template void Definition::validateBasic<Policy::Ptr>
440 (std::string
const &,
Policy::Ptr const &, int, ValidationError*)
const;
450 void Definition::validate(
const string& name,
bool value,
int curcount,
451 ValidationError *errs)
const
453 validateBasic<bool>(
name, value, curcount, errs);
456 void Definition::validate(
const string& name,
int value,
int curcount,
457 ValidationError *errs)
const
459 validateBasic<int>(
name, value, curcount, errs);
462 void Definition::validate(
const string& name,
double value,
int curcount,
463 ValidationError *errs)
const
465 validateBasic<double>(
name, value, curcount, errs);
468 void Definition::validate(
const string& name,
string value,
int curcount,
469 ValidationError *errs)
const
471 validateBasic<string>(
name, value, curcount, errs);
474 void Definition::validate(
const string& name,
const Policy& value,
475 int curcount, ValidationError *errs)
const
477 validateBasic<Policy>(
name, value, curcount, errs);
478 validateRecurse(name, value, errs);
491 ValidationError *errs)
const
493 validateBasic<bool>(
name, value, errs);
496 void Definition::validate(
const string& name,
const Policy::IntArray& value,
497 ValidationError *errs)
const
499 validateBasic<int>(
name, value, errs);
503 ValidationError *errs)
const
505 validateBasic<double>(
name, value, errs);
508 ValidationError *errs)
const
510 validateBasic<string>(
name, value, errs);
514 ValidationError *errs)
const
516 validateBasic<Policy::ConstPtr>(
name, value, errs);
517 validateRecurse(name, value, errs);
520 void Definition::check()
const {
521 static set<string> okayKeywords;
522 if (okayKeywords.size() == 0) {
523 okayKeywords.insert(Dictionary::KW_TYPE);
524 okayKeywords.insert(Dictionary::KW_DICT);
525 okayKeywords.insert(Dictionary::KW_DICT_FILE);
527 okayKeywords.insert(Dictionary::KW_MIN_OCCUR);
528 okayKeywords.insert(Dictionary::KW_MAX_OCCUR);
529 okayKeywords.insert(Dictionary::KW_MIN);
530 okayKeywords.insert(Dictionary::KW_MAX);
531 okayKeywords.insert(Dictionary::KW_ALLOWED);
532 okayKeywords.insert(Dictionary::KW_DESCRIPTION);
533 okayKeywords.insert(Dictionary::KW_DEFAULT);
536 for (Policy::StringArray::const_iterator i = terms.begin();
537 i != terms.end(); ++i)
539 if (okayKeywords.count(*i) == 1)
continue;
541 (DictionaryError,
string(
"Unknown Dictionary property found at ")
542 + _prefix +
_name +
": " + *i);
550 const char* Dictionary::KW_DICT =
"dictionary";
551 const char* Dictionary::KW_DICT_FILE =
"dictionaryFile";
552 const char* Dictionary::KW_TYPE =
"type";
553 const char* Dictionary::KW_DESCRIPTION =
"description";
554 const char* Dictionary::KW_DEFAULT =
"default";
555 const char* Dictionary::KW_DEFINITIONS =
"definitions";
556 const char* Dictionary::KW_CHILD_DEF =
"childDefinition";
557 const char* Dictionary::KW_ALLOWED =
"allowed";
558 const char* Dictionary::KW_MIN_OCCUR =
"minOccurs";
559 const char* Dictionary::KW_MAX_OCCUR =
"maxOccurs";
560 const char* Dictionary::KW_MIN =
"min";
561 const char* Dictionary::KW_MAX =
"max";
562 const char* Dictionary::KW_VALUE =
"value";
564 const boost::regex Dictionary::FIELDSEP_RE(
"\\.");
569 Dictionary::Dictionary(
const char *filePath) : Policy(filePath) {
570 if (!
exists(KW_DEFINITIONS))
571 throw LSST_EXCEPT(pexExcept::RuntimeError,
string(filePath)
572 +
": does not contain a Dictionary");
575 Dictionary::Dictionary(
const string& filePath) : Policy(filePath) {
576 if (!
exists(KW_DEFINITIONS))
577 throw LSST_EXCEPT(pexExcept::RuntimeError,
string(filePath)
578 +
": does not contain a Dictionary");
581 Dictionary::Dictionary(
const PolicyFile& filePath) : Policy(filePath) {
582 if (!
exists(KW_DEFINITIONS))
583 throw LSST_EXCEPT(pexExcept::RuntimeError, filePath.getPath()
584 +
": does not contain a Dictionary");
594 Definition* Dictionary::makeDef(
const string& name)
const {
595 Policy *p =
const_cast<Dictionary*
>(
this);
599 boost::sregex_token_iterator it = boost::make_regex_token_iterator(name, FIELDSEP_RE, -1);
600 boost::sregex_token_iterator end;
602 bool isWildcard =
false;
605 if (! p->isPolicy(KW_DEFINITIONS))
606 throw LSST_EXCEPT(DictionaryError,
"Definition for " + find
608 sp = p->getPolicy(KW_DEFINITIONS);
609 if (sp->isPolicy(find)) {
610 sp = sp->getPolicy(find);
613 else if (sp->isPolicy(Dictionary::KW_CHILD_DEF)) {
614 if (sp->valueCount(Dictionary::KW_CHILD_DEF) > 1)
616 (DictionaryError,
string(
"Multiple ") + KW_CHILD_DEF
617 +
"s found " +
"that match " + getPrefix() + name +
".");
618 sp = sp->getPolicy(Dictionary::KW_CHILD_DEF);
624 if (! sp->isPolicy(Dictionary::KW_DICT))
626 find +
"." + KW_DICT +
" not found.");
627 sp = sp->getPolicy(Dictionary::KW_DICT);
631 Definition* result =
new Definition(name, sp);
632 result->setWildcard(isWildcard);
633 result->setPrefix(getPrefix());
642 Policy::DictPtr Dictionary::getSubDictionary(
const string& name)
const {
643 string subname = string(KW_DEFINITIONS) +
"." + name +
".dictionary";
645 (pexExcept::LogicError,
646 string(
"sub-policy \"") + subname +
"\" not found.");
648 (DictionaryError, subname +
" is a " + getTypeName(subname)
650 ConstPtr subpol = getPolicy(subname);
652 result->setPrefix(_prefix + name +
".");
656 int Dictionary::loadPolicyFiles(
const boost::filesystem::path& repository,
bool strict) {
660 for (
int level = 0; level < maxLevel; ++level) {
663 paramNames(params,
false);
664 list<string> toRemove;
665 for (list<string>::const_iterator ni=params.begin(); ni != params.end(); ++ni) {
667 static string endswith = string(
".") + KW_DICT_FILE;
668 size_t p = ni->rfind(endswith);
669 if (p == ni->length()-endswith.length()) {
670 string parent = ni->substr(0, p);
675 defin->set(Dictionary::KW_DICT, getFile(*ni));
677 defin->set(Dictionary::KW_DICT,
678 boost::make_shared<PolicyFile>(getString(*ni)));
680 toRemove.push_back(*ni);
688 for (list<string>::iterator i = toRemove.begin(); i != toRemove.end(); ++i)
695 else result += newLoads;
698 (DictionaryError,
string(
"Exceeded recursion limit (")
699 + boost::lexical_cast<string>(maxLevel)
700 +
") loading policy files; does this dictionary contain a circular"
708 void Dictionary::check()
const {
709 PolicyPtrArray defs = getValueArray<Policy::Ptr>(KW_DEFINITIONS);
710 if (defs.size() == 0)
712 string(
"no \"") + KW_DEFINITIONS +
"\" section found");
715 (DictionaryError,
string(
"expected a single \"") + KW_DEFINITIONS
716 +
"\" section; found " + boost::lexical_cast<
string>(defs.size()));
719 for (Policy::StringArray::const_iterator i = names.begin();
720 i != names.end(); ++i)
722 boost::scoped_ptr<Definition> def(makeDef(*i));
725 if (hasSubDictionary(*i)) {
728 ConstPtr subPol = defs[0]->getPolicy(*i);
730 getSubDictionary(*i)->check();
739 void Dictionary::validate(
const Policy& pol, ValidationError *errs)
const {
741 ValidationError *use = &ve;
742 if (errs != 0) use = errs;
746 for (Policy::StringArray::const_iterator i = params.begin();
747 i != params.end(); ++i)
750 boost::scoped_ptr<Definition> def(makeDef(*i));
751 def->validate(pol, *i, use);
753 catch (NameNotFound& e) {
754 use->addError(getPrefix() + *i, ValidationError::UNKNOWN_NAME);
761 for (Policy::StringArray::const_iterator i = dn.begin(); i != dn.end(); ++i) {
762 const string& name = *i;
763 if (!pol.exists(name)) {
764 boost::scoped_ptr<Definition> def(makeDef(name));
765 if (name != Dictionary::KW_CHILD_DEF && def->getMinOccurs() > 0)
766 use->addError(getPrefix() + name,
767 ValidationError::MISSING_REQUIRED);
771 if (errs == 0 && ve.getParamCount() > 0)
throw ve;
std::vector< int > IntArray
std::vector< Ptr > PolicyPtrArray
definition of the PolicyFile class
table::Key< std::string > name
boost::shared_ptr< PolicyFile > FilePtr
std::string const & _name
boost::shared_ptr< const Policy > ConstPtr
boost::shared_ptr< Policy > Ptr
boost::shared_ptr< Dictionary > DictPtr
static const char *const typeName[]
#define POL_EXCEPT_VIRTFUNCS(etn)
std::vector< std::string > StringArray
#define LSST_EXCEPT(type,...)
std::vector< double > DoubleArray
static ValueType getTypeByName(const std::string &name)
definition of the Dictionary class
afw::table::Key< double > b
int loadPolicyFiles(bool strict=true)
std::vector< bool > BoolArray
std::vector< ConstPtr > ConstPolicyPtrArray