Refactoring JsonNode...

This commit is contained in:
Benoit Blanchon
2014-10-09 12:14:10 +02:00
parent f7fa9e9467
commit b0e12e8852
13 changed files with 250 additions and 208 deletions

View File

@ -20,7 +20,7 @@ void JsonNode::writeTo(JsonWriter& writer)
writer.writeString(content.asString); writer.writeString(content.asString);
break; break;
case JSON_INTEGER: case JSON_LONG:
writer.writeInteger(content.asInteger); writer.writeInteger(content.asInteger);
break; break;
@ -28,10 +28,6 @@ void JsonNode::writeTo(JsonWriter& writer)
writer.writeBoolean(content.asBoolean); writer.writeBoolean(content.asBoolean);
break; break;
case JSON_PROXY:
content.asProxy.target->writeTo(writer);
break;
default: // >= JSON_DOUBLE_0_DECIMALS default: // >= JSON_DOUBLE_0_DECIMALS
writer.writeDouble(content.asDouble, type - JSON_DOUBLE_0_DECIMALS); writer.writeDouble(content.asDouble, type - JSON_DOUBLE_0_DECIMALS);
break; break;

View File

@ -5,14 +5,13 @@ class JsonBuffer;
enum JsonNodeType enum JsonNodeType
{ {
JSON_UNDEFINED, JSON_UNDEFINED,
JSON_PROXY,
JSON_NULL, JSON_NULL,
JSON_ARRAY, JSON_ARRAY,
JSON_OBJECT, JSON_OBJECT,
JSON_KEY, JSON_KEY_VALUE,
JSON_BOOLEAN, JSON_BOOLEAN,
JSON_STRING, JSON_STRING,
JSON_INTEGER, JSON_LONG,
JSON_DOUBLE_0_DECIMALS, JSON_DOUBLE_0_DECIMALS,
JSON_DOUBLE_1_DECIMAL, JSON_DOUBLE_1_DECIMAL,
JSON_DOUBLE_2_DECIMALS, JSON_DOUBLE_2_DECIMALS,
@ -23,10 +22,134 @@ class JsonWriter;
struct JsonNode struct JsonNode
{ {
JsonNode* next; JsonNode* next;
JsonNodeType type; // <- TODO: hide JsonNodeType type; // <- TODO: hide
void writeTo(JsonWriter&); void writeTo(JsonWriter&); // TODO: <- move in JsonNodeSerializer
void setAsArray(JsonBuffer* buffer)
{
type = JSON_ARRAY;
content.asContainer.child = 0;
content.asContainer.buffer = buffer;
}
void setAsBoolean(bool value)
{
type = JSON_BOOLEAN;
content.asBoolean = value;
}
void setAsLong(int value)
{
type = JSON_LONG;
content.asInteger = value;
}
void setAsString(char const* value)
{
type = JSON_STRING;
content.asString = value;
}
void setAsDouble(double value, int decimals)
{
type = (JsonNodeType) (JSON_DOUBLE_0_DECIMALS + decimals);
content.asDouble = value;
}
void setAsObject(JsonBuffer* buffer)
{
type = JSON_OBJECT;
content.asContainer.child = 0;
content.asContainer.buffer = buffer;
}
void setAsObjectKeyValue(const char* key, JsonNode* value)
{
type = JSON_KEY_VALUE;
content.asKey.key = key;
content.asKey.value = value;
}
bool getAsBoolean()
{
return type == JSON_BOOLEAN ? content.asBoolean : false;
}
double getAsDouble()
{
return type > JSON_DOUBLE_0_DECIMALS ? content.asDouble : 0;
}
long getAsInteger()
{
return type == JSON_LONG ? content.asInteger : 0;
}
const char* getAsString()
{
return type == JSON_STRING ? content.asString : 0;
}
JsonBuffer* getContainerBuffer()
{
return type == JSON_ARRAY || type == JSON_OBJECT ? content.asContainer.buffer : 0;
}
JsonNode* getContainerChild()
{
return type == JSON_ARRAY || type == JSON_OBJECT ? content.asContainer.child : 0;
}
const char* getAsObjectKey()
{
return type == JSON_KEY_VALUE ? content.asKey.key : 0;
}
JsonNode* getAsObjectValue()
{
return type == JSON_KEY_VALUE ? content.asKey.value : 0;
}
void addChildToContainer(JsonNode* childToAdd)
{
if (type != JSON_ARRAY && type != JSON_OBJECT) return;
JsonNode* lastChild = content.asContainer.child;
if (!lastChild)
{
content.asContainer.child = childToAdd;
return;
}
while (lastChild->next)
lastChild = lastChild->next;
lastChild->next = childToAdd;
}
void removeChildFromContainer(JsonNode* childToRemove)
{
if (type != JSON_ARRAY && type != JSON_OBJECT) return;
if (content.asContainer.child == childToRemove)
{
content.asContainer.child = childToRemove->next;
return;
}
for (JsonNode* child = content.asContainer.child; child; child = child->next)
{
if (child->next == childToRemove)
child->next = childToRemove->next;
}
}
private:
inline void writeArrayTo(JsonWriter&);// TODO: <- move in JsonNodeSerializer
inline void writeObjectTo(JsonWriter&);// TODO: <- move in JsonNodeSerializer
union union
{ {
@ -35,7 +158,7 @@ struct JsonNode
long asInteger; long asInteger;
const char* asString; const char* asString;
struct struct
{ {
const char* key; const char* key;
JsonNode* value; JsonNode* value;
@ -53,8 +176,4 @@ struct JsonNode
} asProxy; } asProxy;
} content; } content;
private:
inline void writeArrayTo(JsonWriter&);
inline void writeObjectTo(JsonWriter&);
}; };

View File

@ -16,68 +16,71 @@ JsonValue JsonArray::operator[](int index) const
void JsonArray::add(bool value) void JsonArray::add(bool value)
{ {
JsonNode* node = createNode(JSON_BOOLEAN); JsonNode* node = createNode();
if (!node) return; if (!node) return;
node->content.asBoolean = value; node->setAsBoolean(value);
addChild(node); addChild(node);
} }
void JsonArray::add(char const* value) void JsonArray::add(char const* value)
{ {
JsonNode* node = createNode(JSON_STRING); JsonNode* node = createNode();
if (!node) return; if (!node) return;
node->content.asString = value; node->setAsString(value);
addChild(node); addChild(node);
} }
void JsonArray::add(double value, int decimals) void JsonArray::add(double value, int decimals)
{ {
JsonNode* node = createNode((JsonNodeType)(JSON_DOUBLE_0_DECIMALS + decimals)); JsonNode* node = createNode();
if (!node) return; if (!node) return;
node->content.asDouble = value; node->setAsDouble(value, decimals);
addChild(node); addChild(node);
} }
void JsonArray::add(long value) void JsonArray::add(long value)
{ {
JsonNode* node = createNode(JSON_INTEGER); JsonNode* node = createNode(JSON_LONG);
if (!node) return; if (!node) return;
node->content.asInteger = value; node->setAsLong(value);
addChild(node); addChild(node);
} }
void JsonArray::add(JsonContainer innerContainer) void JsonArray::add(JsonContainer nestedContainer)
{ {
JsonNode* node = createNode(JSON_PROXY); JsonNode* node = createNode();
if (!node) return; if (!node) return;
node->content.asProxy.target = innerContainer._node; *node = *nestedContainer._node;
addChild(node); addChild(node);
} }
JsonArray JsonArray::createNestedArray() JsonArray JsonArray::createNestedArray()
{ {
JsonNode* node = createNestedContainer(JSON_ARRAY); JsonNode* node = createNode();
if (node)
{
node->setAsArray(_node->getContainerBuffer());
addChild(node);
}
return JsonArray(node); return JsonArray(node);
} }
JsonObject JsonArray::createNestedObject() JsonObject JsonArray::createNestedObject()
{ {
JsonNode* node = createNestedContainer(JSON_OBJECT); JsonNode* node = createNode();
if (node)
{
node->setAsObject(_node->getContainerBuffer());
addChild(node);
}
return JsonObject(node); return JsonObject(node);
}
JsonNode* JsonArray::createNestedContainer(JsonNodeType type)
{
JsonNode* node = createNode(type);
if (!node) return 0;
node->content.asContainer.buffer = _node->content.asContainer.buffer;
addChild(node);
return node;
} }

View File

@ -21,12 +21,9 @@ public:
void add(double value, int decimals=2); void add(double value, int decimals=2);
void add(int value) { add((long) value); } void add(int value) { add((long) value); }
void add(long value); void add(long value);
void add(JsonContainer nestedArray); void add(JsonContainer nestedArray); // TODO: should allow JsonValue too
JsonArray createNestedArray(); JsonArray createNestedArray();
JsonObject createNestedObject(); JsonObject createNestedObject();
private:
JsonNode* createNestedContainer(JsonNodeType type);
}; };

View File

@ -20,14 +20,4 @@ JsonNode* JsonBuffer::createNode(JsonNodeType type)
memset(node, 0, sizeof(JsonNode)); memset(node, 0, sizeof(JsonNode));
node->type = type; node->type = type;
return node; return node;
}
JsonNode* JsonBuffer::createContainerNode(JsonNodeType type)
{
JsonNode* node = createNode(type);
if (node)
node->content.asContainer.buffer = this;
return node;
} }

View File

@ -12,12 +12,16 @@ public:
JsonArray createArray() JsonArray createArray()
{ {
return JsonArray(createContainerNode(JSON_ARRAY)); JsonNode* node = createNode();
if (node) node->setAsArray(this);
return JsonArray(node);
} }
JsonObject createObject() JsonObject createObject()
{ {
return JsonObject(createContainerNode(JSON_OBJECT)); JsonNode* node = createNode();
if (node) node->setAsObject(this);
return JsonObject(node);
} }
JsonValue createValue(); JsonValue createValue();
@ -26,7 +30,6 @@ protected:
virtual JsonNode* allocateNode() = 0; virtual JsonNode* allocateNode() = 0;
private: private:
JsonNode* createNode(JsonNodeType type); JsonNode* createNode(JsonNodeType type = JSON_UNDEFINED);
JsonNode* createContainerNode(JsonNodeType type);
}; };

View File

@ -37,7 +37,9 @@ JsonNode* JsonContainer::createNode(JsonNodeType type)
{ {
if (!_node) return 0; if (!_node) return 0;
JsonBuffer* buffer = _node->content.asContainer.buffer; JsonBuffer* buffer = _node->getContainerBuffer();
if (!buffer) return 0;
return buffer->createNode(type); return buffer->createNode(type);
} }
@ -48,31 +50,19 @@ bool JsonContainer::checkNodeType(JsonNodeType expectedType)
bool JsonContainer::operator==(const JsonContainer & other) const bool JsonContainer::operator==(const JsonContainer & other) const
{ {
return _node == other._node; return _node->getContainerChild() == other._node->getContainerChild();
} }
void JsonContainer::addChild(JsonNode* newChild) void JsonContainer::addChild(JsonNode* childToAdd)
{ {
JsonNode* lastChild = _node->content.asContainer.child; if (_node)
_node->addChildToContainer(childToAdd);
if (!lastChild)
{
_node->content.asContainer.child = newChild = newChild;
return;
}
while (lastChild->next)
lastChild = lastChild->next;
lastChild->next = newChild;
} }
void JsonContainer::removeChildAfter(JsonNode* child, JsonNode* previous) void JsonContainer::removeChild(JsonNode* childToRemove)
{ {
if (previous) if (_node)
previous->next = child->next; _node->removeChildFromContainer(childToRemove);
else
_node->content.asContainer.child = child->next;
} }
size_t JsonContainer::size() const size_t JsonContainer::size() const

View File

@ -43,7 +43,7 @@ protected:
JsonNodeIterator beginChildren() const JsonNodeIterator beginChildren() const
{ {
return JsonNodeIterator(_node ? _node->content.asContainer.child : 0); return JsonNodeIterator(_node ? _node->getContainerChild() : 0);
} }
JsonNodeIterator endChildren() const JsonNodeIterator endChildren() const
@ -51,9 +51,9 @@ protected:
return JsonNodeIterator(0); return JsonNodeIterator(0);
} }
void addChild(JsonNode* newChild); void addChild(JsonNode*);
void removeChildAfter(JsonNode* child, JsonNode* previous); void removeChild(JsonNode*);
JsonNode* createNode(JsonNodeType type); JsonNode* createNode(JsonNodeType type = JSON_UNDEFINED);
bool checkNodeType(JsonNodeType expectedType); bool checkNodeType(JsonNodeType expectedType);

View File

@ -18,30 +18,34 @@ JsonValue JsonObject::operator[](char const* key)
void JsonObject::remove(char const* key) void JsonObject::remove(char const* key)
{ {
JsonNode* lastChild = 0;
for (JsonNodeIterator it = beginChildren(); it != endChildren(); ++it) for (JsonNodeIterator it = beginChildren(); it != endChildren(); ++it)
{ {
const char* childKey = it->content.asKey.key; const char* childKey = it->getAsObjectKey();
if (!strcmp(childKey, key)) if (!strcmp(childKey, key))
{ {
removeChildAfter(*it, lastChild); removeChild(*it);
} }
lastChild = *it;
} }
} }
JsonArray JsonObject::createNestedArray(char const* key) JsonArray JsonObject::createNestedArray(char const* key)
{ {
JsonNode* node = createContainerNodeAt(key, JSON_ARRAY); JsonNode* node = getOrCreateNodeAt(key);
if (node)
node->setAsArray(_node->getContainerBuffer());
return JsonArray(node); return JsonArray(node);
} }
JsonObject JsonObject::createNestedObject(char const* key) JsonObject JsonObject::createNestedObject(char const* key)
{ {
JsonNode* node = createContainerNodeAt(key, JSON_OBJECT); JsonNode* node = getOrCreateNodeAt(key);
if (node)
node->setAsObject(_node->getContainerBuffer());
return JsonObject(node); return JsonObject(node);
} }
@ -51,20 +55,19 @@ JsonNode* JsonObject::getOrCreateNodeAt(const char* key)
for (JsonNodeIterator it = beginChildren(); it != endChildren(); ++it) for (JsonNodeIterator it = beginChildren(); it != endChildren(); ++it)
{ {
const char* childKey = it->content.asKey.key; const char* childKey = it->getAsObjectKey();
if (!strcmp(childKey, key)) if (!strcmp(childKey, key))
return it->content.asKey.value; return it->getAsObjectValue();
} }
JsonNode* newValueNode = createNode(JSON_UNDEFINED); JsonNode* newValueNode = createNode(JSON_UNDEFINED);
if (!newValueNode) return 0; if (!newValueNode) return 0;
JsonNode* newKeyNode = createNode(JSON_KEY); JsonNode* newKeyNode = createNode(JSON_KEY_VALUE);
if (!newKeyNode) return 0; if (!newKeyNode) return 0;
newKeyNode->content.asKey.key = key; newKeyNode->setAsObjectKeyValue(key, newValueNode);
newKeyNode->content.asKey.value = newValueNode;
addChild(newKeyNode); addChild(newKeyNode);
@ -76,9 +79,7 @@ JsonNode* JsonObject::createContainerNodeAt(char const* key, JsonNodeType type)
JsonNode* node = getOrCreateNodeAt(key); JsonNode* node = getOrCreateNodeAt(key);
if (!node) return 0; if (!node) return 0;
node->type = type; node->setAsArray(_node->getContainerBuffer());
node->content.asContainer.child = 0;
node->content.asContainer.buffer = _node->content.asContainer.buffer;
return node; return node;
} }

View File

@ -6,144 +6,80 @@
void JsonValue::operator=(bool value) void JsonValue::operator=(bool value)
{ {
if (!_node) return; if (_node)
_node->setAsBoolean(value);
_node->type = JSON_BOOLEAN;
_node->content.asBoolean = value;
} }
void JsonValue::operator=(char const* value) void JsonValue::operator=(char const* value)
{ {
if (!_node) return; if (_node)
_node->setAsString(value);
_node->type = JSON_STRING;
_node->content.asString = value;
}
void JsonValue::operator=(double value)
{
set(value, 2);
} }
void JsonValue::set(double value, int decimals) void JsonValue::set(double value, int decimals)
{ {
if (!_node) return; if (_node)
_node->setAsDouble(value, decimals);
_node->type = (JsonNodeType) (JSON_DOUBLE_0_DECIMALS + decimals);
_node->content.asDouble = value;
} }
void JsonValue::operator=(int value) void JsonValue::operator=(int value)
{ {
if (!_node) return; if (_node)
_node->setAsLong(value);
_node->type = JSON_INTEGER;
_node->content.asInteger = value;
} }
// TODO: it's a duplicate
void JsonValue::operator=(const JsonContainer& object) void JsonValue::operator=(const JsonContainer& object)
{ {
setAsProxyTo(object._node); if (!_node)
{
_node = object._node;
}
else
{
*_node = *object._node;
}
} }
// TODO: it's a duplicate
void JsonValue::operator=(JsonValue const& value) void JsonValue::operator=(JsonValue const& value)
{ {
if (!_node) if (!_node)
{ {
_node = value._node; _node = value._node;
return;
} }
else
JsonNodeType nodeType = value._node ? value._node->type : JSON_UNDEFINED;
switch (nodeType)
{ {
case JSON_UNDEFINED:
_node->type = JSON_UNDEFINED;
break;
case JSON_INTEGER:
_node->type = JSON_INTEGER;
_node->content.asInteger = value._node->content.asInteger;
break;
case JSON_DOUBLE_0_DECIMALS:
case JSON_OBJECT:
case JSON_ARRAY:
case JSON_PROXY:
setAsProxyTo(value._node);
break;
default:
*_node = *value._node; *_node = *value._node;
break; }
}
} }
JsonValue::operator bool() const JsonValue::operator bool() const
{ {
const JsonNode* node = getActualNode(); return _node ? _node->getAsBoolean() : false;
if (!node || node->type != JSON_BOOLEAN) return 0;
return node->content.asBoolean;
} }
JsonValue::operator char const*() const JsonValue::operator char const*() const
{ {
const JsonNode* node = getActualNode(); return _node ? _node->getAsString() : 0;
if (!node || node->type != JSON_STRING) return 0;
return node->content.asString;
} }
JsonValue::operator double() const JsonValue::operator double() const
{ {
const JsonNode* node = getActualNode(); return _node ? _node->getAsDouble() : 0;
if (!node || node->type < JSON_DOUBLE_0_DECIMALS) return 0;
return node->content.asDouble;
} }
JsonValue::operator int() const JsonValue::operator long() const
{ {
const JsonNode* node = getActualNode(); return _node ? _node->getAsInteger() : 0;
if (!node || node->type != JSON_INTEGER) return 0;
return node->content.asInteger;
} }
JsonValue::operator JsonArray() const JsonValue::operator JsonArray() const
{ {
return JsonArray(getActualNode()); return JsonArray(_node);
} }
JsonValue::operator JsonObject() const JsonValue::operator JsonObject() const
{ {
return JsonObject(getActualNode()); return JsonObject(_node);
}
void JsonValue::setAsProxyTo(JsonNode* target)
{
if (_node)
{
_node->type = JSON_PROXY;
_node->content.asProxy.target = target;
}
_node = target;
}
JsonNode* JsonValue::getActualNode() const
{
JsonNode* target = _node;
while (target && target->type == JSON_PROXY)
target = target->content.asProxy.target;
return target;
} }

View File

@ -21,7 +21,7 @@ public:
void operator=(bool); void operator=(bool);
void operator=(const char*); void operator=(const char*);
void operator=(double); void operator=(double x) { set(x, 2); }
void operator=(int); void operator=(int);
void operator=(const JsonContainer&); void operator=(const JsonContainer&);
void operator=(const JsonValue&); void operator=(const JsonValue&);
@ -29,7 +29,8 @@ public:
operator bool() const; operator bool() const;
operator const char*() const; operator const char*() const;
operator double() const; operator double() const;
operator int() const; operator long() const;
operator int() const { return operator long(); }
operator JsonArray() const; operator JsonArray() const;
operator JsonObject() const; operator JsonObject() const;
@ -37,7 +38,4 @@ public:
private: private:
JsonNode* _node; JsonNode* _node;
void setAsProxyTo(JsonNode*);
JsonNode* getActualNode() const;
}; };

View File

@ -135,18 +135,14 @@ TEST_F(JsonArray_PrintTo_Tests, OneBooleanOverCapacity)
TEST_F(JsonArray_PrintTo_Tests, OneEmptyNestedArray) TEST_F(JsonArray_PrintTo_Tests, OneEmptyNestedArray)
{ {
JsonArray nestedArray = json.createArray(); array.createNestedArray();
array.add(nestedArray);
outputMustBe("[[]]"); outputMustBe("[[]]");
} }
TEST_F(JsonArray_PrintTo_Tests, OneEmptyNestedHash) TEST_F(JsonArray_PrintTo_Tests, OneEmptyNestedHash)
{ {
JsonObject nestedObject = json.createObject(); array.createNestedObject();
array.add(nestedObject);
outputMustBe("[{}]"); outputMustBe("[{}]");
} }

View File

@ -59,7 +59,7 @@ TEST_F(JsonValueTests, CanStoreObject)
EXPECT_EQ(innerObject1, (JsonObject) jsonValue1); EXPECT_EQ(innerObject1, (JsonObject) jsonValue1);
} }
TEST_F(JsonValueTests, IntegerValuesAreCopied) TEST_F(JsonValueTests, IntegersAreCopiedByValue)
{ {
jsonValue1 = 123; jsonValue1 = 123;
jsonValue2 = jsonValue1; jsonValue2 = jsonValue1;
@ -68,7 +68,7 @@ TEST_F(JsonValueTests, IntegerValuesAreCopied)
EXPECT_EQ(123, (int) jsonValue2); EXPECT_EQ(123, (int) jsonValue2);
} }
TEST_F(JsonValueTests, DoubleValuesAreCopied) TEST_F(JsonValueTests, DoublesAreCopiedByValue)
{ {
jsonValue1 = 123.45; jsonValue1 = 123.45;
jsonValue2 = jsonValue1; jsonValue2 = jsonValue1;
@ -77,7 +77,7 @@ TEST_F(JsonValueTests, DoubleValuesAreCopied)
EXPECT_EQ(123.45, (double) jsonValue2); EXPECT_EQ(123.45, (double) jsonValue2);
} }
TEST_F(JsonValueTests, BooleanValuesAreCopied) TEST_F(JsonValueTests, BooleansAreCopiedByValue)
{ {
jsonValue1 = true; jsonValue1 = true;
jsonValue2 = jsonValue1; jsonValue2 = jsonValue1;
@ -86,7 +86,7 @@ TEST_F(JsonValueTests, BooleanValuesAreCopied)
EXPECT_TRUE((bool) jsonValue2); EXPECT_TRUE((bool) jsonValue2);
} }
TEST_F(JsonValueTests, CharPointersAreCopied) TEST_F(JsonValueTests, StringsAreCopiedByValue)
{ {
jsonValue1 = "hello"; jsonValue1 = "hello";
jsonValue2 = jsonValue1; jsonValue2 = jsonValue1;
@ -95,14 +95,27 @@ TEST_F(JsonValueTests, CharPointersAreCopied)
EXPECT_STREQ("hello", (const char*) jsonValue2); EXPECT_STREQ("hello", (const char*) jsonValue2);
} }
TEST_F(JsonValueTests, ObjectPointsAreCopied)
TEST_F(JsonValueTests, ObjectsAreCopiedByReference)
{ {
JsonObject object = json.createObject(); JsonObject object = json.createObject();
jsonValue1 = object; jsonValue1 = object;
jsonValue2 = jsonValue1; jsonValue2 = jsonValue1;
object["hello"] = "world"; object["hello"] = "world";
EXPECT_EQ(1, ((JsonObject) jsonValue2).size());
}
TEST_F(JsonValueTests, ArraysAreCopiedByReference)
{
JsonArray array = json.createArray();
jsonValue1 = array;
jsonValue2 = jsonValue1;
array.add("world");
EXPECT_EQ(1, ((JsonObject) jsonValue2).size()); EXPECT_EQ(1, ((JsonObject) jsonValue2).size());
} }