#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 string_type& v1, const string_type& v2, const string_type& v3) { allowed_.insert(v1); allowed_.insert(v2); allowed_.insert(v3); } // 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 AllowedValues makeAllowedValues(const string_type& a1, const string_type& a2) { return AllowedValues(a1, a2); } // makeAllowedValues template AllowedValues makeAllowedValues(const string_type& a1, const string_type& a2, const string_type& a3) { return AllowedValues(a1, a2, a3); } // makeAllowedValues template class AttributeValidator { public: AttributeValidator() : required_(false), has_def_(false) { } // AttributeValidator bool mandatory() const { return required_; } bool has_default() const { return has_def_; } 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), has_def_(false) { } // AttributeValidator AttributeValidator(bool required, const string_type& def) : required_(required), def_(def), has_def_(true) { } // AttributeValidator AttributeValidator(bool required, const string_type& def, const AllowedValues allowed) : required_(required), def_(def), has_def_(true), allowed_(allowed) { } // AttributeValidator bool required_; bool has_def_; string_type def_; AllowedValues allowed_; AttributeValidator& operator=(const AttributeValidator& rhs) { required_ = rhs.required_; has_def_ = rhs.has_def_; def_ = rhs.def_; allowed_ = rhs.allowed_; return *this; } // operator= friend class AttributeValidators; friend class AttributeValidatorsBuilder; }; // class AttributeValidator template class AttributeValidators { typedef StylesheetConstant SC; public: static AttributeValidatorsBuilder rule(const string_type& name, bool required); static AttributeValidatorsBuilder rule(const string_type& name, bool required, const string_type& def); static AttributeValidatorsBuilder rule(const string_type& name, bool required, const string_type& def, const AllowedValues& allowed); 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()) && (string_adaptor::empty(atts.getValue(name)))) 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, const AttributeValidator& av) { rules_[name] = av; } // 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, AttributeValidator(required)); return *this; } // rule AttributeValidatorsBuilder& rule(const string_type& name, bool required, const string_type& def) { validators_.put(name, AttributeValidator(required, def)); return *this; } // rule AttributeValidatorsBuilder& rule(const string_type& name, bool required, const string_type& def, const AllowedValues& av) { validators_.put(name, AttributeValidator(required, def, av)); 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 AttributeValidatorsBuilder AttributeValidators::rule(const string_type& name, bool required, const string_type& def) { AttributeValidatorsBuilder builder; builder.rule(name, required, def); return builder; } // AttributeValidator::rule template AttributeValidatorsBuilder AttributeValidators::rule(const string_type& name, bool required, const string_type& def, const AllowedValues& allowed) { AttributeValidatorsBuilder builder; builder.rule(name, required, def, allowed); return builder; } // AttributeValidator::rule template void verifyNoCharacterData(const string_type& ch, const string_type& name) { for(typename string_adaptor::const_iterator i = string_adaptor::begin(ch), e = string_adaptor::end(ch); 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