#include "DataValidator.h" #include "Logging.h" #include "Regex.h" using namespace Framework; using namespace Validator; #pragma region ValidationResult ValidationResult::ValidationResult() : ReferenceCounter() {} ValidationResult::~ValidationResult() {} void ValidationResult::logInvalidInfo(std::source_location location) const { Logging::error(location) << getInvalidInfo(0).getText(); } #pragma region TypeMissmatch Framework::Validator::TypeMissmatch::TypeMissmatch(Text path, AbstractElement* foundValue, XML::Element* expected, ValidationResult* reason) : ValidationResult() { this->path = path; this->foundValue = foundValue; this->expected = expected; this->reason = reason; } TypeMissmatch::~TypeMissmatch() { expected->release(); if (reason) reason->release(); foundValue->release(); } bool TypeMissmatch::isValid() const { return 0; } Text TypeMissmatch::getInvalidInfo(int indent) const { Text ind = ""; ind.fillText(' ', indent); Text error = ""; error.append() << ind.getText() << "Type missmatch at path '" << path.getText() << "'. Value\n" << ind.getText() << foundValue->toString().getText() << "\n" << ind.getText() << "does not match expected type\n"; if (reason) { error.append() << ind.getText() << "Reason for type mismatch:\n" << reason->getInvalidInfo(indent + 4); } else { error.append() << ind.getText() << expected->toString().getText() << "\n"; } return error; } JSON::JSONValue* TypeMissmatch::getValidPart( RCArray* removedPartsValidationResults) { if (reason) { RCArray temporaryInvalidParts; JSON::JSONValue* valid = reason->getValidPart(&temporaryInvalidParts); Text* p = reason->getPath().getTeilText(path.getLength()); if (foundValue->getType() == AbstractType::ARRAY) { if (!valid && (!expected->hasAttribute("removeInvalidEntries") || !expected->getAttributeValue("removeInvalidEntries") .istGleich("true"))) { if (removedPartsValidationResults) { if (temporaryInvalidParts.getEintragAnzahl() == 1) { if (reason) reason->release(); reason = temporaryInvalidParts.get(0); } else { for (ValidationResult* res : temporaryInvalidParts) { if (res->isDifferent(this)) { removedPartsValidationResults->add( dynamic_cast( res->getThis())); } } } removedPartsValidationResults->add( dynamic_cast(getThis())); } p->release(); return 0; } if (p->hatAt(0, "[") && p->hatAt(p->getLength() - 1, "]")) { Text* it = p->getTeilText(1, p->getLength() - 1); int i = (int)*it; it->release(); if (i >= 0 && foundValue->asAbstractArray()->getLength() > i) { if (removedPartsValidationResults) { for (ValidationResult* res : temporaryInvalidParts) { removedPartsValidationResults->add( dynamic_cast( res->getThis())); } } JSON::JSONValue* foundValueJson = dynamic_cast(foundValue); if (foundValueJson) { JSON::JSONValue* tmp = foundValueJson->clone(); if (valid) tmp->asArray()->setValue( i, dynamic_cast(valid)); else tmp->asArray()->removeValue(i); ValidationResult* res = DataValidator( dynamic_cast(expected->getThis())) .validate(tmp); res->addBasePath(path); if (res->isValid()) { res->release(); p->release(); return tmp; } else if (res->isDifferent(this) || !valid) { p->release(); JSON::JSONValue* result = res->getValidPart( removedPartsValidationResults); tmp->release(); res->release(); return result; } tmp->release(); res->release(); } } } } else if (foundValue->getType() == AbstractType::OBJECT) { if (!valid && (!expected->hasAttribute("removeInvalidEntries") || !expected->getAttributeValue("removeInvalidEntries") .istGleich("true"))) { if (removedPartsValidationResults) { if (temporaryInvalidParts.getEintragAnzahl() == 1) { if (reason) reason->release(); reason = temporaryInvalidParts.get(0); } else { for (ValidationResult* res : temporaryInvalidParts) { if (res->isDifferent(this)) { removedPartsValidationResults->add( dynamic_cast( res->getThis())); } } } removedPartsValidationResults->add( dynamic_cast(getThis())); } p->release(); return 0; } if (p->hatAt(0, ".")) { if (removedPartsValidationResults) { for (ValidationResult* res : temporaryInvalidParts) { removedPartsValidationResults->add( dynamic_cast(res->getThis())); } } Text* at = p->getTeilText(1); Text attr = *at; at->release(); JSON::JSONValue* foundValueJson = dynamic_cast(foundValue); if (foundValueJson) { JSON::JSONValue* tmp = foundValueJson->clone(); tmp->asObject()->removeValue(attr); if (valid) tmp->asObject()->addValue( attr, dynamic_cast(valid)); ValidationResult* res = DataValidator( dynamic_cast(expected->getThis())) .validate(tmp); res->addBasePath(path); if (res->isValid()) { res->release(); p->release(); return tmp; } else if (res->isDifferent(this) || !valid) { p->release(); JSON::JSONValue* result = res->getValidPart(removedPartsValidationResults); tmp->release(); res->release(); return result; } tmp->release(); res->release(); } } } p->release(); if (valid) valid->release(); } if (removedPartsValidationResults) { removedPartsValidationResults->add( dynamic_cast(getThis())); } return 0; } Text TypeMissmatch::getPath() const { return path; } bool TypeMissmatch::isDifferent(const ValidationResult* zResult) const { const TypeMissmatch* casted = dynamic_cast(zResult); if (casted == 0) return 1; if (!casted->getPath().istGleich(path)) return 1; return reason->isDifferent(casted->reason); } void TypeMissmatch::addBasePath(Text basePath) { path = basePath + path; if (reason) reason->addBasePath(basePath); } #pragma endregion Cotent #pragma region UnknownValue UnknownValue::UnknownValue(Text path, AbstractElement* foundValue) : ValidationResult() { this->path = path; this->foundValue = foundValue; } UnknownValue::~UnknownValue() { foundValue->release(); } bool UnknownValue::isValid() const { return 0; } Text UnknownValue::getInvalidInfo(int indent) const { Text ind = ""; ind.fillText(' ', indent); Text error = ""; error.append() << ind.getText() << "Unknown Value at '" << path.getText() << "'. Value found:\n" << ind.getText() << foundValue->toString().getText() << "\n"; return error; } JSON::JSONValue* UnknownValue::getValidPart( RCArray* removedPartsValidationResults) { if (removedPartsValidationResults) { removedPartsValidationResults->add( dynamic_cast(getThis())); } return 0; } Text UnknownValue::getPath() const { return path; } bool UnknownValue::isDifferent(const ValidationResult* zResult) const { const UnknownValue* casted = dynamic_cast(zResult); if (casted == 0) return 1; if (!casted->getPath().istGleich(path)) return 1; return 0; } void UnknownValue::addBasePath(Text basePath) { path = basePath + path; } #pragma endregion Cotent #pragma region MissingValue MissingValue::MissingValue(Text path, XML::Element* expected) : ValidationResult() { this->path = path; this->expected = expected; } MissingValue::~MissingValue() { expected->release(); } bool MissingValue::isValid() const { return 0; } Text MissingValue::getInvalidInfo(int indent) const { Text ind = ""; ind.fillText(' ', indent); Text error = ""; error.append() << ind.getText() << "Missing Value at '" << path.getText() << "'. Expected type:\n" << ind.getText() << expected->toString().getText() << "\n"; return error; } JSON::JSONValue* MissingValue::getValidPart( RCArray* removedPartsValidationResults) { if (expected->hasAttribute("default")) { JSON::JSONValue* def = JSON::Parser::getValue(expected->getAttributeValue("default")); ValidationResult* res = DataValidator(dynamic_cast(expected->getThis())) .validate(def); res->addBasePath(path); if (res->isValid()) { res->release(); return def; } else if (res->isDifferent(this)) { def->release(); JSON::JSONValue* result = res->getValidPart(removedPartsValidationResults); res->release(); return result; } def->release(); } if (removedPartsValidationResults) { removedPartsValidationResults->add( dynamic_cast(getThis())); } return 0; } Text MissingValue::getPath() const { return path; } bool MissingValue::isDifferent(const ValidationResult* zResult) const { const MissingValue* casted = dynamic_cast(zResult); if (casted == 0) return 1; if (!casted->getPath().istGleich(path)) return 1; return 0; } void MissingValue::addBasePath(Text basePath) { path = basePath + path; } #pragma endregion Cotent #pragma region MissingOneOf MissingOneOf::MissingOneOf(Text path, XML::Editor expected) : ValidationResult() { this->path = path; for (XML::Element* e : expected) this->expected.add(dynamic_cast(e->getThis())); } MissingOneOf::~MissingOneOf() {} bool MissingOneOf::isValid() const { return 0; } Text MissingOneOf::getInvalidInfo(int indent) const { Text ind = ""; ind.fillText(' ', indent); Text error = ""; error.append() << ind.getText() << "Missing Value at '" << path.getText() << "'. The value should have one of the specified types:\n"; ind += " "; for (XML::Element* e : expected) { error.append() << ind.getText() << e->toString().getText() << "\n"; } return error; } JSON::JSONValue* MissingOneOf::getValidPart( RCArray* removedPartsValidationResults) { for (XML::Element* e : expected) { if (e->hasAttribute("default")) { JSON::JSONValue* defaultValue = JSON::Parser::getValue(e->getAttributeValue("default")); if (defaultValue) { JSON::JSONValue* valid = DataValidator(dynamic_cast(e->getThis())) .getValidParts( defaultValue, removedPartsValidationResults); defaultValue->release(); if (valid) { return valid; } } } } if (removedPartsValidationResults) { removedPartsValidationResults->add( dynamic_cast(getThis())); } // multiple possibilities are undecidable return 0; } Text MissingOneOf::getPath() const { return path; } bool MissingOneOf::isDifferent(const ValidationResult* zResult) const { const MissingOneOf* casted = dynamic_cast(zResult); if (casted == 0) return 1; if (!casted->getPath().istGleich(path)) return 1; return 0; } void MissingOneOf::addBasePath(Text basePath) { path = basePath + path; } #pragma endregion Content #pragma region NoTypeMatching NoTypeMatching::NoTypeMatching(Text path, AbstractElement* foundValue, RCArray& expected, RCArray& reasons) : ValidationResult() { this->path = path; this->foundValue = foundValue; this->expected = expected; this->reasons = reasons; } NoTypeMatching::~NoTypeMatching() { foundValue->release(); } bool NoTypeMatching::isValid() const { return 0; } Text NoTypeMatching::getInvalidInfo(int indent) const { Text ind = ""; ind.fillText(' ', indent); Text error = ""; error.append() << ind.getText() << "Found Value at '" << path.getText() << "' did not match any of the specified types\n"; Text ind2 = ind + " "; error.append() << ind.getText() << "Reasons:\n"; for (ValidationResult* reason : reasons) { error += reason->getInvalidInfo(indent + 4); } return error; } JSON::JSONValue* NoTypeMatching::getValidPart( RCArray* removedPartsValidationResults) { RCArray tempErrors; for (ValidationResult* reason : reasons) { JSON::JSONValue* result = reason->getValidPart(&tempErrors); if (result) { return result; } } if (removedPartsValidationResults) { removedPartsValidationResults->add( dynamic_cast(getThis())); reasons.leeren(); for (ValidationResult* error : tempErrors) { reasons.add(dynamic_cast(error->getThis())); } } // multiple possibilities are undecidable return 0; } Text NoTypeMatching::getPath() const { return path; } bool NoTypeMatching::isDifferent(const ValidationResult* zResult) const { const NoTypeMatching* casted = dynamic_cast(zResult); if (casted == 0) return 1; if (!casted->getPath().istGleich(path)) return 1; for (int i = 0; i < reasons.getEintragAnzahl(); i++) { if (reasons.z(i)->isDifferent(casted->reasons.z(i))) return 1; } return 0; } void NoTypeMatching::addBasePath(Text basePath) { path = basePath + path; for (ValidationResult* reason : reasons) { reason->addBasePath(basePath); } } #pragma endregion Content #pragma region ValidationPathNotFound ValidationPathNotFound::ValidationPathNotFound( Text path, AbstractElement* foundValue, Text validationPath) : ValidationResult() { this->path = path; this->foundValue = foundValue; this->validationPath = validationPath; } ValidationPathNotFound::~ValidationPathNotFound() { foundValue->release(); } bool ValidationPathNotFound::isValid() const { return false; } Text ValidationPathNotFound::getInvalidInfo(int indent) const { Text result; result.append() << "Expected to validate path '" << validationPath.getText() << "' but at path '" << path.getText() << "' the value " << foundValue->toString().getText() << " was found."; return result; } JSON::JSONValue* ValidationPathNotFound::getValidPart( RCArray* zRemovedPartsValidationResults) { return 0; } Text ValidationPathNotFound::getPath() const { return path; } bool ValidationPathNotFound::isDifferent(const ValidationResult* zResult) const { const ValidationPathNotFound* other = dynamic_cast(zResult); if (other == 0) return 1; if (!other->path.istGleich(path)) return 1; return 0; } void ValidationPathNotFound::addBasePath(Text basePath) { path = basePath + path; } #pragma endregion Content #pragma region ValidValue ValidValue::ValidValue(Text path, AbstractElement* value) : ValidationResult() { this->path = path; this->value = value; } ValidValue::~ValidValue() { value->release(); } bool ValidValue::isValid() const { return 1; } Text ValidValue::getInvalidInfo(int indent) const { return ""; } JSON::JSONValue* ValidValue::getValidPart( RCArray* removedPartsValidationResults) { JSON::JSONValue* json = dynamic_cast(value); return json ? json->clone() : 0; } Text ValidValue::getPath() const { return path; } bool ValidValue::isDifferent(const ValidationResult* zResult) const { const ValidValue* casted = dynamic_cast(zResult); if (casted == 0) return 1; if (!casted->getPath().istGleich(path)) return 1; return 0; } void ValidValue::addBasePath(Text basePath) { path = basePath + path; } #pragma endregion Content #pragma endregion Content #pragma region DataValidator DataValidator::DataValidator(XML::Element* constraints) : ReferenceCounter(), constraints(constraints), typeConstraints(new RCTrie()) { for (XML::Element* e : constraints->select().selectAllElements().whereAttributeExists("id")) { Framework::Text id = e->getAttributeValue("id"); typeConstraints->set( id, id.getLength(), dynamic_cast(e->getThis())); } } DataValidator::DataValidator( XML::Element* constraints, RCTrie* typeConstraints) : ReferenceCounter(), constraints(constraints), typeConstraints(typeConstraints) {} DataValidator::~DataValidator() { constraints->release(); typeConstraints->release(); } ValidationResult* DataValidator::validate(AbstractElement* zValue) const { return validate(ElementPathBuilder().build(), zValue); } ValidationResult* Framework::Validator::DataValidator::validate( ElementPath* path, AbstractElement* zValue) const { ValidationResult* result = validate(path, zValue, constraints, ""); path->release(); return result; } bool DataValidator::isValid(AbstractElement* zValue) const { ValidationResult* res = validate(zValue); if (res->isValid()) { res->release(); return 1; } res->release(); return 0; } JSON::JSONValue* DataValidator::getValidParts(JSON::JSONValue* zValue, RCArray* removedPartsValidationResults) const { ValidationResult* res = validate(zValue); if (res->isValid()) { res->release(); return zValue->clone(); } JSON::JSONValue* valid = res->getValidPart(removedPartsValidationResults); res->release(); return valid; } XML::Element* DataValidator::zConstraints() { return constraints; } JSON::JSONObject* Framework::Validator::DataValidator::getJsonSchema() const { JSON::JSONObject* zDefs = new JSON::JSONObject(); JSON::JSONObject* result = getJsonSchema(constraints, zDefs); if (zDefs->getFieldCount() > 0) { result->addValue("$defs", zDefs); } else { zDefs->release(); } return result; } void Framework::Validator::DataValidator::updateValidator( Text id, DataValidator* validator) { typeConstraints->set(id, id.getLength(), validator->constraints); validator->release(); } ValidationResult* DataValidator::validate(ElementPath* pathToValidate, AbstractElement* zValue, XML::Element* zConstraints, Text path) const { if (zConstraints->getName().istGleich("oneOf")) { return validateMultipleTypes( pathToValidate, zValue, zConstraints, path); } if (zConstraints->getName().istGleich("ref")) { Text id = zConstraints->getAttributeValue("ref"); XML::Element* e = typeConstraints->z(id, id.getLength()); if (e) { zConstraints = e; } } switch (zValue->getType()) { case AbstractType::NULL_: if (pathToValidate->isValid()) { return new ValidationPathNotFound(path, dynamic_cast(zValue->getThis()), pathToValidate->toString()); } if (!zConstraints->hasAttribute("nullable") || !zConstraints->getAttributeValue("nullable").istGleich("true")) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } break; case AbstractType::BOOLEAN: if (pathToValidate->isValid()) { return new ValidationPathNotFound(path, dynamic_cast(zValue->getThis()), pathToValidate->toString()); } if (!zConstraints->getName().istGleich("bool")) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } else if (zConstraints->hasAttribute("equals")) { if (!zConstraints->getAttributeValue("equals").istGleich("true") == !zValue->asAbstractBool()->getBool()) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } } break; case AbstractType::NUMBER: if (pathToValidate->isValid()) { return new ValidationPathNotFound(path, dynamic_cast(zValue->getThis()), pathToValidate->toString()); } if (!zConstraints->getName().istGleich("number")) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } else { double number = zValue->asAbstractNumber()->getNumber(); if (zConstraints->hasAttribute("equals") && (double)zConstraints->getAttributeValue("equals") != number) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } if (zConstraints->hasAttribute("lessOrEqual") && number > (double)zConstraints->getAttributeValue("lessOrEqual")) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } if (zConstraints->hasAttribute("greaterOrEqual") && number < (double)zConstraints->getAttributeValue( "greaterOrEqual")) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } if (zConstraints->hasAttribute("less") && number >= (double)zConstraints->getAttributeValue("less")) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } if (zConstraints->hasAttribute("greater") && number <= (double)zConstraints->getAttributeValue("greater")) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } } break; case AbstractType::STRING: if (pathToValidate->isValid()) { return new ValidationPathNotFound(path, dynamic_cast(zValue->getThis()), pathToValidate->toString()); } if (!zConstraints->getName().istGleich("string")) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } else { Text string = zValue->asAbstractString()->getString(); if (zConstraints->hasAttribute("equals") && !zConstraints->getAttributeValue("equals").istGleich(string)) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } if (zConstraints->hasAttribute("contains") && string.hat( zConstraints->getAttributeValue("contains").getText())) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } if (zConstraints->hasAttribute("startsWith") && string.positionVon( zConstraints->getAttributeValue("startsWith").getText()) == 0) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } if (zConstraints->hasAttribute("endsWith") && string.positionVon( zConstraints->getAttributeValue("endsWith").getText()) == string.getLength() - zConstraints->getAttributeValue("endsWith") .getLength()) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } if (zConstraints->hasAttribute("oneOf")) { JSON::JSONArray* array = JSON::Parser::getValue( zConstraints->getAttributeValue("oneOf")) ->asArray(); bool ok = 0; for (JSON::JSONValue* v : *array) { if (v->asString()->getString().istGleich(string)) { ok = 1; break; } } array->release(); if (!ok) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } } } break; case AbstractType::ARRAY: if (!zConstraints->getName().istGleich("array")) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } else { if (pathToValidate->isValid()) { if (!pathToValidate->isArrayElement()) { return new ValidationPathNotFound(path, dynamic_cast(zValue->getThis()), pathToValidate->toString()); } else { int index = pathToValidate->getArrayIndex(); const AbstractArray* array = zValue->asAbstractArray(); if (index >= array->getLength()) { return new ValidationPathNotFound(path, dynamic_cast(zValue->getThis()), pathToValidate->toString()); } Text p = path; p += "["; p += index; p += "]"; pathToValidate->next(); return validateMultipleTypes(pathToValidate, array->zAbstractValue(index), zConstraints, p); } } const AbstractArray* array = zValue->asAbstractArray(); for (int i = 0; i < array->getLength(); i++) { Text p = path; p += "["; p += i; p += "]"; ValidationResult* res = validateMultipleTypes( pathToValidate, array->zAbstractValue(i), zConstraints, p); if (!res->isValid()) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), res); } res->release(); } } break; case AbstractType::OBJECT: if (!zConstraints->getName().istGleich("object")) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast(zConstraints->getThis()), 0); } else { if (pathToValidate->isValid()) { if (!pathToValidate->isObjectAttribute()) { return new ValidationPathNotFound(path, dynamic_cast(zValue->getThis()), pathToValidate->toString()); } else { Text key = pathToValidate->getObjectAttribute(); const AbstractObject* obj = zValue->asAbstractObject(); if (!obj->hasValue(key)) { return new ValidationPathNotFound(path, dynamic_cast(zValue->getThis()), pathToValidate->toString()); } Text p = path; p += "."; p += key; if (!zConstraints->selectChildsByAttribute("name", key) .exists()) { if (!zConstraints ->getAttributeValue( "allowAdditionalAttributes") .istGleich("true")) { return new TypeMissmatch(path, dynamic_cast( zValue->getThis()), dynamic_cast( zConstraints->getThis()), new UnknownValue(p, dynamic_cast( obj->zAbstractValue(key)->getThis()))); } } else { XML::Editor tmp = zConstraints->selectChildsByAttribute( "name", key); pathToValidate->next(); return validateMultipleTypes(pathToValidate, obj->zAbstractValue(key), tmp.begin().val(), p); } } } const AbstractObject* obj = zValue->asAbstractObject(); for (int i = 0; i < obj->getFieldCount(); i++) { Text key = obj->getFieldKey(i); Text p = path; p += "."; p += key; if (!zConstraints->selectChildsByAttribute("name", key) .exists()) { if (!zConstraints ->getAttributeValue("allowAdditionalAttributes") .istGleich("true")) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast( zConstraints->getThis()), new UnknownValue(p, dynamic_cast( obj->zAbstractValue(i)->getThis()))); } } else { XML::Editor tmp = zConstraints->selectChildsByAttribute("name", key); ValidationResult* res = validateMultipleTypes(pathToValidate, obj->zAbstractValue(i), tmp.begin().val(), p); if (!res->isValid()) { return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast( zConstraints->getThis()), res); } res->release(); } } for (XML::Element* constraint : zConstraints->selectChildren()) { if (!obj->hasValue(constraint->getAttributeValue("name"))) { XML::Editor tmp = constraint->selectChildren(); bool optional = true; std::function checkOptional; checkOptional = [&optional, &checkOptional]( XML::Element* zElement) { if (zElement->getName().istGleich("oneOf")) { XML::Editor tmp = zElement->selectChildren(); tmp.forEach(checkOptional); } else { optional &= zElement->hasAttribute("optional") && zElement->getAttributeValue("optional") .istGleich("true"); } }; tmp.forEach(checkOptional); if (!optional) { Text p = path; p += "."; p += constraint->getAttributeValue("name"); if (constraint->getChildCount() != 1) return new TypeMissmatch(path, dynamic_cast( zValue->getThis()), dynamic_cast( zConstraints->getThis()), new MissingOneOf(p, tmp)); return new TypeMissmatch(path, dynamic_cast(zValue->getThis()), dynamic_cast( zConstraints->getThis()), new MissingValue(p, dynamic_cast( tmp.begin()->getThis()))); } } } } break; } return new ValidValue( path, dynamic_cast(zValue->getThis())); } ValidationResult* DataValidator::validateMultipleTypes( ElementPath* pathToValidate, AbstractElement* zChildValue, XML::Element* zPossibleChildConstraints, Text childPath) const { if (zPossibleChildConstraints->getChildCount() == 1 && !zPossibleChildConstraints->hasAttribute("typeSpecifiedBy")) { XML::Editor children = zPossibleChildConstraints->selectChildren(); return validate(pathToValidate, zChildValue, children.begin().val(), childPath); // only one type is possible } bool hasTypeAttr = 0; RCArray possibleConstraints; if (zPossibleChildConstraints->hasAttribute("typeSpecifiedBy")) { // try to find the correct constraints based on the type attribute hasTypeAttr = 1; Text typeAttr = zPossibleChildConstraints->getAttributeValue("typeSpecifiedBy"); if (zChildValue->getType() == AbstractType::OBJECT) { const AbstractObject* obj = zChildValue->asAbstractObject(); if (obj->hasValue(typeAttr)) { AbstractElement* typeV = obj->zAbstractValue(typeAttr); ElementPath* tmp = ElementPathBuilder().build(); for (XML::Element* constraint : zPossibleChildConstraints->selectChildsByName("object") .whereChildWithAttributeExists("name", typeAttr)) { XML::Editor nameChildren = constraint->selectChildsByAttribute("name", typeAttr); XML::Element* typeAttrContraints = nameChildren.begin().val(); ValidationResult* res = validateMultipleTypes(tmp, typeV, typeAttrContraints, childPath + "." + typeAttr); if (res->isValid()) possibleConstraints.add( dynamic_cast(constraint->getThis())); res->release(); } tmp->release(); } } } if (hasTypeAttr && possibleConstraints.getEintragAnzahl() == 1) return validate(pathToValidate, zChildValue, possibleConstraints.begin().val(), childPath); // if the type is clear else if (hasTypeAttr && possibleConstraints.getEintragAnzahl() > 0) { // more then one type is possible RCArray invalidResults; int index = pathToValidate->getCurrentPathElementIndex(); for (XML::Element* constraint : possibleConstraints) { pathToValidate->setCurrentPathElementIndex(index); ValidationResult* res = validate(pathToValidate, zChildValue, constraint, childPath); invalidResults.add(res); if (res->isValid()) return new ValidValue(childPath, dynamic_cast(zChildValue->getThis())); } return new NoTypeMatching(childPath, dynamic_cast(zChildValue->getThis()), possibleConstraints, invalidResults); } // try all types possibleConstraints.leeren(); RCArray invalidResults; int index = pathToValidate->getCurrentPathElementIndex(); for (XML::Element* constraint : zPossibleChildConstraints->selectChildren()) { pathToValidate->setCurrentPathElementIndex(index); ValidationResult* res = validate(pathToValidate, zChildValue, constraint, childPath); invalidResults.add(res); if (res->isValid()) return new ValidValue(childPath, dynamic_cast(zChildValue->getThis())); possibleConstraints.add( dynamic_cast(constraint->getThis())); } pathToValidate->setCurrentPathElementIndex(index); if (pathToValidate->isValid()) { return new ValidationPathNotFound(childPath, dynamic_cast(zChildValue->getThis()), pathToValidate->toString()); } return new NoTypeMatching(childPath, dynamic_cast(zChildValue->getThis()), possibleConstraints, invalidResults); } JSON::JSONObject* Framework::Validator::DataValidator::getJsonSchema( XML::Element* zConstraint, JSON::JSONObject* zDefs) const { JSON::JSONObject* result = new JSON::JSONObject(); if (zConstraint->hasAttribute("id") && zDefs->hasValue(zConstraint->getAttributeValue("id"))) { Text jsonRef = Text("#/$defs/") + zConstraint->getAttributeValue("id"); result->addValue("$ref", new JSON::JSONString(jsonRef)); } else { if (zConstraint->getName().istGleich("bool")) { if (zConstraint->hasAttribute("equals")) { JSON::JSONArray* array = new JSON::JSONArray(); array->addValue(new JSON::JSONBool( zConstraint->getAttributeValue("equals").istGleich( "true"))); result->addValue("enum", array); } else { result->addValue("type", new JSON::JSONString("boolean")); } } else if (zConstraint->getName().istGleich("number")) { if (zConstraint->hasAttribute("equals")) { JSON::JSONArray* array = new JSON::JSONArray(); array->addValue(new JSON::JSONNumber( (double)zConstraint->getAttributeValue("equals"))); result->addValue("enum", array); } else { result->addValue("type", new JSON::JSONString("number")); if (zConstraint->hasAttribute("lessOrEqual")) { result->addValue("maximum", new JSON::JSONNumber( (double)zConstraint->getAttributeValue( "lessOrEqual"))); } if (zConstraint->hasAttribute("greaterOrEqual")) { result->addValue("minimum", new JSON::JSONNumber( (double)zConstraint->getAttributeValue( "greaterOrEqual"))); } if (zConstraint->hasAttribute("less")) { result->addValue("exclusiveMaximum", new JSON::JSONNumber( (double)zConstraint->getAttributeValue("less"))); } if (zConstraint->hasAttribute("greater")) { result->addValue("exclusiveMinimum", new JSON::JSONNumber( (double)zConstraint->getAttributeValue("greater"))); } } } else if (zConstraint->getName().istGleich("string")) { if (zConstraint->hasAttribute("equals")) { JSON::JSONArray* array = new JSON::JSONArray(); array->addValue(new JSON::JSONString( zConstraint->getAttributeValue("equals"))); result->addValue("enum", array); } else if (zConstraint->hasAttribute("oneOf")) { JSON::JSONArray* array = JSON::Parser::getValue( zConstraint->getAttributeValue("oneOf")) ->asArray(); result->addValue("enum", array); } else { result->addValue("type", new JSON::JSONString("string")); if (zConstraint->hasAttribute("contains")) { result->addValue("pattern", new JSON::JSONString( Text(".*") + Regex::quote( zConstraint->getAttributeValue("contains")) + ".*")); } if (zConstraint->hasAttribute("startsWith")) { result->addValue("pattern", new JSON::JSONString( Text("^") + Regex::quote( zConstraint->getAttributeValue("startsWith")) + ".*")); } if (zConstraint->hasAttribute("endsWith")) { result->addValue("pattern", new JSON::JSONString( Text(".*") + Regex::quote( zConstraint->getAttributeValue("endsWith")) + "$")); } } } else if (zConstraint->getName().istGleich("ref")) { Text id = zConstraint->getAttributeValue("ref"); XML::Element* e = typeConstraints->z(id, id.getLength()); if (!zDefs->hasValue(id)) { zDefs->addValue( id, new JSON::JSONValue()); // avoid endless recursion for // cyclic datastructures JSON::JSONObject* def = getJsonSchema(e, zDefs); zDefs->removeValue(id); zDefs->addValue(id, def); } Text jsonRef = Text("#/$defs/") + id; result->addValue("$ref", new JSON::JSONString(jsonRef)); } else if (zConstraint->getName().istGleich("object")) { result->addValue("type", new JSON::JSONString("object")); result->addValue("additionalProperties", new JSON::JSONBool( zConstraint->getAttributeValue("allowAdditionalAttributes") .istGleich("true"))); JSON::JSONObject* properties = new JSON::JSONObject(); JSON::JSONArray* required = new JSON::JSONArray(); for (XML::Element* e : zConstraint->selectChildren()) { JSON::JSONObject* prop = getJsonSchema(e, zDefs); properties->addValue(e->getAttributeValue("name"), prop); if (!e->getAttributeValue("optional").istGleich("true")) { required->addValue( new JSON::JSONString(e->getAttributeValue("name"))); } } result->addValue("properties", properties); result->addValue("required", required); } else if (zConstraint->getName().istGleich("array")) { result->addValue("type", new JSON::JSONString("array")); JSON::JSONObject* items = new JSON::JSONObject(); JSON::JSONArray* oneOf = new JSON::JSONArray(); for (XML::Element* e : zConstraint->selectChildren()) { oneOf->addValue(getJsonSchema(e, zDefs)); } items->addValue("oneOf", oneOf); result->addValue("items", items); } else if (zConstraint->getName().istGleich("oneOf")) { JSON::JSONArray* oneOf = new JSON::JSONArray(); for (XML::Element* e : zConstraint->selectChildren()) { oneOf->addValue(getJsonSchema(e, zDefs)); } result->addValue("oneOf", oneOf); } if (zConstraint->hasAttribute("nullable") && zConstraint->getAttributeValue("nullable").istGleich("true")) { if (!result->hasValue("type")) { if (result->hasValue("enum")) { result->zValue("enum")->asArray()->addValue( new JSON::JSONValue()); } else { JSON::JSONObject* oneOf = new JSON::JSONObject(); JSON::JSONArray* array = new JSON::JSONArray(); array->addValue(result); JSON::JSONObject* nullType = new JSON::JSONObject(); nullType->addValue("type", new JSON::JSONString("null")); array->addValue(nullType); oneOf->addValue("oneOf", array); result = oneOf; } } else { if (result->zValue("Type")->getType() == AbstractType::ARRAY) { result->zValue("Type")->asArray()->addValue( new JSON::JSONString("null")); } else { JSON::JSONArray* array = new JSON::JSONArray(); array->addValue(result->getValue("Type")); array->addValue(new JSON::JSONString("null")); result->removeValue("type"); result->addValue("type", array); } } } if (zConstraint->hasAttribute("id")) { zDefs->addValue(zConstraint->getAttributeValue("id"), result); result = new JSON::JSONObject(); Text jsonRef = Text("#/$defs/") + zConstraint->getAttributeValue("id"); result->addValue("$ref", new JSON::JSONString(jsonRef)); } } return result; } StringValidationBuilder* DataValidator::buildForString() { return new StringValidationBuilder( [](XML::Element& e) { return new DataValidator(e.dublicate()); }); } NumberValidationBuilder* DataValidator::buildForNumber() { return new NumberValidationBuilder( [](XML::Element& e) { return new DataValidator(e.dublicate()); }); } BoolValidationBuilder* DataValidator::buildForBool() { return new BoolValidationBuilder( [](XML::Element& e) { return new DataValidator(e.dublicate()); }); } ObjectValidationBuilder* DataValidator::buildForObject() { return new ObjectValidationBuilder( [](XML::Element& e) { return new DataValidator(e.dublicate()); }); } ArrayValidationBuilder* DataValidator::buildForArray() { return new ArrayValidationBuilder( [](XML::Element& e) { return new DataValidator(e.dublicate()); }); } DataValidator* DataValidator::buildForReference(Text id) { return new DataValidator( new XML::Element(Text(""))); } OneOfValidationBuilder* DataValidator::buildForOneOf() { return new OneOfValidationBuilder( [](XML::Element& e) { return new DataValidator(e.dublicate()); }); }