Browse Source

Add support for clonin `JObject` and `JArray`

Addition of cloning also allows us to add regular setters for JSON
object and arrays that would clone a passed object to ensure each object
can only have one parent.
pull/8/head
Anton Tarasenko 4 years ago
parent
commit
4df3436393
  1. 79
      sources/Data/JSON/JArray.uc
  2. 69
      sources/Data/JSON/JObject.uc
  3. 5
      sources/Data/JSON/JSON.uc
  4. 52
      sources/Data/JSON/Tests/TEST_JSON.uc

79
sources/Data/JSON/JArray.uc

@ -316,6 +316,60 @@ public final function JArray SetNull
return self;
}
public final function JArray SetArray(
int index,
JArray template,
optional bool preventExpansion
)
{
local JStorageAtom newStorageValue;
if (index < 0) return self;
if (template == none) return self;
if (index >= data.length)
{
if (preventExpansion)
{
return self;
}
else
{
SetLength(index + 1);
}
}
newStorageValue.type = JSON_Array;
newStorageValue.complexValue = template.Clone();
data[index] = newStorageValue;
return self;
}
public final function JArray SetObject(
int index,
JObject template,
optional bool preventExpansion
)
{
local JStorageAtom newStorageValue;
if (index < 0) return self;
if (template == none) return self;
if (index >= data.length)
{
if (preventExpansion)
{
return self;
}
else
{
SetLength(index + 1);
}
}
newStorageValue.type = JSON_Object;
newStorageValue.complexValue = template.Clone();
data[index] = newStorageValue;
return self;
}
// JSON array and object types don't have setters, but instead have
// functions to create a new, empty array/object under a certain name.
// If passed index is negative - does nothing.
@ -452,6 +506,31 @@ public function bool IsSubsetOf(JSON rightValue)
return true;
}
public function JSON Clone()
{
local int i;
local JArray clonedArray;
local array<JStorageAtom> clonedData;
clonedArray = _.json.NewArray();
if (clonedArray == none)
{
_.logger.Failure("Cannot clone `JArray`: cannot spawn a new instance.");
return none;
}
clonedData = data;
for (i = 0; i < clonedData.length; i += 1)
{
if (clonedData[i].complexValue == none) continue;
if ( clonedData[i].type != JSON_Array
&& clonedData[i].type != JSON_Object) {
continue;
}
clonedData[i].complexValue = clonedData[i].complexValue.Clone();
}
clonedArray.data = clonedData;
return clonedArray;
}
defaultproperties
{
}

69
sources/Data/JSON/JObject.uc

@ -389,6 +389,34 @@ public final function JObject SetNull(string name)
return self;
}
public final function JObject SetArray(string name, JArray template)
{
local JProperty property;
if (template == none) return self;
FindProperty(name, property);
if (property.value.complexValue != none) {
property.value.complexValue.Destroy();
}
property.value.type = JSON_Array;
property.value.complexValue = template.Clone();
UpdateProperty(property);
return self;
}
public final function JObject SetObject(string name, JObject template)
{
local JProperty property;
if (template == none) return self;
FindProperty(name, property);
if (property.value.complexValue != none) {
property.value.complexValue.Destroy();
}
property.value.type = JSON_Object;
property.value.complexValue = template.Clone();
UpdateProperty(property);
return self;
}
// JSON array and object types don't have setters, but instead have
// functions to create a new, empty array/object under a certain name.
// They return object itself, allowing user to chain calls like this:
@ -400,8 +428,8 @@ public final function JObject CreateArray(string name)
if (property.value.complexValue != none) {
property.value.complexValue.Destroy();
}
property.value.type = JSON_Array;
property.value.complexValue = _.json.NewArray();
property.value.type = JSON_Array;
property.value.complexValue = _.json.NewArray();
UpdateProperty(property);
return self;
}
@ -413,8 +441,8 @@ public final function JObject CreateObject(string name)
if (property.value.complexValue != none) {
property.value.complexValue.Destroy();
}
property.value.type = JSON_Object;
property.value.complexValue = _.json.NewObject();
property.value.type = JSON_Object;
property.value.complexValue = _.json.NewObject();
UpdateProperty(property);
return self;
}
@ -466,6 +494,39 @@ public function bool IsSubsetOf(JSON rightJSON)
return true;
}
public function JSON Clone()
{
local int i, j;
local JObject clonedObject;
local array<PropertyBucket> clonedHashTable;
local array<JProperty> nextProperties;
clonedObject = _.json.NewObject();
if (clonedObject == none)
{
_.logger.Failure("Cannot clone `JObject`: cannot spawn a"
@ "new instance.");
return none;
}
clonedHashTable = hashTable;
for (i = 0; i < clonedHashTable.length; i += 1)
{
nextProperties = clonedHashTable[i].properties;
for (j = 0; j < nextProperties.length; j += 1)
{
if (nextProperties[j].value.complexValue == none) continue;
if ( nextProperties[j].value.type != JSON_Array
&& nextProperties[j].value.type != JSON_Object) {
continue;
}
nextProperties[j].value.complexValue =
nextProperties[j].value.complexValue.Clone();
}
clonedHashTable[i].properties = nextProperties;
}
clonedObject.hashTable = clonedHashTable;
return clonedObject;
}
defaultproperties
{
ABSOLUTE_LOWER_CAPACITY_LIMIT = 10

5
sources/Data/JSON/JSON.uc

@ -84,6 +84,11 @@ enum JComparisonResult
JCR_Equal
};
public function JSON Clone()
{
return none;
}
public function bool IsSubsetOf(JSON rightJSON)
{
return false;

52
sources/Data/JSON/Tests/TEST_JSON.uc

@ -29,6 +29,8 @@ protected static function TESTS()
Test_ObjectKeys();
Test_ArrayGetSetRemove();
Test_JSONComparison();
Test_JSONCloning();
Test_JSONSetComplexValues();
}
protected static function Test_ObjectGetSetRemove()
@ -1109,6 +1111,56 @@ protected static function SubTest_JSONCompare()
TEST_ExpectTrue(test2.Compare(test1) == JCR_Incomparable);
}
protected static function Test_JSONCloning()
{
local JObject original, clone;
original = Prepare_FoldedObject();
clone = JObject(original.Clone());
Context("Testing cloning functionality of JSON data.");
Issue("JSON data is cloned incorrectly.");
TEST_ExpectTrue(original.IsEqual(clone));
Issue("`Clone()` produces only a shallow copy.");
TEST_ExpectTrue(original != clone);
TEST_ExpectTrue( original.GetObject("innerObject")
!= clone.GetObject("innerObject"));
TEST_ExpectTrue( original.GetObject("innerObject").GetArray("array")
!= clone.GetObject("innerObject").GetArray("array"));
TEST_ExpectTrue( original.GetObject("innerObject").GetObject("one more")
!= clone.GetObject("innerObject").GetObject("one more"));
TEST_ExpectTrue(
original.GetObject("innerObject").GetArray("array").GetObject(3)
!= clone.GetObject("innerObject").GetArray("array").GetObject(3));
}
protected static function Test_JSONSetComplexValues()
{
local JObject testObject, original;
local JArray testArray;
Context("Testing `Set...()` operation for `JObject` / `JArray`.");
original = Prepare_FoldedObject();
testObject = Prepare_FoldedObject();
testArray =
JArray(testObject.GetObject("innerObject").GetArray("array").Clone());
testObject.SetObject("newObjectCopy", testObject);
testObject.SetArray("newArrayCopy", testArray);
testArray.SetObject(0, testObject);
testArray.SetArray(1, testArray);
Issue("`Set() for `JObject` / `JArray` does not produce correct copy.");
Test_ExpectTrue(testObject.GetObject("newObjectCopy").IsEqual(original));
Test_ExpectTrue(testObject.GetArray("newArrayCopy")
.IsEqual(original.GetObject("innerObject").GetArray("array")));
Test_ExpectTrue(testArray.GetObject(0).IsEqual(testObject));
Issue("`Set() for `JObject` / `JArray` produces a shallow copy.");
Test_ExpectTrue(testObject.GetObject("newObjectCopy") != original);
Test_ExpectTrue( testObject.GetObject("newArrayCopy")
!= original.GetObject("innerObject").GetArray("array"));
Test_ExpectTrue(testArray.GetObject(0) != original);
Test_ExpectTrue( testArray.GetArray(1)
!= original.GetObject("innerObject").GetArray("array"));
}
defaultproperties
{
caseName = "JSON"

Loading…
Cancel
Save