Quellcode durchsuchen

improve json editor

Kolja Strohm vor 3 Wochen
Ursprung
Commit
1f0017ee2d
22 geänderte Dateien mit 6418 neuen und 2243 gelöschten Zeilen
  1. 117 0
      AbstractElement.cpp
  2. 118 0
      AbstractElement.h
  3. 19 19
      DX12PixelShader.h
  4. 17 17
      DX12VertexShader.h
  5. 1303 0
      DataValidator.cpp
  6. 896 0
      DataValidator.h
  7. 10 32
      Fenster.cpp
  8. 2 0
      Framework Linux.vcxproj
  9. 24 12
      Framework Linux.vcxproj.filters
  10. 7 0
      Framework.vcxproj
  11. 42 12
      Framework.vcxproj.filters
  12. 70 1162
      JSON.cpp
  13. 51 901
      JSON.h
  14. 3300 0
      JsonEditor.cpp
  15. 265 0
      JsonEditor.h
  16. 19 13
      Schrift.cpp
  17. 0 3
      Schrift.h
  18. 104 33
      Text.cpp
  19. 17 2
      Text.h
  20. 2 2
      TextFeld.cpp
  21. 20 20
      UIPixelShader.h
  22. 15 15
      UIVertexShader.h

+ 117 - 0
AbstractElement.cpp

@@ -0,0 +1,117 @@
+#include "AbstractElement.h"
+
+using namespace Framework;
+
+ElementPath::ElementPath(RCArray<Text>* path)
+    : ReferenceCounter(),
+      path(path)
+{}
+
+ElementPath::~ElementPath() {}
+
+bool Framework::ElementPath::isValid() const
+{
+    return index < path->getEintragAnzahl();
+}
+
+bool Framework::ElementPath::hasNext() const
+{
+    return index < path->getEintragAnzahl() - 1;
+}
+
+void Framework::ElementPath::next()
+{
+    index++;
+}
+
+bool Framework::ElementPath::isObjectAttribute() const
+{
+    return path->z(index)->hatAt(0, ".");
+}
+
+bool Framework::ElementPath::isArrayElement() const
+{
+    return path->z(index)->hatAt(0, "[");
+}
+
+int Framework::ElementPath::getArrayIndex() const
+{
+    Text result = path->z(index)->getText() + 1;
+    return (int)result;
+}
+
+Text Framework::ElementPath::getObjectAttribute() const
+{
+    return path->z(index)->getText() + 1;
+}
+
+Text Framework::ElementPath::toString() const
+{
+    Text result = "value";
+    for (Text* txt : *path)
+    {
+        result += *txt;
+    }
+    return result;
+}
+
+int Framework::ElementPath::getCurrentPathElementIndex() const
+{
+    return index;
+}
+
+void Framework::ElementPath::setCurrentPathElementIndex(int index)
+{
+    this->index = index;
+}
+
+ElementPathBuilder::ElementPathBuilder()
+    : path(new RCArray<Text>())
+{}
+
+Framework::ElementPathBuilder::ElementPathBuilder(
+    const ElementPathBuilder& other)
+    : path(dynamic_cast<RCArray<Text>*>(path->getThis()))
+{}
+
+ElementPathBuilder::~ElementPathBuilder()
+{
+    path->release();
+}
+
+ElementPathBuilder& ElementPathBuilder::appendArrayElement(int index)
+{
+    path->add(new Text(Text("[") + Text(index) + "]"));
+    return *this;
+}
+
+ElementPathBuilder& ElementPathBuilder::appendObjectElement(Text key)
+{
+    path->add(new Text(Text(".") + key));
+    return *this;
+}
+
+ElementPathBuilder& ElementPathBuilder::prependArrayElement(int index)
+{
+    path->add(new Text(Text("[") + Text(index) + "]"), 0);
+    return *this;
+}
+
+ElementPathBuilder& ElementPathBuilder::prependObjectElement(Text key)
+{
+    path->add(new Text(Text(".") + key), 0);
+    return *this;
+}
+
+ElementPath* ElementPathBuilder::build()
+{
+    return new ElementPath(dynamic_cast<RCArray<Text>*>(path->getThis()));
+}
+
+ElementPathBuilder& Framework::ElementPathBuilder::operator=(
+    const ElementPathBuilder& other)
+{
+    if (path) path->release();
+    path = dynamic_cast<RCArray<Text>*>(other.path->getThis());
+    return *this;
+}

+ 118 - 0
AbstractElement.h

@@ -0,0 +1,118 @@
+#pragma once
+
+#include "Array.h"
+#include "Text.h"
+
+namespace Framework
+{
+    enum class AbstractType
+    {
+        NULL_,
+        BOOLEAN,
+        NUMBER,
+        STRING,
+        ARRAY,
+        OBJECT
+    };
+
+    class AbstractBool;
+    class AbstractNumber;
+    class AbstractString;
+    class AbstractArray;
+    class AbstractObject;
+
+    class AbstractElement
+    {
+    public:
+        virtual AbstractType getType() const = 0;
+        virtual const AbstractBool* asAbstractBool() const = 0;
+        virtual const AbstractNumber* asAbstractNumber() const = 0;
+        virtual const AbstractString* asAbstractString() const = 0;
+        virtual const AbstractArray* asAbstractArray() const = 0;
+        virtual const AbstractObject* asAbstractObject() const = 0;
+        virtual Text toString() const = 0;
+    };
+
+    class AbstractBool : public virtual AbstractElement
+    {
+    public:
+        virtual bool getBool() const = 0;
+    };
+
+    class AbstractNumber : public virtual AbstractElement
+    {
+    public:
+        virtual double getNumber() const = 0;
+    };
+
+    class AbstractString : public virtual AbstractElement
+    {
+    public:
+        virtual Text getString() const = 0;
+    };
+
+    class AbstractArray : public virtual AbstractElement
+    {
+    public:
+        virtual AbstractElement* zAbstractValue(int i) const = 0;
+        virtual int getLength() const = 0;
+    };
+
+    class AbstractObject : public virtual AbstractElement
+    {
+    public:
+        virtual AbstractElement* zAbstractValue(Text field) const = 0;
+        virtual int getFieldCount() const = 0;
+        virtual Text getFieldKey(int i) const = 0;
+        virtual AbstractElement* zAbstractValue(int i) const = 0;
+        virtual bool hasValue(Text field) const = 0;
+    };
+
+    class ElementPathBuilder;
+
+    class ElementPath : public virtual ReferenceCounter
+    {
+    private:
+        RCArray<Text>* path;
+        int index;
+
+        __declspec(dllexport) ElementPath(RCArray<Text>* path);
+
+    public:
+        __declspec(dllexport) ~ElementPath();
+
+        __declspec(dllexport) bool isValid() const;
+        __declspec(dllexport) bool hasNext() const;
+        __declspec(dllexport) void next();
+        __declspec(dllexport) bool isObjectAttribute() const;
+        __declspec(dllexport) bool isArrayElement() const;
+        __declspec(dllexport) int getArrayIndex() const;
+        __declspec(dllexport) Text getObjectAttribute() const;
+        __declspec(dllexport) Text toString() const;
+        __declspec(dllexport) int getCurrentPathElementIndex() const;
+        __declspec(dllexport) void setCurrentPathElementIndex(int index);
+
+        friend ElementPathBuilder;
+    };
+
+    class ElementPathBuilder
+    {
+    private:
+        RCArray<Text>* path;
+
+    public:
+        __declspec(dllexport) ElementPathBuilder();
+        __declspec(dllexport)
+            ElementPathBuilder(const ElementPathBuilder& other);
+        __declspec(dllexport) ~ElementPathBuilder();
+        __declspec(dllexport) ElementPathBuilder& appendArrayElement(int index);
+        __declspec(dllexport) ElementPathBuilder& appendObjectElement(Text key);
+        __declspec(dllexport) ElementPathBuilder& prependArrayElement(
+            int index);
+        __declspec(dllexport) ElementPathBuilder& prependObjectElement(
+            Text key);
+        __declspec(dllexport) ElementPath* build();
+        __declspec(dllexport) ElementPathBuilder& operator=(
+            const ElementPathBuilder& other);
+    };
+} // namespace Framework

+ 19 - 19
DX12PixelShader.h

@@ -92,10 +92,10 @@ ret
 
 const BYTE DX12PixelShaderBytes[] =
 {
-     68,  88,  66,  67, 190,  75, 
-    240, 172, 112,   8, 127,  82, 
-    240, 248,  94, 114, 224, 120, 
-     40, 144,   1,   0,   0,   0, 
+     68,  88,  66,  67,  50,  32, 
+    192, 140, 164, 134,  56,  47, 
+    229, 173, 230, 163,  24, 227, 
+    138, 116,   1,   0,   0,   0, 
     184,  91,   0,   0,   6,   0, 
       0,   0,  56,   0,   0,   0, 
      36,   2,   0,   0, 188,   2, 
@@ -763,11 +763,11 @@ const BYTE DX12PixelShaderBytes[] =
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0, 148,  46, 
-     49,   1, 255, 116,  62, 102, 
-      1,   0,   0,   0, 138, 240, 
-    235,  37, 208, 254,  45,  70, 
-    162, 234, 185,  56, 245,   7, 
-     85, 161,   0,   0,   0,   0, 
+     49,   1,  61, 236, 210, 102, 
+      1,   0,   0,   0, 158,  90, 
+    126,  53, 146,  92, 142,  72, 
+    166,  90, 183, 255, 213, 196, 
+    151, 223,   0,   0,   0,   0, 
       0,   0,   0,   0,   1,   0, 
       0,   0,   1,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
@@ -938,10 +938,10 @@ const BYTE DX12PixelShaderBytes[] =
       3,   0, 242,  56,   1,   0, 
      43, 236,   3,   0,  28,  19, 
       2,   0,  65,  36,   1,   0, 
-    236, 179,   1,   0,  84,  45, 
+    236, 179,   1,   0,  37,  75, 
       1,   0, 125,  10,   2,   0, 
-    125, 181,   2,   0,  86,  34, 
-      1,   0, 193,  33,   3,   0, 
+    125, 181,   2,   0, 107, 226, 
+      0,   0, 193,  33,   3,   0, 
      65, 185,   2,   0, 140, 239, 
       1,   0, 246,  49,   0,   0, 
     213, 255,   0,   0, 115, 108, 
@@ -1788,7 +1788,7 @@ const BYTE DX12PixelShaderBytes[] =
     117, 114, 101,  50,  68,  32, 
     115, 104,  97, 100,  27, 226, 
      48,   1, 128,   0,   0,   0, 
-    114,  98, 219,   4,  16, 163, 
+    249, 250, 223,  25, 142, 251, 
     218,   1,   1,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
@@ -2348,14 +2348,14 @@ const BYTE DX12PixelShaderBytes[] =
       0,   0,  23,   0,   1,   0, 
       5,  16,   0,   0,  14,   0, 
      23,  21,   0,  16,   0,   0, 
-      3,   2,  64,  72,   0,   0, 
+      3,   2, 240,  19,   0,   0, 
     242, 241,  10,   0,  24,  21, 
       8,  16,   0,   0,   1,   0, 
       1,   0,  10,   0,  24,  21, 
       9,  16,   0,   0,   1,   0, 
       0,   2,  14,   0,  23,  21, 
       0,   0,   0,   0,  10,   2, 
-     64,  72,   0,   0, 242, 241, 
+    240,  19,   0,   0, 242, 241, 
      10,   0,  24,  21,  11,  16, 
       0,   0,   1,   0,   1,   0, 
      10,   0,  24,  21,  12,  16, 
@@ -3494,10 +3494,10 @@ const BYTE DX12PixelShaderBytes[] =
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0, 148,  46,  49,   1, 
-    255, 116,  62, 102,   1,   0, 
-      0,   0, 138, 240, 235,  37, 
-    208, 254,  45,  70, 162, 234, 
-    185,  56, 245,   7,  85, 161, 
+     61, 236, 210, 102,   1,   0, 
+      0,   0, 158,  90, 126,  53, 
+    146,  92, 142,  72, 166,  90, 
+    183, 255, 213, 196, 151, 223, 
     128,   0,   0,   0,  47,  76, 
     105, 110, 107,  73, 110, 102, 
     111,   0,  47, 110,  97, 109, 

+ 17 - 17
DX12VertexShader.h

@@ -131,10 +131,10 @@ ret
 
 const BYTE DX12VertexShaderBytes[] =
 {
-     68,  88,  66,  67, 108, 254, 
-    182, 147, 215, 169,  14,  36, 
-     13, 164, 153, 127, 125, 128, 
-     61, 219,   1,   0,   0,   0, 
+     68,  88,  66,  67,  15,  74, 
+     80, 135,  91, 167, 152,  89, 
+     80,  96, 112, 137, 181, 127, 
+    160, 220,   1,   0,   0,   0, 
     144,  78,   0,   0,   6,   0, 
       0,   0,  56,   0,   0,   0, 
     124,   2,   0,   0,  52,   3, 
@@ -923,11 +923,11 @@ const BYTE DX12VertexShaderBytes[] =
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
-    148,  46,  49,   1, 255, 116, 
-     62, 102,   1,   0,   0,   0, 
-     62,  58, 140,  40,  64,  23, 
-     77,  71, 171,  87,  76, 125, 
-     20, 102, 246, 140,   0,   0, 
+    148,  46,  49,   1,  62, 236, 
+    210, 102,   1,   0,   0,   0, 
+     31, 217,  17, 212, 110, 222, 
+    218,  67, 166, 145,  39, 231, 
+     16, 108,  63,  52,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       1,   0,   0,   0,   1,   0, 
       0,   0,   0,   0,   0,   0, 
@@ -1103,7 +1103,7 @@ const BYTE DX12VertexShaderBytes[] =
       0,   0, 103, 159,   1,   0, 
     179, 120,   1,   0, 238,  97, 
       2,   0,  90,  28,   0,   0, 
-    166, 148,   2,   0,  53, 174, 
+    242, 196,   2,   0,  53, 174, 
       3,   0, 206,  21,   0,   0, 
     193, 205,   3,   0, 207, 193, 
       1,   0,  62,   3,   3,   0, 
@@ -1607,7 +1607,7 @@ const BYTE DX12VertexShaderBytes[] =
     107,  97, 109, 101, 114,  97, 
      13,  10, 115, 116,  27, 226, 
      48,   1, 128,   0,   0,   0, 
-    135, 204, 239,   4,  16, 163, 
+     25, 250, 248,  25, 142, 251, 
     218,   1,   1,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
@@ -2125,7 +2125,7 @@ const BYTE DX12VertexShaderBytes[] =
      24,  21,  12,  16,   0,   0, 
       1,   0,   1,   0,  14,   0, 
      23,  21,  13,  16,   0,   0, 
-     36,   2, 208, 188,   0,   0, 
+     36,   2, 112, 164,   0,   0, 
     242, 241,  10,   0,  24,  21, 
      14,  16,   0,   0,   1,   0, 
       0,   2,  18,   0,  22,  21, 
@@ -3142,11 +3142,11 @@ const BYTE DX12VertexShaderBytes[] =
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0, 148,  46, 
-     49,   1, 255, 116,  62, 102, 
-      1,   0,   0,   0,  62,  58, 
-    140,  40,  64,  23,  77,  71, 
-    171,  87,  76, 125,  20, 102, 
-    246, 140, 129,   0,   0,   0, 
+     49,   1,  62, 236, 210, 102, 
+      1,   0,   0,   0,  31, 217, 
+     17, 212, 110, 222, 218,  67, 
+    166, 145,  39, 231,  16, 108, 
+     63,  52, 129,   0,   0,   0, 
      47,  76, 105, 110, 107,  73, 
     110, 102, 111,   0,  47, 110, 
      97, 109, 101, 115,   0,  47, 

+ 1303 - 0
DataValidator.cpp

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

+ 896 - 0
DataValidator.h

@@ -0,0 +1,896 @@
+#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* zFoundValue,
+                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* zFoundValue);
+            __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* zFoundValue,
+                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* zFoundValue, 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* zValue);
+            __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();
+
+        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;
+
+        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* buildForObjectReference(
+                Text objectId);
+        };
+
+        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)
+            {
+                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>* 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>* 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>* setObjectReferenceId(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;
+                    });
+            }
+
+            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, 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>* 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;
+            }
+
+            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>>*
+            addAcceptedStringInArray()
+            {
+                return new StringValidationBuilder<OneOfValidationBuilder<T>>(
+                    [this](XML::Element& e) {
+                        element.addChild(e.dublicate());
+                        return this;
+                    });
+            }
+
+            NumberValidationBuilder<OneOfValidationBuilder<T>>*
+            addAcceptedNumberInArray()
+            {
+                return new NumberValidationBuilder<OneOfValidationBuilder<T>>(
+                    [this](XML::Element& e) {
+                        element.addChild(e.dublicate());
+                        return this;
+                    });
+            }
+
+            BoolValidationBuilder<OneOfValidationBuilder<T>>*
+            addAcceptedBooleanInArray()
+            {
+                return new BoolValidationBuilder<OneOfValidationBuilder<T>>(
+                    [this](XML::Element& e) {
+                        element.addChild(e.dublicate());
+                        return this;
+                    });
+            }
+
+            ObjectValidationBuilder<OneOfValidationBuilder<T>>*
+            addAcceptedObjectInArray()
+            {
+                return new ObjectValidationBuilder<OneOfValidationBuilder<T>>(
+                    [this](XML::Element& e) {
+                        element.addChild(e.dublicate());
+                        return this;
+                    });
+            }
+
+            ArrayValidationBuilder<OneOfValidationBuilder<T>>*
+            addAcceptedArrayInArray()
+            {
+                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

+ 10 - 32
Fenster.cpp

@@ -16,6 +16,7 @@
 #endif
 #include <iostream>
 
+#include "Logging.h"
 #include "Zeit.h"
 
 using namespace Framework;
@@ -64,28 +65,19 @@ LRESULT CALLBACK Framework::WindowProc(
     if (msgExit) return (DefWindowProc(hwnd, message, wparam, lparam));
     switch (message)
     {
-        // Maus Zeiger
-    case WM_SETCURSOR:
+    case WM_SETCURSOR: // Maus Zeiger
         MausZeiger.update();
         return 0;
-        // Fenster
-    case WM_SIZE:
+    case WM_SIZE: // Fenster
         if (wparam == SIZE_RESTORED) WFensterA.sendRestoreMessage(hwnd);
         break;
-        // Schließen
-    case WM_CLOSE:
-        if (WFensterA.sendVSchließMessage(hwnd))
-            return 0;
-        else
-            break;
+    case WM_CLOSE: // Schließen
+        if (WFensterA.sendVSchließMessage(hwnd)) return 0;
+        break;
     case WM_DESTROY:
-        if (WFensterA.sendNSchließMessage(hwnd))
-            return 0;
-        else
-            break;
-        // Maus
+        if (WFensterA.sendNSchließMessage(hwnd)) return 0;
+        break;           // Maus
     case WM_LBUTTONDOWN: // Linksklick
-        if (1)
         {
             MausStand[M_Links] = 1;
             MausEreignis me = {ME_PLinks,
@@ -99,7 +91,6 @@ LRESULT CALLBACK Framework::WindowProc(
             break;
         }
     case WM_RBUTTONDOWN: // Rechtsklick
-        if (1)
         {
             MausStand[M_Rechts] = 1;
             MausEreignis me = {ME_PRechts,
@@ -113,7 +104,6 @@ LRESULT CALLBACK Framework::WindowProc(
             break;
         }
     case WM_MBUTTONDOWN: // Mittelklick
-        if (1)
         {
             MausStand[M_Mitte] = 1;
             MausEreignis me = {ME_PMitte,
@@ -127,7 +117,6 @@ LRESULT CALLBACK Framework::WindowProc(
             break;
         }
     case WM_LBUTTONUP: // Linksrelease
-        if (1)
         {
             MausStand[M_Links] = 0;
             MausEreignis me = {ME_RLinks,
@@ -141,7 +130,6 @@ LRESULT CALLBACK Framework::WindowProc(
             break;
         }
     case WM_RBUTTONUP: // Rechtsrelease
-        if (1)
         {
             MausStand[M_Rechts] = 0;
             MausEreignis me = {ME_RRechts,
@@ -155,7 +143,6 @@ LRESULT CALLBACK Framework::WindowProc(
             break;
         }
     case WM_MBUTTONUP: // Mittelrelease
-        if (1)
         {
             MausStand[M_Mitte] = 0;
             MausEreignis me = {ME_RMitte,
@@ -169,7 +156,6 @@ LRESULT CALLBACK Framework::WindowProc(
             break;
         }
     case WM_LBUTTONDBLCLK: // Linksdoppelklick
-        if (1)
         {
             MausEreignis me = {ME_DKLinks,
                 (int)LOWORD(lparam),
@@ -182,7 +168,6 @@ LRESULT CALLBACK Framework::WindowProc(
             break;
         }
     case WM_RBUTTONDBLCLK: // Rechtsdoppelklick
-        if (1)
         {
             MausEreignis me = {ME_DKRechts,
                 (int)LOWORD(lparam),
@@ -195,7 +180,6 @@ LRESULT CALLBACK Framework::WindowProc(
             break;
         }
     case WM_MBUTTONDBLCLK: // Mitteldoppelklick
-        if (1)
         {
             MausEreignis me = {ME_DKMitte,
                 (int)LOWORD(lparam),
@@ -208,7 +192,6 @@ LRESULT CALLBACK Framework::WindowProc(
             break;
         }
     case WM_MOUSEHOVER: // Maus betritt Fenster
-        if (1)
         {
             MausTrack = 1;
             MausEreignis me = {ME_Betritt,
@@ -222,7 +205,6 @@ LRESULT CALLBACK Framework::WindowProc(
             break;
         }
     case WM_MOUSELEAVE: // Maus verlässt Fenster
-        if (1)
         {
             MausTrack = 1;
             MausEreignis me = {ME_Leaves,
@@ -236,7 +218,6 @@ LRESULT CALLBACK Framework::WindowProc(
             break;
         }
     case WM_MOUSEMOVE: // Maus wird bewegt
-        if (1)
         {
             if (MausTrack)
             {
@@ -259,7 +240,6 @@ LRESULT CALLBACK Framework::WindowProc(
             break;
         }
     case WM_MOUSEWHEEL: // Maus scroll
-        if (1)
         {
             Punkt pos = getMausPos();
             RECT r;
@@ -276,7 +256,6 @@ LRESULT CALLBACK Framework::WindowProc(
         }
         // Tastatur
     case WM_KEYDOWN:
-        if (1)
         {
             TastaturEreignis te = {
                 TE_Press, {0, 0, 0},
@@ -284,10 +263,9 @@ LRESULT CALLBACK Framework::WindowProc(
             };
             CalculateEnteredString((int)wparam, 0, te);
             WFensterA.sendTastaturMessage(hwnd, te);
-            break;
+            return 0;
         } // Taste wird gedrückt
     case WM_KEYUP:
-        if (1)
         {
             TastaturEreignis te = {
                 TE_Release, {0, 0, 0},
@@ -295,7 +273,7 @@ LRESULT CALLBACK Framework::WindowProc(
             };
             CalculateEnteredString((int)wparam, HIWORD(lparam) & 0xFF, te);
             WFensterA.sendTastaturMessage(hwnd, te);
-            break;
+            return 0;
         } // Taste wird losgelassen
     }
     return (DefWindowProc(hwnd, message, wparam, lparam));

+ 2 - 0
Framework Linux.vcxproj

@@ -133,6 +133,7 @@
     <ClCompile Include="InitDatei.cpp" />
     <ClCompile Include="InMemoryBuffer.cpp" />
     <ClCompile Include="JSON.cpp" />
+    <ClCompile Include="JsonEditor.cpp" />
     <ClCompile Include="Kamera2D.cpp" />
     <ClCompile Include="Key.cpp" />
     <ClCompile Include="Knopf.cpp" />
@@ -154,6 +155,7 @@
     <ClCompile Include="Random.cpp" />
     <ClInclude Include="Console.h" />
     <ClInclude Include="Iterator.h" />
+    <ClInclude Include="JsonEditor.h" />
     <ClInclude Include="Logging.h" />
     <ClInclude Include="RCPointer.h" />
     <ClCompile Include="Reader.cpp" />

+ 24 - 12
Framework Linux.vcxproj.filters

@@ -47,6 +47,12 @@
     <Filter Include="Framework\Log">
       <UniqueIdentifier>{89822540-d142-43f2-a08f-9f495192ce1c}</UniqueIdentifier>
     </Filter>
+    <Filter Include="Framework\Data\JSON">
+      <UniqueIdentifier>{50758255-c5da-44da-83a1-de1d45d3682e}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Framework\Data\XML">
+      <UniqueIdentifier>{29d045f7-7d16-4633-928e-4fc16117b7b9}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="Model2D.h">
@@ -226,12 +232,6 @@
     <ClInclude Include="Rect2.h">
       <Filter>Framework\Grafik\2D</Filter>
     </ClInclude>
-    <ClInclude Include="JSON.h">
-      <Filter>Framework</Filter>
-    </ClInclude>
-    <ClInclude Include="XML.h">
-      <Filter>Framework</Filter>
-    </ClInclude>
     <ClInclude Include="Array.h">
       <Filter>Framework\Data</Filter>
     </ClInclude>
@@ -322,6 +322,15 @@
     <ClInclude Include="Console.h">
       <Filter>Framework\IO</Filter>
     </ClInclude>
+    <ClInclude Include="JSON.h">
+      <Filter>Framework\Data\JSON</Filter>
+    </ClInclude>
+    <ClInclude Include="XML.h">
+      <Filter>Framework\Data\XML</Filter>
+    </ClInclude>
+    <ClInclude Include="JsonEditor.h">
+      <Filter>Framework\Data\JSON</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Bild.cpp">
@@ -471,9 +480,6 @@
     <ClCompile Include="Global.cpp">
       <Filter>Framework</Filter>
     </ClCompile>
-    <ClCompile Include="JSON.cpp">
-      <Filter>Framework</Filter>
-    </ClCompile>
     <ClCompile Include="Key.cpp">
       <Filter>Framework</Filter>
     </ClCompile>
@@ -501,9 +507,6 @@
     <ClCompile Include="UIInitialization.cpp">
       <Filter>Framework</Filter>
     </ClCompile>
-    <ClCompile Include="XML.cpp">
-      <Filter>Framework</Filter>
-    </ClCompile>
     <ClCompile Include="Zeit.cpp">
       <Filter>Framework</Filter>
     </ClCompile>
@@ -525,5 +528,14 @@
     <ClCompile Include="Writer.cpp">
       <Filter>Framework\IO</Filter>
     </ClCompile>
+    <ClCompile Include="JSON.cpp">
+      <Filter>Framework\Data\JSON</Filter>
+    </ClCompile>
+    <ClCompile Include="XML.cpp">
+      <Filter>Framework\Data\XML</Filter>
+    </ClCompile>
+    <ClCompile Include="JsonEditor.cpp">
+      <Filter>Framework\Data\JSON</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 7 - 0
Framework.vcxproj

@@ -198,6 +198,7 @@ copy "x64\Release\Framework.dll" "..\..\Spiele Platform\SMP\Fertig\x64\framework
     </Bscmake>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClInclude Include="AbstractElement.h" />
     <ClInclude Include="AlphaFeld.h" />
     <ClInclude Include="Animation.h" />
     <ClInclude Include="Animation3D.h" />
@@ -205,7 +206,9 @@ copy "x64\Release\Framework.dll" "..\..\Spiele Platform\SMP\Fertig\x64\framework
     <ClInclude Include="AsynchronCall.h" />
     <ClInclude Include="AuswahlBox.h" />
     <ClInclude Include="Console.h" />
+    <ClInclude Include="DataValidator.h" />
     <ClInclude Include="Iterator.h" />
+    <ClInclude Include="JsonEditor.h" />
     <ClInclude Include="Logging.h" />
     <ClInclude Include="Regex.h" />
     <ClInclude Include="Base64.h" />
@@ -257,6 +260,7 @@ copy "x64\Release\Framework.dll" "..\..\Spiele Platform\SMP\Fertig\x64\framework
     <ClInclude Include="RCPointer.h" />
     <ClInclude Include="ReferenceCounter.h" />
     <ClInclude Include="Slider.h" />
+    <ClInclude Include="Stack.h" />
     <ClInclude Include="Stream.h" />
     <ClInclude Include="UIDialog.h" />
     <ClInclude Include="Model2D.h" />
@@ -303,12 +307,15 @@ copy "x64\Release\Framework.dll" "..\..\Spiele Platform\SMP\Fertig\x64\framework
     <ClInclude Include="Zeit.h" />
   </ItemGroup>
   <ItemGroup>
+    <ClCompile Include="AbstractElement.cpp" />
     <ClCompile Include="AlphaFeld.cpp" />
     <ClCompile Include="Animation.cpp" />
     <ClCompile Include="Animation3D.cpp" />
     <ClCompile Include="AsynchronCall.cpp" />
     <ClCompile Include="AuswahlBox.cpp" />
     <ClCompile Include="Console.cpp" />
+    <ClCompile Include="DataValidator.cpp" />
+    <ClCompile Include="JsonEditor.cpp" />
     <ClCompile Include="Logging.cpp" />
     <ClCompile Include="Regex.cpp" />
     <ClCompile Include="Base64.cpp" />

+ 42 - 12
Framework.vcxproj.filters

@@ -53,6 +53,15 @@
     <Filter Include="Framework\Log">
       <UniqueIdentifier>{73eabc9b-6ad1-4dff-870e-f5a846c66522}</UniqueIdentifier>
     </Filter>
+    <Filter Include="Framework\Data\JSON">
+      <UniqueIdentifier>{8685472c-c517-4a52-93b1-3bb16fdd8fc9}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Framework\Data\XML">
+      <UniqueIdentifier>{6ece3117-894b-45f6-90b5-2c81a92980ce}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="Framework\Data\Abstract">
+      <UniqueIdentifier>{3b8ce08b-1328-41f9-bb01-fea44b107dab}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="Model2D.h">
@@ -250,15 +259,9 @@
     <ClInclude Include="Rect2.h">
       <Filter>Framework\Grafik\2D</Filter>
     </ClInclude>
-    <ClInclude Include="JSON.h">
-      <Filter>Framework</Filter>
-    </ClInclude>
     <ClInclude Include="Dialog.h">
       <Filter>Framework</Filter>
     </ClInclude>
-    <ClInclude Include="XML.h">
-      <Filter>Framework</Filter>
-    </ClInclude>
     <ClInclude Include="UIMLView.h">
       <Filter>Framework\Objekte2D</Filter>
     </ClInclude>
@@ -364,6 +367,24 @@
     <ClInclude Include="Console.h">
       <Filter>Framework\IO</Filter>
     </ClInclude>
+    <ClInclude Include="JSON.h">
+      <Filter>Framework\Data\JSON</Filter>
+    </ClInclude>
+    <ClInclude Include="XML.h">
+      <Filter>Framework\Data\XML</Filter>
+    </ClInclude>
+    <ClInclude Include="JsonEditor.h">
+      <Filter>Framework\Data\JSON</Filter>
+    </ClInclude>
+    <ClInclude Include="Stack.h">
+      <Filter>Framework\Data</Filter>
+    </ClInclude>
+    <ClInclude Include="AbstractElement.h">
+      <Filter>Framework\Data\Abstract</Filter>
+    </ClInclude>
+    <ClInclude Include="DataValidator.h">
+      <Filter>Framework\Data\Abstract</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Model3DCollection.h">
@@ -567,9 +588,6 @@
     <ClCompile Include="Global.cpp">
       <Filter>Framework</Filter>
     </ClCompile>
-    <ClCompile Include="JSON.cpp">
-      <Filter>Framework</Filter>
-    </ClCompile>
     <ClCompile Include="Key.cpp">
       <Filter>Framework</Filter>
     </ClCompile>
@@ -597,9 +615,6 @@
     <ClCompile Include="UIInitialization.cpp">
       <Filter>Framework</Filter>
     </ClCompile>
-    <ClCompile Include="XML.cpp">
-      <Filter>Framework</Filter>
-    </ClCompile>
     <ClCompile Include="Zeit.cpp">
       <Filter>Framework</Filter>
     </ClCompile>
@@ -618,6 +633,21 @@
     <ClCompile Include="Console.cpp">
       <Filter>Framework\IO</Filter>
     </ClCompile>
+    <ClCompile Include="JSON.cpp">
+      <Filter>Framework\Data\JSON</Filter>
+    </ClCompile>
+    <ClCompile Include="XML.cpp">
+      <Filter>Framework\Data\XML</Filter>
+    </ClCompile>
+    <ClCompile Include="JsonEditor.cpp">
+      <Filter>Framework\Data\JSON</Filter>
+    </ClCompile>
+    <ClCompile Include="DataValidator.cpp">
+      <Filter>Framework\Data\Abstract</Filter>
+    </ClCompile>
+    <ClCompile Include="AbstractElement.cpp">
+      <Filter>Framework\Data\Abstract</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <FxCompile Include="DX12VertexShader.hlsl">

+ 70 - 1162
JSON.cpp

@@ -1,7 +1,6 @@
 #include "JSON.h"
 
 #include "Datei.h"
-#include "Logging.h"
 
 using namespace Framework;
 using namespace JSON;
@@ -11,18 +10,18 @@ using namespace JSON;
 JSONValue::JSONValue()
     : ReferenceCounter()
 {
-    this->type = JSONType::NULL_;
+    this->type = AbstractType::NULL_;
 }
 
 JSONValue::~JSONValue() {}
 
-JSONValue::JSONValue(JSONType type)
+JSONValue::JSONValue(AbstractType type)
     : ReferenceCounter()
 {
     this->type = type;
 }
 
-JSONType JSONValue::getType() const
+AbstractType JSONValue::getType() const
 {
     return type;
 }
@@ -62,12 +61,37 @@ JSONObject* JSONValue::asObject() const
     return (JSONObject*)this;
 }
 
-#pragma endregion Content
+const AbstractBool* Framework::JSON::JSONValue::asAbstractBool() const
+{
+    return dynamic_cast<const AbstractBool*>(this);
+}
+
+const AbstractNumber* Framework::JSON::JSONValue::asAbstractNumber() const
+{
+    return dynamic_cast<const AbstractNumber*>(this);
+}
+
+const AbstractString* Framework::JSON::JSONValue::asAbstractString() const
+{
+    return dynamic_cast<const AbstractString*>(this);
+}
+
+const AbstractArray* Framework::JSON::JSONValue::asAbstractArray() const
+{
+    return dynamic_cast<const AbstractArray*>(this);
+}
+
+const AbstractObject* Framework::JSON::JSONValue::asAbstractObject() const
+{
+    return dynamic_cast<const AbstractObject*>(this);
+}
+
+#pragma endregion Cotent
 
 #pragma region JSONBool
 
 JSONBool::JSONBool(bool b)
-    : JSONValue(JSONType::BOOLEAN)
+    : JSONValue(AbstractType::BOOLEAN)
 {
     this->b = b;
 }
@@ -90,12 +114,12 @@ JSONValue* JSONBool::clone() const
     return new JSONBool(b);
 }
 
-#pragma endregion Content
+#pragma endregion Cotent
 
 #pragma region JSONNumber
 
 JSONNumber::JSONNumber(double num)
-    : JSONValue(JSONType::NUMBER)
+    : JSONValue(AbstractType::NUMBER)
 {
     number = num;
 }
@@ -115,12 +139,12 @@ JSONValue* JSONNumber::clone() const
     return new JSONNumber(number);
 }
 
-#pragma endregion Content
+#pragma endregion Cotent
 
 #pragma region JSONString
 
 JSONString::JSONString(Text string)
-    : JSONValue(JSONType::STRING)
+    : JSONValue(AbstractType::STRING)
 {
     this->string = string;
     this->string.ersetzen("\\\"", "\"");
@@ -148,18 +172,18 @@ JSONValue* JSONString::clone() const
     return new JSONString(esc);
 }
 
-#pragma endregion Content
+#pragma endregion Cotent
 
 #pragma region JSONArray
 
 JSONArray::JSONArray()
-    : JSONValue(JSONType::ARRAY)
+    : JSONValue(AbstractType::ARRAY)
 {
     array = new RCArray<JSONValue>();
 }
 
 JSONArray::JSONArray(Text string)
-    : JSONValue(JSONType::ARRAY)
+    : JSONValue(AbstractType::ARRAY)
 {
     array = new RCArray<JSONValue>();
     string = Parser::removeWhitespace(string);
@@ -180,7 +204,7 @@ JSONArray::JSONArray(Text string)
 }
 
 JSONArray::JSONArray(const JSONArray& arr)
-    : JSONValue(JSONType::ARRAY)
+    : JSONValue(AbstractType::ARRAY)
 {
     array = dynamic_cast<RCArray<JSONValue>*>(arr.array->getThis());
 }
@@ -222,12 +246,17 @@ JSONValue* JSONArray::zValue(int i) const
     return array->z(i);
 }
 
+AbstractElement* JSONArray::zAbstractValue(int i) const
+{
+    return zValue(i);
+}
+
 int JSONArray::getLength() const
 {
     return array->getEintragAnzahl();
 }
 
-bool JSONArray::isValueOfType(int i, JSONType type) const
+bool JSONArray::isValueOfType(int i, AbstractType type) const
 {
     return i >= 0 && i < array->getEintragAnzahl()
         && array->z(i)->getType() == type;
@@ -265,19 +294,19 @@ JSONValue* JSONArray::clone() const
     return arr;
 }
 
-#pragma endregion Content
+#pragma endregion Cotent
 
 #pragma region JSONObject
 
 JSONObject::JSONObject()
-    : JSONValue(JSONType::OBJECT)
+    : JSONValue(AbstractType::OBJECT)
 {
     fields = new Array<Text>();
     values = new RCArray<JSONValue>();
 }
 
 JSONObject::JSONObject(Text string)
-    : JSONValue(JSONType::OBJECT)
+    : JSONValue(AbstractType::OBJECT)
 {
     fields = new Array<Text>();
     values = new RCArray<JSONValue>();
@@ -307,7 +336,7 @@ JSONObject::JSONObject(Text string)
 }
 
 JSONObject::JSONObject(const JSONObject& obj)
-    : JSONValue(JSONType::OBJECT)
+    : JSONValue(AbstractType::OBJECT)
 {
     fields = dynamic_cast<Array<Text>*>(obj.fields->getThis());
     values = dynamic_cast<RCArray<JSONValue>*>(obj.values->getThis());
@@ -350,16 +379,12 @@ bool JSONObject::removeValue(Text field)
     return 0;
 }
 
-bool JSONObject::hasValue(Text field)
+bool JSONObject::hasValue(Text field) const
 {
-    for (int i = 0; i < fields->getEintragAnzahl(); i++)
-    {
-        if (fields->get(i).istGleich(field)) return 1;
-    }
-    return 0;
+    return zValue(field);
 }
 
-JSONValue* JSONObject::getValue(Text field)
+JSONValue* JSONObject::getValue(Text field) const
 {
     for (int i = 0; i < fields->getEintragAnzahl(); i++)
     {
@@ -368,7 +393,7 @@ JSONValue* JSONObject::getValue(Text field)
     return 0;
 }
 
-JSONValue* JSONObject::zValue(Text field)
+JSONValue* JSONObject::zValue(Text field) const
 {
     for (int i = 0; i < fields->getEintragAnzahl(); i++)
     {
@@ -377,6 +402,11 @@ JSONValue* JSONObject::zValue(Text field)
     return 0;
 }
 
+AbstractElement* JSONObject::zAbstractValue(Text field) const
+{
+    return zValue(field);
+}
+
 ArrayIterator<Text> JSONObject::getFields()
 {
     return fields->begin();
@@ -387,12 +417,22 @@ ArrayIterator<JSONValue*> JSONObject::getValues()
     return values->begin();
 }
 
+Text Framework::JSON::JSONObject::getFieldKey(int i) const
+{
+    return fields->get(i);
+}
+
+AbstractElement* Framework::JSON::JSONObject::zAbstractValue(int i) const
+{
+    return values->z(i);
+}
+
 int JSONObject::getFieldCount() const
 {
     return fields->getEintragAnzahl();
 }
 
-bool JSONObject::isValueOfType(Text field, JSONType type) const
+bool JSONObject::isValueOfType(Text field, AbstractType type) const
 {
     for (int i = 0; i < fields->getEintragAnzahl(); i++)
     {
@@ -430,7 +470,7 @@ JSONValue* JSONObject::clone() const
     return obj;
 }
 
-#pragma endregion Content
+#pragma endregion Cotent
 
 JSONValue* JSON::loadJSONFromFile(Text path)
 {
@@ -651,1136 +691,4 @@ int Parser::findValueEndInObject(const char* str)
     return i;
 }
 
-#pragma endregion Content
-
-using namespace Validator;
-
-#pragma region JSONValidationResult
-
-JSONValidationResult::JSONValidationResult()
-    : ReferenceCounter()
-{}
-
-JSONValidationResult::~JSONValidationResult() {}
-
-void Framework::JSON::Validator::JSONValidationResult::logInvalidInfo(
-    std::source_location location) const
-{
-    Logging::error(location) << getInvalidInfo(0).getText();
-}
-
-#pragma region JSONTypeMissmatch
-
-JSONTypeMissmatch::JSONTypeMissmatch(Text path,
-    JSONValue* foundValue,
-    XML::Element* expected,
-    JSONValidationResult* reason)
-    : JSONValidationResult()
-{
-    this->path = path;
-    this->foundValue = foundValue;
-    this->expected = expected;
-    this->reason = reason;
-}
-
-JSONTypeMissmatch::~JSONTypeMissmatch()
-{
-    foundValue->release();
-    expected->release();
-    if (reason) reason->release();
-}
-
-bool JSONTypeMissmatch::isValid() const
-{
-    return 0;
-}
-
-Text JSONTypeMissmatch::getInvalidInfo(int indent) const
-{
-    Text ind = "";
-    ind.fillText(' ', indent);
-    Text error = "";
-    error.append() << ind.getText() << "Type missmatch at path '"
-                   << path.getText() << "'. JSON Value\n"
-                   << ind.getText() << foundValue->toString().getText() << "\n"
-                   << ind.getText() << "does not match expected type\n";
-    if (reason)
-    {
-        error.append() << ind.getText() << "Reason for type mismatch:\n"
-                       << reason->getInvalidInfo(indent + 4);
-    }
-    else
-    {
-        error.append() << ind.getText() << expected->toString().getText()
-                       << "\n";
-    }
-    return error;
-}
-
-JSONValue* JSONTypeMissmatch::getValidPart(
-    RCArray<JSONValidationResult>* removedPartsValidationResults)
-{
-    if (reason)
-    {
-        RCArray<JSONValidationResult> temporaryInvalidParts;
-        JSONValue* valid = reason->getValidPart(&temporaryInvalidParts);
-        Text* p = reason->getPath().getTeilText(path.getLength());
-        if (foundValue->getType() == JSONType::ARRAY)
-        {
-            if (!valid
-                && (!expected->hasAttribute("removeInvalidEntries")
-                    || !expected->getAttributeValue("removeInvalidEntries")
-                            .istGleich("true")))
-            {
-                if (removedPartsValidationResults)
-                {
-                    if (temporaryInvalidParts.getEintragAnzahl() == 1)
-                    {
-                        if (reason) reason->release();
-                        reason = temporaryInvalidParts.get(0);
-                    }
-                    else
-                    {
-                        for (JSONValidationResult* res : temporaryInvalidParts)
-                        {
-                            if (res->isDifferent(this))
-                            {
-                                removedPartsValidationResults->add(
-                                    dynamic_cast<JSONValidationResult*>(
-                                        res->getThis()));
-                            }
-                        }
-                    }
-                    removedPartsValidationResults->add(
-                        dynamic_cast<JSONValidationResult*>(getThis()));
-                }
-                p->release();
-                return 0;
-            }
-            if (p->hatAt(0, "[") && p->hatAt(p->getLength() - 1, "]"))
-            {
-                Text* it = p->getTeilText(1, p->getLength() - 1);
-                int i = (int)*it;
-                it->release();
-                if (i >= 0 && foundValue->asArray()->getLength() > i)
-                {
-                    if (removedPartsValidationResults)
-                    {
-                        for (JSONValidationResult* res : temporaryInvalidParts)
-                        {
-                            removedPartsValidationResults->add(
-                                dynamic_cast<JSONValidationResult*>(
-                                    res->getThis()));
-                        }
-                    }
-                    JSONValue* tmp = foundValue->clone();
-                    if (valid)
-                        tmp->asArray()->setValue(i, valid);
-                    else
-                        tmp->asArray()->removeValue(i);
-                    JSONValidationResult* res = JSONValidator(
-                        dynamic_cast<XML::Element*>(expected->getThis()))
-                                                    .validate(tmp);
-                    res->addBasePath(path);
-                    if (res->isValid())
-                    {
-                        res->release();
-                        p->release();
-                        return tmp;
-                    }
-                    else if (res->isDifferent(this) || !valid)
-                    {
-                        p->release();
-                        tmp->release();
-                        JSONValue* result
-                            = res->getValidPart(removedPartsValidationResults);
-                        res->release();
-                        return result;
-                    }
-                    tmp->release();
-                    res->release();
-                }
-            }
-        }
-        else if (foundValue->getType() == JSONType::OBJECT)
-        {
-            if (!valid
-                && (!expected->hasAttribute("removeInvalidEntries")
-                    || !expected->getAttributeValue("removeInvalidEntries")
-                            .istGleich("true")))
-            {
-                if (removedPartsValidationResults)
-                {
-                    if (temporaryInvalidParts.getEintragAnzahl() == 1)
-                    {
-                        if (reason) reason->release();
-                        reason = temporaryInvalidParts.get(0);
-                    }
-                    else
-                    {
-                        for (JSONValidationResult* res : temporaryInvalidParts)
-                        {
-                            if (res->isDifferent(this))
-                            {
-                                removedPartsValidationResults->add(
-                                    dynamic_cast<JSONValidationResult*>(
-                                        res->getThis()));
-                            }
-                        }
-                    }
-                    removedPartsValidationResults->add(
-                        dynamic_cast<JSONValidationResult*>(getThis()));
-                }
-                p->release();
-                return 0;
-            }
-            if (p->hatAt(0, "."))
-            {
-                if (removedPartsValidationResults)
-                {
-                    for (JSONValidationResult* res : temporaryInvalidParts)
-                    {
-                        removedPartsValidationResults->add(
-                            dynamic_cast<JSONValidationResult*>(
-                                res->getThis()));
-                    }
-                }
-                Text* at = p->getTeilText(1);
-                Text attr = *at;
-                at->release();
-                JSONValue* tmp = foundValue->clone();
-                tmp->asObject()->removeValue(attr);
-                if (valid) tmp->asObject()->addValue(attr, valid);
-                JSONValidationResult* res = JSONValidator(
-                    dynamic_cast<XML::Element*>(expected->getThis()))
-                                                .validate(tmp);
-                res->addBasePath(path);
-                if (res->isValid())
-                {
-                    res->release();
-                    p->release();
-                    return tmp;
-                }
-                else if (res->isDifferent(this) || !valid)
-                {
-                    p->release();
-                    tmp->release();
-                    JSONValue* result
-                        = res->getValidPart(removedPartsValidationResults);
-                    res->release();
-                    return result;
-                }
-                tmp->release();
-                res->release();
-            }
-        }
-        p->release();
-        if (valid) valid->release();
-    }
-    if (removedPartsValidationResults)
-    {
-        removedPartsValidationResults->add(
-            dynamic_cast<JSONValidationResult*>(getThis()));
-    }
-    return 0;
-}
-
-Text JSONTypeMissmatch::getPath() const
-{
-    return path;
-}
-
-bool JSONTypeMissmatch::isDifferent(const JSONValidationResult* zResult) const
-{
-    const JSONTypeMissmatch* casted
-        = dynamic_cast<const JSONTypeMissmatch*>(zResult);
-    if (casted == 0) return 1;
-    if (!casted->getPath().istGleich(path)) return 1;
-    return reason->isDifferent(casted->reason);
-}
-
-void Framework::JSON::Validator::JSONTypeMissmatch::addBasePath(Text basePath)
-{
-    path = basePath + path;
-    if (reason) reason->addBasePath(basePath);
-}
-
-#pragma endregion Content
-
-#pragma region JSONUnknownValue
-
-JSONUnknownValue::JSONUnknownValue(Text path, JSONValue* foundValue)
-    : JSONValidationResult()
-{
-    this->path = path;
-    this->foundValue = foundValue;
-}
-
-JSONUnknownValue::~JSONUnknownValue()
-{
-    foundValue->release();
-}
-
-bool JSONUnknownValue::isValid() const
-{
-    return 0;
-}
-
-Text JSONUnknownValue::getInvalidInfo(int indent) const
-{
-    Text ind = "";
-    ind.fillText(' ', indent);
-    Text error = "";
-    error.append() << ind.getText() << "Unknown Value at '" << path.getText()
-                   << "'. Value found:\n"
-                   << ind.getText() << foundValue->toString().getText() << "\n";
-    return error;
-}
-
-JSONValue* JSONUnknownValue::getValidPart(
-    RCArray<JSONValidationResult>* removedPartsValidationResults)
-{
-    if (removedPartsValidationResults)
-    {
-        removedPartsValidationResults->add(
-            dynamic_cast<JSONValidationResult*>(getThis()));
-    }
-    return 0;
-}
-
-Text JSONUnknownValue::getPath() const
-{
-    return path;
-}
-
-bool JSONUnknownValue::isDifferent(const JSONValidationResult* zResult) const
-{
-    const JSONUnknownValue* casted
-        = dynamic_cast<const JSONUnknownValue*>(zResult);
-    if (casted == 0) return 1;
-    if (!casted->getPath().istGleich(path)) return 1;
-    return 0;
-}
-
-void Framework::JSON::Validator::JSONUnknownValue::addBasePath(Text basePath)
-{
-    path = basePath + path;
-}
-
-#pragma endregion Content
-
-#pragma region JSONMissingValue
-
-JSONMissingValue::JSONMissingValue(Text path, XML::Element* expected)
-    : JSONValidationResult()
-{
-    this->path = path;
-    this->expected = expected;
-}
-
-JSONMissingValue::~JSONMissingValue()
-{
-    expected->release();
-}
-
-bool JSONMissingValue::isValid() const
-{
-    return 0;
-}
-
-Text JSONMissingValue::getInvalidInfo(int indent) const
-{
-    Text ind = "";
-    ind.fillText(' ', indent);
-    Text error = "";
-    error.append() << ind.getText() << "Missing Value at '" << path.getText()
-                   << "'. Expected type:\n"
-                   << ind.getText() << expected->toString().getText() << "\n";
-    return error;
-}
-
-JSONValue* JSONMissingValue::getValidPart(
-    RCArray<JSONValidationResult>* removedPartsValidationResults)
-{
-    if (expected->hasAttribute("default"))
-    {
-        JSONValue* def
-            = Parser::getValue(expected->getAttributeValue("default"));
-        JSONValidationResult* res
-            = JSONValidator(dynamic_cast<XML::Element*>(expected->getThis()))
-                  .validate(def);
-        res->addBasePath(path);
-        if (res->isValid())
-        {
-            res->release();
-            return def;
-        }
-        else if (res->isDifferent(this))
-        {
-            def->release();
-            JSONValue* result
-                = res->getValidPart(removedPartsValidationResults);
-            res->release();
-            return result;
-        }
-        def->release();
-    }
-    if (removedPartsValidationResults)
-    {
-        removedPartsValidationResults->add(
-            dynamic_cast<JSONValidationResult*>(getThis()));
-    }
-    return 0;
-}
-
-Text JSONMissingValue::getPath() const
-{
-    return path;
-}
-
-bool JSONMissingValue::isDifferent(const JSONValidationResult* zResult) const
-{
-    const JSONMissingValue* casted
-        = dynamic_cast<const JSONMissingValue*>(zResult);
-    if (casted == 0) return 1;
-    if (!casted->getPath().istGleich(path)) return 1;
-    return 0;
-}
-
-void Framework::JSON::Validator::JSONMissingValue::addBasePath(Text basePath)
-{
-    path = basePath + path;
-}
-
-#pragma endregion Content
-
-#pragma region JSONMissingOneOf
-
-JSONMissingOneOf::JSONMissingOneOf(Text path, XML::Editor expected)
-    : JSONValidationResult()
-{
-    this->path = path;
-    for (XML::Element* e : expected)
-        this->expected.add(dynamic_cast<XML::Element*>(e->getThis()));
-}
-
-JSONMissingOneOf::~JSONMissingOneOf() {}
-
-bool JSONMissingOneOf::isValid() const
-{
-    return 0;
-}
-
-Text JSONMissingOneOf::getInvalidInfo(int indent) const
-{
-    Text ind = "";
-    ind.fillText(' ', indent);
-    Text error = "";
-    error.append() << ind.getText() << "Missing Value at '" << path.getText()
-                   << "'. The value should have one of the specified types:\n";
-    ind += "    ";
-    for (XML::Element* e : expected)
-    {
-        error.append() << ind.getText() << e->toString().getText() << "\n";
-    }
-    return error;
-}
-
-JSONValue* JSONMissingOneOf::getValidPart(
-    RCArray<JSONValidationResult>* removedPartsValidationResults)
-{
-    for (XML::Element* e : expected)
-    {
-        if (e->hasAttribute("default"))
-        {
-            JSONValue* defaultValue
-                = Parser::getValue(e->getAttributeValue("default"));
-            if (defaultValue)
-            {
-                JSONValue* valid
-                    = JSONValidator(dynamic_cast<XML::Element*>(e->getThis()))
-                          .getValidParts(
-                              defaultValue, removedPartsValidationResults);
-                defaultValue->release();
-                if (valid)
-                {
-                    return valid;
-                }
-            }
-        }
-    }
-    if (removedPartsValidationResults)
-    {
-        removedPartsValidationResults->add(
-            dynamic_cast<JSONValidationResult*>(getThis()));
-    }
-    // multiple possibilities are undecidable
-    return 0;
-}
-
-Text JSONMissingOneOf::getPath() const
-{
-    return path;
-}
-
-bool JSONMissingOneOf::isDifferent(const JSONValidationResult* zResult) const
-{
-    const JSONMissingOneOf* casted
-        = dynamic_cast<const JSONMissingOneOf*>(zResult);
-    if (casted == 0) return 1;
-    if (!casted->getPath().istGleich(path)) return 1;
-    return 0;
-}
-
-void Framework::JSON::Validator::JSONMissingOneOf::addBasePath(Text basePath)
-{
-    path = basePath + path;
-}
-
-#pragma endregion Content
-
-#pragma region JSONNoTypeMatching
-
-JSONNoTypeMatching::JSONNoTypeMatching(Text path,
-    JSONValue* foundValue,
-    RCArray<XML::Element>& expected,
-    RCArray<JSONValidationResult>& reasons)
-    : JSONValidationResult()
-{
-    this->path = path;
-    this->foundValue = foundValue;
-    this->expected = expected;
-    this->reasons = reasons;
-}
-
-JSONNoTypeMatching::~JSONNoTypeMatching()
-{
-    foundValue->release();
-}
-
-bool JSONNoTypeMatching::isValid() const
-{
-    return 0;
-}
-
-Text JSONNoTypeMatching::getInvalidInfo(int indent) const
-{
-    Text ind = "";
-    ind.fillText(' ', indent);
-    Text error = "";
-    error.append() << ind.getText() << "Found Value at '" << path.getText()
-                   << "' did not match any of the specified types\n";
-    Text ind2 = ind + "    ";
-    error.append() << ind.getText() << "Reasons:\n";
-    for (JSONValidationResult* reason : reasons)
-    {
-        error += reason->getInvalidInfo(indent + 4);
-    }
-    return error;
-}
-
-JSONValue* JSONNoTypeMatching::getValidPart(
-    RCArray<JSONValidationResult>* removedPartsValidationResults)
-{
-    RCArray<JSONValidationResult> tempErrors;
-    for (JSONValidationResult* reason : reasons)
-    {
-        JSONValue* result = reason->getValidPart(&tempErrors);
-        if (result)
-        {
-            return result;
-        }
-    }
-    if (removedPartsValidationResults)
-    {
-        removedPartsValidationResults->add(
-            dynamic_cast<JSONValidationResult*>(getThis()));
-        reasons.leeren();
-        for (JSONValidationResult* error : tempErrors)
-        {
-            reasons.add(dynamic_cast<JSONValidationResult*>(error->getThis()));
-        }
-    }
-    // multiple possibilities are undecidable
-    return 0;
-}
-
-Text JSONNoTypeMatching::getPath() const
-{
-    return path;
-}
-
-bool JSONNoTypeMatching::isDifferent(const JSONValidationResult* zResult) const
-{
-    const JSONNoTypeMatching* casted
-        = dynamic_cast<const JSONNoTypeMatching*>(zResult);
-    if (casted == 0) return 1;
-    if (!casted->getPath().istGleich(path)) return 1;
-    for (int i = 0; i < reasons.getEintragAnzahl(); i++)
-    {
-        if (reasons.z(i)->isDifferent(casted->reasons.z(i))) return 1;
-    }
-    return 0;
-}
-
-void Framework::JSON::Validator::JSONNoTypeMatching::addBasePath(Text basePath)
-{
-    path = basePath + path;
-    for (JSONValidationResult* reason : reasons)
-    {
-        reason->addBasePath(basePath);
-    }
-}
-
-#pragma endregion Content
-
-#pragma region JSONValidValue
-
-JSONValidValue::JSONValidValue(Text path, JSONValue* value)
-    : JSONValidationResult()
-{
-    this->path = path;
-    this->value = value;
-}
-
-JSONValidValue::~JSONValidValue()
-{
-    value->release();
-}
-
-bool JSONValidValue::isValid() const
-{
-    return 1;
-}
-
-Text JSONValidValue::getInvalidInfo(int indent) const
-{
-    return "";
-}
-
-JSONValue* JSONValidValue::getValidPart(
-    RCArray<JSONValidationResult>* removedPartsValidationResults)
-{
-    return value->clone();
-}
-
-Text JSONValidValue::getPath() const
-{
-    return path;
-}
-
-bool JSONValidValue::isDifferent(const JSONValidationResult* zResult) const
-{
-    const JSONValidValue* casted = dynamic_cast<const JSONValidValue*>(zResult);
-    if (casted == 0) return 1;
-    if (!casted->getPath().istGleich(path)) return 1;
-    return 0;
-}
-
-void Framework::JSON::Validator::JSONValidValue::addBasePath(Text basePath)
-{
-    path = basePath + path;
-}
-
-#pragma endregion Content
-
-#pragma endregion Content
-
-#pragma region JSONValidator
-
-JSONValidator::JSONValidator(XML::Element* constraints)
-    : ReferenceCounter(),
-      constraints(constraints)
-{
-    for (XML::Element* e : constraints->select()
-                               .selectAllElements()
-                               .whereNameEquals("object")
-                               .whereAttributeExists("id"))
-    {
-        Framework::Text id = e->getAttributeValue("id");
-        typeConstraints.set(id, id.getLength(), e);
-    }
-}
-
-JSONValidator::~JSONValidator()
-{
-    constraints->release();
-}
-
-JSONValidationResult* JSONValidator::validate(JSONValue* zValue) const
-{
-    return validate(zValue, constraints, "");
-}
-
-bool JSONValidator::isValid(JSONValue* zValue) const
-{
-    JSONValidationResult* res = validate(zValue);
-    if (res->isValid())
-    {
-        res->release();
-        return 1;
-    }
-    res->release();
-    return 0;
-}
-
-JSONValue* JSONValidator::getValidParts(JSONValue* zValue,
-    RCArray<JSONValidationResult>* removedPartsValidationResults) const
-{
-    JSONValidationResult* res = validate(zValue);
-    if (res->isValid())
-    {
-        res->release();
-        return zValue->clone();
-    }
-    JSONValue* valid = res->getValidPart(removedPartsValidationResults);
-    res->release();
-    return valid;
-}
-
-XML::Element* JSONValidator::zConstraints()
-{
-    return constraints;
-}
-
-JSONValidationResult* JSONValidator::validate(
-    JSONValue* zValue, XML::Element* zConstraints, Text path) const
-{
-    if (zConstraints->getName().istGleich("oneOf"))
-    {
-        return validateMultipleTypes(zValue, zConstraints, path);
-    }
-    switch (zValue->getType())
-    {
-    case JSONType::NULL_:
-        if (!zConstraints->hasAttribute("nullable")
-            || !zConstraints->getAttributeValue("nullable").istGleich("true"))
-        {
-            return new JSONTypeMissmatch(path,
-                dynamic_cast<JSONValue*>(zValue->getThis()),
-                dynamic_cast<XML::Element*>(zConstraints->getThis()),
-                0);
-        }
-        break;
-    case JSONType::BOOLEAN:
-        if (!zConstraints->getName().istGleich("bool"))
-        {
-            return new JSONTypeMissmatch(path,
-                dynamic_cast<JSONValue*>(zValue->getThis()),
-                dynamic_cast<XML::Element*>(zConstraints->getThis()),
-                0);
-        }
-        else if (zConstraints->hasAttribute("equals"))
-        {
-            if (!zConstraints->getAttributeValue("equals").istGleich("true")
-                == !zValue->asBool()->getBool())
-            {
-                return new JSONTypeMissmatch(path,
-                    dynamic_cast<JSONValue*>(zValue->getThis()),
-                    dynamic_cast<XML::Element*>(zConstraints->getThis()),
-                    0);
-            }
-        }
-        break;
-    case JSONType::NUMBER:
-        if (!zConstraints->getName().istGleich("number"))
-        {
-            return new JSONTypeMissmatch(path,
-                dynamic_cast<JSONValue*>(zValue->getThis()),
-                dynamic_cast<XML::Element*>(zConstraints->getThis()),
-                0);
-        }
-        else
-        {
-            if (zConstraints->hasAttribute("equals")
-                && (double)zConstraints->getAttributeValue("equals")
-                       != zValue->asNumber()->getNumber())
-            {
-                return new JSONTypeMissmatch(path,
-                    dynamic_cast<JSONValue*>(zValue->getThis()),
-                    dynamic_cast<XML::Element*>(zConstraints->getThis()),
-                    0);
-            }
-            if (zConstraints->hasAttribute("lessOrEqual")
-                && zValue->asNumber()->getNumber()
-                       > (double)zConstraints->getAttributeValue("lessOrEqual"))
-            {
-                return new JSONTypeMissmatch(path,
-                    dynamic_cast<JSONValue*>(zValue->getThis()),
-                    dynamic_cast<XML::Element*>(zConstraints->getThis()),
-                    0);
-            }
-            if (zConstraints->hasAttribute("greaterOrEqual")
-                && zValue->asNumber()->getNumber()
-                       < (double)zConstraints->getAttributeValue(
-                           "greaterOrEqual"))
-            {
-                return new JSONTypeMissmatch(path,
-                    dynamic_cast<JSONValue*>(zValue->getThis()),
-                    dynamic_cast<XML::Element*>(zConstraints->getThis()),
-                    0);
-            }
-            if (zConstraints->hasAttribute("less")
-                && zValue->asNumber()->getNumber()
-                       >= (double)zConstraints->getAttributeValue("less"))
-            {
-                return new JSONTypeMissmatch(path,
-                    dynamic_cast<JSONValue*>(zValue->getThis()),
-                    dynamic_cast<XML::Element*>(zConstraints->getThis()),
-                    0);
-            }
-            if (zConstraints->hasAttribute("greater")
-                && zValue->asNumber()->getNumber()
-                       <= (double)zConstraints->getAttributeValue("greater"))
-            {
-                return new JSONTypeMissmatch(path,
-                    dynamic_cast<JSONValue*>(zValue->getThis()),
-                    dynamic_cast<XML::Element*>(zConstraints->getThis()),
-                    0);
-            }
-        }
-        break;
-    case JSONType::STRING:
-        if (!zConstraints->getName().istGleich("string"))
-        {
-            return new JSONTypeMissmatch(path,
-                dynamic_cast<JSONValue*>(zValue->getThis()),
-                dynamic_cast<XML::Element*>(zConstraints->getThis()),
-                0);
-        }
-        else
-        {
-            if (zConstraints->hasAttribute("equals")
-                && !zConstraints->getAttributeValue("equals").istGleich(
-                    zValue->asString()->getString()))
-            {
-                return new JSONTypeMissmatch(path,
-                    dynamic_cast<JSONValue*>(zValue->getThis()),
-                    dynamic_cast<XML::Element*>(zConstraints->getThis()),
-                    0);
-            }
-            if (zConstraints->hasAttribute("contains")
-                && zValue->asString()->getString().hat(
-                    zConstraints->getAttributeValue("contains").getText()))
-            {
-                return new JSONTypeMissmatch(path,
-                    dynamic_cast<JSONValue*>(zValue->getThis()),
-                    dynamic_cast<XML::Element*>(zConstraints->getThis()),
-                    0);
-            }
-            if (zConstraints->hasAttribute("startsWith")
-                && zValue->asString()->getString().positionVon(
-                       zConstraints->getAttributeValue("startsWith").getText())
-                       == 0)
-            {
-                return new JSONTypeMissmatch(path,
-                    dynamic_cast<JSONValue*>(zValue->getThis()),
-                    dynamic_cast<XML::Element*>(zConstraints->getThis()),
-                    0);
-            }
-            if (zConstraints->hasAttribute("endsWith")
-                && zValue->asString()->getString().positionVon(
-                       zConstraints->getAttributeValue("endsWith").getText())
-                       == zValue->asString()->getString().getLength()
-                              - zConstraints->getAttributeValue("endsWith")
-                                    .getLength())
-            {
-                return new JSONTypeMissmatch(path,
-                    dynamic_cast<JSONValue*>(zValue->getThis()),
-                    dynamic_cast<XML::Element*>(zConstraints->getThis()),
-                    0);
-            }
-            if (zConstraints->hasAttribute("oneOf"))
-            {
-                JSONArray* array
-                    = Parser::getValue(zConstraints->getAttributeValue("oneOf"))
-                          ->asArray();
-                bool ok = 0;
-                for (JSONValue* v : *array)
-                {
-                    if (v->asString()->getString().istGleich(
-                            zValue->asString()->getString()))
-                    {
-                        ok = 1;
-                        break;
-                    }
-                }
-                array->release();
-                if (!ok)
-                {
-                    return new JSONTypeMissmatch(path,
-                        dynamic_cast<JSONValue*>(zValue->getThis()),
-                        dynamic_cast<XML::Element*>(zConstraints->getThis()),
-                        0);
-                }
-            }
-        }
-        break;
-    case JSONType::ARRAY:
-        if (!zConstraints->getName().istGleich("array"))
-        {
-            return new JSONTypeMissmatch(path,
-                dynamic_cast<JSONValue*>(zValue->getThis()),
-                dynamic_cast<XML::Element*>(zConstraints->getThis()),
-                0);
-        }
-        else
-        {
-            int index = 0;
-            for (JSON::JSONValue* value : *zValue->asArray())
-            {
-                Text p = path;
-                p += "[";
-                p += index;
-                p += "]";
-                JSONValidationResult* res
-                    = validateMultipleTypes(value, zConstraints, p);
-                if (!res->isValid())
-                {
-                    return new JSONTypeMissmatch(path,
-                        dynamic_cast<JSONValue*>(zValue->getThis()),
-                        dynamic_cast<XML::Element*>(zConstraints->getThis()),
-                        res);
-                }
-                res->release();
-                index++;
-            }
-        }
-        break;
-    case JSONType::OBJECT:
-        if (zConstraints->getName().istGleich("objectRef"))
-        {
-            Text id = zConstraints->getAttributeValue("ref");
-            XML::Element* e = typeConstraints.get(id, id.getLength());
-            if (e)
-            {
-                zConstraints = e;
-            }
-        }
-        if (!zConstraints->getName().istGleich("object"))
-        {
-            return new JSONTypeMissmatch(path,
-                dynamic_cast<JSONValue*>(zValue->getThis()),
-                dynamic_cast<XML::Element*>(zConstraints->getThis()),
-                0);
-        }
-        else
-        {
-            JSON::JSONObject* obj = zValue->asObject();
-            for (auto i = obj->getFields(); i; i++)
-            {
-                Text p = path;
-                p += ".";
-                p += i.val();
-                if (!zConstraints->selectChildsByAttribute("name", i.val())
-                         .exists())
-                {
-                    if (!zConstraints
-                             ->getAttributeValue("allowAdditionalAttributes")
-                             .istGleich("true"))
-                    {
-                        return new JSONTypeMissmatch(path,
-                            dynamic_cast<JSONValue*>(zValue->getThis()),
-                            dynamic_cast<XML::Element*>(
-                                zConstraints->getThis()),
-                            new JSONUnknownValue(
-                                p, zValue->asObject()->getValue(i.val())));
-                    }
-                }
-                else
-                {
-                    XML::Editor tmp = zConstraints->selectChildsByAttribute(
-                        "name", i.val());
-                    JSONValidationResult* res = validateMultipleTypes(
-                        zValue->asObject()->zValue(i.val()),
-                        tmp.begin().val(),
-                        p);
-                    if (!res->isValid())
-                    {
-                        return new JSONTypeMissmatch(path,
-                            dynamic_cast<JSONValue*>(zValue->getThis()),
-                            dynamic_cast<XML::Element*>(
-                                zConstraints->getThis()),
-                            res);
-                    }
-                    res->release();
-                }
-            }
-            for (XML::Element* constraint : zConstraints->selectChildren())
-            {
-                if (!zValue->asObject()->hasValue(
-                        constraint->getAttributeValue("name")))
-                {
-                    XML::Editor tmp = constraint->selectChildren();
-                    bool optional = true;
-                    std::function<void(XML::Element*)> checkOptional;
-                    checkOptional = [&optional, &checkOptional](
-                                        XML::Element* zElement) {
-                        if (zElement->getName().istGleich("oneOf"))
-                        {
-                            XML::Editor tmp = zElement->selectChildren();
-                            tmp.forEach(checkOptional);
-                        }
-                        else
-                        {
-                            optional &= zElement->hasAttribute("optional")
-                                     && zElement->getAttributeValue("optional")
-                                            .istGleich("true");
-                        }
-                    };
-                    tmp.forEach(checkOptional);
-                    if (!optional)
-                    {
-                        Text p = path;
-                        p += ".";
-                        p += constraint->getAttributeValue("name");
-                        if (constraint->getChildCount() != 1)
-                            return new JSONTypeMissmatch(path,
-                                dynamic_cast<JSONValue*>(zValue->getThis()),
-                                dynamic_cast<XML::Element*>(
-                                    zConstraints->getThis()),
-                                new JSONMissingOneOf(p, tmp));
-                        return new JSONTypeMissmatch(path,
-                            dynamic_cast<JSONValue*>(zValue->getThis()),
-                            dynamic_cast<XML::Element*>(
-                                zConstraints->getThis()),
-                            new JSONMissingValue(p,
-                                dynamic_cast<XML::Element*>(
-                                    tmp.begin()->getThis())));
-                    }
-                }
-            }
-        }
-        break;
-    }
-    return new JSONValidValue(
-        path, dynamic_cast<JSONValue*>(zValue->getThis()));
-}
-
-JSONValidationResult* JSONValidator::validateMultipleTypes(
-    JSONValue* zChildValue,
-    XML::Element* zPossibleChildConstraints,
-    Text childPath) const
-{
-    if (zPossibleChildConstraints->getChildCount() == 1
-        && !zPossibleChildConstraints->hasAttribute("typeSpecifiedBy"))
-    {
-        XML::Editor children = zPossibleChildConstraints->selectChildren();
-        return validate(zChildValue,
-            children.begin().val(),
-            childPath); // only one type is possible
-    }
-    bool hasTypeAttr = 0;
-    RCArray<XML::Element> possibleConstraints;
-    if (zPossibleChildConstraints->hasAttribute("typeSpecifiedBy"))
-    { // try to find the correct constraints based on the type attribute
-        hasTypeAttr = 1;
-        Text typeAttr
-            = zPossibleChildConstraints->getAttributeValue("typeSpecifiedBy");
-        if (zChildValue->getType() == JSONType::OBJECT)
-        {
-            if (zChildValue->asObject()->hasValue(typeAttr))
-            {
-                JSONValue* typeV = zChildValue->asObject()->zValue(typeAttr);
-                for (XML::Element* constraint :
-                    zPossibleChildConstraints->selectChildsByName("object")
-                        .whereChildWithAttributeExists("name", typeAttr))
-                {
-                    XML::Editor nameChildren
-                        = constraint->selectChildsByAttribute("name", typeAttr);
-                    XML::Element* typeAttrContraints
-                        = nameChildren.begin().val();
-                    JSONValidationResult* res = validateMultipleTypes(
-                        typeV, typeAttrContraints, childPath + "." + typeAttr);
-                    if (res->isValid())
-                        possibleConstraints.add(
-                            dynamic_cast<XML::Element*>(constraint->getThis()));
-                    res->release();
-                }
-            }
-        }
-    }
-    if (hasTypeAttr && possibleConstraints.getEintragAnzahl() == 1)
-        return validate(zChildValue,
-            possibleConstraints.begin().val(),
-            childPath); // if the type is clear
-    else if (hasTypeAttr && possibleConstraints.getEintragAnzahl() > 0)
-    { // more then one type is possible
-        RCArray<JSONValidationResult> invalidResults;
-        for (XML::Element* constraint : possibleConstraints)
-        {
-            JSONValidationResult* res
-                = validate(zChildValue, constraint, childPath);
-            invalidResults.add(res);
-            if (res->isValid())
-                return new JSONValidValue(childPath,
-                    dynamic_cast<JSONValue*>(zChildValue->getThis()));
-        }
-        return new JSONNoTypeMatching(childPath,
-            dynamic_cast<JSONValue*>(zChildValue->getThis()),
-            possibleConstraints,
-            invalidResults);
-    }
-    // try all types
-    possibleConstraints.leeren();
-    RCArray<JSONValidationResult> invalidResults;
-    for (XML::Element* constraint : zPossibleChildConstraints->selectChildren())
-    {
-        JSONValidationResult* res
-            = validate(zChildValue, constraint, childPath);
-        invalidResults.add(res);
-        if (res->isValid())
-            return new JSONValidValue(
-                childPath, dynamic_cast<JSONValue*>(zChildValue->getThis()));
-        possibleConstraints.add(
-            dynamic_cast<XML::Element*>(constraint->getThis()));
-    }
-    return new JSONNoTypeMatching(childPath,
-        dynamic_cast<JSONValue*>(zChildValue->getThis()),
-        possibleConstraints,
-        invalidResults);
-}
-
-StringValidationBuilder<JSONValidator>* JSONValidator::buildForString()
-{
-    return new StringValidationBuilder<JSONValidator>(
-        [](XML::Element& e) { return new JSONValidator(e.dublicate()); });
-}
-
-NumberValidationBuilder<JSONValidator>* JSONValidator::buildForNumber()
-{
-    return new NumberValidationBuilder<JSONValidator>(
-        [](XML::Element& e) { return new JSONValidator(e.dublicate()); });
-}
-
-BoolValidationBuilder<JSONValidator>* JSONValidator::buildForBool()
-{
-    return new BoolValidationBuilder<JSONValidator>(
-        [](XML::Element& e) { return new JSONValidator(e.dublicate()); });
-}
-
-ObjectValidationBuilder<JSONValidator>* JSONValidator::buildForObject()
-{
-    return new ObjectValidationBuilder<JSONValidator>(
-        [](XML::Element& e) { return new JSONValidator(e.dublicate()); });
-}
-
-ArrayValidationBuilder<JSONValidator>* JSONValidator::buildForArray()
-{
-    return new ArrayValidationBuilder<JSONValidator>(
-        [](XML::Element& e) { return new JSONValidator(e.dublicate()); });
-}
-
-JSONValidator* JSONValidator::buildForObjectReference(Text objectId)
-{
-    return new JSONValidator(
-        new XML::Element(Text("<objectRef ref=\"") + objectId + Text("\"/>")));
-}
-
-OneOfValidationBuilder<JSONValidator>* JSONValidator::buildForOneOf()
-{
-    return new OneOfValidationBuilder<JSONValidator>(
-        [](XML::Element& e) { return new JSONValidator(e.dublicate()); });
-}
-
-#pragma endregion Content
+#pragma endregion Cotent

+ 51 - 901
JSON.h

@@ -1,57 +1,60 @@
 #pragma once
 
 #include <functional>
-#include <source_location>
 
+#include "AbstractElement.h"
 #include "Array.h"
 #include "ReferenceCounter.h"
 #include "Text.h"
-#include "Trie.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
+        class JSONValue : public virtual ReferenceCounter,
+                          public virtual AbstractElement
         {
         private:
-            JSONType type;
+            AbstractType type;
 
         protected:
-            __declspec(dllexport) JSONValue(JSONType type);
+            __declspec(dllexport) JSONValue(AbstractType type);
 
         public:
             __declspec(dllexport) JSONValue();
             __declspec(dllexport) virtual ~JSONValue();
-            __declspec(dllexport) JSONType getType() const;
-            __declspec(dllexport) virtual Text toString() const;
+            __declspec(dllexport) AbstractType getType() const override;
+            __declspec(dllexport) virtual Text toString() const override;
             __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;
+            __declspec(
+                dllexport) const AbstractBool* asAbstractBool() const override;
+            __declspec(dllexport) const
+                AbstractNumber* asAbstractNumber() const override;
+            __declspec(dllexport) const
+                AbstractString* asAbstractString() const override;
+            __declspec(dllexport) const
+                AbstractArray* asAbstractArray() const override;
+            __declspec(dllexport) const
+                AbstractObject* asAbstractObject() const override;
         };
 
-        class JSONBool : public JSONValue
+#pragma warning(push)
+#pragma warning(disable : 4250)
+
+        class JSONBool : public AbstractBool,
+                         public JSONValue
+
         {
         private:
             bool b;
@@ -59,12 +62,13 @@ namespace Framework
         public:
             __declspec(dllexport) JSONBool(bool b);
 
-            __declspec(dllexport) bool getBool() const;
+            __declspec(dllexport) bool getBool() const override;
             __declspec(dllexport) Text toString() const override;
             __declspec(dllexport) JSONValue* clone() const override;
         };
 
-        class JSONNumber : public JSONValue
+        class JSONNumber : public JSONValue,
+                           public AbstractNumber
         {
         private:
             double number;
@@ -72,12 +76,13 @@ namespace Framework
         public:
             __declspec(dllexport) JSONNumber(double num);
 
-            __declspec(dllexport) double getNumber() const;
+            __declspec(dllexport) double getNumber() const override;
             __declspec(dllexport) Text toString() const override;
             __declspec(dllexport) JSONValue* clone() const override;
         };
 
-        class JSONString : public JSONValue
+        class JSONString : public JSONValue,
+                           public AbstractString
         {
         private:
             Text string;
@@ -85,12 +90,13 @@ namespace Framework
         public:
             __declspec(dllexport) JSONString(Text string);
 
-            __declspec(dllexport) Text getString() const;
+            __declspec(dllexport) Text getString() const override;
             __declspec(dllexport) Text toString() const override;
             __declspec(dllexport) JSONValue* clone() const override;
         };
 
-        class JSONArray : public JSONValue
+        class JSONArray : public JSONValue,
+                          public AbstractArray
         {
         private:
             RCArray<JSONValue>* array;
@@ -108,9 +114,11 @@ namespace Framework
             __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) AbstractElement* zAbstractValue(
+                int i) const override;
+            __declspec(dllexport) int getLength() const override;
             __declspec(dllexport) bool isValueOfType(
-                int i, JSONType type) const;
+                int i, AbstractType type) const;
 
             //! Gibt einen Iterator zurück.
             //! Mit ++ kann durch die Liste iteriert werden
@@ -186,7 +194,8 @@ namespace Framework
             }
         };
 
-        class JSONObject : public JSONValue
+        class JSONObject : public JSONValue,
+                           public AbstractObject
         {
         private:
             Array<Text>* fields;
@@ -202,14 +211,19 @@ namespace Framework
 
             __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) bool hasValue(Text field) const override;
+            __declspec(dllexport) JSONValue* getValue(Text field) const;
+            __declspec(dllexport) JSONValue* zValue(Text field) const;
+            __declspec(dllexport) AbstractElement* zAbstractValue(
+                Text field) const override;
             __declspec(dllexport) ArrayIterator<Text> getFields();
             __declspec(dllexport) ArrayIterator<JSONValue*> getValues();
-            __declspec(dllexport) int getFieldCount() const;
+            __declspec(dllexport) Text getFieldKey(int i) const override;
+            __declspec(dllexport) AbstractElement* zAbstractValue(
+                int i) const override;
+            __declspec(dllexport) int getFieldCount() const override;
             __declspec(dllexport) bool isValueOfType(
-                Text field, JSONType type) const;
+                Text field, AbstractType type) const;
 
             template<class T> T* parseTo(T* initialState,
                 std::function<void(
@@ -231,6 +245,8 @@ namespace Framework
             __declspec(dllexport) JSONValue* clone() const override;
         };
 
+#pragma warning(pop)
+
         __declspec(dllexport) JSONValue* loadJSONFromFile(Text path);
 
         namespace Parser
@@ -241,871 +257,5 @@ namespace Framework
             __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;
-                __declspec(dllexport) void logInvalidInfo(
-                    std::source_location location
-                    = std::source_location::current()) const;
-                virtual Text getInvalidInfo(int indent = 0) const = 0;
-                virtual JSONValue* getValidPart(RCArray<JSONValidationResult>*
-                        zRemovedPartsValidationResults)
-                    = 0;
-                virtual Text getPath() const = 0;
-                virtual void addBasePath(Text basePath) = 0;
-                virtual bool isDifferent(
-                    const JSONValidationResult* zResult) 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) Text
-                    getInvalidInfo(int indent) const override;
-
-            protected:
-                __declspec(dllexport) JSONValue* getValidPart(
-                    RCArray<JSONValidationResult>*
-                        zRemovedPartsValidationResults) override;
-                __declspec(dllexport) Text getPath() const override;
-                __declspec(dllexport) bool isDifferent(
-                    const JSONValidationResult* zResult) const override;
-                __declspec(dllexport) void addBasePath(Text basePath) 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) Text
-                    getInvalidInfo(int indent) const override;
-                __declspec(dllexport) JSONValue* getValidPart(
-                    RCArray<JSONValidationResult>*
-                        zRemovedPartsValidationResults) override;
-                __declspec(dllexport) Text getPath() const override;
-                __declspec(dllexport) bool isDifferent(
-                    const JSONValidationResult* zResult) const override;
-                __declspec(dllexport) void addBasePath(Text basePath) 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) Text
-                    getInvalidInfo(int indent) const override;
-                __declspec(dllexport) JSONValue* getValidPart(
-                    RCArray<JSONValidationResult>*
-                        zRemovedPartsValidationResults) override;
-                __declspec(dllexport) Text getPath() const override;
-                __declspec(dllexport) bool isDifferent(
-                    const JSONValidationResult* zResult) const override;
-                __declspec(dllexport) void addBasePath(Text basePath) 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) Text
-                    getInvalidInfo(int indent) const override;
-                __declspec(dllexport) JSONValue* getValidPart(
-                    RCArray<JSONValidationResult>*
-                        zRemovedPartsValidationResults) override;
-                __declspec(dllexport) Text getPath() const override;
-                __declspec(dllexport) bool isDifferent(
-                    const JSONValidationResult* zResult) const override;
-                __declspec(dllexport) void addBasePath(Text basePath) 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) Text
-                    getInvalidInfo(int indent) const override;
-                __declspec(dllexport) JSONValue* getValidPart(
-                    RCArray<JSONValidationResult>*
-                        zRemovedPartsValidationResults) override;
-                __declspec(dllexport) Text getPath() const override;
-                __declspec(dllexport) bool isDifferent(
-                    const JSONValidationResult* zResult) const override;
-                __declspec(dllexport) void addBasePath(Text basePath) 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) Text
-                    getInvalidInfo(int indent) const override;
-                __declspec(dllexport) JSONValue* getValidPart(
-                    RCArray<JSONValidationResult>*
-                        zRemovedPartsValidationResults) override;
-                __declspec(dllexport) Text getPath() const override;
-                __declspec(dllexport) bool isDifferent(
-                    const JSONValidationResult* 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 JSONValidator : public Framework::ReferenceCounter
-            {
-            private:
-                XML::Element* constraints;
-                Trie<XML::Element*> typeConstraints;
-
-            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,
-                    RCArray<JSONValidationResult>*
-                        zRemovedPartsValidationResults) 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();
-                __declspec(dllexport) static OneOfValidationBuilder<
-                    JSONValidator>* buildForOneOf();
-                __declspec(
-                    dllexport) static JSONValidator* buildForObjectReference(Text
-                        objectId);
-            };
-
-            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>* whichIsOneOf(
-                    std::initializer_list<Text> values)
-                {
-                    JSONArray arr;
-                    for (Text str : values)
-                        arr.addValue(new JSONString(str));
-                    element.setAttribute("oneOf", arr.toString());
-                    return this;
-                }
-
-                StringValidationBuilder<T>* whichIs(Text value)
-                {
-                    JSONArray arr;
-                    arr.addValue(new JSONString(value));
-                    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)
-                {}
-
-                ObjectValidationBuilder<T>* setObjectReferenceId(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;
-                        });
-                }
-
-                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;
-                }
-
-                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(
-                    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;
-                }
-            };
-
-            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>>*
-                addAcceptedStringInArray()
-                {
-                    return new StringValidationBuilder<
-                        OneOfValidationBuilder<T>>([this](XML::Element& e) {
-                        element.addChild(e.dublicate());
-                        return this;
-                    });
-                }
-
-                NumberValidationBuilder<OneOfValidationBuilder<T>>*
-                addAcceptedNumberInArray()
-                {
-                    return new NumberValidationBuilder<
-                        OneOfValidationBuilder<T>>([this](XML::Element& e) {
-                        element.addChild(e.dublicate());
-                        return this;
-                    });
-                }
-
-                BoolValidationBuilder<OneOfValidationBuilder<T>>*
-                addAcceptedBooleanInArray()
-                {
-                    return new BoolValidationBuilder<OneOfValidationBuilder<T>>(
-                        [this](XML::Element& e) {
-                            element.addChild(e.dublicate());
-                            return this;
-                        });
-                }
-
-                ObjectValidationBuilder<OneOfValidationBuilder<T>>*
-                addAcceptedObjectInArray()
-                {
-                    return new ObjectValidationBuilder<
-                        OneOfValidationBuilder<T>>([this](XML::Element& e) {
-                        element.addChild(e.dublicate());
-                        return this;
-                    });
-                }
-
-                ArrayValidationBuilder<OneOfValidationBuilder<T>>*
-                addAcceptedArrayInArray()
-                {
-                    return new ArrayValidationBuilder<
-                        OneOfValidationBuilder<T>>([this](XML::Element& e) {
-                        element.addChild(e.dublicate());
-                        return this;
-                    });
-                }
-
-                OneOfValidationBuilder<T>* addAcceptedType(
-                    JSONValidator* 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 JSON
+    }      // namespace JSON
 } // namespace Framework

+ 3300 - 0
JsonEditor.cpp

@@ -0,0 +1,3300 @@
+#include "JsonEditor.h"
+
+#include "Bild.h"
+#include "Globals.h"
+#include "MausEreignis.h"
+#include "Regex.h"
+#include "Schrift.h"
+#include "TastaturEreignis.h"
+#include "TextFeld.h"
+
+using namespace Framework;
+using namespace JSON;
+
+const char* errors[] = {"Unexpected start of object",
+    "Unexpected start of array",
+    "Unexpected value",
+    "Unexpected ','",
+    "Unexpected ':'",
+    "Unexpected end of object",
+    "Unexpected end of array",
+    "Unexpected start of string",
+    "Invalid json value",
+    ""};
+
+class EditableJsonElementReference : public virtual AbstractElement,
+                                     public virtual ReferenceCounter,
+                                     public virtual AbstractBool,
+                                     public virtual AbstractNumber,
+                                     public virtual AbstractString,
+                                     public virtual AbstractArray,
+                                     public virtual AbstractObject
+{
+private:
+    EditableJsonElement* root;
+    RCArray<EditableJsonElementReference>* children;
+
+    EditableJsonElementReference(EditableJsonElement* root,
+        RCArray<EditableJsonElementReference>* children)
+        : root(root),
+          children(children)
+    {}
+
+public:
+    EditableJsonElementReference(EditableJsonElement* root)
+        : root(root),
+          children(new RCArray<EditableJsonElementReference>())
+    {}
+
+    ~EditableJsonElementReference()
+    {
+        children->release();
+    }
+
+    AbstractType getType() const override
+    {
+        if (root->hasError() || root->getValue().istGleich("null"))
+        {
+            return AbstractType::NULL_;
+        }
+        else if (root->getOpeningControllChar() == '{')
+        {
+            return AbstractType::OBJECT;
+        }
+        else if (root->getOpeningControllChar() == '[')
+        {
+            return AbstractType::ARRAY;
+        }
+        else if (root->getValue().istGleich("true")
+                 || root->getValue().istGleich("false"))
+        {
+            return AbstractType::BOOLEAN;
+        }
+        else if (root->getValue().hatAt(0, "\""))
+        {
+            return AbstractType::STRING;
+        }
+        else
+        {
+            return AbstractType::NUMBER;
+        }
+    }
+
+    const AbstractBool* asAbstractBool() const override
+    {
+        return this;
+    }
+
+    const AbstractNumber* asAbstractNumber() const override
+    {
+        return this;
+    }
+
+    const AbstractString* asAbstractString() const override
+    {
+        return this;
+    }
+
+    const AbstractArray* asAbstractArray() const override
+    {
+        return this;
+    }
+
+    const AbstractObject* asAbstractObject() const override
+    {
+        return this;
+    }
+
+    Text toString() const override
+    {
+        return root->getRecursiveContent();
+    }
+
+    bool getBool() const override
+    {
+        return (bool)root->getValue();
+    }
+
+    double getNumber() const override
+    {
+        return (double)root->getValue();
+    }
+
+    Text getString() const override
+    {
+        Text txt = root->getValue().getText() + 1;
+        txt.remove(txt.getLength() - 1, 1);
+        return (double)root->getValue();
+    }
+
+    AbstractElement* zAbstractValue(int i) const override
+    {
+        EditableJsonElement* e = root->zFirstChildren();
+        while (e && i > 0)
+        {
+            e = e->zMextSibling();
+            i--;
+        }
+        if (e)
+        {
+            EditableJsonElementReference* result
+                = new EditableJsonElementReference(e, children);
+            children->add(result);
+            return result;
+        }
+        return 0;
+    }
+
+    int getLength() const override
+    {
+        EditableJsonElement* e = root->zFirstChildren();
+        int i = 0;
+        while (e)
+        {
+            e = e->zMextSibling();
+            i++;
+        }
+        return i;
+    }
+
+    AbstractElement* zAbstractValue(Text field) const override
+    {
+        EditableJsonElement* e = root->zFirstChildren();
+        while (e)
+        {
+            if (e->getKey().istGleich(field))
+            {
+                EditableJsonElementReference* result
+                    = new EditableJsonElementReference(e, children);
+                children->add(result);
+                return result;
+            }
+            e = e->zMextSibling();
+        }
+        return 0;
+    }
+
+    int getFieldCount() const override
+    {
+        return getLength();
+    }
+
+    Text getFieldKey(int i) const override
+    {
+        EditableJsonElement* e = root->zFirstChildren();
+        while (e && i > 0)
+        {
+            e = e->zMextSibling();
+            i--;
+        }
+        if (e)
+        {
+            return e->getKey();
+        }
+        return 0;
+    }
+
+    bool hasValue(Text field) const override
+    {
+        EditableJsonElement* e = root->zFirstChildren();
+        while (e)
+        {
+            if (e->getKey().istGleich(field))
+            {
+                return 1;
+            }
+            e = e->zMextSibling();
+        }
+        return 0;
+    }
+};
+
+SyntaxError::SyntaxError(int start, int end, SyntaxErrorType errorType)
+    : start(start),
+      end(end),
+      errorType(errorType)
+{}
+
+SyntaxError::SyntaxError()
+    : start(-1),
+      end(-1),
+      errorType(SyntaxErrorType::OTHER)
+{}
+
+int SyntaxError::getStart() const
+{
+    return start;
+}
+
+int SyntaxError::getEnd() const
+{
+    return end;
+}
+
+SyntaxErrorType SyntaxError::getErrorType() const
+{
+    return errorType;
+}
+
+Framework::JSON::ParserState::ParserState(
+    JsonStructure parent, bool valueExpected)
+    : index(0),
+      keyStart(-1),
+      keyEnd(0),
+      valueStart(-1),
+      valueEnd(0),
+      errorStart(0),
+      parent(parent),
+      inString(0),
+      escaped(0),
+      colon(parent != JsonStructure::OBJECT),
+      key(parent != JsonStructure::OBJECT),
+      value(0),
+      inValue(0),
+      valueValidationNeeded(0),
+      openingControllChar(0),
+      closingControllChar(0),
+      valueExpected(valueExpected)
+{}
+
+bool Framework::JSON::ParserState::next(
+    const char* current, const char* next, SyntaxError& error)
+{
+    if (current[0] == 0) return next == 0;
+    bool stop = 0;
+    if (current[0] == '{' && !inString)
+    {
+        stop = 1;
+        if (!colon || value)
+        {
+            error.start = index;
+            error.end = index + 1;
+            error.errorType = SyntaxErrorType::UNEXPECTED_OBJECT_START;
+        }
+        value = 1;
+        openingControllChar = current[0];
+    }
+    else if (current[0] == '[' && !inString)
+    {
+        stop = 1;
+        if (!colon || value)
+        {
+            error.start = index;
+            error.end = index + 1;
+            error.errorType = SyntaxErrorType::UNEXPECTED_ARRAY_START;
+        }
+        value = 1;
+        openingControllChar = current[0];
+    }
+    else if (current[0] == '}' && !inString)
+    {
+        stop = current[1] == ']' || current[1] == '}';
+        if (!stop)
+        {
+            const char* n = current + 1;
+            while (*n && (*n == ' ' || *n == '\t' || *n == '\r' || *n == '\n'))
+            {
+                n++;
+            }
+            stop = *n == ']' || *n == '}';
+            if (!stop && next)
+            {
+                n = next;
+                while (
+                    *n && (*n == ' ' || *n == '\t' || *n == '\r' || *n == '\n'))
+                {
+                    n++;
+                }
+                stop = *n == ']' || *n == '}';
+            }
+        }
+        if (parent != JsonStructure::OBJECT || (!value && valueExpected))
+        {
+            error.start = index;
+            error.end = index + 1;
+            error.errorType = SyntaxErrorType::UNEXPECTED_END_OF_OBJECT;
+        }
+        value = 1;
+        closingControllChar = current[0];
+    }
+    else if (current[0] == ']' && !inString)
+    {
+        stop = current[1] == ']' || current[1] == '}';
+        if (!stop)
+        {
+            const char* n = current + 1;
+            while (*n && (*n == ' ' || *n == '\t' || *n == '\r' || *n == '\n'))
+            {
+                n++;
+            }
+            stop = *n == ']' || *n == '}';
+            if (!stop && next)
+            {
+                n = next;
+                while (
+                    *n && (*n == ' ' || *n == '\t' || *n == '\r' || *n == '\n'))
+                {
+                    n++;
+                }
+                stop = *n == ']' || *n == '}';
+            }
+        }
+        if (parent != JsonStructure::ARRAY || (!value && valueExpected))
+        {
+            error.start = index;
+            error.end = index + 1;
+            error.errorType = SyntaxErrorType::UNEXPECTED_END_OF_ARRAY;
+        }
+        value = 1;
+        closingControllChar = current[0];
+    }
+    else if (current[0] == '"' && !escaped)
+    {
+        inString = !inString;
+        if (inString)
+        {
+            if (!key)
+            {
+                keyStart = index + 1;
+            }
+            else if (colon && !value)
+            {
+                valueStart = index;
+            }
+            else
+            {
+                error.start = index;
+                error.end = index + 1;
+                error.errorType = SyntaxErrorType::UNEXPECTED_START_OF_STRING;
+            }
+        }
+        else
+        {
+            if (!key)
+            {
+                keyEnd = index;
+                key = 1;
+            }
+            else if (colon && !value)
+            {
+                valueEnd = index + 1;
+                value = 1;
+            }
+        }
+    }
+    else if (current[0] == '\\' && inString)
+    {
+        escaped = !escaped;
+    }
+    else if (current[0] == ':' && !inString)
+    {
+        if (key && !colon)
+        {
+            colon = 1;
+        }
+        else
+        {
+            error.start = index;
+            error.end = index + 1;
+            error.errorType = SyntaxErrorType::UNEXPECTED_COLON;
+        }
+    }
+    else if (current[0] == ',' && !inString)
+    {
+        stop = 1;
+        if (!value)
+        {
+            error.start = index;
+            error.end = index + 1;
+            error.errorType = SyntaxErrorType::UNEXPECTED_COMMA;
+        }
+    }
+    else
+    {
+        escaped = 0;
+        if (!inString && current[0] != ' ' && current[0] != '\t'
+            && current[0] != '\n' && current[0] != '\r')
+        {
+            if (colon && !value)
+            {
+                if (!inValue)
+                {
+                    valueStart = index;
+                    inValue = 1;
+                }
+            }
+            else
+            {
+                if (!inValue)
+                {
+                    errorStart = index;
+                    inValue = 1;
+                }
+            }
+            if (inValue
+                && (current[1] == ':' || current[1] == '"' || current[1] == '{'
+                    || current[1] == '[' || current[1] == ']'
+                    || current[1] == '}' || current[1] == ','))
+            {
+                inValue = 0;
+                if (colon && !value)
+                {
+                    value = 1;
+                    valueEnd = index + 1;
+                    valueValidationNeeded = 1;
+                }
+                else
+                {
+                    error.start = errorStart;
+                    error.end = index;
+                    error.errorType = SyntaxErrorType::UNEXPECTED_VALUE;
+                }
+            }
+        }
+        else if (inValue)
+        {
+            inValue = 0;
+            if (colon && !value)
+            {
+                valueEnd = index;
+                value = 1;
+                valueValidationNeeded = 1;
+            }
+            else
+            {
+                error.start = errorStart;
+                error.end = index;
+                error.errorType = SyntaxErrorType::UNEXPECTED_VALUE;
+            }
+        }
+    }
+    index++;
+    return stop;
+}
+
+EditableJsonElement::EditableJsonElement(Regex::Automata<char>* valueValidator)
+    : ReferenceCounter(),
+      content(),
+      valueValidator(valueValidator),
+      valueStart(-1),
+      keyStart(-1),
+      newLineCount(0)
+{
+    children = 0;
+    siblings = 0;
+    lastChild = 0;
+    parent = 0;
+    hidden = false;
+    parentStructure = JsonStructure::VALUE;
+}
+
+Framework::JSON::EditableJsonElement::EditableJsonElement(
+    Regex::Automata<char>* valueValidator, const char* content)
+    : EditableJsonElement(valueValidator)
+{
+    setContent(content);
+    valueValidator->release();
+}
+
+EditableJsonElement::~EditableJsonElement()
+{
+    if (children)
+    {
+        children->release();
+    }
+    if (siblings)
+    {
+        siblings->release();
+    }
+}
+
+void Framework::JSON::EditableJsonElement::setContentOnly(const char** content,
+    int& startPos,
+    Array<EditableJsonElement*>& afterList,
+    Array<EditableJsonElement*>& deleteList)
+{
+    errors.leeren();
+    int start = startPos;
+    EditableJsonElement* before = zBefore(0, false, 0);
+    ParserState p(parentStructure,
+        before
+            ? before->getContent()[before->getContent().getLength() - 1] == ','
+            : 0);
+    SyntaxError error;
+    key = "";
+    value = "";
+    this->content = "";
+    while (true)
+    {
+        const char* current = (*content) + startPos++;
+        const char* next = 0;
+        if (afterList.getEintragAnzahl())
+        {
+            next = afterList.get(0)->getContent();
+        }
+        if (p.next(current, next, error))
+        {
+            break;
+        }
+        if (error.getErrorType() != SyntaxErrorType::OTHER)
+        {
+            errors.add(error);
+            error = SyntaxError();
+        }
+        if (!*current)
+        {
+            startPos--;
+        }
+        if (!(*content)[startPos])
+        {
+            if (!afterList.getEintragAnzahl())
+            {
+                break;
+            }
+            else
+            {
+                if (p.keyStart >= 0
+                    && (p.keyEnd < p.keyStart || p.keyEnd + start >= 0)
+                    && parentStructure == JsonStructure::OBJECT)
+                {
+                    int s = p.keyStart + start < 0 ? 0 : p.keyStart + start;
+                    int e = p.keyEnd < p.keyStart ? p.index : p.keyEnd + start;
+                    key += Text(*content, s, e - s);
+                }
+                if (p.valueStart >= 0
+                    && (p.valueEnd < p.valueStart || p.valueEnd + start >= 0)
+                    && parentStructure == JsonStructure::OBJECT)
+                {
+                    int s = p.valueStart + start < 0 ? 0 : p.valueStart + start;
+                    int e = p.valueEnd < p.valueStart ? p.index + start
+                                                      : p.valueEnd + start;
+                    value += Text(*content, s, e - s);
+                }
+                this->content += Text(*content,
+                    start < 0 ? 0 : start,
+                    start < 0 ? startPos : startPos - start);
+                EditableJsonElement* next = afterList.get(0);
+                int i = 0;
+                afterList.remove(i);
+                if (next->children)
+                {
+                    afterList.add(next->children, i);
+                    i++;
+                }
+                if (next->siblings)
+                {
+                    afterList.add(next->siblings, i);
+                }
+                next->disconnect();
+                deleteList.add(next);
+                *content = next->getContent();
+                startPos = 0;
+                start = -p.index;
+            }
+        }
+    }
+    if (error.getErrorType() != SyntaxErrorType::OTHER)
+    {
+        errors.add(error);
+        error = SyntaxError();
+    }
+    if (p.keyStart >= 0 && (p.keyEnd < p.keyStart || p.keyEnd + start >= 0)
+        && parentStructure == JsonStructure::OBJECT)
+    {
+        int s = p.keyStart + start < 0 ? 0 : p.keyStart + start;
+        int e = p.keyEnd < p.keyStart ? p.index : p.keyEnd + start;
+        key += Text(*content, s, e - s);
+    }
+    if (p.valueStart >= 0
+        && (p.valueEnd < p.valueStart || p.valueEnd + start >= 0))
+    {
+        int s = p.valueStart + start < 0 ? 0 : p.valueStart + start;
+        int e
+            = p.valueEnd < p.valueStart ? p.index + start : p.valueEnd + start;
+        value += Text(*content, s, e - s);
+    }
+    if (p.valueValidationNeeded)
+    {
+        if (!isValueValid(value, 0, value.getLength()))
+        {
+            errors.add(SyntaxError(
+                p.valueStart, p.valueEnd, SyntaxErrorType::INVALID_VALUE));
+        }
+    }
+    this->content += Text(*content,
+        start < 0 ? 0 : start,
+        start < 0 ? startPos : startPos - start);
+    newLineCount = this->content.anzahlVon('\n');
+    openingControllChar = p.openingControllChar;
+    closingControllChar = p.closingControllChar;
+    valueStart = p.valueStart;
+    keyStart = p.keyStart;
+    if ((*content)[startPos]
+        && ((*content)[startPos - 1] == '[' || (*content)[startPos - 1] == '{'))
+    {
+        EditableJsonElement* next = new EditableJsonElement(valueValidator);
+        addChildren(next);
+        next->setContentRecursive(content, startPos, afterList, deleteList);
+    }
+    // TODO: set parent structure of children
+    // TODO: update tree
+}
+
+void Framework::JSON::EditableJsonElement::setContentRecursive(
+    const char** content,
+    int& startPos,
+    Array<EditableJsonElement*>& afterList,
+    Array<EditableJsonElement*>& deleteList)
+{
+    setContentOnly(content, startPos, afterList, deleteList);
+    char lastControl = closingControllChar;
+    while ((*content)[startPos]
+           && !((lastControl == ']' && parentStructure == JsonStructure::ARRAY)
+                || (lastControl == '}'
+                    && parentStructure == JsonStructure::OBJECT)))
+    {
+        if (closingControllChar == ']'
+            && parentStructure == JsonStructure::ARRAY)
+        {
+            break;
+        }
+        EditableJsonElement* next = new EditableJsonElement(valueValidator);
+        addSibling(next);
+        next->setContentOnly(content, startPos, afterList, deleteList);
+        lastControl = next->getClosingControllChar();
+    }
+}
+
+void Framework::JSON::EditableJsonElement::format(
+    int indent, bool insertNewLine)
+{
+    Text content = this->content;
+    content.removeWhitespaceAfter(0);
+    int index = 0;
+    if (insertNewLine)
+    {
+        content.insert(0, "\n");
+        for (int i = 0; i < indent; i++)
+        {
+            content.insert(1, "    ");
+        }
+        index = indent * 4 + 1;
+    }
+    bool keyStart = false;
+    bool keyEnd = false;
+    bool colonFound = false;
+    bool valueStart = false;
+    bool valueEnd = false;
+    bool escaped = false;
+    for (int i = index; i < content.getLength(); i++)
+    {
+        if ((content[i] == '{' || content[i] == '[' || content[i] == ',')
+            && !(keyStart && !keyEnd) && !(valueStart && !valueEnd))
+        {
+            if (content[i] == ',')
+            {
+                i -= content.removeWhitespaceBefore(i);
+            }
+            content.removeWhitespaceAfter(i + 1);
+        }
+        else if ((content[i] == '}' || content[i] == ']')
+                 && !(keyStart && !keyEnd) && !(valueStart && !valueEnd))
+        {
+            content.removeWhitespaceAfter(i + 1);
+            if (!parent || parent->children != this)
+            {
+                i -= content.removeWhitespaceBefore(i);
+                content.insert(i, '\n');
+                for (int j = 0; j < indent - 1; j++)
+                {
+                    content.insert(i + 1, "    ");
+                }
+                i += 1 + (indent > 0 ? (indent - 1) * 4 : 0);
+            }
+            else
+            {
+                i -= content.removeWhitespaceBefore(i);
+            }
+        }
+        else if (content[i] == '"' && !escaped)
+        { // format whitespaces arround values and keys
+            if (!keyStart)
+            {
+                keyStart = true;
+            }
+            else
+            {
+                if (!keyEnd)
+                {
+                    keyEnd = true;
+                    content.removeWhitespaceAfter(i + 1);
+                }
+                else
+                {
+                    if (colonFound)
+                    {
+                        if (!valueStart)
+                        {
+                            valueStart = true;
+                        }
+                        else if (!valueEnd)
+                        {
+                            valueEnd = true;
+                            content.removeWhitespaceAfter(i + 1);
+                        }
+                    }
+                }
+            }
+        }
+        else if (content[i] == ':' && !(keyStart && !keyEnd)
+                 && !(valueStart && !valueEnd))
+        {
+            if (!colonFound && keyEnd)
+            {
+                colonFound = true;
+                content.removeWhitespaceAfter(i + 1);
+                content.insert(i + 1, " ");
+            }
+        }
+        else if (content[i] == '\t' && !(keyStart && !keyEnd)
+                 && !(valueStart && !valueEnd))
+        {
+            content.remove(i, 1);
+            i--;
+        }
+        else if (content[i] == '\\')
+        {
+            if ((keyStart && !keyEnd) || (valueStart && !valueEnd))
+            {
+                escaped = !escaped;
+            }
+        }
+        else
+        {
+            escaped = false;
+        }
+    }
+    int count = 0;
+    Array<EditableJsonElement*> empty;
+    const char* tmp = content.getText();
+    setContentOnly(&tmp, count, empty, empty);
+    if (count < content.getLength())
+    {
+        throw "Illegal State Exception: tree changed after formatting";
+    }
+    if (children)
+    {
+        children->formatRecursive(indent + 1, true);
+    }
+}
+
+void Framework::JSON::EditableJsonElement::formatRecursive(
+    int indent, bool insertNewLine)
+{
+    format(indent, insertNewLine);
+    EditableJsonElement* tmp = siblings;
+    while (tmp)
+    {
+        tmp->format(indent, true);
+        tmp = tmp->siblings;
+    }
+}
+
+bool Framework::JSON::EditableJsonElement::isValueValid(
+    const char* content, int start, int end)
+{
+    Text value(content, start, end - start);
+    if (value.istGleich("null") || value.istGleich("true")
+        || value.istGleich("false"))
+    {
+        return true;
+    }
+    RCArray<Regex::Result>* results
+        = valueValidator->match(value, value.getLength());
+    bool ok = results->getEintragAnzahl() == 1;
+    results->release();
+    return ok;
+}
+
+void Framework::JSON::EditableJsonElement::setContent(
+    const char* content, int& startPos, Array<EditableJsonElement*>& afterList)
+{
+    if (children)
+    {
+        afterList.add(children);
+    }
+    if (siblings)
+    {
+        afterList.add(siblings);
+    }
+    EditableJsonElement* p = parent;
+    EditableJsonElement* last = this;
+    while (p)
+    {
+        if (p->siblings)
+        {
+            afterList.add(p->siblings);
+            p->siblings = 0;
+        }
+        p->lastChild = last;
+        last = p;
+        p = p->parent;
+    }
+    lastChild = 0;
+    children = 0;
+    siblings = 0;
+    if (parent)
+    {
+        parent->lastChild = this;
+    }
+    Array<EditableJsonElement*> deleteList;
+    setContentRecursive(&content, startPos, afterList, deleteList);
+    if (content[startPos])
+    {
+        EditableJsonElement* next = new EditableJsonElement(valueValidator);
+        if (parent)
+        {
+            parent->addSibling(next);
+        }
+        else
+        {
+            addSibling(next);
+        }
+        next->setContent(content, startPos, afterList);
+    }
+    else if (afterList.getEintragAnzahl())
+    {
+        if (openingControllChar)
+        {
+            while (afterList.getEintragAnzahl()
+                   && (!lastChild
+                       || !((openingControllChar == '['
+                                && lastChild->closingControllChar == ']')
+                            || (openingControllChar == '{'
+                                && lastChild->closingControllChar == '}'))))
+            {
+                EditableJsonElement* next = afterList.get(0);
+                afterList.remove(0);
+                EditableJsonElement* end = next;
+                while (end)
+                {
+                    if ((end->closingControllChar == ']'
+                            && this->openingControllChar == '[')
+                        || (end->closingControllChar == '}'
+                            && this->openingControllChar == '{'))
+                    {
+                        if (end->siblings)
+                        {
+                            afterList.add(end->siblings, 0);
+                            end->siblings = 0;
+                        }
+                        break;
+                    }
+                    end = end->siblings;
+                }
+                addChildren(next);
+                next->checkSyntax();
+                while (next->siblings)
+                {
+                    next = next->siblings;
+                    next->checkSyntax();
+                }
+            }
+        }
+        EditableJsonElement* p = parent;
+        EditableJsonElement* c = this;
+        while (afterList.getEintragAnzahl())
+        {
+            if (p)
+            {
+                if (p->siblings)
+                {
+                    afterList.add(p->siblings);
+                    p->siblings = 0;
+                    if (p->parent)
+                    {
+                        p->parent->lastChild = p;
+                    }
+                }
+                while (
+                    afterList.getEintragAnzahl()
+                    && (!p->lastChild
+                        || !((p->openingControllChar == '['
+                                 && p->lastChild->closingControllChar == ']')
+                             || (p->openingControllChar == '{'
+                                 && p->lastChild->closingControllChar == '}'))))
+                {
+                    EditableJsonElement* next = afterList.get(0);
+                    afterList.remove(0);
+                    EditableJsonElement* end = next;
+                    while (end)
+                    {
+                        if ((end->closingControllChar == ']'
+                                && p->openingControllChar == '[')
+                            || (end->closingControllChar == '}'
+                                && p->openingControllChar == '{'))
+                        {
+                            if (end->siblings)
+                            {
+                                afterList.add(end->siblings, 0);
+                                end->siblings = 0;
+                            }
+                            break;
+                        }
+                        end = end->siblings;
+                    }
+                    p->addChildren(next);
+                    next->checkSyntax();
+                    while (next->siblings)
+                    {
+                        next = next->siblings;
+                        next->checkSyntax();
+                    }
+                }
+                c = p;
+                p = p->parent;
+            }
+            else
+            {
+                while (afterList.getEintragAnzahl())
+                {
+                    EditableJsonElement* next = afterList.get(0);
+                    afterList.remove(0);
+                    c->addSibling(next);
+                    next->checkSyntax();
+                    while (next->siblings)
+                    {
+                        next = next->siblings;
+                        next->checkSyntax();
+                    }
+                }
+            }
+        }
+    }
+    for (EditableJsonElement* e : deleteList)
+    {
+        e->release();
+    }
+}
+
+void Framework::JSON::EditableJsonElement::checkSyntax()
+{
+    errors.leeren();
+    EditableJsonElement* before = zBefore(0, false, 0);
+    ParserState p(parentStructure,
+        before
+            ? before->getContent()[before->getContent().getLength() - 1] == ','
+            : 0);
+    SyntaxError error;
+    key = "";
+    value = "";
+    int pos = 0;
+    while (pos < content.getLength())
+    {
+        const char* current = content.getText() + pos++;
+        p.next(current, 0, error);
+        if (error.getErrorType() != SyntaxErrorType::OTHER)
+        {
+            errors.add(error);
+            error = SyntaxError();
+        }
+    }
+    if (error.getErrorType() != SyntaxErrorType::OTHER)
+    {
+        errors.add(error);
+        error = SyntaxError();
+    }
+    if (p.keyStart >= 0 && p.keyEnd >= p.keyStart
+        && parentStructure == JsonStructure::OBJECT)
+    {
+        key += Text(content, p.keyStart, p.keyEnd - p.keyStart);
+    }
+    if (p.valueStart >= 0 && p.valueEnd >= p.valueStart)
+    {
+        value += Text(content, p.valueStart, p.valueEnd - p.valueStart);
+    }
+    if (p.valueValidationNeeded)
+    {
+        if (!isValueValid(value, 0, value.getLength()))
+        {
+            errors.add(SyntaxError(
+                p.valueStart, p.valueEnd, SyntaxErrorType::INVALID_VALUE));
+        }
+    }
+    openingControllChar = p.openingControllChar;
+    closingControllChar = p.closingControllChar;
+    valueStart = p.valueStart;
+    keyStart = p.keyStart;
+}
+
+void EditableJsonElement::setContent(const char* content)
+{
+    Array<EditableJsonElement*> afterList;
+    int p = 0;
+    setContent(content, p, afterList);
+}
+
+void EditableJsonElement::addChildren(EditableJsonElement* content)
+{
+    JsonStructure currentStructure
+        = openingControllChar == '['
+            ? JsonStructure::ARRAY
+            : (openingControllChar == '{' ? JsonStructure::OBJECT
+                                          : JsonStructure::VALUE);
+    if (this->children)
+    {
+        this->lastChild->siblings = content;
+    }
+    else
+    {
+        this->children = content;
+        this->lastChild = content;
+    }
+    if (content->parent != this)
+    {
+        content->parent = this;
+        content->parentStructure = currentStructure;
+        for (; lastChild->siblings; lastChild = lastChild->siblings)
+        {
+            lastChild->siblings->parent = this;
+            lastChild->siblings->parentStructure = currentStructure;
+        }
+    }
+    while (lastChild->siblings)
+    {
+        lastChild = lastChild->siblings;
+    }
+}
+
+void Framework::JSON::EditableJsonElement::setNextSibling(
+    EditableJsonElement* content)
+{
+    this->siblings = content;
+    EditableJsonElement* current = this;
+    if (content->parent != parent)
+    {
+        for (; current->siblings; current = current->siblings)
+        {
+            current->siblings->parent = parent;
+            current->siblings->parentStructure = parentStructure;
+        }
+    }
+    if (parent)
+    {
+        parent->lastChild = current;
+        while (parent->lastChild->siblings)
+        {
+            parent->lastChild = parent->lastChild->siblings;
+        }
+    }
+}
+
+void EditableJsonElement::addSibling(EditableJsonElement* content)
+{
+    EditableJsonElement* current = parent ? parent->lastChild : this;
+    while (current->siblings)
+    {
+        current = current->siblings;
+    }
+    current->siblings = content;
+    if (content->parent != parent)
+    {
+        for (; current->siblings; current = current->siblings)
+        {
+            current->siblings->parent = parent;
+            current->siblings->parentStructure = parentStructure;
+        }
+    }
+    if (parent)
+    {
+        parent->lastChild = current;
+        while (parent->lastChild->siblings)
+        {
+            parent->lastChild = parent->lastChild->siblings;
+        }
+    }
+}
+
+void Framework::JSON::EditableJsonElement::format()
+{
+    int indent = getIndent();
+    formatRecursive(indent, false);
+}
+
+const Text& Framework::JSON::EditableJsonElement::getContent()
+{
+    return content;
+}
+
+Text Framework::JSON::EditableJsonElement::getRecursiveContent()
+{
+    Text result
+        = content + (children ? children->getRecursiveContent() : Text(""));
+    EditableJsonElement* s = siblings;
+    while (s)
+    {
+        result += s->content
+                + (s->children ? s->children->getRecursiveContent() : Text(""));
+        s = s->siblings;
+    }
+    return result;
+}
+
+int Framework::JSON::EditableJsonElement::lineCount()
+{
+    return newLineCount;
+}
+
+bool Framework::JSON::EditableJsonElement::lineCount(bool includeChildren,
+    bool includeSiblings,
+    EditableJsonElement* stop,
+    int& count)
+{
+    count += newLineCount;
+    if (this == stop)
+    {
+        return false;
+    }
+    if (includeChildren && children)
+    {
+        if (!children->lineCount(true, true, stop, count))
+        {
+            return false;
+        }
+    }
+    if (includeSiblings && siblings)
+    {
+        return siblings->lineCount(true, true, stop, count);
+    };
+    return true;
+}
+
+bool Framework::JSON::EditableJsonElement::isHidden()
+{
+    return hidden;
+}
+
+void Framework::JSON::EditableJsonElement::setHidden(bool hidden)
+{
+    this->hidden = hidden;
+}
+
+EditableJsonElement* Framework::JSON::EditableJsonElement::zParent()
+{
+    return parent;
+}
+
+EditableJsonElement* Framework::JSON::EditableJsonElement::zMextSibling()
+{
+    return siblings;
+}
+
+EditableJsonElement* Framework::JSON::EditableJsonElement::zFirstChildren()
+{
+    return children;
+}
+
+EditableJsonElement* Framework::JSON::EditableJsonElement::zLastChildren()
+{
+    return lastChild;
+}
+
+char Framework::JSON::EditableJsonElement::getOpeningControllChar()
+{
+    return openingControllChar;
+}
+
+char Framework::JSON::EditableJsonElement::getClosingControllChar()
+{
+    return closingControllChar;
+}
+
+Text& Framework::JSON::EditableJsonElement::getKey()
+{
+    return key;
+}
+
+Text& Framework::JSON::EditableJsonElement::getValue()
+{
+    return value;
+}
+
+void Framework::JSON::EditableJsonElement::disconnect()
+{
+    children = 0;
+    lastChild = 0;
+    siblings = 0;
+    parent = 0;
+}
+
+void Framework::JSON::EditableJsonElement::removeChild(
+    EditableJsonElement* zElement)
+{
+    // TODO: needed?
+    if (children == zElement)
+    {
+        children = 0;
+        lastChild = 0;
+    }
+    else
+    {
+        EditableJsonElement* current = children;
+        while (current)
+        {
+            if (current->siblings == zElement)
+            {
+                current->siblings = 0;
+                if (current->parent)
+                {
+                    current->parent->lastChild = current;
+                }
+                break;
+            }
+            current = current->siblings;
+        }
+    }
+}
+
+void Framework::JSON::EditableJsonElement::getWordBounds(
+    int pos, int* left, int* right)
+{
+    bool escaped = false;
+    int lastStartStr = -1;
+    int lastBlank = 0;
+    bool found = false;
+    int len = content.getLength();
+    for (int i = 0; i < len; i++)
+    {
+        if ((content[i] == ' ' || content[i] == ',' || content[i] == '\n')
+            && lastStartStr == -1)
+        {
+            if (i >= pos)
+            {
+                if (left)
+                {
+                    *left = lastBlank;
+                }
+                if (right)
+                {
+                    *right = i;
+                }
+                found = true;
+                break;
+            }
+            lastBlank = i + 1;
+        }
+        else if (content[i] == '"')
+        {
+            if (lastStartStr == -1)
+            {
+                lastStartStr = i + 1;
+                if (i >= pos)
+                {
+                    if (left)
+                    {
+                        *left = i;
+                    }
+                    if (right)
+                    {
+                        *right = i + 1;
+                    }
+                    found = true;
+                    break;
+                }
+            }
+            else if (!escaped)
+            {
+                if (i >= pos)
+                {
+                    if (left)
+                    {
+                        *left = lastStartStr;
+                    }
+                    if (right)
+                    {
+                        *right = i;
+                    }
+                    found = true;
+                    break;
+                }
+                lastStartStr = -1;
+            }
+        }
+        else if (content[i] == '\\' && lastStartStr >= 0)
+        {
+            escaped = !escaped;
+        }
+        else
+        {
+            escaped = false;
+        }
+    }
+    if (!found && lastBlank >= 0)
+    {
+        if (left)
+        {
+            *left = lastBlank;
+        }
+        if (right)
+        {
+            *right = len;
+        }
+    }
+}
+
+bool Framework::JSON::EditableJsonElement::hasError(int column) const
+{
+    for (const SyntaxError& error : errors)
+    {
+        if (error.getStart() <= column && error.getEnd() >= column)
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+SyntaxError Framework::JSON::EditableJsonElement::getError(int column) const
+{
+    for (const SyntaxError& error : errors)
+    {
+        if (error.getStart() <= column && error.getEnd() >= column)
+        {
+            return error;
+        }
+    }
+    return SyntaxError();
+}
+
+bool Framework::JSON::EditableJsonElement::hasError() const
+{
+    return errors.getEintragAnzahl() > 0;
+}
+
+void Framework::JSON::EditableJsonElement::makeVisible()
+{
+    EditableJsonElement* current = parent;
+    while (current)
+    {
+        current->setHidden(false);
+        current = current->zParent();
+    }
+}
+
+bool Framework::JSON::EditableJsonElement::isVisible() const
+{
+    EditableJsonElement* current = parent;
+    while (current)
+    {
+        if (current->isHidden())
+        {
+            return false;
+        }
+        current = current->zParent();
+    }
+    return true;
+}
+
+int Framework::JSON::EditableJsonElement::getIndent() const
+{
+    int indent = 0;
+    EditableJsonElement* tmp = parent;
+    while (tmp)
+    {
+        indent++;
+        tmp = tmp->parent;
+    }
+    return indent;
+}
+
+int Framework::JSON::EditableJsonElement::getColor(int index)
+{
+    int color = 0xFFA0A0A0;
+    if (hasError(index))
+    {
+        color = 0xFFFF3030;
+    }
+    else if (keyStart >= 0 && index >= keyStart - 1
+             && index < keyStart + key.getLength() + 1)
+    {
+        color = 0xFF7070C0;
+    }
+    else if (valueStart >= 0 && index >= valueStart
+             && index < valueStart + value.getLength())
+    {
+        if (value[0] == '"')
+        {
+            color = 0xFF70C070;
+        }
+        else if (value.istGleich("null"))
+        {
+            color = 0xFFC0A070;
+        }
+        else if (value.istGleich("true") || value.istGleich("false"))
+        {
+            color = 0xFFC070C0;
+        }
+        else
+        {
+            color = 0xFF70C0C0;
+        }
+    }
+    return color;
+}
+
+void Framework::JSON::EditableJsonElement::removeUntil(
+    int startIndex, EditableJsonElement* end, int endIndex)
+{
+    Text* result = content.getTeilText(0, startIndex);
+    if (end == this)
+    {
+        result->append(content.getTeilText(endIndex));
+        setContent(*result);
+        result->release();
+        return;
+    }
+    else
+    {
+        Array<EditableJsonElement*> toDelete;
+        Stack<EditableJsonElement*> stack;
+        if (siblings)
+        {
+            stack.push(siblings);
+        }
+        if (children)
+        {
+            stack.push(children);
+        }
+        if (!stack.getSize())
+        {
+            EditableJsonElement* p = parent;
+            while (p)
+            {
+                if (p->siblings)
+                {
+                    stack.push(p->siblings);
+                    break;
+                }
+                p = p->parent;
+            }
+        }
+        if (stack.getSize())
+        {
+            EditableJsonElement* next = stack.pop();
+            while (true)
+            {
+                if (next->siblings)
+                {
+                    stack.push(siblings);
+                }
+                if (next->children)
+                {
+                    stack.push(children);
+                }
+                if (!stack.getSize() && next != end)
+                {
+                    EditableJsonElement* p = next->parent;
+                    while (p)
+                    {
+                        if (p->siblings)
+                        {
+                            stack.push(p->siblings);
+                            break;
+                        }
+                        p = p->parent;
+                    }
+                }
+                if (next->parent)
+                {
+                    next->parent->removeChild(next);
+                }
+                else
+                {
+                    EditableJsonElement* p = this;
+                    while (p->parent)
+                    {
+                        p = p->parent;
+                    }
+                    while (p->siblings != next)
+                    {
+                        p = p->siblings;
+                    }
+                    p->siblings = 0;
+                }
+                toDelete.add(next);
+                if (!stack.getSize())
+                {
+                    next = 0;
+                    break;
+                }
+                if (next == end)
+                {
+                    break;
+                }
+                next = stack.pop();
+            }
+            if (next == end)
+            {
+                result->append(next->content.getTeilText(endIndex));
+            }
+            for (EditableJsonElement* del : toDelete)
+            {
+                del->disconnect();
+                del->release();
+            }
+            Array<EditableJsonElement*> afterList;
+            while (stack.getSize())
+            {
+                afterList.add(stack.pop());
+            }
+            int p = 0;
+            setContent(*result, p, afterList);
+        }
+        else
+        {
+            setContent(*result);
+            result->release();
+            return;
+        }
+    }
+}
+
+EditableJsonElement* Framework::JSON::EditableJsonElement::zBefore(
+    EditableJsonElement* zRoot, bool onlyVisible, int* lineCount)
+{
+    if (this == zRoot)
+    {
+        return 0;
+    }
+    EditableJsonElement* parent = this->parent;
+    EditableJsonElement* current = parent ? parent->children : zRoot;
+    EditableJsonElement* last = parent;
+    while (current)
+    {
+        if (current == this)
+        {
+            break;
+        }
+        last = current;
+        current = current->siblings;
+    }
+    if (last && last->children != this && (!last->hidden || !onlyVisible))
+    {
+        current = last->lastChild;
+        while (current)
+        {
+            last = current;
+            if (current->children)
+            {
+                if ((!current->hidden || !onlyVisible))
+                {
+                    current = current->lastChild;
+                }
+                else if (*lineCount)
+                {
+                    EditableJsonElement* e = current->children;
+                    while (e)
+                    {
+                        int lc = 0;
+                        e->lineCount(true, false, 0, lc);
+                        *lineCount -= lc;
+                        e = e->siblings;
+                    }
+                    break;
+                }
+            }
+            else
+            {
+                break;
+            }
+        }
+    }
+    else if (last && last->hidden && onlyVisible && lineCount)
+    {
+        EditableJsonElement* e = last->children;
+        while (e)
+        {
+            int lc = 0;
+            e->lineCount(true, false, 0, lc);
+            *lineCount -= lc;
+            e = e->siblings;
+        }
+    }
+    return last;
+}
+
+EditableJsonElement* Framework::JSON::EditableJsonElement::zAfter(
+    bool onlyVisible, int* lineCount)
+{
+    EditableJsonElement* zElement = this;
+    if (zElement->children)
+    {
+        if (!zElement->hidden || !onlyVisible)
+        {
+            return zElement->children;
+        }
+        else if (lineCount)
+        {
+            EditableJsonElement* e = zElement->children;
+            while (e)
+            {
+                int lc = 0;
+                e->lineCount(true, false, 0, lc);
+                *lineCount += lc;
+                e = e->siblings;
+            }
+        }
+    }
+    if (zElement->siblings)
+    {
+        return zElement->siblings;
+    }
+    while (zElement->parent)
+    {
+        zElement = zElement->parent;
+        if (zElement->siblings)
+        {
+            return zElement->siblings;
+        }
+    }
+    return 0;
+}
+
+JsonEditor::JsonEditor(UIInit uiInit)
+    : ZeichnungHintergrund(),
+      uiInit(uiInit)
+{
+    valueValidator = Regex::parse("^-?[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?$");
+    validator = 0;
+    content = 0;
+    renderStart = {0, 0};
+    renderStartLine = 0;
+    lineCount = 0;
+    cursorPos.x = -1;
+    cursorPos.y = -1;
+    textCursor = {0, 0};
+    selectionStart = {0, 0};
+    renderStartOffset = 0;
+    selectionEnd = {0, 0};
+    textRenderer = new TextRenderer();
+    textRenderer->setSchriftSize(12);
+    textRenderer->setZeilenAbstand(2);
+    textRenderer->setZeichenAbstand(1);
+    pressed = 0;
+    time = 0;
+    drag = 0;
+    drawCursor = 0;
+    dragSart = {-1, -1};
+    pressedPos = {-1, -1};
+    dragStartPos = {0, -1};
+    timeSicePress = 0.0;
+    lastClickCursorPos = {0, 0};
+    tps = 0;
+    renderings = 0;
+    renderedLines = 0;
+    renderStopLine = 0;
+    errorDescription = uiInit.createTextFeld(uiInit.initParam);
+    errorDescription->setStyle(TextFeld::Style::Text | TextFeld::Style::Center
+                               | TextFeld::Style::Rahmen
+                               | TextFeld::Style::Hintergrund
+                               | TextFeld::Style::HAlpha);
+    errorDescription->removeStyle(TextFeld::Style::Sichtbar);
+    errorDescription->setSchriftFarbe(0xFFFFFFFF);
+    errorDescription->setRahmenFarbe(0xFFA0A0A0);
+    errorDescription->setHintergrundFarbe(0xA0000000);
+    hasSyntaxError = false;
+}
+
+JsonEditor::~JsonEditor()
+{
+    if (content) content->release();
+    if (validator) validator->release();
+    textRenderer->release();
+    errorDescription->release();
+}
+
+void Framework::JSON::JsonEditor::doMausEreignis(MausEreignis& me, bool userRet)
+{
+    LOCK(&cs);
+    rend |= cursorPos.x != me.mx || cursorPos.y != me.my;
+    cursorPos.x = me.mx;
+    cursorPos.y = me.my;
+    if (me.id == ME_RLinks)
+    {
+        pressedPos = cursorPos;
+        if (timeSicePress < 0.5)
+        {
+            if (lastClickCursorPos.line == textCursor.line
+                && lastClickCursorPos.column == textCursor.column
+                && lastClickCursorPos.line && lastClickCursorPos.column > 0)
+            {
+                EditableJsonElement* current = textCursor.line;
+                int start = 0;
+                int end = 0;
+                current->getWordBounds(textCursor.column, &start, &end);
+                selectionStart = {current, start};
+                selectionEnd = {current, end};
+                unifyPosition(selectionEnd);
+                textCursor = selectionEnd;
+            }
+        }
+        lastClickCursorPos = textCursor;
+        pressed = true;
+        drag = false;
+        rend = 1;
+        time = 0;
+        timeSicePress = 0.0;
+    }
+    if (me.id == ME_PLinks || (me.id == ME_Betritt && getMausStand(M_Links)))
+    {
+        if (me.mx >= getInnenBreite() - 15)
+        {
+            int line = (int)(me.my / ((double)getInnenHeight() / lineCount));
+            if (line >= lineCount)
+            {
+                line = lineCount - 1;
+            }
+            scrollToLine(line + 1, Center);
+        }
+        if (!drag)
+        {
+            drag = true;
+            textCursor = getScreenPosition(cursorPos.x, cursorPos.y);
+            selectionStart = textCursor;
+            selectionEnd = textCursor;
+            dragSart = cursorPos;
+            dragStartPos = textCursor;
+            rend = 1;
+            time = 0;
+        }
+    }
+    if (me.id == ME_Bewegung)
+    {
+        if (drag)
+        {
+            if (dragSart.x >= getInnenBreite() - 15)
+            {
+                int line
+                    = (int)(me.my / ((double)getInnenHeight() / lineCount));
+                if (line >= lineCount)
+                {
+                    line = lineCount - 1;
+                }
+                scrollToLine(line + 1, Center);
+            }
+            else
+            {
+                EditorPosition pos
+                    = getScreenPosition(cursorPos.x, cursorPos.y);
+                if (pos.line == dragStartPos.line)
+                {
+                    if (pos.column < dragStartPos.column)
+                    {
+                        selectionStart = pos;
+                        selectionEnd = dragStartPos;
+                    }
+                    else if (pos.column > dragStartPos.column)
+                    {
+                        selectionStart = dragStartPos;
+                        selectionEnd = pos;
+                    }
+                }
+                else
+                {
+                    if (cursorPos.y < dragSart.y)
+                    {
+                        selectionStart = pos;
+                        selectionEnd = dragStartPos;
+                    }
+                    else if (cursorPos.y > dragSart.y)
+                    {
+                        selectionStart = dragStartPos;
+                        selectionEnd = pos;
+                    }
+                }
+                textCursor = pos;
+            }
+        }
+    }
+    if (me.id == ME_UScroll)
+    {
+        if (renderStart.column > 0)
+        {
+            const char* content = renderStart.line->getContent();
+            int index = renderStart.column - 2;
+            while (index > 0 && content[index] != '\n')
+            {
+                index--;
+            }
+            if (content[index] == '\n')
+            {
+                renderStart.column = index + 1;
+                rend = 1;
+                renderStartLine--;
+                unifyPosition(renderStart);
+                return;
+            }
+        }
+        while (true)
+        {
+            EditableJsonElement* next
+                = renderStart.line->zBefore(content, true, &renderStartLine);
+            if (next)
+            {
+                renderStart.line = next;
+                const char* content = next->getContent();
+                int index = next->getContent().getLength() - 1;
+                while (index > 0 && content[index] != '\n')
+                {
+                    index--;
+                }
+                if (content[index] == '\n')
+                {
+                    renderStart.column = index + 1;
+                    rend = 1;
+                    renderStartLine--;
+                    unifyPosition(renderStart);
+                    return;
+                }
+            }
+            else
+            {
+                renderStart.column = 0;
+                rend = 1;
+                renderStartLine = 1;
+                return;
+            }
+        }
+    }
+    if (me.id == ME_DScroll)
+    {
+        const char* content = renderStart.line->getContent();
+        int length = renderStart.line->getContent().getLength();
+        int index = renderStart.column;
+        while (index < length && content[index] != '\n')
+        {
+            index++;
+        }
+        if (content[index] == '\n')
+        {
+            renderStart.column = index + 1;
+            rend = 1;
+            renderStartLine++;
+            unifyPosition(renderStart);
+            return;
+        }
+        while (true)
+        {
+            EditableJsonElement* next
+                = renderStart.line->zAfter(true, &renderStartLine);
+            if (next)
+            {
+                renderStart.line = next;
+                const char* content = next->getContent();
+                int length = next->getContent().getLength();
+                int index = 0;
+                while (index < length && content[index] != '\n')
+                {
+                    index++;
+                }
+                if (content[index] == '\n')
+                {
+                    renderStart.column = index + 1;
+                    rend = 1;
+                    renderStartLine++;
+                    unifyPosition(renderStart);
+                    return;
+                }
+            }
+            else
+            {
+                return;
+            }
+        }
+    }
+}
+
+EditorPosition Framework::JSON::JsonEditor::getScreenPosition(
+    int localX, int localY)
+{
+    if (!renderStart.line) return {0, 0};
+    int y = 0;
+    int lineNumberWidth = 0;
+    for (int i = lineCount; i > 0; i /= 10)
+    {
+        lineNumberWidth++;
+    }
+    lineNumberWidth = lineNumberWidth * textRenderer->getMaxCharWidth()
+                    + textRenderer->getZeichenAbstand() * (lineNumberWidth - 1);
+    int x = lineNumberWidth + 26;
+    EditableJsonElement* current = renderStart.line;
+    int index = renderStart.column;
+    EditableJsonElement* last = current;
+    while (y < gr.y && current)
+    {
+        int length = current->getContent().getLength();
+        const char* content = current->getContent();
+        for (int i = index; i < length; i++)
+        {
+            if (y >= localY - textRenderer->getZeilenHeight()
+                         - textRenderer->getZeilenAbstand()
+                && x >= localX - textRenderer->getCharWidth(content[i]) / 2
+                            - textRenderer->getZeichenAbstand())
+            {
+                EditorPosition result = {current, i};
+                unifyPosition(result);
+                return result;
+            }
+            if (content[i] == '\n')
+            {
+                if (y >= localY - textRenderer->getZeilenHeight()
+                             - textRenderer->getZeilenAbstand())
+                {
+                    EditorPosition result = {current, i};
+                    unifyPosition(result);
+                    return result;
+                }
+                y += textRenderer->getZeilenHeight()
+                   + textRenderer->getZeilenAbstand();
+                x = lineNumberWidth + 26;
+            }
+            else
+            {
+                x += textRenderer->getCharWidth(content[i])
+                   + textRenderer->getZeichenAbstand();
+            }
+        }
+        last = current;
+        current = current->zAfter(true, 0);
+        index = 0;
+    }
+    return {last, last->getContent().getLength()};
+}
+
+void Framework::JSON::JsonEditor::deleteSelection()
+{
+    if (selectionStart.line && selectionEnd.line)
+    {
+        selectionStart.line->removeUntil(
+            selectionStart.column, selectionEnd.line, selectionEnd.column);
+        selectionEnd = selectionStart;
+        textCursor = selectionStart;
+        fixTree(selectionStart.line);
+        if (dragStartPos.line)
+        {
+            dragStartPos = selectionStart;
+        }
+        if (lastClickCursorPos.line)
+        {
+            lastClickCursorPos = selectionStart;
+        }
+    }
+}
+
+void Framework::JSON::JsonEditor::unifyPosition(EditorPosition& pos)
+{
+    while (pos.line && pos.column >= pos.line->getContent().getLength())
+    {
+        if (pos.line->zFirstChildren() && pos.line->isVisible())
+        {
+            pos = {pos.line->zFirstChildren(),
+                pos.column - pos.line->getContent().getLength()};
+        }
+        else if (pos.line->zMextSibling())
+        {
+            pos = {pos.line->zMextSibling(),
+                pos.column - pos.line->getContent().getLength()};
+        }
+        else
+        {
+            EditableJsonElement* p = pos.line->zParent();
+            while (p)
+            {
+                if (p->zMextSibling())
+                {
+                    pos = {p->zMextSibling(), 0};
+                    break;
+                }
+                p = p->zParent();
+            }
+            if (!p)
+            {
+                pos.column = pos.line->getContent().getLength();
+                break;
+            }
+        }
+    }
+}
+
+void Framework::JSON::JsonEditor::fixTree(EditableJsonElement* zElement)
+{
+    EditableJsonElement* before = zElement->zBefore(content, false, 0);
+    int beforeLength = before ? before->getContent().getLength() : 0;
+    if (zElement->hasError() && before)
+    {
+        Text content = before->getContent();
+        before->setContent(content);
+        if (before->getContent().getLength() != beforeLength)
+        {
+            before->makeVisible();
+        }
+    }
+    if (textCursor.line == zElement)
+    {
+        textCursor.column += beforeLength;
+        textCursor.line = before ? before : zElement;
+        unifyPosition(textCursor);
+    }
+    if (renderStart.line == zElement)
+    {
+        renderStart.column += beforeLength;
+        renderStart.line = before ? before : zElement;
+        unifyPosition(renderStart);
+    }
+}
+
+void Framework::JSON::JsonEditor::setFont(Schrift* schrift)
+{
+    textRenderer->setSchriftZ(schrift);
+}
+
+void Framework::JSON::JsonEditor::setFontSize(int size)
+{
+    textRenderer->setSchriftSize(size);
+    textRenderer->setZeilenAbstand(size / 6);
+    textRenderer->setZeichenAbstand(size / 8);
+}
+
+void JsonEditor::setContent(Text content)
+{
+    if (this->content)
+    {
+        this->content->release();
+        this->content = 0;
+        this->renderStart = {0, 0};
+    }
+    this->content = new EditableJsonElement(
+        dynamic_cast<Regex::Automata<char>*>(valueValidator->getThis()));
+    content.ersetzen("\r", "");
+    content.ersetzen("\t", "    ");
+    this->content->setContent(content);
+    textCursor = {this->content, 0};
+    selectionStart = {this->content, 0};
+    selectionEnd = {this->content, 0};
+    format();
+}
+
+void Framework::JSON::JsonEditor::setContent(JSONValue* content)
+{
+    setContent(content->toString());
+    content->release();
+}
+
+void Framework::JSON::JsonEditor::format()
+{
+    if (content)
+    {
+        content->format();
+        lineCount = 0;
+        content->lineCount(true, true, 0, lineCount);
+    }
+}
+
+void Framework::JSON::JsonEditor::setValidator(
+    Validator::DataValidator* validator)
+{
+    if (this->validator) this->validator->release();
+    this->validator = validator;
+    // if (content) validate(); TODO: validate attributes
+}
+
+void Framework::JSON::JsonEditor::doTastaturEreignis(TastaturEreignis& te)
+{
+    LOCK(&cs);
+    if (!textCursor.line)
+    {
+        return;
+    }
+    bool shift = getTastenStand(T_Shift);
+    bool strg = getTastenStand(T_Strg);
+    if (te.id == TE_Press)
+    {
+        switch (te.virtualKey)
+        {
+        case T_Tab:
+            {
+                Text illegalStarts = "[]{},\": ";
+                if (shift)
+                {
+                    bool lastStr
+                        = textCursor.line->getContent().getLength()
+                                > textCursor.column
+                            ? textCursor.line->getContent()[textCursor.column]
+                                  == '"'
+                            : false;
+                    int nextColumn = textCursor.column - 1;
+                    EditableJsonElement* nextLine = textCursor.line;
+                    while (nextLine)
+                    {
+                        if (nextColumn < 0)
+                        {
+                            nextLine = nextLine->zBefore(content, true, 0);
+                            if (!nextLine)
+                            {
+                                return;
+                            }
+                            nextColumn = nextLine->getContent().getLength();
+                        }
+                        int start = 0;
+                        int end = 0;
+                        nextLine->getWordBounds(nextColumn, &start, &end);
+                        if ((start != end
+                                && !illegalStarts.hat(
+                                    nextLine->getContent()[start]))
+                            || (lastStr
+                                && nextLine->getContent()[nextColumn] == '"'))
+                        {
+                            if (lastStr
+                                && nextLine->getContent()[nextColumn] == '"')
+                            {
+                                start = end;
+                            }
+                            if (selectionStart.column != start
+                                || selectionStart.line != nextLine
+                                || selectionEnd.column != end
+                                || selectionEnd.line != nextLine)
+                            {
+                                selectionStart = {nextLine, start};
+                                selectionEnd = {nextLine, end};
+                                unifyPosition(selectionEnd);
+                                textCursor = selectionEnd;
+                                scrollToLine(textCursor, Top);
+                                te.verarbeitet = true;
+                                time = 0;
+                                rend = 1;
+                                return;
+                            }
+                        }
+                        lastStr = nextLine->getContent()[nextColumn] == '"';
+                        nextColumn--;
+                    }
+                }
+                else
+                {
+                    bool lastStr
+                        = textCursor.line->getContent().getLength()
+                                > textCursor.column
+                            ? textCursor.line->getContent()[textCursor.column]
+                                  == '"'
+                            : false;
+                    int nextColumn = textCursor.column + 1;
+                    EditableJsonElement* nextLine = textCursor.line;
+                    while (nextLine)
+                    {
+                        if (nextColumn > nextLine->getContent().getLength())
+                        {
+                            nextLine = nextLine->zAfter(true, 0);
+                            if (!nextLine)
+                            {
+                                return;
+                            }
+                            nextColumn = 0;
+                        }
+                        int start = 0;
+                        int end = 0;
+                        nextLine->getWordBounds(nextColumn, &start, &end);
+                        if (((start != end
+                                 && !illegalStarts.hat(
+                                     nextLine->getContent()[start]))
+                                || (lastStr
+                                    && nextLine->getContent()[nextColumn]
+                                           == '"'))
+                            && (selectionStart.column != start
+                                || selectionStart.line != nextLine
+                                || selectionEnd.column != end
+                                || selectionEnd.line != nextLine))
+                        {
+                            selectionStart = {nextLine, start};
+                            selectionEnd = {nextLine, end};
+                            unifyPosition(selectionEnd);
+                            textCursor = selectionEnd;
+                            scrollToLine(textCursor, Bottom);
+                            te.verarbeitet = true;
+                            time = 0;
+                            rend = 1;
+                            return;
+                        }
+                        lastStr = nextLine->getContent()[nextColumn] == '"';
+                        nextColumn++;
+                    }
+                }
+            }
+            return;
+        case T_Entf:
+            if (selectionStart.line == selectionEnd.line
+                && selectionStart.column == selectionEnd.column)
+            {
+                if (textCursor.column
+                    < textCursor.line->getContent().getLength())
+                {
+                    Text content = textCursor.line->getContent();
+                    content.remove(textCursor.column, textCursor.column + 1);
+                    textCursor.line->setContent(content);
+                    fixTree(textCursor.line);
+                }
+            }
+            else
+            {
+                deleteSelection();
+            }
+            break;
+        case T_BackSpace:
+            if (selectionStart.line == selectionEnd.line
+                && selectionStart.column == selectionEnd.column)
+            {
+                if (textCursor.column > 0)
+                {
+                    Text content = textCursor.line->getContent();
+                    content.remove(textCursor.column - 1, textCursor.column);
+                    textCursor.line->setContent(content);
+                    textCursor.column--;
+                    fixTree(textCursor.line);
+                }
+                else
+                {
+                    EditableJsonElement* before
+                        = textCursor.line->zBefore(content, false, 0);
+                    if (before)
+                    {
+                        before->makeVisible();
+                        Text content = before->getContent();
+                        content.remove(
+                            content.getLength() - 1, content.getLength());
+                        before->setContent(content);
+                        textCursor = {before, content.getLength()};
+                        fixTree(before);
+                        scrollToLine(textCursor, Top);
+                    }
+                }
+            }
+            else
+            {
+                deleteSelection();
+            }
+            break;
+        case T_Enter:
+            {
+                deleteSelection();
+                Text content = textCursor.line->getContent();
+                content.insert(textCursor.column, "\n");
+                textCursor.column += 1;
+                textCursor.line->setContent(content);
+                fixTree(textCursor.line);
+                scrollToLine(textCursor, Bottom);
+            }
+            break;
+        case T_Links:
+            {
+                int nextColumn = textCursor.column - 1;
+                EditableJsonElement* nextLine = textCursor.line;
+                while (nextColumn < 0)
+                {
+                    nextLine = textCursor.line->zBefore(content, true, 0);
+                    if (!nextLine)
+                    {
+                        return;
+                    }
+                    nextColumn = nextLine->getContent().getLength() - 1;
+                }
+                if (strg)
+                {
+                    int start = 0;
+                    nextLine->getWordBounds(nextColumn, &start, 0);
+                    nextColumn = start;
+                }
+                if (shift)
+                {
+                    if (textCursor.line == selectionStart.line
+                        && textCursor.column == selectionStart.column)
+                    {
+                        selectionStart = {nextLine, nextColumn};
+                        unifyPosition(selectionStart);
+                        textCursor = selectionStart;
+                    }
+                    else
+                    {
+                        selectionEnd = {nextLine, nextColumn};
+                        unifyPosition(selectionEnd);
+                        textCursor = selectionEnd;
+                    }
+                }
+                else
+                {
+                    textCursor = {nextLine, nextColumn};
+                    unifyPosition(textCursor);
+                    selectionStart = textCursor;
+                    selectionEnd = textCursor;
+                }
+                scrollToLine(textCursor, Top);
+                te.verarbeitet = true;
+                time = 0;
+                rend = 1;
+                return;
+            }
+        case T_Oben:
+            {
+                EditableJsonElement* current = textCursor.line;
+                int count = 0;
+                bool found = 0;
+                int index = textCursor.column - 1;
+                const char* content = current->getContent();
+                while (current)
+                {
+                    while (index < 0)
+                    {
+                        current = current->zBefore(this->content, true, 0);
+                        if (!current)
+                        {
+                            break;
+                        }
+                        content = current->getContent();
+                        index = current->getContent().getLength() - 1;
+                    }
+                    if (!current)
+                    {
+                        break;
+                    }
+                    if (content[index] == '\n')
+                    {
+                        if (found)
+                        {
+                            break;
+                        }
+                        found = 1;
+                    }
+                    if (!found)
+                    {
+                        count++;
+                    }
+                    index--;
+                }
+                if (!current)
+                {
+                    break;
+                }
+                index++;
+                while (current && count > 0)
+                {
+                    while (index >= current->getContent().getLength())
+                    {
+                        current = current->zAfter(true, 0);
+                        if (!current)
+                        {
+                            break;
+                        }
+                        content = current->getContent();
+                        index = 0;
+                    }
+                    if (!current)
+                    {
+                        break;
+                    }
+                    if (content[index] == '\n')
+                    {
+                        break;
+                    }
+                    count--;
+                    index++;
+                }
+                if (!current)
+                {
+                    break;
+                }
+                if (shift)
+                {
+                    if (textCursor.line == selectionStart.line
+                        && textCursor.column == selectionStart.column)
+                    {
+                        selectionStart = {current, index};
+                        unifyPosition(selectionStart);
+                        textCursor = selectionStart;
+                    }
+                    else
+                    {
+                        selectionEnd = {current, index};
+                        unifyPosition(selectionEnd);
+                        textCursor = selectionEnd;
+                    }
+                }
+                else
+                {
+                    textCursor = {current, index};
+                    unifyPosition(textCursor);
+                    selectionStart = textCursor;
+                    selectionEnd = textCursor;
+                }
+                scrollToLine(textCursor, Top);
+                te.verarbeitet = true;
+                time = 0;
+                rend = 1;
+                return;
+            }
+        case T_Rechts:
+            {
+                int nextColumn = textCursor.column + 1;
+                EditableJsonElement* nextLine = textCursor.line;
+                if (nextColumn > nextLine->getContent().getLength())
+                {
+                    nextLine = textCursor.line->zAfter(true, 0);
+                    if (!nextLine)
+                    {
+                        return;
+                    }
+                    nextColumn = 0;
+                }
+                if (strg)
+                {
+                    int end = 0;
+                    nextLine->getWordBounds(nextColumn, 0, &end);
+                    nextColumn = end;
+                }
+                if (shift)
+                {
+                    if (textCursor.line == selectionEnd.line
+                        && textCursor.column == selectionEnd.column)
+                    {
+                        selectionEnd = {nextLine, nextColumn};
+                        unifyPosition(selectionEnd);
+                        textCursor = selectionEnd;
+                    }
+                    else
+                    {
+                        selectionStart = {nextLine, nextColumn};
+                        unifyPosition(selectionStart);
+                        textCursor = selectionStart;
+                    }
+                }
+                else
+                {
+                    textCursor = {nextLine, nextColumn};
+                    unifyPosition(textCursor);
+                    selectionStart = textCursor;
+                    selectionEnd = textCursor;
+                }
+                scrollToLine(textCursor, Bottom);
+                te.verarbeitet = true;
+                time = 0;
+                rend = 1;
+                return;
+            }
+        case T_Unten:
+            {
+                EditableJsonElement* current = textCursor.line;
+                int count = 0;
+                int index = textCursor.column - 1;
+                const char* content = current->getContent();
+                while (current)
+                {
+                    while (index < 0)
+                    {
+                        current = current->zBefore(this->content, true, 0);
+                        if (!current)
+                        {
+                            break;
+                        }
+                        content = current->getContent();
+                        index = current->getContent().getLength() - 1;
+                    }
+                    if (!current)
+                    {
+                        break;
+                    }
+                    if (content[index] == '\n')
+                    {
+                        break;
+                    }
+                    count++;
+                    index--;
+                }
+                bool found = 0;
+                current = textCursor.line;
+                index = textCursor.column;
+                content = current->getContent();
+                while (current && (count > 0 || !found))
+                {
+                    while (index >= current->getContent().getLength())
+                    {
+                        current = current->zAfter(true, 0);
+                        if (!current)
+                        {
+                            break;
+                        }
+                        content = current->getContent();
+                        index = 0;
+                    }
+                    if (found)
+                    {
+                        count--;
+                    }
+                    if (content[index] == '\n')
+                    {
+                        if (found)
+                        {
+                            break;
+                        }
+                        found = 1;
+                    }
+                    index++;
+                }
+                if (shift)
+                {
+                    if (textCursor.line == selectionEnd.line
+                        && textCursor.column == selectionEnd.column)
+                    {
+                        selectionEnd = {current, index};
+                        unifyPosition(selectionEnd);
+                        textCursor = selectionEnd;
+                    }
+                    else
+                    {
+                        selectionStart = {current, index};
+                        unifyPosition(selectionStart);
+                        textCursor = selectionStart;
+                    }
+                }
+                else
+                {
+                    textCursor = {current, index};
+                    unifyPosition(textCursor);
+                    selectionStart = textCursor;
+                    selectionEnd = textCursor;
+                }
+                scrollToLine(textCursor, Bottom);
+                te.verarbeitet = true;
+                time = 0;
+                rend = 1;
+                return;
+            }
+        default:
+            if (strg)
+            {
+                if (te.virtualKey == 'c' || te.virtualKey == 'C')
+                {
+                    Text content = getSelectedContent();
+                    TextKopieren(content);
+                    return;
+                }
+                else if (te.virtualKey == 'x' || te.virtualKey == 'X')
+                {
+                    Text content = getSelectedContent();
+                    TextKopieren(content);
+                    deleteSelection();
+                    scrollToLine(textCursor, Top);
+                    return;
+                }
+                else if (te.virtualKey == 'v' || te.virtualKey == 'V')
+                {
+                    deleteSelection();
+                    EditableJsonElement* changed = textCursor.line;
+                    Text content = changed->getContent();
+                    const char* inserted = TextInsert();
+                    content.insert(textCursor.column, inserted);
+                    textCursor.column += textLength(inserted);
+                    unifyPosition(textCursor);
+                    scrollToLine(textCursor, Bottom);
+                    break;
+                }
+                else if (te.virtualKey == 'a' || te.virtualKey == 'A')
+                {
+                    selectionStart = {content, 0};
+                    EditableJsonElement* element = content;
+                    bool first = true;
+                    while (element->zMextSibling())
+                    {
+                        element = element->zMextSibling();
+                    }
+                    while (element->zLastChildren())
+                    {
+                        element = element->zLastChildren();
+                    }
+                    selectionEnd = {element, element->getContent().getLength()};
+                    textCursor = selectionEnd;
+                    te.verarbeitet = true;
+                    time = 0;
+                    rend = 1;
+                    scrollToLine(textCursor, Bottom);
+                    return;
+                }
+                else if (te.virtualKey == 'f' || te.virtualKey == 'F')
+                {
+                    format();
+                    return;
+                }
+            }
+            if (istSchreibbar(te.taste[0]))
+            {
+                deleteSelection();
+                Text content = textCursor.line->getContent();
+                content.insert(textCursor.column, te.taste);
+                textCursor.column += textLength(te.taste);
+                textCursor.line->setContent(content);
+                fixTree(textCursor.line);
+            }
+            else
+            {
+                return;
+            }
+        }
+        selectionStart = textCursor;
+        selectionEnd = textCursor;
+        te.verarbeitet = true;
+        lineCount = 0;
+        content->lineCount(true, true, 0, lineCount);
+    }
+    time = 0;
+    rend = 1;
+}
+
+bool Framework::JSON::JsonEditor::tick(double tickVal)
+{
+    LOCK(&cs);
+    time += tickVal;
+    timeSicePress += tickVal;
+    if (time > 1.0)
+    {
+        tps = renderings / time;
+        time = 0.0;
+        renderings = 0;
+    }
+    rend |= drawCursor != (time < 0.5);
+    drawCursor = time < 0.5;
+    errorDescription->tick(tickVal);
+    return ZeichnungHintergrund::tick(tickVal);
+}
+
+void Framework::JSON::JsonEditor::render(Bild& rObj)
+{
+    LOCK(&cs);
+    renderings++;
+    renderedLines = 0;
+    errorDescription->removeStyle(TextFeld::Style::Sichtbar);
+    ZeichnungHintergrund::render(rObj);
+    if (textRenderer->zSchrift() && content
+        && rObj.setDrawOptions(
+            pos + Punkt(getRahmenBreite(), getRahmenBreite()),
+            Punkt(getInnenBreite(), getInnenHeight())))
+    {
+        if (!renderStart.line)
+        {
+            renderStart = {content, 0};
+            renderStartLine = 1;
+        }
+        int y = 0;
+        int lineNumberWidth = 0;
+        for (int i = lineCount; i > 0; i /= 10)
+        {
+            lineNumberWidth++;
+        }
+        lineNumberWidth
+            = lineNumberWidth * textRenderer->getMaxCharWidth()
+            + textRenderer->getZeichenAbstand() * (lineNumberWidth - 1);
+        bool selected = false;
+        EditableJsonElement* element = content;
+        while (element)
+        {
+            if (element == selectionStart.line)
+            {
+                if (element != renderStart.line)
+                {
+                    selected = true;
+                }
+                else
+                {
+                    if (selectionStart.column < renderStart.column)
+                    {
+                        selected = true;
+                    }
+                }
+            }
+            if (element == selectionEnd.line)
+            {
+                if (element != renderStart.line)
+                {
+                    selected = false;
+                    break;
+                }
+                else
+                {
+                    if (selectionEnd.column < renderStart.column)
+                    {
+                        selected = false;
+                    }
+                }
+            }
+            if (element == renderStart.line)
+            {
+                break;
+            }
+            element = element->zAfter(false, 0);
+        }
+        int line = renderStartLine;
+        int x = lineNumberWidth + 26;
+        EditableJsonElement* current = renderStart.line;
+        EditableJsonElement* highlightStop = 0;
+        int index = renderStart.column;
+        bool treeBranchRendered = false;
+        bool highlighted = false;
+        int numberWidth = textRenderer->getTextBreite(Text(line));
+        textRenderer->renderText(
+            5 + lineNumberWidth - numberWidth, y, Text(line), rObj, 0xFF707070);
+        bool whiteSpaceOnly = true;
+        while (y < gr.y && current)
+        {
+            const Text& content = current->getContent();
+            int column = index;
+            for (int i = index; i < content.getLength() && y < gr.y;
+                 i++, column++)
+            {
+                if (selectionStart.line == current
+                    && selectionStart.column == i)
+                {
+                    selected = true;
+                }
+                if (selectionEnd.line == current && selectionEnd.column == i)
+                {
+                    selected = false;
+                }
+                int color = current->getColor(i);
+                int cursorX = x - 1;
+                if (textCursor.line == current && textCursor.column == i
+                    && drawCursor)
+                {
+                    rObj.drawLinieV(
+                        cursorX, y, textRenderer->getSchriftSize(), 0xFFC0C0C0);
+                }
+                if (content[i] == '\n')
+                {
+                    column = -1;
+                    highlighted = false;
+                    if (!treeBranchRendered)
+                    {
+                        // | in tree view
+                        rObj.drawLinieV(lineNumberWidth + 13,
+                            y
+                                - (line > 1 ? textRenderer->getSchriftSize() / 6
+                                            : 0),
+                            textRenderer->getSchriftSize()
+                                + (line > 1 ? textRenderer->getSchriftSize() / 6
+                                            : 0),
+                            0xFF808080);
+                        if (highlightStop)
+                        {
+                            // hover effect in tree view
+                            rObj.fillRegion(lineNumberWidth + 10,
+                                y - textRenderer->getSchriftSize() / 6,
+                                7,
+                                textRenderer->getSchriftSize()
+                                    + textRenderer->getSchriftSize() / 6,
+                                0xFF505050);
+                        }
+                    }
+                    if (selected)
+                    {
+                        rObj.fillRegion(x,
+                            y,
+                            textRenderer->getTextBreite(" "),
+                            textRenderer->getZeilenHeight()
+                                + textRenderer->getZeilenAbstand(),
+                            0xFF502020);
+                    }
+                    x = lineNumberWidth + 26;
+                    y += textRenderer->getZeilenHeight()
+                       + textRenderer->getZeilenAbstand();
+                    whiteSpaceOnly = true;
+                    line++;
+                    renderedLines++;
+                    treeBranchRendered = false;
+                    int numberWidth = textRenderer->getTextBreite(Text(line));
+                    textRenderer->renderText(5 + lineNumberWidth - numberWidth,
+                        y,
+                        Text(line),
+                        rObj,
+                        0xFF707070);
+                }
+                else
+                {
+                    textRenderer->renderChar(x,
+                        y,
+                        content[i],
+                        rObj,
+                        color,
+                        false,
+                        selected,
+                        0xFF502020);
+                }
+                if (cursorPos.x > cursorX && cursorPos.x <= x
+                    && cursorPos.y >= y
+                    && cursorPos.y <= y + textRenderer->getZeilenHeight())
+                {
+                    if (current->hasError(i))
+                    {
+                        const SyntaxError& error = current->getError(i);
+                        errorDescription->setText(errors[error.getErrorType()]);
+                        errorDescription->setSize(
+                            errorDescription->getNeededWidth() + 6,
+                            errorDescription->getNeededHeight() + 6);
+                        errorDescription->setPosition(
+                            cursorPos.x, cursorPos.y + 20);
+                        if (errorDescription->getX()
+                                + errorDescription->getBreite()
+                            > getInnenBreite())
+                        {
+                            errorDescription->setX(
+                                getInnenBreite() - errorDescription->getBreite()
+                                - 5);
+                        }
+                        if (errorDescription->getY()
+                                + errorDescription->getHeight()
+                            > getInnenHeight())
+                        {
+                            errorDescription->setY(
+                                getInnenHeight() - errorDescription->getHeight()
+                                - 5);
+                        }
+                        errorDescription->addStyle(TextFeld::Style::Sichtbar);
+                    }
+                }
+                if (column % 4 == 0 && content[i] == ' ' && whiteSpaceOnly)
+                {
+                    rObj.drawLinieV(
+                        cursorX + 1 + textRenderer->getTextBreite(" ") / 2,
+                        y + 2,
+                        textRenderer->getSchriftSize() - 4,
+                        0xFF353535);
+                }
+                whiteSpaceOnly &= content[i] == ' ' || content[i] == '\n';
+            }
+            if (selectionStart.line == current
+                && selectionStart.column == content.getLength())
+            {
+                selected = true;
+            }
+            if (selectionEnd.line == current
+                && selectionEnd.column == content.getLength())
+            {
+                selected = false;
+            }
+            if (textCursor.line == current
+                && textCursor.column == content.getLength() && drawCursor)
+            {
+                rObj.drawLinieV(
+                    x - 1, y, textRenderer->getSchriftSize(), 0xFFC0C0C0);
+            }
+            if (current->zFirstChildren())
+            {
+                if (pressed && pressedPos.x >= lineNumberWidth + 10
+                    && pressedPos.x <= lineNumberWidth + 16
+                    && pressedPos.y
+                           >= y + textRenderer->getSchriftSize() / 2 - 4
+                    && pressedPos.y
+                           <= y + textRenderer->getSchriftSize() / 2 + 2)
+                {
+                    pressed = false;
+                    current->setHidden(!current->isHidden());
+                }
+                if (cursorPos.x >= lineNumberWidth + 10
+                    && cursorPos.x <= lineNumberWidth + 16
+                    && cursorPos.y >= y + textRenderer->getSchriftSize() / 2 - 4
+                    && cursorPos.y <= y + textRenderer->getSchriftSize() / 2 + 2
+                    && !current->isHidden())
+                {
+                    highlightStop = current->zLastChildren();
+                }
+                if (current->isHidden())
+                {
+                    if (!treeBranchRendered)
+                    {
+                        rObj.fillRegion(lineNumberWidth + 10,
+                            y + textRenderer->getSchriftSize() / 2 - 4,
+                            6,
+                            6,
+                            0xFF000000);
+                        rObj.drawLinie(
+                            Punkt(lineNumberWidth + 10,
+                                y + textRenderer->getSchriftSize() / 2 - 4),
+                            Punkt(lineNumberWidth + 16,
+                                y + textRenderer->getSchriftSize() / 2 - 1),
+                            0xFF808080);
+                        rObj.drawLinie(
+                            Punkt(lineNumberWidth + 10,
+                                y + textRenderer->getSchriftSize() / 2 + 2),
+                            Punkt(lineNumberWidth + 16,
+                                y + textRenderer->getSchriftSize() / 2 - 1),
+                            0xFF808080);
+                    }
+                    for (char c : " ... ")
+                    {
+                        textRenderer->renderChar(x,
+                            y,
+                            c,
+                            rObj,
+                            0xFF505050,
+                            false,
+                            selected,
+                            0xFF502020);
+                    }
+                    if (current->getOpeningControllChar() == '{')
+                    {
+                        textRenderer->renderChar(x,
+                            y,
+                            '}',
+                            rObj,
+                            0xFF505050,
+                            false,
+                            selected,
+                            0xFF502020);
+                    }
+                    else if (current->getOpeningControllChar() == '[')
+                    {
+                        textRenderer->renderChar(x,
+                            y,
+                            ']',
+                            rObj,
+                            0xFF505050,
+                            false,
+                            selected,
+                            0xFF502020);
+                    }
+                }
+                else
+                {
+                    if (!treeBranchRendered)
+                    {
+                        rObj.fillRegion(lineNumberWidth + 10,
+                            y + textRenderer->getSchriftSize() / 2 - 4,
+                            6,
+                            6,
+                            0xFF000000);
+                        rObj.drawLinie(
+                            Punkt(lineNumberWidth + 10,
+                                y + textRenderer->getSchriftSize() / 2 - 4),
+                            Punkt(lineNumberWidth + 13,
+                                y + textRenderer->getSchriftSize() / 2 + 2),
+                            0xFF808080);
+                        rObj.drawLinie(
+                            Punkt(lineNumberWidth + 16,
+                                y + textRenderer->getSchriftSize() / 2 - 4),
+                            Punkt(lineNumberWidth + 13,
+                                y + textRenderer->getSchriftSize() / 2 + 2),
+                            0xFF808080);
+                    }
+                }
+                treeBranchRendered = true;
+            }
+            if (!current->zMextSibling() && current->zParent())
+            {
+                // |_ in tree view
+                rObj.drawLinieH(lineNumberWidth + 14,
+                    y + textRenderer->getSchriftSize() - 1,
+                    3,
+                    0xFF808080);
+            }
+            rObj.drawLinieV(x,
+                y,
+                textRenderer->getSchriftSize(),
+                0xFF30C030); // TODO: debug tree seperator
+            if (current == highlightStop)
+            {
+                highlightStop = 0;
+                if (!treeBranchRendered)
+                {
+                    // hover effect in tree view
+                    rObj.fillRegion(lineNumberWidth + 10,
+                        y - textRenderer->getSchriftSize() / 6,
+                        7,
+                        textRenderer->getSchriftSize()
+                            + textRenderer->getSchriftSize() / 6,
+                        0xFF505050);
+                    highlighted = true;
+                }
+            }
+            current = current->zAfter(true, &line);
+            index = 0;
+        }
+        if (!treeBranchRendered && !highlighted)
+        {
+            // | in tree view
+            rObj.drawLinieV(lineNumberWidth + 13,
+                y - (line > 1 ? textRenderer->getSchriftSize() / 6 : 0),
+                textRenderer->getSchriftSize()
+                    + (line > 1 ? textRenderer->getSchriftSize() / 6 : 0),
+                0xFF808080);
+        }
+        EditableJsonElement* resnderStopElement = current;
+        renderStopLine = line;
+        textRenderer->renderText(5,
+            y + textRenderer->getZeilenHeight()
+                + textRenderer->getZeilenAbstand(),
+            Text("FPS: ") + Text(tps),
+            rObj,
+            0xFFFFFFFF);
+        bool visible = false;
+        double sy = 0.0;
+        double yOffset = (double)getInnenHeight() / lineCount;
+        current = content;
+        EditableJsonElement* hiddenStop = 0;
+        while (current)
+        {
+            if (hiddenStop == current)
+            {
+                hiddenStop = 0;
+            }
+            if (current == renderStart.line)
+            {
+                visible = true;
+            }
+            if (current == resnderStopElement)
+            {
+                visible = false;
+            }
+            int f = 0xFF303030;
+            if (visible)
+            {
+                f = 0xFFA0A0A0;
+            }
+            if (hiddenStop)
+            {
+                f = 0xFF505050;
+            }
+            if (current->hasError())
+            {
+                f = 0xFFFF0000;
+            }
+            double size = current->lineCount() * yOffset;
+            if ((int)(sy + size) > (int)sy || current->hasError())
+            {
+                rObj.fillRegion(getInnenBreite() - 15,
+                    (int)sy,
+                    15,
+                    (int)(sy + MAX(yOffset, 1)),
+                    f);
+            }
+            if (current->isHidden())
+            {
+                if (!hiddenStop)
+                {
+                    hiddenStop = current->zAfter(true, 0);
+                }
+            }
+            current = current->zAfter(false, 0);
+            sy += size;
+        }
+        errorDescription->render(rObj);
+        rObj.releaseDrawOptions();
+    }
+}
+
+Text Framework::JSON::JsonEditor::getContent()
+{
+    return content ? content->getRecursiveContent() : Text("");
+}
+
+Text Framework::JSON::JsonEditor::getSelectedContent()
+{
+    Text result = "";
+    if (selectionStart.line && selectionEnd.line)
+    {
+        if (selectionStart.line == selectionEnd.line)
+        {
+            result.append(selectionStart.line->getContent().getTeilText(
+                selectionStart.column, selectionEnd.column));
+        }
+        else
+        {
+            result.append(selectionStart.line->getContent().getTeilText(
+                selectionStart.column,
+                selectionStart.line->getContent().getLength()));
+            Stack<EditableJsonElement*> stack(0);
+            stack.push(selectionStart.line);
+            while (stack.getSize())
+            {
+                EditableJsonElement* current = stack.pop();
+                if (current->zMextSibling())
+                {
+                    stack.push(current->zMextSibling());
+                }
+                else if (stack.getSize() == 0 && current->zParent())
+                {
+                    EditableJsonElement* parent = current->zParent();
+                    while (parent)
+                    {
+                        if (parent->zMextSibling())
+                        {
+                            stack.push(current->zParent()->zMextSibling());
+                            break;
+                        }
+                        parent = parent->zParent();
+                    }
+                }
+                if (current->zFirstChildren())
+                {
+                    stack.push(current->zFirstChildren());
+                }
+                if (current == selectionEnd.line)
+                {
+                    break;
+                }
+                if (current != selectionStart.line)
+                {
+                    result += current->getContent();
+                }
+            }
+            result.append(selectionEnd.line->getContent().getTeilText(
+                0, selectionEnd.column));
+        }
+    }
+    return result;
+}
+
+JSONValue* Framework::JSON::JsonEditor::getValidContent()
+{
+    return nullptr; // TODO
+}
+
+void Framework::JSON::JsonEditor::scrollToLine(int line, ScrollTargetPos pos)
+{
+    if (pos == Top || pos == Bottom)
+    {
+        if (line >= renderStartLine && line < renderStopLine)
+        {
+            return; // line is already visible
+        }
+    }
+    EditableJsonElement* element = content;
+    if (line == 1)
+    {
+        scrollToLine(line, {content, 0}, pos);
+        return;
+    }
+    int l = 1;
+    int column = 0;
+    while (element)
+    {
+        int lineCount = element->lineCount();
+        if (lineCount + l >= line)
+        {
+            column = element->getContent().positionVon('\n', line - l - 1) + 1;
+            break;
+        }
+        l += lineCount;
+        EditableJsonElement* tmp = element->zAfter(true, &l);
+        if (l >= line)
+        {
+            column = element->getContent().positionVon('\n', lineCount - 1) + 1;
+            break;
+        }
+        element = tmp;
+    }
+    EditorPosition target = {element, column};
+    unifyPosition(target);
+    scrollToLine(line, target, pos);
+}
+
+void Framework::JSON::JsonEditor::scrollToLine(
+    EditorPosition target, ScrollTargetPos pos)
+{
+    if (!target.line || !target.line->isVisible())
+    {
+        return;
+    }
+    int lineNumber = 0;
+    if (!content->lineCount(true, true, target.line, lineNumber))
+    {
+        Text* part = target.line->getContent().getTeilText(0, target.column);
+        lineNumber += part->anzahlVon('\n');
+        part->release();
+        if (pos == Top || pos == Bottom)
+        {
+            if (lineNumber >= renderStartLine && lineNumber < renderStopLine)
+            {
+                return; // line is already visible
+            }
+        }
+        scrollToLine(lineNumber, target, pos);
+    }
+}
+
+void Framework::JSON::JsonEditor::scrollToLine(
+    int lineNum, EditorPosition target, ScrollTargetPos pos)
+{
+    if (!target.line || !target.line->isVisible())
+    {
+        return;
+    }
+    if (pos == Top || pos == Bottom)
+    {
+        if (lineNum >= renderStartLine && lineNum < renderStopLine)
+        {
+            return; // line is already visible
+        }
+    }
+    if (pos == Top)
+    {
+        renderStart = target;
+        const char* content = renderStart.line->getContent();
+        while (
+            renderStart.column > 0 && content[renderStart.column - 1] != '\n')
+        {
+            renderStart.column--;
+        }
+        renderStartLine = lineNum;
+    }
+    else
+    {
+        int linesUntilStart
+            = pos == Bottom ? renderedLines - 1 : renderedLines / 2 - 1;
+        if (linesUntilStart >= lineNum)
+        {
+            renderStart = {content, 0};
+            renderStartLine = 1;
+            return;
+        }
+        EditableJsonElement* element = target.line;
+        EditableJsonElement* before = element;
+        while (linesUntilStart >= 0)
+        {
+            if (!before)
+            {
+                break;
+            }
+            element = before;
+            int newLines = 0;
+            if (element == target.line)
+            {
+                Text* part
+                    = element->getContent().getTeilText(0, target.column);
+                newLines = part->anzahlVon('\n');
+                part->release();
+            }
+            else
+            {
+                newLines = element->lineCount();
+            }
+            if (newLines > linesUntilStart)
+            {
+                renderStart = {element,
+                    element->getContent().positionVon(
+                        '\n', newLines - linesUntilStart - 1)
+                        + 1};
+                unifyPosition(renderStart);
+                renderStartLine = lineNum - linesUntilStart;
+                return;
+            }
+            lineNum -= newLines;
+            linesUntilStart -= newLines;
+            before = element->zBefore(this->content, true, &lineNum);
+        }
+        renderStart = {element, 0};
+        renderStartLine = lineNum;
+    }
+}

+ 265 - 0
JsonEditor.h

@@ -0,0 +1,265 @@
+#pragma once
+
+#include "DataValidator.h"
+#include "JSON.h"
+#include "Regex.h"
+#include "Stack.h"
+#include "Text.h"
+#include "UIInitialization.h"
+#include "Zeichnung.h"
+
+namespace Framework
+{
+    class TextRenderer;
+    class TextFeld;
+
+    namespace JSON
+    {
+        enum JsonStructure
+        {
+            OBJECT,
+            ARRAY,
+            VALUE
+        };
+
+        enum SyntaxErrorType
+        {
+            UNEXPECTED_OBJECT_START,
+            UNEXPECTED_ARRAY_START,
+            UNEXPECTED_VALUE,
+            UNEXPECTED_COMMA,
+            UNEXPECTED_COLON,
+            UNEXPECTED_END_OF_OBJECT,
+            UNEXPECTED_END_OF_ARRAY,
+            UNEXPECTED_START_OF_STRING,
+            INVALID_VALUE,
+            OTHER
+        };
+
+        struct ParserState;
+
+        class SyntaxError
+        {
+        private:
+            int start;
+            int end;
+            SyntaxErrorType errorType;
+
+        public:
+            DLLEXPORT SyntaxError(
+                int start, int end, SyntaxErrorType errorType);
+            DLLEXPORT SyntaxError();
+
+            DLLEXPORT int getStart() const;
+            DLLEXPORT int getEnd() const;
+            DLLEXPORT SyntaxErrorType getErrorType() const;
+
+            friend ParserState;
+        };
+
+        struct ParserState
+        {
+            int index;
+            int keyStart;
+            int keyEnd;
+            int valueStart;
+            int valueEnd;
+            int errorStart;
+            JsonStructure parent;
+            bool inString;
+            bool escaped;
+            bool colon;
+            bool key;
+            bool value;
+            bool inValue;
+            bool valueValidationNeeded;
+            char openingControllChar;
+            char closingControllChar;
+            bool valueExpected;
+
+        public:
+            ParserState(JsonStructure parent, bool valueExpected);
+
+            bool next(
+                const char* current, const char* next, SyntaxError& error);
+        };
+
+        class EditableJsonElement : public ReferenceCounter
+        {
+        private:
+            EditableJsonElement* children;
+            EditableJsonElement* siblings;
+            EditableJsonElement* lastChild;
+            EditableJsonElement* parent;
+            Array<SyntaxError> errors;
+            Text content;
+            Text key;
+            Text value;
+            int valueStart;
+            int keyStart;
+            bool parsed;
+            char openingControllChar;
+            char closingControllChar;
+            JsonStructure parentStructure;
+            Regex::Automata<char>* valueValidator;
+            bool hidden;
+            int newLineCount;
+
+        public:
+            DLLEXPORT EditableJsonElement(
+                Regex::Automata<char>* valueValidator);
+            DLLEXPORT EditableJsonElement(
+                Regex::Automata<char>* valueValidator, const char* content);
+            DLLEXPORT ~EditableJsonElement();
+
+        private:
+            DLLEXPORT void setContentOnly(const char** content,
+                int& startPos,
+                Array<EditableJsonElement*>& afterList,
+                Array<EditableJsonElement*>& deleteList);
+            DLLEXPORT void setContentRecursive(const char** content,
+                int& startPos,
+                Array<EditableJsonElement*>& afterList,
+                Array<EditableJsonElement*>& deleteList);
+            DLLEXPORT void format(int indent, bool insertNewLine);
+            DLLEXPORT void formatRecursive(int indent, bool insertNewLine);
+            DLLEXPORT bool isValueValid(
+                const char* content, int start, int end);
+            DLLEXPORT void setContent(const char* content,
+                int& startPos,
+                Array<EditableJsonElement*>& afterList);
+            DLLEXPORT void checkSyntax();
+
+        public:
+            DLLEXPORT void setContent(const char* content);
+            DLLEXPORT void addChildren(EditableJsonElement* content);
+            DLLEXPORT void setNextSibling(EditableJsonElement* content);
+            DLLEXPORT void addSibling(EditableJsonElement* content);
+            DLLEXPORT void format();
+            DLLEXPORT const Text& getContent();
+            DLLEXPORT Text getRecursiveContent();
+            DLLEXPORT int lineCount();
+            DLLEXPORT bool lineCount(bool includeChildren,
+                bool includeSiblings,
+                EditableJsonElement* stop,
+                int& count);
+            DLLEXPORT bool isHidden();
+            DLLEXPORT void setHidden(bool hidden);
+            DLLEXPORT EditableJsonElement* zParent();
+            DLLEXPORT EditableJsonElement* zMextSibling();
+            DLLEXPORT EditableJsonElement* zFirstChildren();
+            DLLEXPORT EditableJsonElement* zLastChildren();
+            DLLEXPORT char getOpeningControllChar();
+            DLLEXPORT char getClosingControllChar();
+            DLLEXPORT Text& getKey();
+            DLLEXPORT Text& getValue();
+            DLLEXPORT void disconnect();
+            DLLEXPORT void removeChild(EditableJsonElement* zElement);
+            DLLEXPORT void getWordBounds(int pos, int* left, int* right);
+            DLLEXPORT bool hasError(int column) const;
+            DLLEXPORT SyntaxError getError(int column) const;
+            DLLEXPORT bool hasError() const;
+            DLLEXPORT void makeVisible();
+            DLLEXPORT bool isVisible() const;
+            DLLEXPORT int getIndent() const;
+            DLLEXPORT int getColor(int index);
+            DLLEXPORT void removeUntil(
+                int startIndex, EditableJsonElement* end, int endIndex);
+            DLLEXPORT EditableJsonElement* zBefore(
+                EditableJsonElement* zRoot, bool onlyVisible, int* lineCount);
+            DLLEXPORT EditableJsonElement* zAfter(
+                bool onlyVisible, int* lineCount);
+        };
+
+        struct EditorPosition
+        {
+            EditableJsonElement* line;
+            int column;
+        };
+
+        enum ScrollTargetPos
+        {
+            Top,
+            Center,
+            Bottom
+        };
+
+        class JsonEditor : public ZeichnungHintergrund
+        {
+        private:
+            Regex::Automata<char>* valueValidator;
+            Validator::DataValidator* validator;
+            EditableJsonElement* content;
+            EditorPosition renderStart;
+            int renderStartOffset;
+            EditorPosition textCursor;
+            EditorPosition dragStartPos;
+            EditorPosition selectionStart;
+            EditorPosition selectionEnd;
+            EditorPosition lastClickCursorPos;
+            TextRenderer* textRenderer;
+            TextFeld* errorDescription;
+            int renderStartLine;
+            int renderStopLine;
+            int lineCount;
+            Punkt cursorPos;
+            Punkt pressedPos;
+            Punkt dragSart;
+            double timeSicePress;
+            bool pressed;
+            double time;
+            bool drawCursor;
+            bool drag;
+            int renderings;
+            int renderedLines;
+            double tps;
+            bool hasSyntaxError;
+            Critical cs;
+            UIInit uiInit;
+
+        public:
+            DLLEXPORT JsonEditor(UIInit uiInit);
+            DLLEXPORT ~JsonEditor();
+
+        protected:
+            //! Verarbeitet Maus Nachrichten
+            //! \param me Das Ereignis, was durch die Mauseingabe ausgelößt
+            //! wurde
+            DLLEXPORT void doMausEreignis(
+                MausEreignis& me, bool userRet) override;
+            DLLEXPORT EditorPosition getScreenPosition(int localX, int localY);
+            DLLEXPORT void deleteSelection();
+            DLLEXPORT void unifyPosition(EditorPosition& pos);
+            DLLEXPORT void fixTree(EditableJsonElement* zElement);
+
+        public:
+            DLLEXPORT void setFont(Schrift* schrift);
+            DLLEXPORT void setFontSize(int size);
+            DLLEXPORT void setContent(Text content);
+            DLLEXPORT void setContent(JSONValue* content);
+            DLLEXPORT void format();
+            DLLEXPORT void setValidator(Validator::DataValidator* validator);
+
+            //! Verarbeitet ein Tastatur Ereignis. Wird vom Framework
+            //! automatisch aufgerufen \param te Das Ereignis
+            DLLEXPORT void doTastaturEreignis(TastaturEreignis& te) override;
+
+            //! Updated den Zeichenhintergrund
+            //! \param tickVal Die vergangene Zeit in Sekunden, die seit dem
+            //! Letzten Aufruf dieser Funktion verstrichen ist \return 1, wenn
+            //! das Bild neu gezeichnet werden muss. 0 sonnst
+            DLLEXPORT bool tick(double tickVal) override;
+            //! Zeichnet den Hintergrund eines Zeichnunges nach rObj
+            DLLEXPORT void render(Bild& rObj) override;
+
+            DLLEXPORT Text getContent();
+            DLLEXPORT Text getSelectedContent();
+            DLLEXPORT JSONValue* getValidContent();
+            DLLEXPORT void scrollToLine(int line, ScrollTargetPos pos);
+            DLLEXPORT void scrollToLine(
+                EditorPosition line, ScrollTargetPos pos);
+            DLLEXPORT void scrollToLine(
+                int lineNum, EditorPosition target, ScrollTargetPos pos);
+        };
+    } // namespace JSON
+} // namespace Framework

+ 19 - 13
Schrift.cpp

@@ -689,12 +689,6 @@ int TextRenderer::getSchriftSize() const
     return schriftSize;
 }
 
-// Gibt den Abstand in Pixeln zum unteren Ende der darüber ligenden Zeile zurück
-int TextRenderer::getZeilenabstand() const
-{
-    return zeilenAbstand;
-}
-
 // Gibt den Abstand in Pixeln zum zwischen zwei zeichen auf der x Achse zurück
 int TextRenderer::getZeichenAbstand() const
 {
@@ -716,10 +710,6 @@ int TextRenderer::getTextBreite(const char* txt) const
             if (tmp > ret) ret = tmp;
             tmp = 0;
         }
-        else if (txt[i] == '\t')
-            tmp += schriftSize + zeichenAbstand;
-        else if (txt[i] == ' ')
-            tmp += schriftSize / 2 + zeichenAbstand;
         else
             tmp += getCharWidth(txt[i]) + zeichenAbstand;
     }
@@ -741,7 +731,12 @@ int TextRenderer::getTextHeight(const char* txt) const
 //  c: Der Buchstabe, von dem die Breite in Pixeln ermitelt werden soll
 int TextRenderer::getCharWidth(const char c) const
 {
-    return charWidths[(unsigned char)c];
+    if (c == '\t')
+        return schriftSize;
+    else if (c == ' ')
+        return schriftSize / 2;
+    else
+        return charWidths[(unsigned char)c];
 }
 
 int Framework::TextRenderer::getMaxCharWidth() const
@@ -999,7 +994,12 @@ void GravurTextRenderer::renderChar(int& x,
 //  c: Der Buchstabe, von dem die Breite in Pixeln ermitelt werden soll
 int GravurTextRenderer::getCharWidth(const char c) const
 {
-    return TextRenderer::getCharWidth(c) * 2;
+    if (c == '\t')
+        return schriftSize;
+    else if (c == ' ')
+        return schriftSize / 2;
+    else
+        return TextRenderer::getCharWidth(c) * 2;
 }
 
 // Ermittelt, wie viele Pixel benötigt werden, um einen Bestimmten Text
@@ -1172,5 +1172,11 @@ void KursivTextRenderer::renderChar(int& x,
 //  c: Der Buchstabe, von dem die Breite in Pixeln ermitelt werden soll
 int KursivTextRenderer::getCharWidth(const char c) const
 {
-    return (int)(TextRenderer::getCharWidth(c) + getCharHeight(c) / 4.0 + 0.5);
+    if (c == '\t')
+        return schriftSize;
+    else if (c == ' ')
+        return schriftSize / 2;
+    else
+        return (
+            int)(TextRenderer::getCharWidth(c) + getCharHeight(c) / 4.0 + 0.5);
 }

+ 0 - 3
Schrift.h

@@ -271,9 +271,6 @@ namespace Framework
             int selectedBackgroundColor = 0);
         //! Gibt die Schriftgröße zurück, die zum Zeichnen verwendet wird
         DLLEXPORT int getSchriftSize() const;
-        //! Gibt den Abstand in Pixeln zum unteren Ende der darüber ligenden
-        //! Zeile zurück
-        DLLEXPORT int getZeilenabstand() const;
         //! Ermittelt, wie viele Pixel benötigt werden, um einen Bestimmten Text
         //! vollständig darzustellen \param txt Der Text, von dem die Breite in
         //! Pixeln ermitelt werden soll

+ 104 - 33
Text.cpp

@@ -38,9 +38,16 @@ FlushingOStream::~FlushingOStream()
 }
 
 Text::Text(char* t, int l)
-    : Text()
+    : ReferenceCounter(),
+      txt(0),
+      length(0),
+      suchGBeg(0),
+      suchGEnd(0),
+      precision(-1),
+      stringWriter(0)
 {
-    setTextZ(t, l); // Text setzen
+    length = l;
+    txt = t; // neuen Text erstellen
 }
 
 // inhalt der Text Klasse aus Text.h
@@ -81,6 +88,21 @@ Text::Text(const char* t)
     setText(t); // Text setzen
 }
 
+Framework::Text::Text(const char* txt, int offset, int length)
+    : ReferenceCounter(),
+      txt(0),
+      length(0),
+      suchGBeg(0),
+      suchGEnd(0),
+      precision(-1),
+      stringWriter(0)
+{
+    this->length = length;
+    this->txt = new char[length + 1];
+    memcpy(this->txt, txt + offset, length);
+    this->txt[length] = 0;
+}
+
 Text::Text(int zahl)
     : ReferenceCounter(),
       txt(0),
@@ -218,34 +240,64 @@ void Text::append(const char* t) // h
     setTextZ(res, length + tl); // Test setzen
 }
 
+void Text::appendHex(char num) // hängt die zahl in hex anden Text an
+{
+    char* res = new char[(__int64)length + sizeof(char) * 2 + 1];
+    for (int i = 0; i < length; ++i)
+        res[i] = txt[i];
+    std::stringstream stream;
+    stream << std::setfill('0') << std::setw((int)sizeof(char) * 2) << std::hex
+           << (int)num;
+    std::string str = stream.str();
+    for (int i = length; i < length + sizeof(char) * 2; ++i)
+        res[i] = str.c_str()[i - length];
+    res[length + sizeof(char) * 2] = 0;
+    setTextZ(res, length + sizeof(char) * 2);
+}
+
+void Framework::Text::appendHex(short num)
+{
+    char* res = new char[(__int64)length + sizeof(short) * 2 + 1];
+    for (int i = 0; i < length; ++i)
+        res[i] = txt[i];
+    std::stringstream stream;
+    stream << std::setfill('0') << std::setw((int)sizeof(short) * 2) << std::hex
+           << num;
+    std::string str = stream.str();
+    for (int i = length; i < length + sizeof(short) * 2; ++i)
+        res[i] = str.c_str()[i - length];
+    res[length + sizeof(short) * 2] = 0;
+    setTextZ(res, length + sizeof(short) * 2);
+}
+
 void Text::appendHex(int num) // hängt die zahl in hex anden Text an
 {
-    char* res = new char[(__int64)length + 9];
+    char* res = new char[(__int64)length + sizeof(int) * 2 + 1];
     for (int i = 0; i < length; ++i)
         res[i] = txt[i];
     std::stringstream stream;
     stream << std::setfill('0') << std::setw((int)sizeof(int) * 2) << std::hex
            << num;
     std::string str = stream.str();
-    for (int i = length; i < length + 8; ++i)
+    for (int i = length; i < length + sizeof(int) * 2; ++i)
         res[i] = str.c_str()[i - length];
-    res[length + 8] = 0;
-    setTextZ(res, length + 8);
+    res[length + sizeof(int) * 2] = 0;
+    setTextZ(res, length + sizeof(int) * 2);
 }
 
 void Text::appendHex(__int64 num) // hängt die zahl in hex anden Text an
 {
-    char* res = new char[(__int64)length + 17];
+    char* res = new char[(__int64)length + sizeof(__int64) * 2 + 1];
     for (int i = 0; i < length; ++i)
         res[i] = txt[i];
     std::stringstream stream;
     stream << std::setfill('0') << std::setw((int)sizeof(__int64) * 2)
            << std::hex << num;
     std::string str = stream.str();
-    for (int i = length; i < length + 16; ++i)
+    for (int i = length; i < length + sizeof(__int64) * 2; ++i)
         res[i] = str.c_str()[i - length];
-    res[length + 16] = 0;
-    setTextZ(res, length + 16);
+    res[length + sizeof(__int64) * 2] = 0;
+    setTextZ(res, length + sizeof(__int64) * 2);
 }
 
 void Text::append(const char* t, int l)            // hängt an den Text an
@@ -479,13 +531,23 @@ void Text::ersetzen(char c1, char c2) // ersetzt jedes c1 durch c2
     if (!hat(c1)) // prüfen ob c1 vorhanden
         return;
     int suchGCount = 0;
-    for (int i = 0; i < length; ++i) // Text durchsuchen
+    if (suchGBeg && suchGEnd)
     {
-        bool b = suchGCount != 0;
-        if (txt[i] == c1 && !suchGCount) txt[i] = c2; // Text ersetzen
-        if (txt[i] == suchGBeg) ++suchGCount;
-        if (txt[i] == suchGEnd) --suchGCount;
-        if (txt[i] == c1 && !suchGCount && b) txt[i] = c2; // Text ersetzen
+        for (int i = 0; i < length; ++i) // Text durchsuchen
+        {
+            bool b = suchGCount != 0;
+            if (txt[i] == c1 && !suchGCount) txt[i] = c2; // Text ersetzen
+            if (txt[i] == suchGBeg) ++suchGCount;
+            if (txt[i] == suchGEnd) --suchGCount;
+            if (txt[i] == c1 && !suchGCount && b) txt[i] = c2; // Text ersetzen
+        }
+    }
+    else
+    {
+        for (int i = 0; i < length; ++i) // Text durchsuchen
+        {
+            if (txt[i] == c1) txt[i] = c2; // Text ersetzen
+        }
     }
 }
 
@@ -650,10 +712,8 @@ void Text::remove(int p1, int p2) // l
     if (p2 > length) p2 = length;
     int resl = length - (p2 - p1);           // Länge vom Ergebnis
     char* res = new char[(__int64)resl + 1]; // Neuen Text erstellen
-    for (int i = 0; i < p1; ++i)             // Text füllen
-        res[i] = txt[i];
-    for (int i = p2; i < length; ++i)
-        res[i - (p2 - p1)] = txt[i];
+    memcpy(res, txt, p1);
+    memcpy(res + p1, txt + p2, length - p2);
     res[resl] = '\0';    // Testende festlegen
     setTextZ(res, resl); // Text setzen
 }
@@ -771,23 +831,23 @@ void Text::remove(int i, Text* t)
 
 // Löscht alle ' ', '\n', '\r', '\t' bis zu einem anderen buchstaben
 //  pos: Die Position des ersten zeichens
-void Text::removeWhitespaceAfter(int pos)
+int Text::removeWhitespaceAfter(int pos)
 {
-    int length = 0;
-    int len = textLength(txt);
-    for (int i = pos; i < len; i++)
+    int removedLength = 0;
+    for (int i = pos; i < length; i++)
     {
         if (txt[i] == ' ' || txt[i] == '\n' || txt[i] == '\r' || txt[i] == '\t')
-            length++;
+            removedLength++;
         else
             break;
     }
-    remove(pos, pos + length);
+    remove(pos, pos + removedLength);
+    return length;
 }
 
 // Löscht alle ' ', '\n', '\r', '\t' bis zu einem anderen buchstaben
 //  pos: Die Position des ersten zeichens (beginnt bei pos-1)
-void Text::removeWhitespaceBefore(int pos)
+int Text::removeWhitespaceBefore(int pos)
 {
     int length = 0;
     for (int i = pos - 1; i >= 0; i--)
@@ -798,6 +858,7 @@ void Text::removeWhitespaceBefore(int pos)
             break;
     }
     remove(pos - length, pos);
+    return length;
 }
 
 void Text::setPrecision(
@@ -1005,13 +1066,23 @@ int Text::anzahlVon(char c) const // gibt die Anzahl von c im Text zur
 {
     int ret = 0;
     int suchGCount = 0;
-    for (int i = 0; i < length; ++i) // suchen
+    if (suchGBeg && suchGEnd)
     {
-        bool b = suchGCount != 0;
-        ret += txt[i] == c && !suchGCount; // zählen
-        if (txt[i] == suchGBeg) ++suchGCount;
-        if (txt[i] == suchGEnd) --suchGCount;
-        ret += txt[i] == c && !suchGCount && b; // zählen
+        for (int i = 0; i < length; ++i) // suchen
+        {
+            bool b = suchGCount != 0;
+            ret += txt[i] == c && !suchGCount; // zählen
+            if (txt[i] == suchGBeg) ++suchGCount;
+            if (txt[i] == suchGEnd) --suchGCount;
+            ret += txt[i] == c && !suchGCount && b; // zählen
+        }
+    }
+    else
+    {
+        for (int i = 0; i < length; ++i) // suchen
+        {
+            ret += txt[i] == c; // zählen
+        }
     }
     return ret;
 }

+ 17 - 2
Text.h

@@ -63,6 +63,11 @@ namespace Framework
         //! Erstellt ein neues Text Objekt indem der Wert aus (txt) kopiert wird
         //! \param txt Die Zeichenkette, die kopiert werden soll
         DLLEXPORT Text(const char* txt);
+        //! Erstellt ein neues Text Objekt indem ein Abschnitt aus (txt) kopiert
+        //! wird \param txt Die Zeichenkette, aus der ein Abschnitt die kopiert
+        //! werden soll \param offset Startposition ab der kopiert werden soll
+        //! \param length Anzahl der zeichen die kopiert werden sollen
+        DLLEXPORT Text(const char* txt, int offset, int length);
         //! Erstellt ein neues Text Objekt mit einer zahl als text
         //! \param zahl Die Zahl, die im Text sein soll
         DLLEXPORT Text(int zahl);
@@ -100,6 +105,14 @@ namespace Framework
         //! Hängt die übergebene Zahl als Hex Text (0-F) ans Ende des Textes an.
         //! \param num Die Zahl, die ins Hex System umgewandelt und angehängt
         //! werden soll
+        DLLEXPORT void appendHex(char num);
+        //! Hängt die übergebene Zahl als Hex Text (0-F) ans Ende des Textes an.
+        //! \param num Die Zahl, die ins Hex System umgewandelt und angehängt
+        //! werden soll
+        DLLEXPORT void appendHex(short num);
+        //! Hängt die übergebene Zahl als Hex Text (0-F) ans Ende des Textes an.
+        //! \param num Die Zahl, die ins Hex System umgewandelt und angehängt
+        //! werden soll
         DLLEXPORT void appendHex(int num);
         //! Hängt die übergebene Zahl als Hex Text (0-F) ans Ende des Textes an.
         //! \param num Die Zahl, die ins Hex System umgewandelt und angehängt
@@ -289,10 +302,12 @@ namespace Framework
         DLLEXPORT void remove(int i, Text* t);
         //! Löscht alle ' ', '\n', '\r', '\t' bis zu einem anderen buchstaben
         //! \param pos Die Position des ersten zeichens
-        DLLEXPORT void removeWhitespaceAfter(int pos);
+        //! \return the number of removed characters
+        DLLEXPORT int removeWhitespaceAfter(int pos);
         //! Löscht alle ' ', '\n', '\r', '\t' bis zu einem anderen buchstaben
         //! \param pos Die Position des ersten zeichens (beginnt bei pos-1)
-        DLLEXPORT void removeWhitespaceBefore(int pos);
+        //! \return the number of removed characters
+        DLLEXPORT int removeWhitespaceBefore(int pos);
         //! Setzt die Anzahl der Nachkommastellen beim Anhängen von Kommazahlen
         //! \param p Die Anzahl der Stellen nach dem Komma
         DLLEXPORT void setPrecision(int p);

+ 2 - 2
TextFeld.cpp

@@ -1090,7 +1090,7 @@ void TextFeld::setVScrollZuZeile(int zeile) // scrollt zur Zeile
             TextRenderer* r = tm->zCurrentRenderer();
             if (r)
             {
-                int tmp = r->getZeilenabstand() + r->getZeilenHeight();
+                int tmp = r->getZeilenAbstand() + r->getZeilenHeight();
                 max = max >= tmp ? max : tmp;
             }
             tm->nextStyle();
@@ -1150,7 +1150,7 @@ void TextFeld::updateVScroll(int pos) // scrollt nach unten
                 TextRenderer* r = tm->zCurrentRenderer();
                 if (r)
                 {
-                    int tmp = r->getZeilenabstand() + r->getZeilenHeight();
+                    int tmp = r->getZeilenAbstand() + r->getZeilenHeight();
                     max = max >= tmp ? max : tmp;
                 }
                 tm->nextStyle();

+ 20 - 20
UIPixelShader.h

@@ -352,10 +352,10 @@ ret
 
 const BYTE UIPixelShader[] =
 {
-     68,  88,  66,  67, 166, 192, 
-    171,  78,   5, 235, 141, 100, 
-     26, 220,  25, 102,  80, 175, 
-    147, 144,   1,   0,   0,   0, 
+     68,  88,  66,  67,  42,  37, 
+     20, 172,  14,  22,  27, 160, 
+    186,  83, 233, 249, 146, 175, 
+    144, 111,   1,   0,   0,   0, 
      12, 134,   0,   0,   6,   0, 
       0,   0,  56,   0,   0,   0, 
     140,   6,   0,   0,  28,   7, 
@@ -1805,11 +1805,11 @@ const BYTE UIPixelShader[] =
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0, 148,  46, 
-     49,   1, 255, 116,  62, 102, 
-      1,   0,   0,   0, 165, 135, 
-    182,  17,  14, 206, 174,  70, 
-    143, 212,  56, 137,  67, 177, 
-    118,   3,   0,   0,   0,   0, 
+     49,   1,  62, 236, 210, 102, 
+      1,   0,   0,   0, 180, 128, 
+     48,  47,  97,  52, 110,  66, 
+    181,  39,  52, 116, 189, 247, 
+    208, 183,   0,   0,   0,   0, 
       0,   0,   0,   0,   1,   0, 
       0,   0,   1,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
@@ -1980,10 +1980,10 @@ const BYTE UIPixelShader[] =
       3,   0, 242,  56,   1,   0, 
      43, 236,   3,   0,  28,  19, 
       2,   0,  65,  36,   1,   0, 
-    236, 179,   1,   0,  84,  45, 
+    236, 179,   1,   0,  37,  75, 
       1,   0, 125,  10,   2,   0, 
-    125, 181,   2,   0,  86,  34, 
-      1,   0, 193,  33,   3,   0, 
+    125, 181,   2,   0, 107, 226, 
+      0,   0, 193,  33,   3,   0, 
      65, 185,   2,   0,   9, 241, 
       2,   0, 146, 230,   3,   0, 
     125, 218,   1,   0, 118,  19, 
@@ -3000,8 +3000,8 @@ const BYTE UIPixelShader[] =
      84, 101, 120, 116, 117, 114, 
     101,  50,  68,  32, 115, 104, 
      97, 100,  27, 226,  48,   1, 
-    128,   0,   0,   0, 214, 253, 
-      0,   5,  16, 163, 218,   1, 
+    128,   0,   0,   0, 178,  52, 
+     10,  26, 142, 251, 218,   1, 
       1,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
@@ -4243,14 +4243,14 @@ const BYTE UIPixelShader[] =
       6,  16,   0,   0,  23,   0, 
       1,   0,   5,  16,   0,   0, 
      14,   0,  23,  21,   0,  16, 
-      0,   0,   3,   2,  64,  72, 
+      0,   0,   3,   2, 240,  19, 
       0,   0, 242, 241,  10,   0, 
      24,  21,   8,  16,   0,   0, 
       1,   0,   1,   0,  10,   0, 
      24,  21,   9,  16,   0,   0, 
       1,   0,   0,   2,  14,   0, 
      23,  21,   0,   0,   0,   0, 
-     10,   2,  64,  72,   0,   0, 
+     10,   2, 240,  19,   0,   0, 
     242, 241,  10,   0,  24,  21, 
      11,  16,   0,   0,   1,   0, 
       1,   0,  10,   0,  24,  21, 
@@ -5560,10 +5560,10 @@ const BYTE UIPixelShader[] =
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0, 148,  46,  49,   1, 
-    255, 116,  62, 102,   1,   0, 
-      0,   0, 165, 135, 182,  17, 
-     14, 206, 174,  70, 143, 212, 
-     56, 137,  67, 177, 118,   3, 
+     62, 236, 210, 102,   1,   0, 
+      0,   0, 180, 128,  48,  47, 
+     97,  52, 110,  66, 181,  39, 
+     52, 116, 189, 247, 208, 183, 
     128,   0,   0,   0,  47,  76, 
     105, 110, 107,  73, 110, 102, 
     111,   0,  47, 110,  97, 109, 

+ 15 - 15
UIVertexShader.h

@@ -121,10 +121,10 @@ ret
 
 const BYTE UIVertexShader[] =
 {
-     68,  88,  66,  67, 111, 224, 
-    126, 228,  84,  71,  50,  37, 
-    117, 173, 147, 227, 137,  90, 
-    247,  36,   1,   0,   0,   0, 
+     68,  88,  66,  67,  58, 208, 
+    191, 251,  55, 106, 157, 205, 
+    167,  15, 119, 209,  45,   2, 
+     77, 193,   1,   0,   0,   0, 
     204,  77,   0,   0,   6,   0, 
       0,   0,  56,   0,   0,   0, 
      20,   2,   0,   0, 204,   2, 
@@ -881,10 +881,10 @@ const BYTE UIVertexShader[] =
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0, 148,  46,  49,   1, 
-      0, 117,  62, 102,   1,   0, 
-      0,   0,  58, 153,  53, 232, 
-    174,  14,   3,  76, 172, 146, 
-     83, 136, 141, 245, 182, 108, 
+     62, 236, 210, 102,   1,   0, 
+      0,   0,  63,  36,  51, 114, 
+    180, 221, 121,  77, 177,   1, 
+    140, 193,  14, 136,  69,  48, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   1,   0,   0,   0, 
       1,   0,   0,   0,   0,   0, 
@@ -1564,8 +1564,8 @@ const BYTE UIVertexShader[] =
      13,  10,  47,  47,  32,  84, 
      89,  80,  69,  68,  69,  70, 
      27, 226,  48,   1, 128,   0, 
-      0,   0, 144,   4,  28,   5, 
-     16, 163, 218,   1,   1,   0, 
+      0,   0, 240, 216,  36,  26, 
+    142, 251, 218,   1,   1,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
@@ -3014,11 +3014,11 @@ const BYTE UIVertexShader[] =
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0, 148,  46, 
-     49,   1,   0, 117,  62, 102, 
-      1,   0,   0,   0,  58, 153, 
-     53, 232, 174,  14,   3,  76, 
-    172, 146,  83, 136, 141, 245, 
-    182, 108, 129,   0,   0,   0, 
+     49,   1,  62, 236, 210, 102, 
+      1,   0,   0,   0,  63,  36, 
+     51, 114, 180, 221, 121,  77, 
+    177,   1, 140, 193,  14, 136, 
+     69,  48, 129,   0,   0,   0, 
      47,  76, 105, 110, 107,  73, 
     110, 102, 111,   0,  47, 110, 
      97, 109, 101, 115,   0,  47,