/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Copyright 2018 Danny Robson */ #include "properties.hpp" #include "../tree.hpp" using util::json::schema::constraint::properties; using util::json::schema::constraint::pattern_properties; using util::json::schema::constraint::additional_properties; /////////////////////////////////////////////////////////////////////////////// properties::properties (::json::tree::node const &def) { if (!def.is_object ()) throw constraint_error (def); for (auto const &[key,val]: def.as_object ()) { m_properties.emplace (key, *val); } } //----------------------------------------------------------------------------- properties::output_iterator properties::validate (output_iterator res, ::json::tree::node &target) const noexcept { if (!target.is_object ()) return *res++ = { .rule = *this, .target = target }; auto &obj = target.as_object (); // validate the keys that are present against the property schemas for (const auto &[key,val]: obj) { auto const pos = m_properties.find (key); if (pos == m_properties.end ()) continue; res = pos->second.validate (res, *val); } // check if there's a key in the schema that isn't present but has a default for (auto const &[key,doc]: m_properties) { if (!doc.has_default ()) continue; auto pos = obj.find (key); if (pos == obj.end ()) obj.insert (key, doc.default_value ()); } return res; } //----------------------------------------------------------------------------- bool properties::has (std::string const &key) const noexcept { return m_properties.cend () != m_properties.find (key); } //----------------------------------------------------------------------------- std::ostream& properties::describe (std::ostream &os) const { os << "{ properties: [ "; for (auto const &[key,val]: m_properties) { os << key << ": "; val.describe (os); os << ", "; } return os << " ] }"; } /////////////////////////////////////////////////////////////////////////////// pattern_properties::pattern_properties (::json::tree::node const &def) { if (!def.is_object ()) throw constraint_error (def); for (auto const &[key,val]: def.as_object ()) { m_patterns.push_back (pattern_t { .pattern = key, .matcher = std::regex (key), .validator = *val }); } } //----------------------------------------------------------------------------- pattern_properties::output_iterator pattern_properties::validate (output_iterator res, ::json::tree::node &target) const noexcept { if (!target.is_object ()) return *res++ = { .rule = *this, .target = target }; // validate each key and value in turn for (auto &[key, val]: target.as_object ()) { // find any regex matches and forward the validation onwards for (auto const &[pattern,regex,validator]: m_patterns) { if (std::regex_search (key, regex)) { res = validator.validate (res, *val); } } } return res; } //----------------------------------------------------------------------------- bool pattern_properties::has (std::string const &key) const noexcept { return std::any_of ( m_patterns.begin (), m_patterns.end (), [&key] (auto const &i) { return std::regex_search (key, i.matcher); }); } //----------------------------------------------------------------------------- std::ostream& pattern_properties::describe (std::ostream &os) const { os << "{ pattern_properties: { "; for (auto const &[pattern,regex,validator]: m_patterns) os << '"' << pattern << '"' << ": " << validator << ", "; return os << " } }"; }; /////////////////////////////////////////////////////////////////////////////// additional_properties::additional_properties ( ::json::tree::node const &def, properties *_properties, pattern_properties *_patterns ) : m_fallback (def) , m_properties (_properties) , m_patterns (_patterns) { ; } //----------------------------------------------------------------------------- additional_properties::additional_properties (::json::tree::node const &def): additional_properties (def, nullptr, nullptr) { } //----------------------------------------------------------------------------- void additional_properties::use (properties *ptr) { m_properties = ptr; } //----------------------------------------------------------------------------- void additional_properties::use (pattern_properties *ptr) { m_patterns = ptr; } //----------------------------------------------------------------------------- additional_properties::output_iterator additional_properties::validate (output_iterator res, ::json::tree::node &target) const noexcept { if (!target.is_object ()) return *res++ = { .rule = *this, .target = target }; for (auto &[key,val]: target.as_object ()) { // don't check properties that are handled elsewhere if (m_patterns && m_patterns->has (key)) continue; if (m_properties && m_properties->has (key)) continue; res = m_fallback.validate (res, *val); } return res; } //----------------------------------------------------------------------------- std::ostream& additional_properties::describe (std::ostream &os) const { return os << "{ additional_properties: " << m_fallback << " }"; }