Browse Source

Add method for incrementing JSON values into `JSONAPI`

core_refactor
Anton Tarasenko 2 years ago
parent
commit
822d9507bf
  1. 224
      sources/Text/JSON/JSONAPI.uc

224
sources/Text/JSON/JSONAPI.uc

@ -5,7 +5,7 @@
* both valid and invalid JSON. However only correctly parsing valid JSON
* is guaranteed. This means that you should not rely on these methods to parse
* any JSON extensions or validate JSON for you.
* Copyright 2021-2022 Anton Tarasenko
* Copyright 2021-2023 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
@ -1381,6 +1381,228 @@ private final function int GetEscapedVersion(int codePoint)
return codePoint;
}
/**
* Increments given value by another value, producing result as a new
* JSON-compatible value.
*
* What "incrementing" actually does depends on the passed JSON values:
* `valueToIncrement` and `increment` parameters. Unless either of them is `none`
* (then "increment" simply acts as a `_.json.Copy()` method for
* the non-`none` one), they must represent the same JSON type and:
*
* 1. JSON bool: performs logical "or" operation on given values;
* 2. JSON number: adds values together;
* 3. JSON string: appends `increment` at the end of `valueToIncrement`;
* 4. JSON array: appends copy of elements of `increment` at the end of
* array of copies of elements of `valueToIncrement`
* (calls `ArrayList::append()` method);
* 5. JSON object: creates new collection, based on `valueToIncrement`
* with added key-value pairs from `increment` (all elements are
* copied). Does not override old values
* (calls `HashTable::append()` method).
*
* In case they represent different JSON types (that aren't "null") -
* incrementing should produce `none`.
*
* @param valueToIncrement Value to increment. Can be any JSON-compatible
* value.
* @param increment Value to increment it by. Can be any
* JSON-compatible value.
* @return Incremented data (guaranteed to contain copies and not actual
* objects from either `valueToIncrement` or `increment`). `none` if
* argument types were incompatible. Whether type of the result will be
* immutable (boxes and `Text`) or mutable (refs and `MutableText`)
* depends on immutability of `valueToIncrement`. When adding two numbers,
* whether result will be boxed (this includes both boxes and refs) `int`
* or `float` depends on both parameters - if either of them if `float`,
* then result will be `float`.
*/
public final function AcediaObject Increment(
AcediaObject valueToIncrement,
AcediaObject increment)
{
local AcediaObject result;
if (valueToIncrement == none) {
result = _.json.Copy(increment);
}
else if (increment == none) {
result = _.json.Copy(valueToIncrement);
}
else if ( valueToIncrement.class == class'IntBox'
|| valueToIncrement.class == class'IntRef'
|| valueToIncrement.class == class'FloatBox'
|| valueToIncrement.class == class'FloatRef')
{
result = Increment_Number(valueToIncrement, increment);
}
else if ( valueToIncrement.class == class'BoolBox'
|| valueToIncrement.class == class'BoolRef')
{
result = Increment_Bool(valueToIncrement, increment);
}
else if ( valueToIncrement.class == class'Text'
|| valueToIncrement.class == class'MutableText')
{
result = Increment_Text(valueToIncrement, increment);
}
else {
result = Increment_Collections(valueToIncrement, increment);
}
return result;
}
// Assumes `valueToIncrement` and `increment` aren't `none`
private final function AcediaObject Increment_Collections(
AcediaObject valueToIncrement,
AcediaObject increment)
{
local AcediaObject result;
local ArrayList arrayListCopy;
local HashTable hashTableCopy;
if ( valueToIncrement.class == class'ArrayList'
&& increment.class == class'ArrayList')
{
arrayListCopy = ArrayList(Copy(ArrayList(increment)));
result = CopyArrayList(ArrayList(valueToIncrement))
.Append(arrayListCopy);
_.memory.Free(arrayListCopy);
}
else if ( valueToIncrement.class == class'HashTable'
&& increment.class == class'HashTable')
{
hashTableCopy = HashTable(Copy(HashTable(increment)));
result = CopyHashTable(HashTable(valueToIncrement))
.Append(hashTableCopy);
_.memory.Free(hashTableCopy);
}
return result;
}
// Assumes `valueToIncrement` and `increment` aren't `none`
private final function AcediaObject Increment_Text(
AcediaObject valueToIncrement,
AcediaObject increment)
{
local BaseText textIncrement;
local MutableText builder;
textIncrement = BaseText(increment);
if (BaseText(increment) == none) {
return none;
}
builder = BaseText(valueToIncrement).MutableCopy();
builder.Append(textIncrement);
if (Text(valueToIncrement) != none) {
return builder.IntoText();
}
return builder;
}
// Assumes `valueToIncrement` and `increment` aren't `none`
private final function AcediaObject Increment_Bool(
AcediaObject valueToIncrement,
AcediaObject increment)
{
local bool value1, value2;
if (valueToIncrement.class == class'BoolBox') {
value1 = BoolBox(valueToIncrement).Get();
}
if (valueToIncrement.class == class'BoolRef') {
value1 = BoolRef(valueToIncrement).Get();
}
if (increment.class == class'BoolBox') {
value2 = BoolBox(increment).Get();
}
else if (increment.class == class'BoolRef') {
value2 = BoolRef(increment).Get();
}
else {
return none;
}
if (ValueBox(valueToIncrement) != none) {
return _.box.bool(value1 || value2);
}
return _.ref.bool(value1 || value2);
}
// Assumes `valueToIncrement` and `increment` aren't `none`
// Assumes `valueToIncrement` is one of four classes: `IntBox`, `IntRef`,
// `FloatBox` or `FloatRef`.
private final function AcediaObject Increment_Number(
AcediaObject valueToIncrement,
AcediaObject increment)
{
local bool hasFloats;
local int intSummand1, intSummand2, intSum;
local float floatSummand1, floatSummand2, floatSum;
hasFloats = valueToIncrement.class == class'FloatBox'
|| valueToIncrement.class == class'FloatRef'
|| increment.class == class'FloatBox'
|| increment.class == class'FloatRef';
// `valueToIncrement` is guaranteed to have an appropriate type,
// but `increment` might not, so only do check on second call
ExtractBoxedNumericValue(valueToIncrement, intSummand1, floatSummand1);
if (!ExtractBoxedNumericValue(increment, intSummand2, floatSummand2)) {
return none;
}
if (hasFloats)
{
floatSum = floatSummand1 + floatSummand2
+ float(intSummand1 + intSummand2);
if (ValueBox(valueToIncrement) != none) {
return _.box.float(floatSum);
}
return _.ref.float(floatSum);
}
intSum = intSummand1 + intSummand2 + int(floatSummand1 + floatSummand2);
if (ValueBox(valueToIncrement) != none) {
return _.box.int(intSum);
}
return _.ref.int(intSum);
}
// Extracts numeric value and records it into one of two out arguments:
//
// * `asInteger` iff `value` is either `IntBox` or `IntRef`;
// * `asFloat` iff `value` is either `FloatBox` or `FloatRef`;
//
// Does not change the value in remaining parameter.
// Returns `true` in case of success (method managed to read the value) and
// `false` otherwise (`value` had non-numeric or unknown type).
private final function bool ExtractBoxedNumericValue(
AcediaObject value,
out int asInteger,
out float asFloat)
{
local bool success;
if (value.class == class'IntBox')
{
asInteger = IntBox(value).Get();
success = true;
}
else if (value.class == class'IntRef')
{
asInteger = IntRef(value).Get();
success = true;
}
else if (value.class == class'FloatBox')
{
asFloat = FloatBox(value).Get();
success = true;
}
else if (value.class == class'FloatRef')
{
asFloat = FloatRef(value).Get();
success = true;
}
return success;
}
/**
* Performs a deep copy of the `inputData`. This means it copies not only
* `inputData` itself, but (in case it is a container) all of the values

Loading…
Cancel
Save