#pragma once

#include <source_location>

#include "AbstractElement.h"
#include "Array.h"
#include "JSON.h"
#include "Trie.h"
#include "XML.h"

namespace Framework
{
    namespace Validator
    {

        class ValidationResult : public Framework::ReferenceCounter
        {
        public:
            __declspec(dllexport) ValidationResult();
            __declspec(dllexport) virtual ~ValidationResult();
            virtual bool isValid() const = 0;
            __declspec(dllexport) void logInvalidInfo(
                std::source_location location
                = std::source_location::current()) const;
            virtual Text getInvalidInfo(int indent = 0) const = 0;
            virtual JSON::JSONValue* getValidPart(
                RCArray<ValidationResult>* zRemovedPartsValidationResults)
                = 0;
            virtual Text getPath() const = 0;
            virtual void addBasePath(Text basePath) = 0;
            virtual bool isDifferent(const ValidationResult* zResult) const = 0;
        };

        class TypeMissmatch : public ValidationResult
        {
        private:
            Text path;
            AbstractElement* foundValue;
            XML::Element* expected;
            ValidationResult* reason;

        public:
            __declspec(dllexport) TypeMissmatch(Text path,
                AbstractElement* foundValue,
                XML::Element* expected,
                ValidationResult* reason);
            __declspec(dllexport) ~TypeMissmatch();
            __declspec(dllexport) bool isValid() const override;
            __declspec(dllexport) Text getInvalidInfo(
                int indent) const override;

        protected:
            __declspec(dllexport) JSON::JSONValue* getValidPart(
                RCArray<ValidationResult>* zRemovedPartsValidationResults)
                override;
            __declspec(dllexport) Text getPath() const override;
            __declspec(dllexport) bool isDifferent(
                const ValidationResult* zResult) const override;
            __declspec(dllexport) void addBasePath(Text basePath) override;
        };

        class UnknownValue : public ValidationResult
        {
        private:
            Text path;
            AbstractElement* foundValue;

        public:
            __declspec(dllexport) UnknownValue(
                Text path, AbstractElement* foundValue);
            __declspec(dllexport) ~UnknownValue();
            __declspec(dllexport) bool isValid() const override;
            __declspec(dllexport) Text getInvalidInfo(
                int indent) const override;
            __declspec(dllexport) JSON::JSONValue* getValidPart(
                RCArray<ValidationResult>* zRemovedPartsValidationResults)
                override;
            __declspec(dllexport) Text getPath() const override;
            __declspec(dllexport) bool isDifferent(
                const ValidationResult* zResult) const override;
            __declspec(dllexport) void addBasePath(Text basePath) override;
        };

        class MissingValue : public ValidationResult
        {
        private:
            Text path;
            XML::Element* expected;

        public:
            __declspec(dllexport) MissingValue(
                Text path, XML::Element* expected);
            __declspec(dllexport) ~MissingValue();
            __declspec(dllexport) bool isValid() const override;
            __declspec(dllexport) Text getInvalidInfo(
                int indent) const override;
            __declspec(dllexport) JSON::JSONValue* getValidPart(
                RCArray<ValidationResult>* zRemovedPartsValidationResults)
                override;
            __declspec(dllexport) Text getPath() const override;
            __declspec(dllexport) bool isDifferent(
                const ValidationResult* zResult) const override;
            __declspec(dllexport) void addBasePath(Text basePath) override;
        };

        class MissingOneOf : public ValidationResult
        {
        private:
            Text path;
            RCArray<XML::Element> expected;

        public:
            __declspec(dllexport) MissingOneOf(Text path, XML::Editor expected);
            __declspec(dllexport) ~MissingOneOf();
            __declspec(dllexport) bool isValid() const override;
            __declspec(dllexport) Text getInvalidInfo(
                int indent) const override;
            __declspec(dllexport) JSON::JSONValue* getValidPart(
                RCArray<ValidationResult>* zRemovedPartsValidationResults)
                override;
            __declspec(dllexport) Text getPath() const override;
            __declspec(dllexport) bool isDifferent(
                const ValidationResult* zResult) const override;
            __declspec(dllexport) void addBasePath(Text basePath) override;
        };

        class NoTypeMatching : public ValidationResult
        {
        private:
            Text path;
            AbstractElement* foundValue;
            RCArray<XML::Element> expected;
            RCArray<ValidationResult> reasons;

        public:
            __declspec(dllexport) NoTypeMatching(Text path,
                AbstractElement* foundValue,
                RCArray<XML::Element>& expected,
                RCArray<ValidationResult>& reasons);
            __declspec(dllexport) ~NoTypeMatching();
            __declspec(dllexport) bool isValid() const override;
            __declspec(dllexport) Text getInvalidInfo(
                int indent) const override;
            __declspec(dllexport) JSON::JSONValue* getValidPart(
                RCArray<ValidationResult>* zRemovedPartsValidationResults)
                override;
            __declspec(dllexport) Text getPath() const override;
            __declspec(dllexport) bool isDifferent(
                const ValidationResult* zResult) const override;
            __declspec(dllexport) void addBasePath(Text basePath) override;
        };

        class ValidationPathNotFound : public ValidationResult
        {
        private:
            Text path;
            AbstractElement* foundValue;
            Text validationPath;

        public:
            __declspec(dllexport) ValidationPathNotFound(
                Text path, AbstractElement* foundValue, Text validationPath);
            __declspec(dllexport) ~ValidationPathNotFound();
            __declspec(dllexport) bool isValid() const override;
            __declspec(dllexport) Text getInvalidInfo(
                int indent) const override;
            __declspec(dllexport) JSON::JSONValue* getValidPart(
                RCArray<ValidationResult>* zRemovedPartsValidationResults)
                override;
            __declspec(dllexport) Text getPath() const override;
            __declspec(dllexport) bool isDifferent(
                const ValidationResult* zResult) const override;
            __declspec(dllexport) void addBasePath(Text basePath) override;
        };

        class ValidValue : public ValidationResult
        {
        private:
            Text path;
            AbstractElement* value;

        public:
            __declspec(dllexport) ValidValue(Text path, AbstractElement* value);
            __declspec(dllexport) ~ValidValue();
            __declspec(dllexport) bool isValid() const override;
            __declspec(dllexport) Text getInvalidInfo(
                int indent) const override;
            __declspec(dllexport) JSON::JSONValue* getValidPart(
                RCArray<ValidationResult>* zRemovedPartsValidationResults)
                override;
            __declspec(dllexport) Text getPath() const override;
            __declspec(dllexport) bool isDifferent(
                const ValidationResult* zResult) const override;
            __declspec(dllexport) void addBasePath(Text basePath) override;
        };

        template<typename T> class StringValidationBuilder;

        template<typename T> class NumberValidationBuilder;

        template<typename T> class BoolValidationBuilder;

        template<typename T> class ObjectValidationBuilder;

        template<typename T> class ArrayValidationBuilder;

        template<typename T> class OneOfValidationBuilder;

        class DataValidator : public Framework::ReferenceCounter
        {
        private:
            XML::Element* constraints;
            RCTrie<XML::Element>* typeConstraints;

        public:
            __declspec(dllexport) DataValidator(XML::Element* constraints);
            __declspec(dllexport) DataValidator(XML::Element* constraints,
                RCTrie<XML::Element>* typeConstraints);
            __declspec(dllexport) ~DataValidator();
            __declspec(dllexport) ValidationResult* validate(
                AbstractElement* zValue) const;
            __declspec(dllexport) ValidationResult* validate(
                ElementPath* path, AbstractElement* zValue) const;
            __declspec(dllexport) bool isValid(AbstractElement* zValue) const;
            /**
             * returns the valid part of the json by performing the
             * following operations in the specified order untill no further
             * changes are made:
             * - invalid or unknown object properties are removed
             * - missing object properties with default values are added if
             * missing
             * - invalid array elements are removed
             *
             * @param zValue the json value to to extract the valid part
             * without increased reference counter
             * @return the valid part or 0 if no valid part exists
             */
            __declspec(dllexport) JSON::JSONValue* getValidParts(
                JSON::JSONValue* zValue,
                RCArray<ValidationResult>* zRemovedPartsValidationResults)
                const;
            __declspec(dllexport) XML::Element* zConstraints();
            /**
             * returns the json schema for the constraints
             */
            __declspec(dllexport) JSON::JSONObject* getJsonSchema() const;
            /**
             * updates the validator for the datatype with a specified reference
             * id.
             *
             * \param id the reference id of the datatype
             * \param validator the validator that will validate a type with the
             * given reference id
             */
            __declspec(dllexport) void updateValidator(
                Text id, DataValidator* validator);

        private:
            __declspec(dllexport) ValidationResult* validate(
                ElementPath* pathToValidate,
                AbstractElement* zValue,
                XML::Element* zConstraints,
                Text path) const;
            __declspec(dllexport) ValidationResult* validateMultipleTypes(
                ElementPath* pathToValidate,
                AbstractElement* zChildValue,
                XML::Element* zPossibleChildConstraints,
                Text childPath) const;
            __declspec(dllexport) JSON::JSONObject* getJsonSchema(
                XML::Element* zConstraint, JSON::JSONObject* zDefs) const;

        public:
            __declspec(dllexport) static StringValidationBuilder<DataValidator>*
            buildForString();
            __declspec(dllexport) static NumberValidationBuilder<DataValidator>*
            buildForNumber();
            __declspec(dllexport) static BoolValidationBuilder<DataValidator>*
            buildForBool();
            __declspec(dllexport) static ObjectValidationBuilder<DataValidator>*
            buildForObject();
            __declspec(dllexport) static ArrayValidationBuilder<DataValidator>*
            buildForArray();
            __declspec(dllexport) static OneOfValidationBuilder<DataValidator>*
            buildForOneOf();
            __declspec(dllexport) static DataValidator* buildForReference(
                Text id);
        };

        template<typename T> class StringValidationBuilder
        {
        private:
            XML::Element element;
            std::function<T*(XML::Element& element)> builder;

        public:
            StringValidationBuilder(
                std::function<T*(XML::Element& element)> builder)
                : element("<string/>"),
                  builder(builder)
            {}

            StringValidationBuilder<T>* setReferenceId(Text id)
            {
                element.setAttribute("id", id);
                return this;
            }

            StringValidationBuilder<T>* withExactMatch(Text value)
            {
                element.setAttribute("equals", value);
                return this;
            }

            StringValidationBuilder<T>* whichContainsMatch(Text value)
            {
                element.setAttribute("contains", value);
                return this;
            }

            StringValidationBuilder<T>* whichStartsWithMatch(Text value)
            {
                element.setAttribute("startsWith", value);
                return this;
            }

            StringValidationBuilder<T>* whichEndsWithMatch(Text value)
            {
                element.setAttribute("endsWith", value);
                return this;
            }

            StringValidationBuilder<T>* whichIsOneOf(RCArray<Text> values)
            {
                JSON::JSONArray arr;
                for (Text* str : values)
                    arr.addValue(new JSON::JSONString(str->getText()));
                element.setAttribute("oneOf", arr.toString());
                return this;
            }

            StringValidationBuilder<T>* whichIsOneOf(
                std::initializer_list<Text> values)
            {
                JSON::JSONArray arr;
                for (Text str : values)
                    arr.addValue(new JSON::JSONString(str));
                element.setAttribute("oneOf", arr.toString());
                return this;
            }

            StringValidationBuilder<T>* whichIs(Text value)
            {
                JSON::JSONArray arr;
                arr.addValue(new JSON::JSONString(value));
                element.setAttribute("oneOf", arr.toString());
                return this;
            }

            StringValidationBuilder<T>* withDefault(Text value)
            {
                element.setAttribute(
                    "default", JSON::JSONString(value).toString());
                return this;
            }

            StringValidationBuilder<T>* withDefaultNull()
            {
                element.setAttribute("default", "null");
                return this;
            }

            StringValidationBuilder<T>* whichCanBeNull()
            {
                element.setAttribute("nullable", "true");
                return this;
            }

            StringValidationBuilder<T>* whichIsOptional()
            {
                element.setAttribute("optional", "true");
                return this;
            }

            T* finishString()
            {
                T* result = builder(element);
                delete this;
                return result;
            }
        };

        template<typename T> class NumberValidationBuilder
        {
        private:
            XML::Element element;
            std::function<T*(XML::Element& element)> builder;

        public:
            NumberValidationBuilder(
                std::function<T*(XML::Element& element)> builder)
                : element("<number/>"),
                  builder(builder)
            {}

            NumberValidationBuilder<T>* setReferenceId(Text id)
            {
                element.setAttribute("id", id);
                return this;
            }

            NumberValidationBuilder<T>* whichIs(double value)
            {
                element.setAttribute("equals", Text(value));
                return this;
            }

            NumberValidationBuilder<T>* whichIsLessOrEqual(double value)
            {
                element.setAttribute("lessOrEqual", Text(value));
                return this;
            }

            NumberValidationBuilder<T>* whichIsGreaterOrEqual(double value)
            {
                element.setAttribute("greaterOrEqual", Text(value));
                return this;
            }

            NumberValidationBuilder<T>* whichIsLessThen(double value)
            {
                element.setAttribute("less", Text(value));
                return this;
            }

            NumberValidationBuilder<T>* whichIsGreaterThen(double value)
            {
                element.setAttribute("greater", Text(value));
                return this;
            }

            NumberValidationBuilder<T>* withDefault(double value)
            {
                element.setAttribute(
                    "default", JSON::JSONNumber(value).toString());
                return this;
            }

            NumberValidationBuilder<T>* withDefaultNull()
            {
                element.setAttribute("default", "null");
                return this;
            }

            NumberValidationBuilder<T>* whichCanBeNull()
            {
                element.setAttribute("nullable", "true");
                return this;
            }

            NumberValidationBuilder<T>* whichIsOptional()
            {
                element.setAttribute("optional", "true");
                return this;
            }

            T* finishNumber()
            {
                T* result = builder(element);
                delete this;
                return result;
            }
        };

        template<typename T> class BoolValidationBuilder
        {
        private:
            XML::Element element;
            std::function<T*(XML::Element& element)> builder;

        public:
            BoolValidationBuilder(
                std::function<T*(XML::Element& element)> builder)
                : element("<bool/>"),
                  builder(builder)
            {}

            BoolValidationBuilder<T>* setReferenceId(Text id)
            {
                element.setAttribute("id", id);
                return this;
            }

            BoolValidationBuilder<T>* whichIs(bool value)
            {
                element.setAttribute("equals", value ? "true" : "false");
                return this;
            }

            BoolValidationBuilder<T>* withDefault(bool value)
            {
                element.setAttribute(
                    "default", JSON::JSONBool(value).toString());
                return this;
            }

            BoolValidationBuilder<T>* withDefaultNull()
            {
                element.setAttribute("default", "null");
                return this;
            }

            BoolValidationBuilder<T>* whichCanBeNull()
            {
                element.setAttribute("nullable", "true");
                return this;
            }

            BoolValidationBuilder<T>* whichIsOptional()
            {
                element.setAttribute("optional", "true");
                return this;
            }

            T* finishBool()
            {
                T* result = builder(element);
                delete this;
                return result;
            }
        };

        template<typename T> class ArrayValidationBuilder;

        template<typename T> class ObjectValidationBuilder
        {
        private:
            XML::Element element;
            std::function<T*(XML::Element& element)> builder;

        public:
            ObjectValidationBuilder(
                std::function<T*(XML::Element& element)> builder)
                : element("<object></object>"),
                  builder(builder)
            {}

            ObjectValidationBuilder<T>* setReferenceId(Text id)
            {
                element.setAttribute("id", id);
                return this;
            }

            NumberValidationBuilder<ObjectValidationBuilder<T>>*
            withRequiredNumber(Text name)
            {
                return new NumberValidationBuilder<ObjectValidationBuilder<T>>(
                    [this, name](XML::Element& e) {
                        if (!element.selectChildsByAttribute("name", name)
                                .exists())
                        {
                            XML::Element* attr
                                = new XML::Element("<value></value>");
                            attr->setAttribute("name", name);
                            element.addChild(attr);
                        }
                        element.selectChildsByAttribute("name", name)
                            .addChild(e.dublicate());
                        return this;
                    });
            }

            NumberValidationBuilder<ObjectValidationBuilder<T>>*
            withOptionalNumber(Text name)
            {
                return withRequiredNumber(name)->whichIsOptional();
            }

            StringValidationBuilder<ObjectValidationBuilder<T>>*
            withRequiredString(Text name)
            {
                return new StringValidationBuilder<ObjectValidationBuilder<T>>(
                    [this, name](XML::Element& e) {
                        if (!element.selectChildsByAttribute("name", name)
                                .exists())
                        {
                            XML::Element* attr
                                = new XML::Element("<value></value>");
                            attr->setAttribute("name", name);
                            element.addChild(attr);
                        }
                        element.selectChildsByAttribute("name", name)
                            .addChild(e.dublicate());
                        return this;
                    });
            }

            StringValidationBuilder<ObjectValidationBuilder<T>>*
            withOptionalString(Text name)
            {
                return withRequiredString(name)->whichIsOptional();
            }

            BoolValidationBuilder<ObjectValidationBuilder<T>>* withRequiredBool(
                Text name)
            {
                return new BoolValidationBuilder<ObjectValidationBuilder<T>>(
                    [this, name](XML::Element& e) {
                        if (!element.selectChildsByAttribute("name", name)
                                .exists())
                        {
                            XML::Element* attr
                                = new XML::Element("<value></value>");
                            attr->setAttribute("name", name);
                            element.addChild(attr);
                        }
                        element.selectChildsByAttribute("name", name)
                            .addChild(e.dublicate());
                        return this;
                    });
            }

            BoolValidationBuilder<ObjectValidationBuilder<T>>* withOptionalBool(
                Text name)
            {
                return withRequiredBool(name)->whichIsOptional();
            }

            ArrayValidationBuilder<ObjectValidationBuilder<T>>*
            withRequiredArray(Text name)
            {
                return new ArrayValidationBuilder<ObjectValidationBuilder<T>>(
                    [this, name](XML::Element& e) {
                        if (!element.selectChildsByAttribute("name", name)
                                .exists())
                        {
                            XML::Element* attr
                                = new XML::Element("<value></value>");
                            attr->setAttribute("name", name);
                            element.addChild(attr);
                        }
                        element.selectChildsByAttribute("name", name)
                            .addChild(e.dublicate());
                        return this;
                    });
            }

            ArrayValidationBuilder<ObjectValidationBuilder<T>>*
            withOptionalArray(Text name)
            {
                return withRequiredArray(name)->whichIsOptional();
            }

            ObjectValidationBuilder<ObjectValidationBuilder<T>>*
            withRequiredObject(Text name)
            {
                return new ObjectValidationBuilder<ObjectValidationBuilder<T>>(
                    [this, name](XML::Element& e) {
                        if (!element.selectChildsByAttribute("name", name)
                                .exists())
                        {
                            XML::Element* attr
                                = new XML::Element("<value></value>");
                            attr->setAttribute("name", name);
                            element.addChild(attr);
                        }
                        element.selectChildsByAttribute("name", name)
                            .addChild(e.dublicate());
                        return this;
                    });
            }

            ObjectValidationBuilder<ObjectValidationBuilder<T>>*
            withOptionalObject(Text name)
            {
                return withRequiredObject(name)->whichIsOptional();
            }

            ObjectValidationBuilder<T>* withRequiredAttribute(
                Text name, DataValidator* validator)
            {
                if (!element.selectChildsByAttribute("name", name).exists())
                {
                    XML::Element* attr = new XML::Element("<value></value>");
                    attr->setAttribute("name", name);
                    element.addChild(attr);
                }
                element.selectChildsByAttribute("name", name)
                    .addChild(validator->zConstraints()->dublicate());
                validator->release();
                return this;
            }

            ObjectValidationBuilder<T>* withOptionalAttribute(
                Text name, DataValidator* validator)
            {
                return withRequiredAttribute(name, validator, false, true);
            }

            ObjectValidationBuilder<T>* withRequiredAttribute(Text name,
                DataValidator* validator,
                bool canBeNull,
                bool optional)
            {
                if (!element.selectChildsByAttribute("name", name).exists())
                {
                    XML::Element* attr = new XML::Element("<value></value>");
                    attr->setAttribute("name", name);
                    element.addChild(attr);
                }
                XML::Element* element = validator->zConstraints()->dublicate();
                element->setAttribute("nullable", canBeNull ? "true" : "false");
                element->setAttribute("optional", optional ? "true" : "false");
                this->element.selectChildsByAttribute("name", name)
                    .addChild(element);
                validator->release();
                return this;
            }

            ObjectValidationBuilder<T>* withDefault(JSON::JSONObject* obj)
            {
                element.setAttribute("default", obj->toString());
                obj->release();
                return this;
            }

            ObjectValidationBuilder<T>* withDefaultNull()
            {
                element.setAttribute("default", "null");
                return this;
            }

            ObjectValidationBuilder<T>* whichCanBeNull()
            {
                element.setAttribute("nullable", "true");
                return this;
            }

            ObjectValidationBuilder<T>* whichIsOptional()
            {
                element.setAttribute("optional", "true");
                return this;
            }

            ArrayValidationBuilder<T>* typeOfValuesSpecifiedByAttribute(
                Text valueName, Text attributeName)
            {
                if (!element.selectChildsByAttribute("name", valueName)
                        .exists())
                {
                    XML::Element* attr = new XML::Element("<value></value>");
                    attr->setAttribute("name", valueName);
                    element.addChild(attr);
                }
                element.selectChildsByAttribute("name", valueName)
                    .setAttribute("typeSpecifiedBy", attributeName);
                return this;
            }

            ObjectValidationBuilder<T>* removeInvalidEntries()
            {
                element.setAttribute("removeInvalidEntries", "true");
                return this;
            }

            ObjectValidationBuilder<T>* allowAdditionalAttriutes()
            {
                element.setAttribute("allowAdditionalAttributes", "true");
                return this;
            }

            T* finishObject()
            {
                T* result = builder(element);
                delete this;
                return result;
            }
        };

        template<typename T> class ArrayValidationBuilder
        {
        private:
            XML::Element element;
            std::function<T*(XML::Element& element)> builder;

        public:
            ArrayValidationBuilder(
                std::function<T*(XML::Element& element)> builder)
                : element("<array></array>"),
                  builder(builder)
            {}

            StringValidationBuilder<ArrayValidationBuilder<T>>*
            addAcceptedStringInArray()
            {
                return new StringValidationBuilder<ArrayValidationBuilder<T>>(
                    [this](XML::Element& e) {
                        element.addChild(e.dublicate());
                        return this;
                    });
            }

            NumberValidationBuilder<ArrayValidationBuilder<T>>*
            addAcceptedNumberInArray()
            {
                return new NumberValidationBuilder<ArrayValidationBuilder<T>>(
                    [this](XML::Element& e) {
                        element.addChild(e.dublicate());
                        return this;
                    });
            }

            BoolValidationBuilder<ArrayValidationBuilder<T>>*
            addAcceptedBooleanInArray()
            {
                return new BoolValidationBuilder<ArrayValidationBuilder<T>>(
                    [this](XML::Element& e) {
                        element.addChild(e.dublicate());
                        return this;
                    });
            }

            ObjectValidationBuilder<ArrayValidationBuilder<T>>*
            addAcceptedObjectInArray()
            {
                return new ObjectValidationBuilder<ArrayValidationBuilder<T>>(
                    [this](XML::Element& e) {
                        element.addChild(e.dublicate());
                        return this;
                    });
            }

            ArrayValidationBuilder<ArrayValidationBuilder<T>>*
            addAcceptedArrayInArray()
            {
                return new ArrayValidationBuilder<ArrayValidationBuilder<T>>(
                    [this](XML::Element& e) {
                        element.addChild(e.dublicate());
                        return this;
                    });
            }

            ArrayValidationBuilder<T>* addAcceptedTypeInArray(
                DataValidator* validator)
            {
                element.addChild(validator->zConstraints()->dublicate());
                validator->release();
                return this;
            }

            ArrayValidationBuilder<T>* acceptNullsInArray()
            {
                element.setAttribute("nullsEnabled", "true");
                return this;
            }

            ArrayValidationBuilder<T>* withDefault(JSON::JSONArray* array)
            {
                element.setAttribute("default", array->toString());
                array->release();
                return this;
            }

            ArrayValidationBuilder<T>* withDefaultNull()
            {
                element.setAttribute("default", "null");
                return this;
            }

            ArrayValidationBuilder<T>* whichCanBeNull()
            {
                element.setAttribute("nullable", "true");
                return this;
            }

            ArrayValidationBuilder<T>* whichIsOptional()
            {
                element.setAttribute("optional", "true");
                return this;
            }

            ArrayValidationBuilder<T>* typeSpecifiedByAttribute(Text name)
            {
                element.setAttribute("typeSpecifiedBy", name);
                return this;
            }

            ArrayValidationBuilder<T>* removeInvalidEntries()
            {
                element.setAttribute("removeInvalidEntries", "true");
                return this;
            }

            ArrayValidationBuilder<T>* withMinSize(int size)
            {
                element.setAttribute("minSize", Text(size));
                return this;
            }

            ArrayValidationBuilder<T>* withMaxSize(int size)
            {
                element.setAttribute("maxSize", Text(size));
                return this;
            }

            ArrayValidationBuilder<T>* withRequiredSize(int size)
            {
                element.setAttribute("minSize", Text(size));
                element.setAttribute("maxSize", Text(size));
                return this;
            }

            T* finishArray()
            {
                T* result = builder(element);
                delete this;
                return result;
            }
        };

        template<typename T> class OneOfValidationBuilder
        {
        private:
            XML::Element element;
            std::function<T*(XML::Element& element)> builder;

        public:
            OneOfValidationBuilder(
                std::function<T*(XML::Element& element)> builder)
                : element("<oneOf></oneOf>"),
                  builder(builder)
            {}

            StringValidationBuilder<OneOfValidationBuilder<T>>*
            addAcceptedString()
            {
                return new StringValidationBuilder<OneOfValidationBuilder<T>>(
                    [this](XML::Element& e) {
                        element.addChild(e.dublicate());
                        return this;
                    });
            }

            NumberValidationBuilder<OneOfValidationBuilder<T>>*
            addAcceptedNumber()
            {
                return new NumberValidationBuilder<OneOfValidationBuilder<T>>(
                    [this](XML::Element& e) {
                        element.addChild(e.dublicate());
                        return this;
                    });
            }

            BoolValidationBuilder<OneOfValidationBuilder<T>>*
            addAcceptedBoolean()
            {
                return new BoolValidationBuilder<OneOfValidationBuilder<T>>(
                    [this](XML::Element& e) {
                        element.addChild(e.dublicate());
                        return this;
                    });
            }

            ObjectValidationBuilder<OneOfValidationBuilder<T>>*
            addAcceptedObject()
            {
                return new ObjectValidationBuilder<OneOfValidationBuilder<T>>(
                    [this](XML::Element& e) {
                        element.addChild(e.dublicate());
                        return this;
                    });
            }

            ArrayValidationBuilder<OneOfValidationBuilder<T>>*
            addAcceptedArray()
            {
                return new ArrayValidationBuilder<OneOfValidationBuilder<T>>(
                    [this](XML::Element& e) {
                        element.addChild(e.dublicate());
                        return this;
                    });
            }

            OneOfValidationBuilder<T>* addAcceptedType(DataValidator* validator)
            {
                element.addChild(validator->zConstraints()->dublicate());
                validator->release();
                return this;
            }

            OneOfValidationBuilder<T>* typeSpecifiedByAttribute(Text name)
            {
                element.setAttribute("typeSpecifiedBy", name);
                return this;
            }

            T* finishOneOf()
            {
                T* result = builder(element);
                delete this;
                return result;
            }
        };
    } // namespace Validator
} // namespace Framework