/**
* Class for representing a JSON pointer (see
* https://tools.ietf.org/html/rfc6901).
* Allows quick and simple access to parts/segments of it's path.
* Objects of this class should only be used after initialization.
* Copyright 2021 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 .
*/
class JSONPointer extends AcediaObject;
var private bool initialized;
// Segments of the path this JSON pointer was initialized with
var private array keys;
var protected const int TSLASH, TJSON_ESCAPE, TJSON_ESCAPED_SLASH;
var protected const int TJSON_ESCAPED_ESCAPE;
protected function Finalizer()
{
_.memory.FreeMany(keys);
keys.length = 0;
initialized = false;
}
/**
* Initializes caller `JSONPointer` with a given path.
*
* @param pointerAsText Treated as a JSON pointer if it starts with "/"
* character or is an empty `Text`, otherwise treated as an item's
* name / identificator inside the caller collection (without resolving
* escaped sequences "~0" and "~1").
* @return `true` if caller `JSONPointer` was correctly initialized with this
* call. `false` otherwise: can happen if `none` was passed as a parameter
* or caller `JSONPointer` was already initialized.
*/
public final function bool Initialize(Text pointerAsText)
{
local int i;
local bool hasEscapedSequences;
if (initialized) return false;
if (pointerAsText == none) return false;
initialized = true;
if (!pointerAsText.StartsWith(T(TSLASH)) && !pointerAsText.IsEmpty()) {
keys[0] = pointerAsText.MutableCopy();
}
else
{
hasEscapedSequences = (pointerAsText.IndexOf(T(TJSON_ESCAPE)) >= 0);
keys = pointerAsText.SplitByCharacter(T(TSLASH).GetCharacter(0));
// First elements of the array will be empty, so throw it away
_.memory.Free(keys[0]);
keys.Remove(0, 1);
}
if (!hasEscapedSequences) {
return true;
}
// 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 < keys.length; i += 1)
{
keys[i].Replace(T(TJSON_ESCAPED_SLASH), T(TSLASH));
keys[i].Replace(T(TJSON_ESCAPED_ESCAPE), T(TJSON_ESCAPE));
}
return true;
}
/**
* Returns a segment of the path by it's index.
*
* For path "/a/b/c":
* `GetSegment(0) == "a"`
* `GetSegment(1) == "b"`
* `GetSegment(2) == "c"`
* `GetSegment(3) == none`
* For path "/":
* `GetSegment(0) == ""`
* `GetSegment(1) == none`
* For path "":
* `GetSegment(0) == none`
* For path "abc":
* `GetSegment(0) == "abc"`
* `GetSegment(1) == none`
*
* @param index Index of the segment to return. Must be inside
* `[0; GetLength() - 1]` segment.
* @return Path's segment as a `Text`. If passed `index` is outside of
* `[0; GetLength() - 1]` segment - returns `none`.
*/
public final function Text GetSegment(int index)
{
if (index < 0) return none;
if (index >= keys.length) return none;
if (keys[index] == none) return none;
return keys[index].Copy();
}
/**
* Amount of path segments in this JSON pointer.
*
* For more details see `GetSegment()`.
*
* @return Amount of segments in the caller `JSONPointer`.
*/
public final function int GetLength()
{
return keys.length;
}
defaultproperties
{
TSLASH = 0
stringConstants(0) = "/"
TJSON_ESCAPE = 1
stringConstants(1) = "~"
TJSON_ESCAPED_SLASH = 2
stringConstants(2) = "~1"
TJSON_ESCAPED_ESCAPE = 3
stringConstants(3) = "~0"
}