Browse Source

Converted json validator into a class

Ivan Savenko 1 year ago
parent
commit
c90fb47c23
5 changed files with 540 additions and 584 deletions
  1. 1 1
      lib/json/JsonFormatException.h
  2. 1 1
      lib/json/JsonNode.h
  3. 2 1
      lib/json/JsonUtils.cpp
  4. 523 562
      lib/json/JsonValidator.cpp
  5. 13 19
      lib/json/JsonValidator.h

+ 1 - 1
lib/json/JsonFormatException.h

@@ -11,7 +11,7 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-class JsonFormatException : public std::runtime_error
+class DLL_LINKAGE JsonFormatException : public std::runtime_error
 {
 public:
 	using runtime_error::runtime_error;

+ 1 - 1
lib/json/JsonNode.h

@@ -17,7 +17,7 @@ class JsonNode;
 using JsonMap = std::map<std::string, JsonNode>;
 using JsonVector = std::vector<JsonNode>;
 
-struct JsonParsingSettings
+struct DLL_LINKAGE JsonParsingSettings
 {
 	enum class JsonFormatMode
 	{

+ 2 - 1
lib/json/JsonUtils.cpp

@@ -105,7 +105,8 @@ void JsonUtils::maximize(JsonNode & node, const std::string & schemaName)
 
 bool JsonUtils::validate(const JsonNode & node, const std::string & schemaName, const std::string & dataName)
 {
-	std::string log = Validation::check(schemaName, node);
+	JsonValidator validator;
+	std::string log = validator.check(schemaName, node);
 	if (!log.empty())
 	{
 		logMod->warn("Data in %s is invalid!", dataName);

+ 523 - 562
lib/json/JsonValidator.cpp

@@ -21,677 +21,638 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-static const std::unordered_map<std::string, JsonNode::JsonType> stringToType =
+static std::string emptyCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
 {
-	{"null",   JsonNode::JsonType::DATA_NULL},
-	{"boolean", JsonNode::JsonType::DATA_BOOL},
-	{"number", JsonNode::JsonType::DATA_FLOAT},
-	{"integer", JsonNode::JsonType::DATA_INTEGER},
-	{"string",  JsonNode::JsonType::DATA_STRING},
-	{"array",  JsonNode::JsonType::DATA_VECTOR},
-	{"object",  JsonNode::JsonType::DATA_STRUCT}
-};
+	// check is not needed - e.g. incorporated into another check
+	return "";
+}
 
-namespace
+static std::string notImplementedCheck(JsonValidator & validator,
+								const JsonNode & baseSchema,
+								const JsonNode & schema,
+								const JsonNode & data)
 {
-	namespace Common
-	{
-		std::string emptyCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
-		{
-			// check is not needed - e.g. incorporated into another check
-			return "";
-		}
+	return "Not implemented entry in schema";
+}
 
-		std::string notImplementedCheck(Validation::ValidationData & validator,
-										const JsonNode & baseSchema,
-										const JsonNode & schema,
-										const JsonNode & data)
-		{
-			return "Not implemented entry in schema";
-		}
+static std::string schemaListCheck(JsonValidator & validator,
+							const JsonNode & baseSchema,
+							const JsonNode & schema,
+							const JsonNode & data,
+							const std::string & errorMsg,
+							const std::function<bool(size_t)> & isValid)
+{
+	std::string errors = "<tested schemas>\n";
+	size_t result = 0;
 
-		std::string schemaListCheck(Validation::ValidationData & validator,
-									const JsonNode & baseSchema,
-									const JsonNode & schema,
-									const JsonNode & data,
-									const std::string & errorMsg,
-									const std::function<bool(size_t)> & isValid)
+	for(const auto & schemaEntry : schema.Vector())
+	{
+		std::string error = validator.check(schemaEntry, data);
+		if (error.empty())
 		{
-			std::string errors = "<tested schemas>\n";
-			size_t result = 0;
-
-			for(const auto & schemaEntry : schema.Vector())
-			{
-				std::string error = check(schemaEntry, data, validator);
-				if (error.empty())
-				{
-					result++;
-				}
-				else
-				{
-					errors += error;
-					errors += "<end of schema>\n";
-				}
-			}
-			if (isValid(result))
-				return "";
-			else
-				return validator.makeErrorMessage(errorMsg) + errors;
+			result++;
 		}
-
-		std::string allOfCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		else
 		{
-			return schemaListCheck(validator, baseSchema, schema, data, "Failed to pass all schemas", [&schema](size_t count)
-			{
-				return count == schema.Vector().size();
-			});
+			errors += error;
+			errors += "<end of schema>\n";
 		}
+	}
+	if (isValid(result))
+		return "";
+	else
+		return validator.makeErrorMessage(errorMsg) + errors;
+}
 
-		std::string anyOfCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
-		{
-			return schemaListCheck(validator, baseSchema, schema, data, "Failed to pass any schema", [](size_t count)
-			{
-				return count > 0;
-			});
-		}
+static std::string allOfCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	return schemaListCheck(validator, baseSchema, schema, data, "Failed to pass all schemas", [&schema](size_t count)
+	{
+		return count == schema.Vector().size();
+	});
+}
 
-		std::string oneOfCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
-		{
-			return schemaListCheck(validator, baseSchema, schema, data, "Failed to pass exactly one schema", [](size_t count)
-			{
-				return count == 1;
-			});
-		}
+static std::string anyOfCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	return schemaListCheck(validator, baseSchema, schema, data, "Failed to pass any schema", [](size_t count)
+	{
+		return count > 0;
+	});
+}
 
-		std::string notCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
-		{
-			if (check(schema, data, validator).empty())
-				return validator.makeErrorMessage("Successful validation against negative check");
-			return "";
-		}
+static std::string oneOfCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	return schemaListCheck(validator, baseSchema, schema, data, "Failed to pass exactly one schema", [](size_t count)
+	{
+		return count == 1;
+	});
+}
 
-		std::string enumCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
-		{
-			for(const auto & enumEntry : schema.Vector())
-			{
-				if (data == enumEntry)
-					return "";
-			}
-			return validator.makeErrorMessage("Key must have one of predefined values");
-		}
+static std::string notCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	if (validator.check(schema, data).empty())
+		return validator.makeErrorMessage("Successful validation against negative check");
+	return "";
+}
 
-		std::string typeCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
-		{
-			const auto & typeName = schema.String();
-			auto it = stringToType.find(typeName);
-			if(it == stringToType.end())
-			{
-				return validator.makeErrorMessage("Unknown type in schema:" + typeName);
-			}
+static std::string enumCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	for(const auto & enumEntry : schema.Vector())
+	{
+		if (data == enumEntry)
+			return "";
+	}
+	return validator.makeErrorMessage("Key must have one of predefined values");
+}
 
-			JsonNode::JsonType type = it->second;
+static std::string typeCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	static const std::unordered_map<std::string, JsonNode::JsonType> stringToType =
+	{
+		{"null",   JsonNode::JsonType::DATA_NULL},
+		{"boolean", JsonNode::JsonType::DATA_BOOL},
+		{"number", JsonNode::JsonType::DATA_FLOAT},
+		{"integer", JsonNode::JsonType::DATA_INTEGER},
+		{"string",  JsonNode::JsonType::DATA_STRING},
+		{"array",  JsonNode::JsonType::DATA_VECTOR},
+		{"object",  JsonNode::JsonType::DATA_STRUCT}
+	};
+
+	const auto & typeName = schema.String();
+	auto it = stringToType.find(typeName);
+	if(it == stringToType.end())
+	{
+		return validator.makeErrorMessage("Unknown type in schema:" + typeName);
+	}
 
-			// for "number" type both float and integer are allowed
-			if(type == JsonNode::JsonType::DATA_FLOAT && data.isNumber())
-				return "";
+	JsonNode::JsonType type = it->second;
 
-			if(type != data.getType() && data.getType() != JsonNode::JsonType::DATA_NULL)
-				return validator.makeErrorMessage("Type mismatch! Expected " + schema.String());
-			return "";
-		}
+	// for "number" type both float and integer are allowed
+	if(type == JsonNode::JsonType::DATA_FLOAT && data.isNumber())
+		return "";
 
-		std::string refCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
-		{
-			std::string URI = schema.String();
-			//node must be validated using schema pointed by this reference and not by data here
-			//Local reference. Turn it into more easy to handle remote ref
-			if (boost::algorithm::starts_with(URI, "#"))
-			{
-				const std::string name = validator.usedSchemas.back();
-				const std::string nameClean = name.substr(0, name.find('#'));
-				URI = nameClean + URI;
-			}
-			return check(URI, data, validator);
-		}
+	if(type != data.getType() && data.getType() != JsonNode::JsonType::DATA_NULL)
+		return validator.makeErrorMessage("Type mismatch! Expected " + schema.String());
+	return "";
+}
 
-		std::string formatCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
-		{
-			auto formats = Validation::getKnownFormats();
-			std::string errors;
-			auto checker = formats.find(schema.String());
-			if (checker != formats.end())
-			{
-				if (data.isString())
-				{
-					std::string result = checker->second(data);
-					if (!result.empty())
-						errors += validator.makeErrorMessage(result);
-				}
-				else
-				{
-					errors += validator.makeErrorMessage("Format value must be string: " + schema.String());
-				}
-			}
-			else
-				errors += validator.makeErrorMessage("Unsupported format type: " + schema.String());
-			return errors;
-		}
+static std::string refCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	std::string URI = schema.String();
+	//node must be validated using schema pointed by this reference and not by data here
+	//Local reference. Turn it into more easy to handle remote ref
+	if (boost::algorithm::starts_with(URI, "#"))
+	{
+		const std::string name = validator.usedSchemas.back();
+		const std::string nameClean = name.substr(0, name.find('#'));
+		URI = nameClean + URI;
 	}
+	return validator.check(URI, data);
+}
 
-	namespace String
+static std::string formatCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	auto formats = validator.getKnownFormats();
+	std::string errors;
+	auto checker = formats.find(schema.String());
+	if (checker != formats.end())
 	{
-		std::string maxLengthCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		if (data.isString())
 		{
-			if (data.String().size() > schema.Float())
-				return validator.makeErrorMessage((boost::format("String is longer than %d symbols") % schema.Float()).str());
-			return "";
+			std::string result = checker->second(data);
+			if (!result.empty())
+				errors += validator.makeErrorMessage(result);
 		}
-
-		std::string minLengthCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		else
 		{
-			if (data.String().size() < schema.Float())
-				return validator.makeErrorMessage((boost::format("String is shorter than %d symbols") % schema.Float()).str());
-			return "";
+			errors += validator.makeErrorMessage("Format value must be string: " + schema.String());
 		}
 	}
+	else
+		errors += validator.makeErrorMessage("Unsupported format type: " + schema.String());
+	return errors;
+}
 
-	namespace Number
-	{
+static std::string maxLengthCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	if (data.String().size() > schema.Float())
+		return validator.makeErrorMessage((boost::format("String is longer than %d symbols") % schema.Float()).str());
+	return "";
+}
 
-		std::string maximumCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
-		{
-			if (data.Float() > schema.Float())
-				return validator.makeErrorMessage((boost::format("Value is bigger than %d") % schema.Float()).str());
-			return "";
-		}
+static std::string minLengthCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	if (data.String().size() < schema.Float())
+		return validator.makeErrorMessage((boost::format("String is shorter than %d symbols") % schema.Float()).str());
+	return "";
+}
 
-		std::string minimumCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
-		{
-			if (data.Float() < schema.Float())
-				return validator.makeErrorMessage((boost::format("Value is smaller than %d") % schema.Float()).str());
-			return "";
-		}
+static std::string maximumCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	if (data.Float() > schema.Float())
+		return validator.makeErrorMessage((boost::format("Value is bigger than %d") % schema.Float()).str());
+	return "";
+}
 
-		std::string exclusiveMaximumCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
-		{
-			if (data.Float() >= schema.Float())
-				return validator.makeErrorMessage((boost::format("Value is bigger than %d") % schema.Float()).str());
-			return "";
-		}
+static std::string minimumCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	if (data.Float() < schema.Float())
+		return validator.makeErrorMessage((boost::format("Value is smaller than %d") % schema.Float()).str());
+	return "";
+}
 
-		std::string exclusiveMinimumCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
-		{
-			if (data.Float() <= schema.Float())
-				return validator.makeErrorMessage((boost::format("Value is smaller than %d") % schema.Float()).str());
-			return "";
-		}
+static std::string exclusiveMaximumCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	if (data.Float() >= schema.Float())
+		return validator.makeErrorMessage((boost::format("Value is bigger than %d") % schema.Float()).str());
+	return "";
+}
 
-		std::string multipleOfCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
-		{
-			double result = data.Integer() / schema.Integer();
-			if (!vstd::isAlmostEqual(floor(result), result))
-				return validator.makeErrorMessage((boost::format("Value is not divisible by %d") % schema.Float()).str());
-			return "";
-		}
-	}
+static std::string exclusiveMinimumCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	if (data.Float() <= schema.Float())
+		return validator.makeErrorMessage((boost::format("Value is smaller than %d") % schema.Float()).str());
+	return "";
+}
 
-	namespace Vector
+static std::string multipleOfCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	double result = data.Integer() / schema.Integer();
+	if (!vstd::isAlmostEqual(floor(result), result))
+		return validator.makeErrorMessage((boost::format("Value is not divisible by %d") % schema.Float()).str());
+	return "";
+}
+
+static std::string itemEntryCheck(JsonValidator & validator, const JsonVector & items, const JsonNode & schema, size_t index)
+{
+	validator.currentPath.emplace_back();
+	validator.currentPath.back().Float() = static_cast<double>(index);
+	auto onExit = vstd::makeScopeGuard([&validator]()
 	{
-		std::string itemEntryCheck(Validation::ValidationData & validator, const JsonVector & items, const JsonNode & schema, size_t index)
-		{
-			validator.currentPath.emplace_back();
-			validator.currentPath.back().Float() = static_cast<double>(index);
-			auto onExit = vstd::makeScopeGuard([&validator]()
-			{
-				validator.currentPath.pop_back();
-			});
+		validator.currentPath.pop_back();
+	});
 
-			if (!schema.isNull())
-				return check(schema, items[index], validator);
-			return "";
-		}
+	if (!schema.isNull())
+		return validator.check(schema, items[index]);
+	return "";
+}
 
-		std::string itemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+static std::string itemsCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	std::string errors;
+	for (size_t i=0; i<data.Vector().size(); i++)
+	{
+		if (schema.getType() == JsonNode::JsonType::DATA_VECTOR)
 		{
-			std::string errors;
-			for (size_t i=0; i<data.Vector().size(); i++)
-			{
-				if (schema.getType() == JsonNode::JsonType::DATA_VECTOR)
-				{
-					if (schema.Vector().size() > i)
-						errors += itemEntryCheck(validator, data.Vector(), schema.Vector()[i], i);
-				}
-				else
-				{
-					errors += itemEntryCheck(validator, data.Vector(), schema, i);
-				}
-			}
-			return errors;
+			if (schema.Vector().size() > i)
+				errors += itemEntryCheck(validator, data.Vector(), schema.Vector()[i], i);
 		}
-
-		std::string additionalItemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		else
 		{
-			std::string errors;
-			// "items" is struct or empty (defaults to empty struct) - validation always successful
-			const JsonNode & items = baseSchema["items"];
-			if (items.getType() != JsonNode::JsonType::DATA_VECTOR)
-				return "";
-
-			for (size_t i=items.Vector().size(); i<data.Vector().size(); i++)
-			{
-				if (schema.getType() == JsonNode::JsonType::DATA_STRUCT)
-					errors += itemEntryCheck(validator, data.Vector(), schema, i);
-				else if(!schema.isNull() && !schema.Bool())
-					errors += validator.makeErrorMessage("Unknown entry found");
-			}
-			return errors;
+			errors += itemEntryCheck(validator, data.Vector(), schema, i);
 		}
+	}
+	return errors;
+}
 
-		std::string minItemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
-		{
-			if (data.Vector().size() < schema.Float())
-				return validator.makeErrorMessage((boost::format("Length is smaller than %d") % schema.Float()).str());
-			return "";
-		}
+static std::string additionalItemsCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	std::string errors;
+	// "items" is struct or empty (defaults to empty struct) - validation always successful
+	const JsonNode & items = baseSchema["items"];
+	if (items.getType() != JsonNode::JsonType::DATA_VECTOR)
+		return "";
 
-		std::string maxItemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
-		{
-			if (data.Vector().size() > schema.Float())
-				return validator.makeErrorMessage((boost::format("Length is bigger than %d") % schema.Float()).str());
-			return "";
-		}
+	for (size_t i=items.Vector().size(); i<data.Vector().size(); i++)
+	{
+		if (schema.getType() == JsonNode::JsonType::DATA_STRUCT)
+			errors += itemEntryCheck(validator, data.Vector(), schema, i);
+		else if(!schema.isNull() && !schema.Bool())
+			errors += validator.makeErrorMessage("Unknown entry found");
+	}
+	return errors;
+}
+
+static std::string minItemsCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	if (data.Vector().size() < schema.Float())
+		return validator.makeErrorMessage((boost::format("Length is smaller than %d") % schema.Float()).str());
+	return "";
+}
+
+static std::string maxItemsCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	if (data.Vector().size() > schema.Float())
+		return validator.makeErrorMessage((boost::format("Length is bigger than %d") % schema.Float()).str());
+	return "";
+}
 
-		std::string uniqueItemsCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+static std::string uniqueItemsCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	if (schema.Bool())
+	{
+		for (auto itA = schema.Vector().begin(); itA != schema.Vector().end(); itA++)
 		{
-			if (schema.Bool())
+			auto itB = itA;
+			while (++itB != schema.Vector().end())
 			{
-				for (auto itA = schema.Vector().begin(); itA != schema.Vector().end(); itA++)
-				{
-					auto itB = itA;
-					while (++itB != schema.Vector().end())
-					{
-						if (*itA == *itB)
-							return validator.makeErrorMessage("List must consist from unique items");
-					}
-				}
+				if (*itA == *itB)
+					return validator.makeErrorMessage("List must consist from unique items");
 			}
-			return "";
 		}
 	}
+	return "";
+}
 
-	namespace Struct
+static std::string maxPropertiesCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	if (data.Struct().size() > schema.Float())
+		return validator.makeErrorMessage((boost::format("Number of entries is bigger than %d") % schema.Float()).str());
+	return "";
+}
+
+static std::string minPropertiesCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	if (data.Struct().size() < schema.Float())
+		return validator.makeErrorMessage((boost::format("Number of entries is less than %d") % schema.Float()).str());
+	return "";
+}
+
+static std::string uniquePropertiesCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	for (auto itA = data.Struct().begin(); itA != data.Struct().end(); itA++)
 	{
-		std::string maxPropertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+		auto itB = itA;
+		while (++itB != data.Struct().end())
 		{
-			if (data.Struct().size() > schema.Float())
-				return validator.makeErrorMessage((boost::format("Number of entries is bigger than %d") % schema.Float()).str());
-			return "";
+			if (itA->second == itB->second)
+				return validator.makeErrorMessage("List must consist from unique items");
 		}
+	}
+	return "";
+}
 
-		std::string minPropertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
-		{
-			if (data.Struct().size() < schema.Float())
-				return validator.makeErrorMessage((boost::format("Number of entries is less than %d") % schema.Float()).str());
-			return "";
-		}
+static std::string requiredCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	std::string errors;
+	for(const auto & required : schema.Vector())
+	{
+		if (data[required.String()].isNull())
+			errors += validator.makeErrorMessage("Required entry " + required.String() + " is missing");
+	}
+	return errors;
+}
 
-		std::string uniquePropertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+static std::string dependenciesCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	std::string errors;
+	for(const auto & deps : schema.Struct())
+	{
+		if (!data[deps.first].isNull())
 		{
-			for (auto itA = data.Struct().begin(); itA != data.Struct().end(); itA++)
+			if (deps.second.getType() == JsonNode::JsonType::DATA_VECTOR)
 			{
-				auto itB = itA;
-				while (++itB != data.Struct().end())
+				JsonVector depList = deps.second.Vector();
+				for(auto & depEntry : depList)
 				{
-					if (itA->second == itB->second)
-						return validator.makeErrorMessage("List must consist from unique items");
+					if (data[depEntry.String()].isNull())
+						errors += validator.makeErrorMessage("Property " + depEntry.String() + " required for " + deps.first + " is missing");
 				}
 			}
-			return "";
-		}
-
-		std::string requiredCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
-		{
-			std::string errors;
-			for(const auto & required : schema.Vector())
-			{
-				if (data[required.String()].isNull())
-					errors += validator.makeErrorMessage("Required entry " + required.String() + " is missing");
-			}
-			return errors;
-		}
-
-		std::string dependenciesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
-		{
-			std::string errors;
-			for(const auto & deps : schema.Struct())
+			else
 			{
-				if (!data[deps.first].isNull())
-				{
-					if (deps.second.getType() == JsonNode::JsonType::DATA_VECTOR)
-					{
-						JsonVector depList = deps.second.Vector();
-						for(auto & depEntry : depList)
-						{
-							if (data[depEntry.String()].isNull())
-								errors += validator.makeErrorMessage("Property " + depEntry.String() + " required for " + deps.first + " is missing");
-						}
-					}
-					else
-					{
-						if (!check(deps.second, data, validator).empty())
-							errors += validator.makeErrorMessage("Requirements for " + deps.first + " are not fulfilled");
-					}
-				}
+				if (!validator.check(deps.second, data).empty())
+					errors += validator.makeErrorMessage("Requirements for " + deps.first + " are not fulfilled");
 			}
-			return errors;
 		}
+	}
+	return errors;
+}
 
-		std::string propertyEntryCheck(Validation::ValidationData & validator, const JsonNode &node, const JsonNode & schema, const std::string & nodeName)
-		{
-			validator.currentPath.emplace_back();
-			validator.currentPath.back().String() = nodeName;
-			auto onExit = vstd::makeScopeGuard([&validator]()
-			{
-				validator.currentPath.pop_back();
-			});
+static std::string propertyEntryCheck(JsonValidator & validator, const JsonNode &node, const JsonNode & schema, const std::string & nodeName)
+{
+	validator.currentPath.emplace_back();
+	validator.currentPath.back().String() = nodeName;
+	auto onExit = vstd::makeScopeGuard([&validator]()
+	{
+		validator.currentPath.pop_back();
+	});
 
-			// there is schema specifically for this item
-			if (!schema.isNull())
-				return check(schema, node, validator);
-			return "";
-		}
+	// there is schema specifically for this item
+	if (!schema.isNull())
+		return validator.check(schema, node);
+	return "";
+}
 
-		std::string propertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
-		{
-			std::string errors;
+static std::string propertiesCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	std::string errors;
 
-			for(const auto & entry : data.Struct())
-				errors += propertyEntryCheck(validator, entry.second, schema[entry.first], entry.first);
-			return errors;
-		}
+	for(const auto & entry : data.Struct())
+		errors += propertyEntryCheck(validator, entry.second, schema[entry.first], entry.first);
+	return errors;
+}
 
-		std::string additionalPropertiesCheck(Validation::ValidationData & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+static std::string additionalPropertiesCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
+{
+	std::string errors;
+	for(const auto & entry : data.Struct())
+	{
+		if (baseSchema["properties"].Struct().count(entry.first) == 0)
 		{
-			std::string errors;
-			for(const auto & entry : data.Struct())
-			{
-				if (baseSchema["properties"].Struct().count(entry.first) == 0)
-				{
-					// try generic additionalItems schema
-					if (schema.getType() == JsonNode::JsonType::DATA_STRUCT)
-						errors += propertyEntryCheck(validator, entry.second, schema, entry.first);
+			// try generic additionalItems schema
+			if (schema.getType() == JsonNode::JsonType::DATA_STRUCT)
+				errors += propertyEntryCheck(validator, entry.second, schema, entry.first);
 
-					// or, additionalItems field can be bool which indicates if such items are allowed
-					else if(!schema.isNull() && !schema.Bool()) // present and set to false - error
-						errors += validator.makeErrorMessage("Unknown entry found: " + entry.first);
-				}
-			}
-			return errors;
+			// or, additionalItems field can be bool which indicates if such items are allowed
+			else if(!schema.isNull() && !schema.Bool()) // present and set to false - error
+				errors += validator.makeErrorMessage("Unknown entry found: " + entry.first);
 		}
 	}
+	return errors;
+}
 
-	namespace Formats
+static bool testFilePresence(const std::string & scope, const ResourcePath & resource)
+{
+	std::set<std::string> allowedScopes;
+	if(scope != ModScope::scopeBuiltin() && !scope.empty()) // all real mods may have dependencies
 	{
-		bool testFilePresence(const std::string & scope, const ResourcePath & resource)
-		{
-			std::set<std::string> allowedScopes;
-			if(scope != ModScope::scopeBuiltin() && !scope.empty()) // all real mods may have dependencies
-			{
-				//NOTE: recursive dependencies are not allowed at the moment - update code if this changes
-				bool found = true;
-				allowedScopes = VLC->modh->getModDependencies(scope, found);
+		//NOTE: recursive dependencies are not allowed at the moment - update code if this changes
+		bool found = true;
+		allowedScopes = VLC->modh->getModDependencies(scope, found);
 
-				if(!found)
-					return false;
-
-				allowedScopes.insert(ModScope::scopeBuiltin()); // all mods can use H3 files
-			}
-			allowedScopes.insert(scope); // mods can use their own files
-
-			for(const auto & entry : allowedScopes)
-			{
-				if (CResourceHandler::get(entry)->existsResource(resource))
-					return true;
-			}
+		if(!found)
 			return false;
-		}
 
-		#define TEST_FILE(scope, prefix, file, type) \
-			if (testFilePresence(scope, ResourcePath(prefix + file, type))) \
-				return ""
+		allowedScopes.insert(ModScope::scopeBuiltin()); // all mods can use H3 files
+	}
+	allowedScopes.insert(scope); // mods can use their own files
 
-		std::string testAnimation(const std::string & path, const std::string & scope)
-		{
-			TEST_FILE(scope, "Sprites/", path, EResType::ANIMATION);
-			TEST_FILE(scope, "Sprites/", path, EResType::JSON);
-			return "Animation file \"" + path + "\" was not found";
-		}
+	for(const auto & entry : allowedScopes)
+	{
+		if (CResourceHandler::get(entry)->existsResource(resource))
+			return true;
+	}
+	return false;
+}
 
-		std::string textFile(const JsonNode & node)
-		{
-			TEST_FILE(node.getModScope(), "", node.String(), EResType::JSON);
-			return "Text file \"" + node.String() + "\" was not found";
-		}
+#define TEST_FILE(scope, prefix, file, type) \
+	if (testFilePresence(scope, ResourcePath(prefix + file, type))) \
+	return ""
 
-		std::string musicFile(const JsonNode & node)
-		{
-			TEST_FILE(node.getModScope(), "Music/", node.String(), EResType::SOUND);
-			TEST_FILE(node.getModScope(), "", node.String(), EResType::SOUND);
-			return "Music file \"" + node.String() + "\" was not found";
-		}
+static std::string testAnimation(const std::string & path, const std::string & scope)
+{
+	TEST_FILE(scope, "Sprites/", path, EResType::ANIMATION);
+	TEST_FILE(scope, "Sprites/", path, EResType::JSON);
+	return "Animation file \"" + path + "\" was not found";
+}
 
-		std::string soundFile(const JsonNode & node)
-		{
-			TEST_FILE(node.getModScope(), "Sounds/", node.String(), EResType::SOUND);
-			return "Sound file \"" + node.String() + "\" was not found";
-		}
+static std::string textFile(const JsonNode & node)
+{
+	TEST_FILE(node.getModScope(), "", node.String(), EResType::JSON);
+	return "Text file \"" + node.String() + "\" was not found";
+}
 
-		std::string defFile(const JsonNode & node)
-		{
-			return testAnimation(node.String(), node.getModScope());
-		}
+static std::string musicFile(const JsonNode & node)
+{
+	TEST_FILE(node.getModScope(), "Music/", node.String(), EResType::SOUND);
+	TEST_FILE(node.getModScope(), "", node.String(), EResType::SOUND);
+	return "Music file \"" + node.String() + "\" was not found";
+}
 
-		std::string animationFile(const JsonNode & node)
-		{
-			return testAnimation(node.String(), node.getModScope());
-		}
+static std::string soundFile(const JsonNode & node)
+{
+	TEST_FILE(node.getModScope(), "Sounds/", node.String(), EResType::SOUND);
+	return "Sound file \"" + node.String() + "\" was not found";
+}
 
-		std::string imageFile(const JsonNode & node)
-		{
-			TEST_FILE(node.getModScope(), "Data/", node.String(), EResType::IMAGE);
-			TEST_FILE(node.getModScope(), "Sprites/", node.String(), EResType::IMAGE);
-			if (node.String().find(':') != std::string::npos)
-				return testAnimation(node.String().substr(0, node.String().find(':')), node.getModScope());
-			return "Image file \"" + node.String() + "\" was not found";
-		}
+static std::string animationFile(const JsonNode & node)
+{
+	return testAnimation(node.String(), node.getModScope());
+}
 
-		std::string videoFile(const JsonNode & node)
-		{
-			TEST_FILE(node.getModScope(), "Video/", node.String(), EResType::VIDEO);
-			return "Video file \"" + node.String() + "\" was not found";
-		}
+static std::string imageFile(const JsonNode & node)
+{
+	TEST_FILE(node.getModScope(), "Data/", node.String(), EResType::IMAGE);
+	TEST_FILE(node.getModScope(), "Sprites/", node.String(), EResType::IMAGE);
+	if (node.String().find(':') != std::string::npos)
+		return testAnimation(node.String().substr(0, node.String().find(':')), node.getModScope());
+	return "Image file \"" + node.String() + "\" was not found";
+}
 
-		#undef TEST_FILE
-	}
+static std::string videoFile(const JsonNode & node)
+{
+	TEST_FILE(node.getModScope(), "Video/", node.String(), EResType::VIDEO);
+	return "Video file \"" + node.String() + "\" was not found";
+}
+#undef TEST_FILE
 
-	Validation::TValidatorMap createCommonFields()
-	{
-		Validation::TValidatorMap ret;
-
-		ret["format"] =  Common::formatCheck;
-		ret["allOf"] = Common::allOfCheck;
-		ret["anyOf"] = Common::anyOfCheck;
-		ret["oneOf"] = Common::oneOfCheck;
-		ret["enum"]  = Common::enumCheck;
-		ret["type"]  = Common::typeCheck;
-		ret["not"]   = Common::notCheck;
-		ret["$ref"]  = Common::refCheck;
-
-		// fields that don't need implementation
-		ret["title"] = Common::emptyCheck;
-		ret["$schema"] = Common::emptyCheck;
-		ret["default"] = Common::emptyCheck;
-		ret["defaultIOS"] = Common::emptyCheck;
-		ret["defaultAndroid"] = Common::emptyCheck;
-		ret["defaultWindows"] = Common::emptyCheck;
-		ret["description"] = Common::emptyCheck;
-		ret["definitions"] = Common::emptyCheck;
-
-		// Not implemented
-		ret["propertyNames"] = Common::emptyCheck;
-		ret["contains"] = Common::emptyCheck;
-		ret["const"] = Common::emptyCheck;
-		ret["examples"] = Common::emptyCheck;
-
-		return ret;
-	}
+JsonValidator::TValidatorMap createCommonFields()
+{
+	JsonValidator::TValidatorMap ret;
+
+	ret["format"] =  formatCheck;
+	ret["allOf"] = allOfCheck;
+	ret["anyOf"] = anyOfCheck;
+	ret["oneOf"] = oneOfCheck;
+	ret["enum"]  = enumCheck;
+	ret["type"]  = typeCheck;
+	ret["not"]   = notCheck;
+	ret["$ref"]  = refCheck;
+
+	// fields that don't need implementation
+	ret["title"] = emptyCheck;
+	ret["$schema"] = emptyCheck;
+	ret["default"] = emptyCheck;
+	ret["defaultIOS"] = emptyCheck;
+	ret["defaultAndroid"] = emptyCheck;
+	ret["defaultWindows"] = emptyCheck;
+	ret["description"] = emptyCheck;
+	ret["definitions"] = emptyCheck;
+
+	// Not implemented
+	ret["propertyNames"] = notImplementedCheck;
+	ret["contains"] = notImplementedCheck;
+	ret["const"] = notImplementedCheck;
+	ret["examples"] = notImplementedCheck;
+
+	return ret;
+}
 
-	Validation::TValidatorMap createStringFields()
-	{
-		Validation::TValidatorMap ret = createCommonFields();
-		ret["maxLength"] = String::maxLengthCheck;
-		ret["minLength"] = String::minLengthCheck;
+JsonValidator::TValidatorMap createStringFields()
+{
+	JsonValidator::TValidatorMap ret = createCommonFields();
+	ret["maxLength"] = maxLengthCheck;
+	ret["minLength"] = minLengthCheck;
 
-		ret["pattern"] = Common::notImplementedCheck;
-		return ret;
-	}
+	ret["pattern"] = notImplementedCheck;
+	return ret;
+}
 
-	Validation::TValidatorMap createNumberFields()
-	{
-		Validation::TValidatorMap ret = createCommonFields();
-		ret["maximum"]    = Number::maximumCheck;
-		ret["minimum"]    = Number::minimumCheck;
-		ret["multipleOf"] = Number::multipleOfCheck;
-
-		ret["exclusiveMaximum"] = Number::exclusiveMaximumCheck;
-		ret["exclusiveMinimum"] = Number::exclusiveMinimumCheck;
-		return ret;
-	}
+JsonValidator::TValidatorMap createNumberFields()
+{
+	JsonValidator::TValidatorMap ret = createCommonFields();
+	ret["maximum"]    = maximumCheck;
+	ret["minimum"]    = minimumCheck;
+	ret["multipleOf"] = multipleOfCheck;
+
+	ret["exclusiveMaximum"] = exclusiveMaximumCheck;
+	ret["exclusiveMinimum"] = exclusiveMinimumCheck;
+	return ret;
+}
 
-	Validation::TValidatorMap createVectorFields()
-	{
-		Validation::TValidatorMap ret = createCommonFields();
-		ret["items"]           = Vector::itemsCheck;
-		ret["minItems"]        = Vector::minItemsCheck;
-		ret["maxItems"]        = Vector::maxItemsCheck;
-		ret["uniqueItems"]     = Vector::uniqueItemsCheck;
-		ret["additionalItems"] = Vector::additionalItemsCheck;
-		return ret;
-	}
+JsonValidator::TValidatorMap createVectorFields()
+{
+	JsonValidator::TValidatorMap ret = createCommonFields();
+	ret["items"]           = itemsCheck;
+	ret["minItems"]        = minItemsCheck;
+	ret["maxItems"]        = maxItemsCheck;
+	ret["uniqueItems"]     = uniqueItemsCheck;
+	ret["additionalItems"] = additionalItemsCheck;
+	return ret;
+}
 
-	Validation::TValidatorMap createStructFields()
-	{
-		Validation::TValidatorMap ret = createCommonFields();
-		ret["additionalProperties"]  = Struct::additionalPropertiesCheck;
-		ret["uniqueProperties"]      = Struct::uniquePropertiesCheck;
-		ret["maxProperties"]         = Struct::maxPropertiesCheck;
-		ret["minProperties"]         = Struct::minPropertiesCheck;
-		ret["dependencies"]          = Struct::dependenciesCheck;
-		ret["properties"]            = Struct::propertiesCheck;
-		ret["required"]              = Struct::requiredCheck;
-
-		ret["patternProperties"] = Common::notImplementedCheck;
-		return ret;
-	}
+JsonValidator::TValidatorMap createStructFields()
+{
+	JsonValidator::TValidatorMap ret = createCommonFields();
+	ret["additionalProperties"]  = additionalPropertiesCheck;
+	ret["uniqueProperties"]      = uniquePropertiesCheck;
+	ret["maxProperties"]         = maxPropertiesCheck;
+	ret["minProperties"]         = minPropertiesCheck;
+	ret["dependencies"]          = dependenciesCheck;
+	ret["properties"]            = propertiesCheck;
+	ret["required"]              = requiredCheck;
+
+	ret["patternProperties"] = notImplementedCheck;
+	return ret;
+}
 
-	Validation::TFormatMap createFormatMap()
-	{
-		Validation::TFormatMap ret;
-		ret["textFile"]      = Formats::textFile;
-		ret["musicFile"]     = Formats::musicFile;
-		ret["soundFile"]     = Formats::soundFile;
-		ret["defFile"]       = Formats::defFile;
-		ret["animationFile"] = Formats::animationFile;
-		ret["imageFile"]     = Formats::imageFile;
-		ret["videoFile"]     = Formats::videoFile;
-
-		//TODO:
-		// uri-reference
-		// uri-template
-		// json-pointer
-
-		return ret;
-	}
+JsonValidator::TFormatMap createFormatMap()
+{
+	JsonValidator::TFormatMap ret;
+	ret["textFile"]      = textFile;
+	ret["musicFile"]     = musicFile;
+	ret["soundFile"]     = soundFile;
+	ret["animationFile"] = animationFile;
+	ret["imageFile"]     = imageFile;
+	ret["videoFile"]     = videoFile;
+
+	//TODO:
+	// uri-reference
+	// uri-template
+	// json-pointer
+
+	return ret;
 }
 
-namespace Validation
+std::string JsonValidator::makeErrorMessage(const std::string &message)
 {
-	std::string ValidationData::makeErrorMessage(const std::string &message)
+	std::string errors;
+	errors += "At ";
+	if (!currentPath.empty())
 	{
-		std::string errors;
-		errors += "At ";
-		if (!currentPath.empty())
+		for(const JsonNode &path : currentPath)
 		{
-			for(const JsonNode &path : currentPath)
-			{
-				errors += "/";
-				if (path.getType() == JsonNode::JsonType::DATA_STRING)
-					errors += path.String();
-				else
-					errors += std::to_string(static_cast<unsigned>(path.Float()));
-			}
+			errors += "/";
+			if (path.getType() == JsonNode::JsonType::DATA_STRING)
+				errors += path.String();
+			else
+				errors += std::to_string(static_cast<unsigned>(path.Float()));
 		}
-		else
-			errors += "<root>";
-		errors += "\n\t Error: " + message + "\n";
-		return errors;
-	}
-
-	std::string check(const std::string & schemaName, const JsonNode & data)
-	{
-		ValidationData validator;
-		return check(schemaName, data, validator);
 	}
+	else
+		errors += "<root>";
+	errors += "\n\t Error: " + message + "\n";
+	return errors;
+}
 
-	std::string check(const std::string & schemaName, const JsonNode & data, ValidationData & validator)
+std::string JsonValidator::check(const std::string & schemaName, const JsonNode & data)
+{
+	usedSchemas.push_back(schemaName);
+	auto onscopeExit = vstd::makeScopeGuard([this]()
 	{
-		validator.usedSchemas.push_back(schemaName);
-		auto onscopeExit = vstd::makeScopeGuard([&validator]()
-		{
-			validator.usedSchemas.pop_back();
-		});
-		return check(JsonUtils::getSchema(schemaName), data, validator);
-	}
+		usedSchemas.pop_back();
+	});
+	return check(JsonUtils::getSchema(schemaName), data);
+}
 
-	std::string check(const JsonNode & schema, const JsonNode & data, ValidationData & validator)
+std::string JsonValidator::check(const JsonNode & schema, const JsonNode & data)
+{
+	const TValidatorMap & knownFields = getKnownFieldsFor(data.getType());
+	std::string errors;
+	for(const auto & entry : schema.Struct())
 	{
-		const TValidatorMap & knownFields = getKnownFieldsFor(data.getType());
-		std::string errors;
-		for(const auto & entry : schema.Struct())
-		{
-			auto checker = knownFields.find(entry.first);
-			if (checker != knownFields.end())
-				errors += checker->second(validator, schema, entry.second, data);
-		}
-		return errors;
+		auto checker = knownFields.find(entry.first);
+		if (checker != knownFields.end())
+			errors += checker->second(*this, schema, entry.second, data);
 	}
+	return errors;
+}
 
-	const TValidatorMap & getKnownFieldsFor(JsonNode::JsonType type)
-	{
-		static const TValidatorMap commonFields = createCommonFields();
-		static const TValidatorMap numberFields = createNumberFields();
-		static const TValidatorMap stringFields = createStringFields();
-		static const TValidatorMap vectorFields = createVectorFields();
-		static const TValidatorMap structFields = createStructFields();
-
-		switch (type)
-		{
-			case JsonNode::JsonType::DATA_FLOAT:
-			case JsonNode::JsonType::DATA_INTEGER:
-				return numberFields;
-			case JsonNode::JsonType::DATA_STRING: return stringFields;
-			case JsonNode::JsonType::DATA_VECTOR: return vectorFields;
-			case JsonNode::JsonType::DATA_STRUCT: return structFields;
-			default: return commonFields;
-		}
-	}
+const JsonValidator::TValidatorMap & JsonValidator::getKnownFieldsFor(JsonNode::JsonType type)
+{
+	static const TValidatorMap commonFields = createCommonFields();
+	static const TValidatorMap numberFields = createNumberFields();
+	static const TValidatorMap stringFields = createStringFields();
+	static const TValidatorMap vectorFields = createVectorFields();
+	static const TValidatorMap structFields = createStructFields();
 
-	const TFormatMap & getKnownFormats()
+	switch (type)
 	{
-		static const TFormatMap knownFormats = createFormatMap();
-		return knownFormats;
+		case JsonNode::JsonType::DATA_FLOAT:
+		case JsonNode::JsonType::DATA_INTEGER:
+			return numberFields;
+		case JsonNode::JsonType::DATA_STRING: return stringFields;
+		case JsonNode::JsonType::DATA_VECTOR: return vectorFields;
+		case JsonNode::JsonType::DATA_STRUCT: return structFields;
+		default: return commonFields;
 	}
+}
 
-} // Validation namespace
+const JsonValidator::TFormatMap & JsonValidator::getKnownFormats()
+{
+	static const TFormatMap knownFormats = createFormatMap();
+	return knownFormats;
+}
 
 VCMI_LIB_NAMESPACE_END

+ 13 - 19
lib/json/JsonValidator.h

@@ -13,28 +13,23 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-
-//Internal class for Json validation. Mostly compilant with json-schema v6 draft
-namespace Validation
+/// Class for Json validation. Mostly compilant with json-schema v6 draf
+struct JsonValidator
 {
-	/// struct used to pass data around during validation
-	struct ValidationData
-	{
-		/// path from root node to current one.
-		/// JsonNode is used as variant - either string (name of node) or as float (index in list)
-		std::vector<JsonNode> currentPath;
+	/// path from root node to current one.
+	/// JsonNode is used as variant - either string (name of node) or as float (index in list)
+	std::vector<JsonNode> currentPath;
 
-		/// Stack of used schemas. Last schema is the one used currently.
-		/// May contain multiple items in case if remote references were found
-		std::vector<std::string> usedSchemas;
+	/// Stack of used schemas. Last schema is the one used currently.
+	/// May contain multiple items in case if remote references were found
+	std::vector<std::string> usedSchemas;
 
-		/// generates error message
-		std::string makeErrorMessage(const std::string &message);
-	};
+	/// generates error message
+	std::string makeErrorMessage(const std::string &message);
 
 	using TFormatValidator = std::function<std::string(const JsonNode &)>;
 	using TFormatMap = std::unordered_map<std::string, TFormatValidator>;
-	using TFieldValidator = std::function<std::string(ValidationData &, const JsonNode &, const JsonNode &, const JsonNode &)>;
+	using TFieldValidator = std::function<std::string(JsonValidator &, const JsonNode &, const JsonNode &, const JsonNode &)>;
 	using TValidatorMap = std::unordered_map<std::string, TFieldValidator>;
 
 	/// map of known fields in schema
@@ -42,8 +37,7 @@ namespace Validation
 	const TFormatMap & getKnownFormats();
 
 	std::string check(const std::string & schemaName, const JsonNode & data);
-	std::string check(const std::string & schemaName, const JsonNode & data, ValidationData & validator);
-	std::string check(const JsonNode & schema, const JsonNode & data, ValidationData & validator);
-}
+	std::string check(const JsonNode & schema, const JsonNode & data);
+};
 
 VCMI_LIB_NAMESPACE_END