/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2021 * * * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * * software and associated documentation files (the "Software"), to deal in the Software * * without restriction, including without limitation the rights to use, copy, modify, * * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * * permit persons to whom the Software is furnished to do so, subject to the following * * conditions: * * * * The above copyright notice and this permission notice shall be included in all copies * * or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ #include #include #include #include #include namespace openspace::documentation { // The explicit template instantiations for many of the commonly used template values // This cuts down on the compilation time by only compiling these once //template struct Vector2Verifier; template struct Vector2Verifier; template struct Vector2Verifier; //template struct Vector3Verifier; template struct Vector3Verifier; template struct Vector3Verifier; //template struct Vector4Verifier; template struct Vector4Verifier; template struct Vector4Verifier; //template struct Vector2ListVerifier; template struct Vector2ListVerifier; template struct Vector2ListVerifier; //template struct Vector3ListVerifier; template struct Vector3ListVerifier; template struct Vector3ListVerifier; //template struct Vector4ListVerifier; template struct Vector4ListVerifier; template struct Vector4ListVerifier; template struct Matrix2x2Verifier; template struct Matrix2x3Verifier; template struct Matrix2x4Verifier; template struct Matrix3x2Verifier; template struct Matrix3x3Verifier; template struct Matrix3x4Verifier; template struct Matrix4x2Verifier; template struct Matrix4x3Verifier; template struct Matrix4x4Verifier; template struct LessVerifier; template struct LessVerifier; template struct LessEqualVerifier; template struct LessEqualVerifier; template struct GreaterVerifier; template struct GreaterVerifier; template struct GreaterEqualVerifier; template struct GreaterEqualVerifier; template struct EqualVerifier; template struct EqualVerifier; template struct EqualVerifier; template struct EqualVerifier; template struct UnequalVerifier; template struct UnequalVerifier; template struct UnequalVerifier; template struct UnequalVerifier; template struct InListVerifier; template struct InListVerifier; template struct InListVerifier; template struct InListVerifier; template struct NotInListVerifier; template struct NotInListVerifier; template struct NotInListVerifier; template struct NotInListVerifier; template struct InRangeVerifier; template struct InRangeVerifier; template struct NotInRangeVerifier; template struct NotInRangeVerifier; template struct AnnotationVerifier; template struct AnnotationVerifier; template struct AnnotationVerifier; template struct AnnotationVerifier; template struct AnnotationVerifier; //template struct AnnotationVerifier; template struct AnnotationVerifier; template struct AnnotationVerifier; //template struct AnnotationVerifier; template struct AnnotationVerifier; template struct AnnotationVerifier; //template struct AnnotationVerifier; template struct AnnotationVerifier; template struct AnnotationVerifier; template struct DeprecatedVerifier; template struct DeprecatedVerifier; template struct DeprecatedVerifier; template struct DeprecatedVerifier; template struct DeprecatedVerifier; //template struct DeprecatedVerifier; template struct DeprecatedVerifier; template struct DeprecatedVerifier; //template struct DeprecatedVerifier; template struct DeprecatedVerifier; template struct DeprecatedVerifier; //template struct DeprecatedVerifier; template struct DeprecatedVerifier; template struct DeprecatedVerifier; std::string BoolVerifier::type() const { return "Boolean"; } std::string DoubleVerifier::type() const { return "Double"; } TestResult IntVerifier::operator()(const ghoul::Dictionary& dict, const std::string & key) const { if (dict.hasValue(key)) { // We have a key and the value is int, we are done return { true, {}, {} }; } else { if (dict.hasKey(key)) { if (dict.hasValue(key)) { // If we have a double value, we need to check if it is integer double value = dict.value(key); double intPart; bool isInt = modf(value, &intPart) == 0.0; if (isInt) { return { true, {}, {} }; } else { return { false, { { key, TestResult::Offense::Reason::WrongType } }, {} }; } } else { // If we don't have a double value, we cannot have an int value return { false, {{ key, TestResult::Offense::Reason::WrongType }}, {} }; } } else { return { false, {{ key, TestResult::Offense::Reason::MissingKey }}, {} }; } } } std::string IntVerifier::type() const { return "Integer"; } StringVerifier::StringVerifier(bool mustBeNotEmpty) : TemplateVerifier() , _mustBeNotEmpty(mustBeNotEmpty) {} TestResult StringVerifier::operator()(const ghoul::Dictionary& dictionary, const std::string& key) const { TestResult res = TemplateVerifier::operator()(dictionary, key); if (!res.success) { return res; } std::string value = dictionary.value(key); if (value.empty() && _mustBeNotEmpty) { res.success = false; res.offenses.push_back({ key, TestResult::Offense::Reason::Verification, "value must not be empty" }); } return res; } bool StringVerifier::mustBeNotEmpty() const { return _mustBeNotEmpty; } std::string StringVerifier::type() const { return "String"; } TestResult FileVerifier::operator()(const ghoul::Dictionary& dict, const std::string& key) const { TestResult res = StringVerifier::operator()(dict, key); if (!res.success) { return res; } std::string file = dict.value(key); if (!std::filesystem::exists(file) || !std::filesystem::is_regular_file(file)) { res.success = false; TestResult::Offense off; off.offender = key; off.reason = TestResult::Offense::Reason::Verification; off.explanation = "File did not exist"; res.offenses.push_back(off); } return res; } std::string FileVerifier::type() const { return "File"; } TestResult DirectoryVerifier::operator()(const ghoul::Dictionary& dict, const std::string& key) const { TestResult res = StringVerifier::operator()(dict, key); if (!res.success) { return res; } std::string dir = dict.value(key); if (!std::filesystem::exists(dir) || !std::filesystem::is_directory(dir)) { res.success = false; TestResult::Offense off; off.offender = key; off.reason = TestResult::Offense::Reason::Verification; off.explanation = "Directory did not exist"; res.offenses.push_back(off); } return res; } std::string DirectoryVerifier::type() const { return "Directory"; } TestResult Color3Verifier::operator()(const ghoul::Dictionary& dictionary, const std::string& key) const { TestResult res = Vector3Verifier::operator()(dictionary, key); if (!res.success) { return res; } glm::dvec3 values = dictionary.value(key); if (values.x < 0.0 || values.x > 1.0) { res.success = false; res.offenses.push_back({ key + ".x", TestResult::Offense::Reason::Verification }); } if (values.y < 0.0 || values.y > 1.0) { res.success = false; res.offenses.push_back({ key + ".y", TestResult::Offense::Reason::Verification }); } if (values.z < 0.0 || values.z > 1.0) { res.success = false; res.offenses.push_back({ key + ".z", TestResult::Offense::Reason::Verification }); } return res; } std::string Color3Verifier::type() const { return std::string("Color3"); } TestResult Color4Verifier::operator()(const ghoul::Dictionary& dictionary, const std::string& key) const { TestResult res = Vector4Verifier::operator()(dictionary, key); if (!res.success) { return res; } glm::dvec4 values = dictionary.value(key); if (values.x < 0.0 || values.x > 1.0) { res.success = false; res.offenses.push_back({ key + ".x", TestResult::Offense::Reason::Verification }); } if (values.y < 0.0 || values.y > 1.0) { res.success = false; res.offenses.push_back({ key + ".y", TestResult::Offense::Reason::Verification }); } if (values.z < 0.0 || values.z > 1.0) { res.success = false; res.offenses.push_back({ key + ".z", TestResult::Offense::Reason::Verification }); } if (values.w < 0.0 || values.w > 1.0) { res.success = false; res.offenses.push_back({ key + ".a", TestResult::Offense::Reason::Verification }); } return res; } std::string Color4Verifier::type() const { return std::string("Color4"); } template <> TestResult TemplateVerifier::operator()(const ghoul::Dictionary& dict, const std::string& key) const { if (dict.hasValue(key)) { return { true, {}, {} }; } else { if (dict.hasKey(key)) { if (dict.hasValue(key)) { glm::dvec2 value = dict.value(key); glm::dvec2 intPart; glm::bvec2 isInt = { modf(value.x, &intPart.x) == 0.0, modf(value.y, &intPart.y) == 0.0 }; if (isInt.x && isInt.y) { return { true, {}, {} }; } else { return { false, {{ key, TestResult::Offense::Reason::WrongType }}, {} }; } } else { return { false, {{ key, TestResult::Offense::Reason::WrongType }}, {} }; } } else { return { false, {{ key, TestResult::Offense::Reason::MissingKey }}, {} }; } } } template <> TestResult TemplateVerifier::operator()(const ghoul::Dictionary& dict, const std::string& key) const { if (dict.hasValue(key)) { return { true, {}, {} }; } else { if (dict.hasKey(key)) { if (dict.hasValue(key)) { glm::dvec3 value = dict.value(key); glm::dvec3 intPart; glm::bvec3 isInt = { modf(value.x, &intPart.x) == 0.0, modf(value.y, &intPart.y) == 0.0, modf(value.z, &intPart.z) == 0.0 }; if (isInt.x && isInt.y && isInt.z) { return { true, {}, {} }; } else { return { false, {{ key, TestResult::Offense::Reason::WrongType }}, {} }; } } else { return { false, {{ key, TestResult::Offense::Reason::WrongType }}, {} }; } } else { return { false, {{ key, TestResult::Offense::Reason::MissingKey }}, {} }; } } } template <> TestResult TemplateVerifier::operator()(const ghoul::Dictionary& dict, const std::string& key) const { if (dict.hasValue(key)) { return { true, {}, {} }; } else { if (dict.hasKey(key)) { if (dict.hasValue(key)) { glm::dvec4 value = dict.value(key); glm::dvec4 intPart; glm::bvec4 isInt = { modf(value.x, &intPart.x) == 0.0, modf(value.y, &intPart.y) == 0.0, modf(value.z, &intPart.z) == 0.0, modf(value.w, &intPart.w) == 0.0 }; if (isInt.x && isInt.y && isInt.z && isInt.w) { return { true, {}, {} }; } else { return { false, {{ key, TestResult::Offense::Reason::WrongType }}, {} }; } } else { return { false, {{ key, TestResult::Offense::Reason::WrongType }}, {} }; } } else { return { false, {{ key, TestResult::Offense::Reason::MissingKey }}, {} }; } } } TableVerifier::TableVerifier(std::vector documentationEntries) : documentations(std::move(documentationEntries)) {} TestResult TableVerifier::operator()(const ghoul::Dictionary& dictionary, const std::string& key) const { if (dictionary.hasValue(key)) { ghoul::Dictionary d = dictionary.value(key); TestResult res = testSpecification({documentations}, d); // Add the 'key' as a prefix to make the new offender a fully qualified identifer for (TestResult::Offense& s : res.offenses) { s.offender = key + "." + s.offender; } // Add the 'key' as a prefix to make the new warning a fully qualified identifer for (TestResult::Warning& w : res.warnings) { w.offender = key + "." + w.offender; } return res; } else { if (dictionary.hasKey(key)) { return { false, { { key, TestResult::Offense::Reason::WrongType } }, {} }; } else { return { false, { { key, TestResult::Offense::Reason::MissingKey } }, {} }; } } } std::string TableVerifier::type() const { return "Table"; } StringListVerifier::StringListVerifier(std::string elementDocumentation) : TableVerifier({ { "*", new StringVerifier, Optional::No, std::move(elementDocumentation) } }) {} std::string StringListVerifier::type() const { return "List of strings"; } IntListVerifier::IntListVerifier(std::string elementDocumentation) : TableVerifier({ { "*", new IntVerifier, Optional::No, std::move(elementDocumentation) } }) {} std::string IntListVerifier::type() const { return "List of ints"; } ReferencingVerifier::ReferencingVerifier(std::string id) : identifier(std::move(id)) { ghoul_assert(!identifier.empty(), "identifier must not be empty"); } TestResult ReferencingVerifier::operator()(const ghoul::Dictionary& dictionary, const std::string& key) const { TestResult res = TableVerifier::operator()(dictionary, key); if (res.success) { std::vector docs = DocEng.documentations(); auto it = std::find_if( docs.begin(), docs.end(), [this](const Documentation& doc) { return doc.id == identifier; } ); if (it == docs.end()) { res.offenses.push_back({ key, TestResult::Offense::Reason::UnknownIdentifier }); res.success = false; return res; } //ghoul_assert( // it != docs.end(), // "Did not find referencing identifier '" + identifier + "'" //); ghoul::Dictionary d = dictionary.value(key); TestResult r = testSpecification(*it, d); // Add the 'key' as a prefix to make the offender a fully qualified identifer for (TestResult::Offense& s : r.offenses) { s.offender = key + "." + s.offender; } // Add the 'key' as a prefix to make the warning a fully qualified identifer for (TestResult::Warning& w : r.warnings) { w.offender = key + "." + w.offender; } return r; } else { return res; } } std::string ReferencingVerifier::documentation() const { return "Referencing Documentation: '" + identifier + "'"; } AndVerifier::AndVerifier(const std::vector values_) { ghoul_assert(!values_.empty(), "values must not be empty"); for (Verifier* v : values_) { this->values.push_back(std::shared_ptr(v)); } } TestResult AndVerifier::operator()(const ghoul::Dictionary& dictionary, const std::string& key) const { std::vector res(values.size()); std::transform( values.cbegin(), values.cend(), res.begin(), [dictionary, key](const std::shared_ptr& v) { return v->operator()(dictionary, key); } ); const bool success = std::all_of( res.cbegin(), res.cend(), std::mem_fn(&TestResult::success) ); if (success) { return { true, {}, {} }; } else { return { false, { { key, TestResult::Offense::Reason::Verification } }, {} }; } } std::string AndVerifier::type() const { // Dirty hack to get an "and " inserted before the last element std::vector types(values.size() - 1); std::transform( values.cbegin(), values.cend() - 1, types.begin(), std::mem_fn(&Verifier::type) ); types.push_back(std::string("and ") + values.back()->type()); return ghoul::join(types, ", "); } std::string AndVerifier::documentation() const { // Dirty hack to get an "and " inserted before the last element std::vector documentations(values.size() - 1); std::transform( values.cbegin(), values.cend() - 1, documentations.begin(), std::mem_fn(&Verifier::documentation) ); documentations.push_back(std::string("and ") + values.back()->documentation()); return ghoul::join(documentations, ", "); } OrVerifier::OrVerifier( const std::vector>> values_) { ghoul_assert(!values_.empty(), "values must not be empty"); for (const std::variant>& v : values_) { if (std::holds_alternative(v)) { this->values.push_back(std::shared_ptr(std::get(v))); } else { this->values.push_back(std::get>(v)); } } } TestResult OrVerifier::operator()(const ghoul::Dictionary& dictionary, const std::string& key) const { std::vector res(values.size()); std::transform( values.cbegin(), values.cend(), res.begin(), [dictionary, key](const std::shared_ptr& v) { return v->operator()(dictionary, key); } ); const bool success = std::any_of( res.cbegin(), res.cend(), std::mem_fn(&TestResult::success) ); if (success) { return { true, {}, {} }; } else { return { false, { { key, TestResult::Offense::Reason::Verification } }, {} }; } } std::string OrVerifier::type() const { // Dirty hack to get an "or " inserted before the last element std::vector types(values.size() - 1); std::transform( values.cbegin(), values.cend() - 1, types.begin(), std::mem_fn(&Verifier::type) ); types.push_back(std::string("or ") + values.back()->type()); return ghoul::join(types, ", "); } std::string OrVerifier::documentation() const { // Dirty hack to get an "or " inserted before the last element std::vector documentations(values.size() - 1); std::transform( values.cbegin(), values.cend() - 1, documentations.begin(), std::mem_fn(&Verifier::documentation) ); documentations.push_back(std::string("or ") + values.back()->documentation()); return ghoul::join(documentations, ", "); } } // namespace openspace::documentation