#pragma once

#include <functional>

#include "Array.h"
#include "ReferenceCounter.h"
#include "Text.h"
#include "Writer.h"
#include "XML.h"

namespace Framework
{
    namespace JSON
    {
        enum class JSONType
        {
            NULL_,
            BOOLEAN,
            NUMBER,
            STRING,
            ARRAY,
            OBJECT
        };

        class JSONArray;
        class JSONObject;
        class JSONBool;
        class JSONNumber;
        class JSONString;

        class JSONValue : public virtual ReferenceCounter
        {
        private:
            JSONType type;

        protected:
            __declspec(dllexport) JSONValue(JSONType type);

        public:
            __declspec(dllexport) JSONValue();
            __declspec(dllexport) virtual ~JSONValue();
            __declspec(dllexport) JSONType getType() const;
            __declspec(dllexport) virtual Text toString() const;
            __declspec(dllexport) virtual JSONValue* clone() const;
            __declspec(dllexport) JSONBool* asBool() const;
            __declspec(dllexport) JSONNumber* asNumber() const;
            __declspec(dllexport) JSONString* asString() const;
            __declspec(dllexport) JSONArray* asArray() const;
            __declspec(dllexport) JSONObject* asObject() const;
        };

        class JSONBool : public JSONValue
        {
        private:
            bool b;

        public:
            __declspec(dllexport) JSONBool(bool b);

            __declspec(dllexport) bool getBool() const;
            __declspec(dllexport) Text toString() const override;
            __declspec(dllexport) JSONValue* clone() const override;
        };

        class JSONNumber : public JSONValue
        {
        private:
            double number;

        public:
            __declspec(dllexport) JSONNumber(double num);

            __declspec(dllexport) double getNumber() const;
            __declspec(dllexport) Text toString() const override;
            __declspec(dllexport) JSONValue* clone() const override;
        };

        class JSONString : public JSONValue
        {
        private:
            Text string;

        public:
            __declspec(dllexport) JSONString(Text string);

            __declspec(dllexport) Text getString() const;
            __declspec(dllexport) Text toString() const override;
            __declspec(dllexport) JSONValue* clone() const override;
        };

        class JSONArray : public JSONValue
        {
        private:
            RCArray<JSONValue>* array;

        public:
            __declspec(dllexport) JSONArray();
            __declspec(dllexport) JSONArray(Text string);
            __declspec(dllexport) JSONArray(const JSONArray& arr);
            __declspec(dllexport) ~JSONArray();

            __declspec(dllexport) JSONArray& operator=(const JSONArray& arr);

            __declspec(dllexport) void addValue(JSONValue* value);
            __declspec(dllexport) void setValue(int i, JSONValue* value);
            __declspec(dllexport) void removeValue(int i);
            __declspec(dllexport) JSONValue* getValue(int i) const;
            __declspec(dllexport) JSONValue* zValue(int i) const;
            __declspec(dllexport) int getLength() const;
            __declspec(dllexport) bool isValueOfType(
                int i, JSONType type) const;

            //! Gibt einen Iterator zurück.
            //! Mit ++ kann durch die Liste iteriert werden
            __declspec(dllexport) Iterator<JSONValue*> begin() const;
            __declspec(dllexport) Iterator<JSONValue*> end() const;

            template<class T>
            RCArray<T>* toRCArray(std::function<T*(JSONValue&)> map) const
            {
                return toRCArray([](JSONValue& v) { return 1; }, map);
            }

            template<class T>
            RCArray<T>* toRCArray(std::function<bool(JSONValue&)> filter,
                std::function<T*(JSONValue&)> map) const
            {
                RCArray<T>* result = new RCArray<T>();
                for (auto v : *array)
                {
                    if (filter(*v))
                    {
                        result->add(map(*v));
                    }
                }
                return result;
            }

            template<class T>
            Array<T>* toArray(std::function<T(JSONValue&)> map) const
            {
                return toArray([](JSONValue& v) { return 1; }, map);
                ;
            }

            template<class T>
            Array<T>* toArray(std::function<bool(JSONValue&)> filter,
                std::function<T(JSONValue&)> map) const
            {
                Array<T>* result = new Array<T>();
                for (auto v : *array)
                {
                    if (filter(*v))
                    {
                        result->add(map(*v));
                    }
                }
                return result;
            }

            __declspec(dllexport) Text toString() const override;
            __declspec(dllexport) JSONValue* clone() const override;

            template<class T> static JSONArray* fromRCArray(
                Framework::RCArray<T>& arr, std::function<JSONValue*(T&)> map)
            {
                JSONArray* array = new JSONArray();
                for (T* v : arr)
                {
                    array->addValue(map(*v));
                }
                return array;
            }

            template<class T> static JSONArray* fromArray(
                Framework::Array<T>& arr, std::function<JSONValue*(T)> map)
            {
                JSONArray* array = new JSONArray();
                for (T v : arr)
                {
                    array->addValue(map(v));
                }
                return array;
            }
        };

        class JSONObject : public JSONValue
        {
        private:
            Array<Text>* fields;
            RCArray<JSONValue>* values;

        public:
            __declspec(dllexport) JSONObject();
            __declspec(dllexport) JSONObject(Text string);
            __declspec(dllexport) JSONObject(const JSONObject& obj);
            __declspec(dllexport) ~JSONObject();

            __declspec(dllexport) JSONObject& operator=(const JSONObject& obj);

            __declspec(dllexport) bool addValue(Text field, JSONValue* value);
            __declspec(dllexport) bool removeValue(Text field);
            __declspec(dllexport) bool hasValue(Text field);
            __declspec(dllexport) JSONValue* getValue(Text field);
            __declspec(dllexport) JSONValue* zValue(Text field);
            __declspec(dllexport) Iterator<Text> getFields();
            __declspec(dllexport) Iterator<JSONValue*> getValues();
            __declspec(dllexport) int getFieldCount() const;
            __declspec(dllexport) bool isValueOfType(
                Text field, JSONType type) const;

            template<class T> T* parseTo(T* initialState,
                std::function<void(
                    T* obj, Text fieldName, JSONValue& fieldValue)> parser)
                const
            {
                auto fieldsI = fields->begin();
                auto valuesI = values->begin();
                while (fieldsI && valuesI)
                {
                    parser(initialState, fieldsI, *(JSONValue*)valuesI);
                    fieldsI++;
                    valuesI++;
                }
                return initialState;
            }

            __declspec(dllexport) Text toString() const override;
            __declspec(dllexport) JSONValue* clone() const override;
        };

        __declspec(dllexport) JSONValue* loadJSONFromFile(Text path);

        namespace Parser
        {
            __declspec(dllexport) int findObjectEndInArray(const char* str);
            __declspec(dllexport) Text removeWhitespace(const char* str);
            __declspec(dllexport) JSONValue* getValue(const char* str);
            __declspec(dllexport) int findFieldEndInObject(const char* str);
            __declspec(dllexport) int findValueEndInObject(const char* str);
        }; // namespace Parser

        namespace Validator
        {
            class JSONValidationResult : public Framework::ReferenceCounter
            {
            public:
                __declspec(dllexport) JSONValidationResult();
                __declspec(dllexport) virtual ~JSONValidationResult();
                virtual bool isValid() const = 0;
                virtual void printInvalidInfo(int indent = 0) const = 0;
                virtual JSONValue* getValidPart() const = 0;
                virtual Text getPath() const = 0;
                virtual bool isDifferent(const JSONValidationResult* zResult,
                    Text additionalPath) const = 0;
            };

            class JSONTypeMissmatch : public JSONValidationResult
            {
            private:
                Text path;
                JSONValue* foundValue;
                XML::Element* expected;
                JSONValidationResult* reason;

            public:
                __declspec(dllexport) JSONTypeMissmatch(Text path,
                    JSONValue* foundValue,
                    XML::Element* expected,
                    JSONValidationResult* reason);
                __declspec(dllexport) ~JSONTypeMissmatch();
                __declspec(dllexport) bool isValid() const override;
                __declspec(dllexport) void printInvalidInfo(
                    int indent) const override;
                __declspec(dllexport) JSONValue* getValidPart() const override;
                __declspec(dllexport) Text getPath() const override;
                bool isDifferent(const JSONValidationResult* zResult,
                    Text additionalPath) const override;
            };

            class JSONUnknownValue : public JSONValidationResult
            {
            private:
                Text path;
                JSONValue* foundValue;

            public:
                __declspec(dllexport)
                    JSONUnknownValue(Text path, JSONValue* foundValue);
                __declspec(dllexport) ~JSONUnknownValue();
                __declspec(dllexport) bool isValid() const override;
                __declspec(dllexport) void printInvalidInfo(
                    int indent) const override;
                __declspec(dllexport) JSONValue* getValidPart() const override;
                __declspec(dllexport) Text getPath() const override;
                __declspec(dllexport) bool isDifferent(
                    const JSONValidationResult* zResult,
                    Text additionalPath) const override;
            };

            class JSONMissingValue : public JSONValidationResult
            {
            private:
                Text path;
                XML::Element* expected;

            public:
                __declspec(dllexport)
                    JSONMissingValue(Text path, XML::Element* expected);
                __declspec(dllexport) ~JSONMissingValue();
                __declspec(dllexport) bool isValid() const override;
                __declspec(dllexport) void printInvalidInfo(
                    int indent) const override;
                __declspec(dllexport) JSONValue* getValidPart() const override;
                __declspec(dllexport) Text getPath() const override;
                __declspec(dllexport) bool isDifferent(
                    const JSONValidationResult* zResult,
                    Text additionalPath) const override;
            };

            class JSONMissingOneOf : public JSONValidationResult
            {
            private:
                Text path;
                RCArray<XML::Element> expected;

            public:
                __declspec(dllexport)
                    JSONMissingOneOf(Text path, XML::Editor expected);
                __declspec(dllexport) ~JSONMissingOneOf();
                __declspec(dllexport) bool isValid() const override;
                __declspec(dllexport) void printInvalidInfo(
                    int indent) const override;
                __declspec(dllexport) JSONValue* getValidPart() const override;
                __declspec(dllexport) Text getPath() const override;
                __declspec(dllexport) bool isDifferent(
                    const JSONValidationResult* zResult,
                    Text additionalPath) const override;
            };

            class JSONNoTypeMatching : public JSONValidationResult
            {
            private:
                Text path;
                JSONValue* foundValue;
                RCArray<XML::Element> expected;
                RCArray<JSONValidationResult> reasons;

            public:
                __declspec(dllexport) JSONNoTypeMatching(Text path,
                    JSONValue* foundValue,
                    RCArray<XML::Element>& expected,
                    RCArray<JSONValidationResult>& reasons);
                __declspec(dllexport) ~JSONNoTypeMatching();
                __declspec(dllexport) bool isValid() const override;
                __declspec(dllexport) void printInvalidInfo(
                    int indent) const override;
                __declspec(dllexport) JSONValue* getValidPart() const override;
                __declspec(dllexport) Text getPath() const override;
                __declspec(dllexport) bool isDifferent(
                    const JSONValidationResult* zResult,
                    Text additionalPath) const override;
            };

            class JSONValidValue : public JSONValidationResult
            {
            private:
                Text path;
                JSONValue* value;

            public:
                __declspec(dllexport)
                    JSONValidValue(Text path, JSONValue* value);
                __declspec(dllexport) ~JSONValidValue();
                __declspec(dllexport) bool isValid() const override;
                __declspec(dllexport) void printInvalidInfo(
                    int indent) const override;
                __declspec(dllexport) JSONValue* getValidPart() const override;
                __declspec(dllexport) Text getPath() const override;
                __declspec(dllexport) bool isDifferent(
                    const JSONValidationResult* zResult,
                    Text additionalPath) const 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;

            class JSONValidator : public Framework::ReferenceCounter
            {
            private:
                XML::Element* constraints;

            public:
                __declspec(dllexport) JSONValidator(XML::Element* constraints);
                __declspec(dllexport) ~JSONValidator();
                __declspec(dllexport) JSONValidationResult* validate(
                    JSONValue* zValue) const;
                __declspec(dllexport) bool isValid(JSONValue* 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) JSONValue* getValidParts(
                    JSONValue* zValue) const;
                __declspec(dllexport) XML::Element* zConstraints();

            private:
                __declspec(dllexport) JSONValidationResult* validate(
                    JSONValue* zValue,
                    XML::Element* zConstraints,
                    Text path) const;
                __declspec(dllexport)
                    JSONValidationResult* validateMultipleTypes(
                        JSONValue* zChildValue,
                        XML::Element* zPossibleChildConstraints,
                        Text childPath) const;

            public:
                __declspec(dllexport) static StringValidationBuilder<
                    JSONValidator>* buildForString();
                __declspec(dllexport) static NumberValidationBuilder<
                    JSONValidator>* buildForNumber();
                __declspec(dllexport) static BoolValidationBuilder<
                    JSONValidator>* buildForBool();
                __declspec(dllexport) static ObjectValidationBuilder<
                    JSONValidator>* buildForObject();
                __declspec(dllexport) static ArrayValidationBuilder<
                    JSONValidator>* buildForArray();
            };

            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>* 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)
                {
                    JSONArray arr;
                    for (Text* str : values)
                        arr.addValue(new JSONString(str->getText()));
                    element.setAttribute("oneOf", arr.toString());
                    return this;
                }

                StringValidationBuilder<T>* withDefault(Text value)
                {
                    element.setAttribute(
                        "default", 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>* 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", 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>* whichIs(bool value)
                {
                    element.setAttribute("equals", value ? "true" : "false");
                    return this;
                }

                BoolValidationBuilder<T>* withDefault(bool value)
                {
                    element.setAttribute("default", 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)
                {}

                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;
                        });
                }

                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;
                        });
                }

                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;
                        });
                }

                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;
                        });
                }

                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<T>* withRequiredAttribute(
                    Text name, JSONValidator* 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>* withDefault(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;
                }

                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(
                    JSONValidator* validator)
                {
                    element.addChild(validator->zConstraints()->dublicate());
                    validator->release();
                    return this;
                }

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

                ArrayValidationBuilder<T>* withDefault(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;
                }

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