OpenShot Audio Library | OpenShotAudio 0.4.0

Loading...
Searching...
No Matches
juce_JSONUtils.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23 namespace juce
24{
25
26 var JSONUtils::makeObject (const std::map<Identifier, var>& source)
27{
28 auto result = std::make_unique<DynamicObject>();
29
30 for (const auto& [name, value] : source)
31 result->setProperty (name, value);
32
33 return var (result.release());
34}
35
36 var JSONUtils::makeObjectWithKeyFirst (const std::map<Identifier, var>& source,
37 Identifier key)
38{
39 auto result = std::make_unique<DynamicObject>();
40
41 if (const auto iter = source.find (key); iter != source.end())
42 result->setProperty (key, iter->second);
43
44 for (const auto& [name, value] : source)
45 if (name != key)
46 result->setProperty (name, value);
47
48 return var (result.release());
49}
50
51std::optional<var> JSONUtils::setPointer (const var& v,
52 String pointer,
53 const var& newValue)
54{
55 if (pointer.isEmpty())
56 return newValue;
57
58 if (! pointer.startsWith ("/"))
59 {
60 // This is not a well-formed JSON pointer
61 jassertfalse;
62 return {};
63 }
64
65 const auto findResult = pointer.indexOfChar (1, '/');
66 const auto pos = findResult < 0 ? pointer.length() : findResult;
67 const String head (pointer.begin() + 1, pointer.begin() + pos);
68 const String tail (pointer.begin() + pos, pointer.end());
69
70 const auto unescaped = head.replace ("~1", "/").replace ("~0", "~");
71
72 if (auto* object = v.getDynamicObject())
73 {
74 if (const auto newProperty = setPointer (object->getProperty (unescaped), tail, newValue))
75 {
76 auto cloned = object->clone();
77 cloned->setProperty (unescaped, *newProperty);
78 return var (cloned.release());
79 }
80 }
81 else if (auto* array = v.getArray())
82 {
83 const auto index = [&]() -> size_t
84 {
85 if (unescaped == "-")
86 return (size_t) array->size();
87
88 if (unescaped == "0")
89 return 0;
90
91 if (! unescaped.startsWith ("0"))
92 return (size_t) unescaped.getLargeIntValue();
93
94 return std::numeric_limits<size_t>::max();
95 }();
96
97 if (const auto newIndex = setPointer ((*array)[(int) index], tail, newValue))
98 {
99 auto copied = *array;
100
101 if ((int) index == copied.size())
102 copied.add ({});
103
104 if (isPositiveAndBelow (index, copied.size()))
105 {
106 copied.getReference ((int) index) = *newIndex;
107 return var (copied);
108 }
109 }
110 }
111
112 return {};
113}
114
115 bool JSONUtils::deepEqual (const var& a, const var& b)
116{
117 const auto compareObjects = [] (const DynamicObject& x, const DynamicObject& y)
118 {
119 if (x.getProperties().size() != y.getProperties().size())
120 return false;
121
122 for (const auto& [key, value] : x.getProperties())
123 {
124 if (! y.hasProperty (key))
125 return false;
126
127 if (! deepEqual (value, y.getProperty (key)))
128 return false;
129 }
130
131 return true;
132 };
133
134 if (auto* i = a.getDynamicObject())
135 if (auto* j = b.getDynamicObject())
136 return compareObjects (*i, *j);
137
138 if (auto* i = a.getArray())
139 if (auto* j = b.getArray())
140 return std::equal (i->begin(), i->end(), j->begin(), j->end(), [] (const var& x, const var& y) { return deepEqual (x, y); });
141
142 return a == b;
143}
144
145//==============================================================================
146//==============================================================================
147#if JUCE_UNIT_TESTS
148
149 class JSONUtilsTests final : public UnitTest
150{
151 public:
152 JSONUtilsTests() : UnitTest ("JSONUtils", UnitTestCategories::json) {}
153
154 void runTest() override
155 {
156 beginTest ("JSON pointers");
157 {
158 const auto obj = JSON::parse (R"({ "name": "PIANO 4"
159 , "lfoSpeed": 30
160 , "lfoWaveform": "triangle"
161 , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [50,50,50,50] }
162 })");
163 expectDeepEqual (JSONUtils::setPointer (obj, "", "hello world"), var ("hello world"));
164 expectDeepEqual (JSONUtils::setPointer (obj, "/lfoWaveform/foobar", "str"), std::nullopt);
165 expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"foo":0,"bar":1})"), "/foo", 2), JSON::parse (R"({"foo":2,"bar":1})"));
166 expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"foo":0,"bar":1})"), "/baz", 2), JSON::parse (R"({"foo":0,"bar":1,"baz":2})"));
167 expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"foo":{},"bar":{}})"), "/foo/bar", 2), JSON::parse (R"({"foo":{"bar":2},"bar":{}})"));
168 expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/rates/01", "str"), std::nullopt);
169 expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/rates/10", "str"), std::nullopt);
170 expectDeepEqual (JSONUtils::setPointer (obj, "/lfoSpeed", 10), JSON::parse (R"({ "name": "PIANO 4"
171 , "lfoSpeed": 10
172 , "lfoWaveform": "triangle"
173 , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [50,50,50,50] }
174 })"));
175 expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"([0,1,2])"), "/0", "bang"), JSON::parse (R"(["bang",1,2])"));
176 expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"([0,1,2])"), "/0", "bang"), JSON::parse (R"(["bang",1,2])"));
177 expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"/":"fizz"})"), "/~1", "buzz"), JSON::parse (R"({"/":"buzz"})"));
178 expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"~":"fizz"})"), "/~0", "buzz"), JSON::parse (R"({"~":"buzz"})"));
179 expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/rates/0", 80), JSON::parse (R"({ "name": "PIANO 4"
180 , "lfoSpeed": 30
181 , "lfoWaveform": "triangle"
182 , "pitchEnvelope": { "rates": [80,67,95,60], "levels": [50,50,50,50] }
183 })"));
184 expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/levels/0", 80), JSON::parse (R"({ "name": "PIANO 4"
185 , "lfoSpeed": 30
186 , "lfoWaveform": "triangle"
187 , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [80,50,50,50] }
188 })"));
189 expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/levels/-", 100), JSON::parse (R"({ "name": "PIANO 4"
190 , "lfoSpeed": 30
191 , "lfoWaveform": "triangle"
192 , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [50,50,50,50,100] }
193 })"));
194 }
195 }
196
197 void expectDeepEqual (const std::optional<var>& a, const std::optional<var>& b)
198 {
199 const auto text = a.has_value() && b.has_value()
200 ? JSON::toString (*a) + " != " + JSON::toString (*b)
201 : String();
202 expect (deepEqual (a, b), text);
203 }
204
205 static bool deepEqual (const std::optional<var>& a, const std::optional<var>& b)
206 {
207 if (a.has_value() && b.has_value())
208 return JSONUtils::deepEqual (*a, *b);
209
210 return a == b;
211 }
212};
213
214 static JSONUtilsTests jsonUtilsTests;
215
216#endif
217
218} // namespace juce
juce::DynamicObject
Definition juce_DynamicObject.h:40
juce::DynamicObject::getProperties
NamedValueSet & getProperties() noexcept
Definition juce_DynamicObject.h:99
juce::Identifier
Definition juce_Identifier.h:39
juce::NamedValueSet::size
int size() const noexcept
Definition juce_NamedValueSet.cpp:125
juce::String
Definition juce_String.h:53
juce::String::begin
CharPointerType begin() const
Definition juce_String.h:923
juce::String::indexOfChar
int indexOfChar(juce_wchar characterToLookFor) const noexcept
Definition juce_String.cpp:874
juce::String::length
int length() const noexcept
Definition juce_String.cpp:511
juce::String::isEmpty
bool isEmpty() const noexcept
Definition juce_String.h:310
juce::String::startsWith
bool startsWith(StringRef text) const noexcept
Definition juce_String.cpp:1393
juce::String::end
CharPointerType end() const
Definition juce_String.h:949
juce::String::replace
String replace(StringRef stringToReplace, StringRef stringToInsertInstead, bool ignoreCase=false) const
Definition juce_String.cpp:1279
juce::UnitTest
Definition juce_UnitTest.h:70
juce::var
Definition juce_Variant.h:42
juce::var::getArray
Array< var > * getArray() const noexcept
Definition juce_Variant.cpp:561
juce::JSONUtils::setPointer
static std::optional< var > setPointer(const var &v, String pointer, const var &newValue)
Definition juce_JSONUtils.cpp:51
juce::JSONUtils::deepEqual
static bool deepEqual(const var &a, const var &b)
Definition juce_JSONUtils.cpp:115
juce::JSONUtils::makeObject
static var makeObject(const std::map< Identifier, var > &source)
Definition juce_JSONUtils.cpp:26
juce::JSONUtils::makeObjectWithKeyFirst
static var makeObjectWithKeyFirst(const std::map< Identifier, var > &source, Identifier key)
Definition juce_JSONUtils.cpp:36