You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
207 lines
7.9 KiB
207 lines
7.9 KiB
2 years ago
|
/**
|
||
|
* Author: dkanus
|
||
|
* Home repo: https://www.insultplayers.ru/git/AcediaFramework/AcediaCore
|
||
|
* License: GPL
|
||
|
* Copyright 2021-2023 Anton Tarasenko
|
||
|
*------------------------------------------------------------------------------
|
||
|
* This file is part of Acedia.
|
||
|
*
|
||
|
* Acedia is free software: you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation, version 3 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
*
|
||
|
* Acedia is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with Acedia. If not, see <https://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
class MutableJsonPointer extends BaseJsonPointer;
|
||
|
|
||
|
//! Mutable representation of a Json pointer as defined in
|
||
|
//! [RFC6901](https://tools.ietf.org/html/rfc6901).
|
||
|
|
||
|
/// Resets the caller [`JsonPointer`] into an empty path, erasing all of its components.
|
||
|
public final function Empty() {
|
||
|
local int i;
|
||
|
|
||
|
for (i = 0; i < components.length; i += 1) {
|
||
|
_.memory.Free(components[i].textRepresentation);
|
||
|
}
|
||
|
components.length = 0;
|
||
|
}
|
||
|
|
||
|
/// Sets the caller [`JsonPointer`] to correspond to a given path in JSON pointer format
|
||
|
/// [RFC6901](https://tools.ietf.org/html/rfc6901).
|
||
|
///
|
||
|
/// If the provided [`BaseText`] value is not a valid pointer, it will be treated as
|
||
|
/// an empty pointer.
|
||
|
///
|
||
|
/// If the given pointer can be fixed by prepending "/", it will be done automatically.
|
||
|
/// For example, "foo/bar" is treated like "/foo/bar", "path" like "/path", but
|
||
|
/// an empty [`BaseText`] "" is treated as itself.
|
||
|
public final function Set(BaseText pointerAsText) {
|
||
|
local int i;
|
||
|
local bool hasEscapedSequences;
|
||
|
local Component nextComponent;
|
||
|
local MutableText nextPart;
|
||
|
local array<BaseText> parts;
|
||
|
|
||
|
Empty();
|
||
|
if (pointerAsText == none) {
|
||
|
return;
|
||
|
}
|
||
|
hasEscapedSequences = (pointerAsText.IndexOf(T(TJson_ESCAPE)) >= 0);
|
||
|
parts = pointerAsText.SplitByCharacter(T(TSLASH).GetCharacter(0),, true);
|
||
|
// The first element of the parts array is expected to be empty,
|
||
|
// because the [`BaseText::SplitByCharacter()`] method always returns an array with an empty
|
||
|
// first element when the input string starts with the delimiter.
|
||
|
//
|
||
|
// Therefore, we need to remove the first element to discard the empty string.
|
||
|
// If the first element is not empty, it means that the input string does not start with "/",
|
||
|
// so we pretend that we have already removed the first element, effectively "fixing"
|
||
|
// the path (e.g. turning "foo/bar" into "/foo/bar").
|
||
|
if (parts[0].IsEmpty()) {
|
||
|
_.memory.Free(parts[0]);
|
||
|
parts.Remove(0, 1);
|
||
|
}
|
||
|
if (hasEscapedSequences) {
|
||
|
// Replace escaped sequences "~0" and "~1".
|
||
|
// Order is specific, necessity of which is explained in Json Pointer's documentation:
|
||
|
// https://tools.ietf.org/html/rfc6901
|
||
|
for (i = 0; i < parts.length; i += 1) {
|
||
|
nextPart = MutableText(parts[i]);
|
||
|
nextPart.Replace(T(TJson_ESCAPED_SLASH), T(TSLASH));
|
||
|
nextPart.Replace(T(TJSON_ESCAPED_ESCAPE), T(TJson_ESCAPE));
|
||
|
}
|
||
|
}
|
||
|
for (i = 0; i < parts.length; i += 1) {
|
||
|
nextComponent.textRepresentation = parts[i].IntoText();
|
||
|
components[components.length] = nextComponent;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// This function appends a new component to the end of the caller [`JsonPointer`].
|
||
|
///
|
||
|
/// For instance, if the caller pointer represents the path "/a/b/c", adding
|
||
|
/// the component "new" would result in it representing the path "/a/b/c/new".
|
||
|
///
|
||
|
/// While this method can be used to add numeric components, it's recommended to use
|
||
|
/// PushNumeric() if possible for better clarity and efficiency.
|
||
|
public final function Push(BaseText newComponent) {
|
||
|
local Component newComponentRecord;
|
||
|
|
||
|
if (newComponent != none) {
|
||
|
newComponentRecord.textRepresentation = newComponent.Copy();
|
||
|
components[components.length] = newComponentRecord;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// This function adds a new numeric component to the end of the caller [`JsonPointer`].
|
||
|
///
|
||
|
/// For example, if the caller pointer represents the path "/a/b/c", adding the numeric
|
||
|
/// component "1" would result in it representing the path "/a/b/c/1".
|
||
|
public final function PushNumeric(int newComponent) {
|
||
|
local Component newComponentRecord;
|
||
|
|
||
|
if (newComponent >= 0) {
|
||
|
newComponentRecord.numericRepresentation = newComponent;
|
||
|
// Obviously this component is going to be numeric
|
||
|
newComponentRecord.testedForBeingNumeric = true;
|
||
|
components[components.length] = newComponentRecord;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Removes and returns the last component of the caller [`MutableJsonPointer`].
|
||
|
///
|
||
|
/// For example, if the caller [`MutableJsonPointer`] corresponds to "/ab/c/d", this method
|
||
|
/// would return "d" and modify the caller [`MutableJsonPointer`] to correspond to "/ab/c".
|
||
|
///
|
||
|
/// If the caller [`MutableJsonPointer`] is empty, the returned last component is `none`.
|
||
|
public final function Text Pop() {
|
||
|
local MutableText mutableResult;
|
||
|
|
||
|
mutableResult = PopMutable();
|
||
|
if (mutableResult != none) {
|
||
|
return mutableResult.IntoText();
|
||
|
}
|
||
|
return none;
|
||
|
}
|
||
|
|
||
|
/// Removes and returns the last component of the caller [`MutableJsonPointer`].
|
||
|
///
|
||
|
/// For example, if the caller [`MutableJsonPointer`] corresponds to "/ab/c/d", this method
|
||
|
/// would return "d" and modify the caller [`MutableJsonPointer`] to correspond to "/ab/c".
|
||
|
///
|
||
|
/// If the caller [`MutableJsonPointer`] is empty, the returned last component is `none`.
|
||
|
public final function MutableText PopMutable() {
|
||
|
local int lastIndex;
|
||
|
local MutableText result;
|
||
|
|
||
|
if (components.length <= 0) {
|
||
|
return none;
|
||
|
}
|
||
|
lastIndex = components.length - 1;
|
||
|
// Do not use `GetComponent()` to avoid unnecessary `Text` copying
|
||
|
result = PeekMutable();
|
||
|
_.memory.Free(components[lastIndex].textRepresentation);
|
||
|
components.length = components.length - 1;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/// Removes and returns the last numeric component of the caller [`MutableJsonPointer`].
|
||
|
///
|
||
|
/// For instance, if the caller [`MutableJsonPointer`] corresponds to "/ab/c/1", this method
|
||
|
/// would return `1` and modify the caller [`MutableJsonPointer`] to correspond to "/ab/c".
|
||
|
///
|
||
|
/// If the caller [`MutableJsonPointer`] does not end with a numeric component or is empty,
|
||
|
/// the returned value is `none`.
|
||
|
/// Value still gets removed.
|
||
|
public final function int PopNumeric() {
|
||
|
local int lastIndex;
|
||
|
local int result;
|
||
|
|
||
|
if (components.length <= 0) {
|
||
|
return -1;
|
||
|
}
|
||
|
lastIndex = components.length - 1;
|
||
|
result = GetNumericComponent(lastIndex);
|
||
|
_.memory.Free(components[lastIndex].textRepresentation);
|
||
|
components.length = components.length - 1;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/// Appends path, contained in Json pointer [`other`] to the caller Json pointer.
|
||
|
///
|
||
|
/// Appending "/A/B/7/C" to "/object/hey/1/there/" produces
|
||
|
/// "/object/hey/1/there//A/B/7/C".
|
||
|
///
|
||
|
/// Method will do nothing if `none` is passed as an argument.
|
||
|
|
||
|
/// Appends the path contained in the argument to the caller [`MutableJsonPointer`].
|
||
|
///
|
||
|
/// For example, appending "/A/B/7/C" to "/object/hey/1/there/" would result in
|
||
|
/// "/object/hey/1/there//A/B/7/C".
|
||
|
///
|
||
|
/// If the [`BaseJsonPointer`] argument is `non`, this method will do nothing.
|
||
|
public final function Append(BaseJsonPointer other) {
|
||
|
local int i;
|
||
|
local array<Component> otherComponents;
|
||
|
|
||
|
if (other == none) {
|
||
|
return;
|
||
|
}
|
||
|
otherComponents = other.components;
|
||
|
for (i = 0; i < otherComponents.length; i += 1) {
|
||
|
if (otherComponents[i].textRepresentation != none) {
|
||
|
otherComponents[i].textRepresentation = otherComponents[i].textRepresentation.Copy();
|
||
|
}
|
||
|
components[components.length] = otherComponents[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
defaultproperties {
|
||
|
}
|