#ifndef ARABICA_XSLT_VALUE_VALIDATION_HPP #define ARABICA_XSLT_VALUE_VALIDATION_HPP namespace Arabica { namespace XSLT { template class AttributeValidatorsBuilder; template class AttributeValidators; template class AllowedValues { public: AllowedValues() { } // AllowedValues AllowedValues(const string_type& v1, const string_type& v2) { allowed_.insert(v1); allowed_.insert(v2); } // AllowedValues AllowedValues(const AllowedValues& rhs) : allowed_(rhs.allowed_) { } // AllowedValues bool is_allowed(const string_type& v) const { if(allowed_.size() == 0) return true; return allowed_.find(v) != allowed_.end(); } // is_allowed AllowedValues& operator=(const AllowedValues& rhs) { allowed_ = rhs.allowed_; return *this; } // operator= private: std::set allowed_; }; // AllowedValues template class AttributeValidator { public: AttributeValidator() : required_(false) { } // AttributeValidator bool mandatory() const { return required_; } bool has_default() const { return !def_.empty(); } const string_type& default_value() const { return def_; } bool is_allowed(const string_type& v) const { return allowed_.is_allowed(v); } private: explicit AttributeValidator(bool required) : required_(required) { } // AttributeValidator AttributeValidator(bool required, const string_type& def, const AllowedValues allowed) : required_(required), def_(def), allowed_(allowed) { } // AttributeValidator bool required_; string_type def_; AllowedValues allowed_; AttributeValidator& operator=(const AttributeValidator& rhs) { required_ = rhs.required_; def_ = rhs.def_; allowed_ = rhs.allowed_; return *this; } // operator= friend class AttributeValidators; }; // class AttributeValidator template class AttributeValidators { typedef StylesheetConstant SC; public: static AttributeValidatorsBuilder rule(const string_type& name, bool required); std::map gather(const string_type& parentElement, const SAX::Attributes& atts) const { typedef StylesheetConstant SC; std::map results; for(ValidatorMapIterator r = rules_.begin(); r != rules_.end(); ++r) { const string_type& name = r->first; const AttributeValidator& av = r->second; if((av.mandatory()) && (atts.getValue(name).empty())) throw SAX::SAXException(string_adaptor::asStdString(parentElement) + ": Attribute " + string_adaptor::asStdString(name) + " must be specified"); if(av.has_default()) results[name] = av.default_value(); } // for ... for(int a = 0; a < atts.getLength(); ++a) { const string_type& name = atts.getLocalName(a); if(name == string_adaptor::empty_string()) continue; // namespace decl const string_type& uri = atts.getURI(a); const string_type& value = atts.getValue(a); results[name] = value; if(uri == string_adaptor::empty_string()) validateAttribute(parentElement, name, value); else if(uri == SC::xml_uri) validateXmlAttribute(parentElement, name, value); // special xml: attributes } // for ... return results; } // gather private: void validateXmlAttribute(const string_type& parentElement, const string_type& name, const string_type& value) const { if(name == SC::space) validateValues(parentElement, name, value, XMLSpaceValidator); } // validateXmlAttribute void validateAttribute(const string_type& parentElement, const string_type& name, const string_type& value) const { ValidatorMapIterator r = rules_.find(name); if(r == rules_.end()) throw SAX::SAXException(string_adaptor::asStdString(parentElement) + ": Illegal attribute " + string_adaptor::asStdString(name)); validateValues(parentElement, name, value, r->second); } void validateValues(const string_type& parentElement, const string_type& name, const string_type& value, const AttributeValidator& rule) const { if(rule.is_allowed(value)) return; throw SAX::SAXException(string_adaptor::asStdString(value) + " is not an allowed value for " + string_adaptor::asStdString(parentElement) + "/@ " + string_adaptor::asStdString(name)); } // validateValues void put(const string_type& name, bool required) { rules_[name] = AttributeValidator(required); } // put AttributeValidators() { } typedef std::map > ValidatorMap; typedef typename ValidatorMap::const_iterator ValidatorMapIterator; ValidatorMap rules_; static const AttributeValidator XMLSpaceValidator; friend class AttributeValidatorsBuilder; }; // class AttributeValidators template const AttributeValidator AttributeValidators::XMLSpaceValidator(false, string_adaptor::empty_string(), AllowedValues(string_adaptor::construct_from_utf8("default"), string_adaptor::construct_from_utf8("preserve"))); template class AttributeValidatorsBuilder { public: operator AttributeValidators&() { return validators_; } AttributeValidatorsBuilder& rule(const string_type& name, bool required) { validators_.put(name, required); return *this; } // rule private: AttributeValidatorsBuilder() { } AttributeValidatorsBuilder(const AttributeValidatorsBuilder& rhs) : validators_(rhs.validators_) { } // AttributeValidatorsBuilder AttributeValidators validators_; friend class AttributeValidators; }; // class AttributeValidatorsBuilder template AttributeValidatorsBuilder AttributeValidators::rule(const string_type& name, bool required) { AttributeValidatorsBuilder builder; builder.rule(name, required); return builder; } // AttributeValidator::rule //// template struct ValueRule { const string_type name; bool mandatory; const string_type default_value; const string_type* allowed; }; // struct ValueRule template void validateValues(const string_type& parentElement, const string_type& name, const string_type& value, const string_type allowed[]) { for(const string_type* a = allowed; *a != string_adaptor::empty_string(); ++a) if(value == *a) return; std::ostringstream os; os << string_adaptor::asStdString(parentElement) + ": " + string_adaptor::asStdString(name) + " may be one of "; while(*allowed != string_adaptor::empty_string()) os << '\'' << string_adaptor::asStdString(*allowed++) << "' "; throw SAX::SAXException(os.str()); } // validateValues template void validateXmlAttribute(const string_type& parentElement, const string_type& name, const string_type& value, std::map& results) { typedef StylesheetConstant SC; results[name] = value; if(name == SC::space) validateValues(parentElement, name, value, SC::DefaultPreserve); } // validateXmlAttribute template void validateAttribute(const string_type& parentElement, const string_type& name, const string_type& value, const ValueRule* rules, std::map& results) { while((rules->name != string_adaptor::empty_string()) && (name != rules->name)) ++rules; if(rules->name == string_adaptor::empty_string()) throw SAX::SAXException(string_adaptor::asStdString(parentElement) + ": Illegal attribute " + string_adaptor::asStdString(name)); results[name] = value; if(rules->allowed != 0) validateValues(parentElement, name, value, rules->allowed); } // validateAttribute template std::map gatherAttributes(const string_type& parentElement, const SAX::Attributes& atts, const ValueRule* rules) { typedef StylesheetConstant SC; std::map results; for(const ValueRule* r = rules ; r->name != string_adaptor::empty_string(); ++r) { if((r->mandatory) && (atts.getValue(r->name).empty())) throw SAX::SAXException(string_adaptor::asStdString(parentElement) + ": Attribute " + string_adaptor::asStdString(r->name) + " must be specified"); if(r->default_value != string_adaptor::empty_string()) results[r->name] = r->default_value; } // for(int a = 0; a < atts.getLength(); ++a) { if(atts.getLocalName(a) == string_adaptor::empty_string()) continue; // namespace decl if(atts.getURI(a) == SC::xml_uri) { validateXmlAttribute(parentElement, atts.getLocalName(a), atts.getValue(a), results); // special xml: attributes continue; } if(atts.getURI(a) == string_adaptor::empty_string()) validateAttribute(parentElement, atts.getLocalName(a), atts.getValue(a), rules, results); } return results; } // validateAttributes template void verifyNoCharacterData(const string_type& ch, const string_type& name) { for(typename string_type::const_iterator i = ch.begin(), e = ch.end(); i != e; ++i) if(!Arabica::XML::is_space(*i)) throw SAX::SAXException(string_adaptor::asStdString(name) + " element can not contain character data."); } // verifyNoCharacterContent } // namespace XSLT } // namespace Arabica #endif