mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Merge eddf488258 into bc7339fe07
This commit is contained in:
commit
73e4a1f625
4 changed files with 273 additions and 0 deletions
|
|
@ -714,6 +714,21 @@ bool var::hasProperty (const Identifier& propertyName) const noexcept
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Array<Identifier> var::getProperties() const
|
||||||
|
{
|
||||||
|
if (auto* o = getDynamicObject())
|
||||||
|
{
|
||||||
|
Array<Identifier> names;
|
||||||
|
|
||||||
|
for (auto itr : o->getProperties())
|
||||||
|
names.add (itr.name);
|
||||||
|
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
var::NativeFunction var::getNativeFunction() const
|
var::NativeFunction var::getNativeFunction() const
|
||||||
{
|
{
|
||||||
return isMethod() && (value.methodValue != nullptr) ? *value.methodValue : nullptr;
|
return isMethod() && (value.methodValue != nullptr) ? *value.methodValue : nullptr;
|
||||||
|
|
@ -899,6 +914,75 @@ var::NativeFunctionArgs::NativeFunctionArgs (const var& t, const var* args, int
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
VarIterator::VarIterator (const var& v_, bool isEnd)
|
||||||
|
: v (v_)
|
||||||
|
{
|
||||||
|
if (isEnd)
|
||||||
|
{
|
||||||
|
if (v.isArray())
|
||||||
|
index = v.getArray()->size();
|
||||||
|
else if (auto o = v.getDynamicObject())
|
||||||
|
itr = o->getProperties().end();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (v.isArray())
|
||||||
|
index = 0;
|
||||||
|
else if (auto o = v.getDynamicObject())
|
||||||
|
itr = o->getProperties().begin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VarIterator& VarIterator::operator++()
|
||||||
|
{
|
||||||
|
if (v.isArray())
|
||||||
|
{
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
else if (v.getDynamicObject())
|
||||||
|
{
|
||||||
|
auto i = (const NamedValueSet::NamedValue*)itr;
|
||||||
|
i++;
|
||||||
|
itr = i;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VarIterator::operator== (const VarIterator& other) const
|
||||||
|
{
|
||||||
|
return index == other.index && itr == other.itr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VarIterator::operator!= (const VarIterator& other) const
|
||||||
|
{
|
||||||
|
return ! (*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
VarIterator::NamedValue VarIterator::operator*() const
|
||||||
|
{
|
||||||
|
if (v.isArray())
|
||||||
|
{
|
||||||
|
return { index, (*v.getArray())[index] };
|
||||||
|
}
|
||||||
|
else if (v.getDynamicObject())
|
||||||
|
{
|
||||||
|
auto i = (const NamedValueSet::NamedValue*)itr;
|
||||||
|
return { i->name.toString(), i->value };
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
VarIterator begin (const var& v)
|
||||||
|
{
|
||||||
|
return VarIterator (v, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
VarIterator end (const var& v)
|
||||||
|
{
|
||||||
|
return VarIterator (v, true);
|
||||||
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
#if JUCE_ALLOW_STATIC_NULL_VARIABLES
|
#if JUCE_ALLOW_STATIC_NULL_VARIABLES
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -262,6 +262,8 @@ public:
|
||||||
var getProperty (const Identifier& propertyName, const var& defaultReturnValue) const;
|
var getProperty (const Identifier& propertyName, const var& defaultReturnValue) const;
|
||||||
/** Returns true if this variant is an object and if it has the given property. */
|
/** Returns true if this variant is an object and if it has the given property. */
|
||||||
bool hasProperty (const Identifier& propertyName) const noexcept;
|
bool hasProperty (const Identifier& propertyName) const noexcept;
|
||||||
|
/** Returns property names if this variant is an object. */
|
||||||
|
Array<Identifier> getProperties() const;
|
||||||
|
|
||||||
/** Invokes a named method call with no arguments. */
|
/** Invokes a named method call with no arguments. */
|
||||||
var call (const Identifier& method) const;
|
var call (const Identifier& method) const;
|
||||||
|
|
@ -354,4 +356,42 @@ JUCE_API bool operator== (const var&, const String&);
|
||||||
JUCE_API bool operator!= (const var&, const String&);
|
JUCE_API bool operator!= (const var&, const String&);
|
||||||
JUCE_API bool operator== (const var&, const char*);
|
JUCE_API bool operator== (const var&, const char*);
|
||||||
JUCE_API bool operator!= (const var&, const char*);
|
JUCE_API bool operator!= (const var&, const char*);
|
||||||
|
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
/** Iterator for a var.
|
||||||
|
You shouldn't ever need to use this class directly - it's used internally by begin()
|
||||||
|
and end() to allow range-based-for loops on a var.
|
||||||
|
*/
|
||||||
|
struct VarIterator
|
||||||
|
{
|
||||||
|
struct NamedValue
|
||||||
|
{
|
||||||
|
var name;
|
||||||
|
var value;
|
||||||
|
};
|
||||||
|
|
||||||
|
VarIterator (const var&, bool isEnd);
|
||||||
|
VarIterator& operator++();
|
||||||
|
|
||||||
|
bool operator== (const VarIterator&) const;
|
||||||
|
bool operator!= (const VarIterator&) const;
|
||||||
|
NamedValue operator*() const;
|
||||||
|
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using value_type = NamedValue;
|
||||||
|
using reference = NamedValue&;
|
||||||
|
using pointer = NamedValue*;
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const var& v;
|
||||||
|
int index = 0;
|
||||||
|
const void* itr = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
VarIterator begin (const var&);
|
||||||
|
VarIterator end (const var&);
|
||||||
|
|
||||||
|
|
||||||
} // namespace juce
|
} // namespace juce
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,125 @@ std::optional<var> JSONUtils::setPointer (const var& v,
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool JSONUtils::updatePointer (var& v, String pointer, const var& newValue)
|
||||||
|
{
|
||||||
|
if (pointer.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (! pointer.startsWith ("/"))
|
||||||
|
{
|
||||||
|
// This is not a well-formed JSON pointer
|
||||||
|
jassertfalse;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto findResult = pointer.indexOfChar (1, '/');
|
||||||
|
const auto pos = findResult < 0 ? pointer.length() : findResult;
|
||||||
|
const String head (pointer.begin() + 1, pointer.begin() + pos);
|
||||||
|
const String tail (pointer.begin() + pos, pointer.end());
|
||||||
|
|
||||||
|
const auto unescaped = head.replace ("~1", "/").replace ("~0", "~");
|
||||||
|
|
||||||
|
if (auto* object = v.getDynamicObject())
|
||||||
|
{
|
||||||
|
if (tail.isEmpty())
|
||||||
|
{
|
||||||
|
object->setProperty (unescaped, newValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto v = object->getProperty (unescaped);
|
||||||
|
return updatePointer (v, tail, newValue);
|
||||||
|
}
|
||||||
|
else if (auto* array = v.getArray())
|
||||||
|
{
|
||||||
|
const auto index = [&]() -> size_t
|
||||||
|
{
|
||||||
|
if (unescaped == "-")
|
||||||
|
return (size_t) array->size();
|
||||||
|
|
||||||
|
if (unescaped == "0")
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (! unescaped.startsWith ("0"))
|
||||||
|
return (size_t) unescaped.getLargeIntValue();
|
||||||
|
|
||||||
|
return std::numeric_limits<size_t>::max();
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (tail.isEmpty())
|
||||||
|
{
|
||||||
|
if (isPositiveAndBelow (index, array->size()))
|
||||||
|
{
|
||||||
|
array->set (int (index), newValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == array->size())
|
||||||
|
{
|
||||||
|
array->add (newValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto v = (*array)[(int) index];
|
||||||
|
return updatePointer (v, tail, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var JSONUtils::getPointer (const var& v, String pointer, const var& defaultValue)
|
||||||
|
{
|
||||||
|
if (pointer.isEmpty())
|
||||||
|
return defaultValue;
|
||||||
|
|
||||||
|
if (! pointer.startsWith ("/"))
|
||||||
|
{
|
||||||
|
// This is not a well-formed JSON pointer
|
||||||
|
jassertfalse;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto findResult = pointer.indexOfChar (1, '/');
|
||||||
|
const auto pos = findResult < 0 ? pointer.length() : findResult;
|
||||||
|
const String head (pointer.begin() + 1, pointer.begin() + pos);
|
||||||
|
const String tail (pointer.begin() + pos, pointer.end());
|
||||||
|
|
||||||
|
const auto unescaped = head.replace ("~1", "/").replace ("~0", "~");
|
||||||
|
|
||||||
|
if (auto* object = v.getDynamicObject())
|
||||||
|
{
|
||||||
|
if (tail.isEmpty())
|
||||||
|
return object->hasProperty (unescaped) ? object->getProperty (unescaped) : defaultValue;
|
||||||
|
else
|
||||||
|
return getPointer (object->getProperty (unescaped), tail, defaultValue);
|
||||||
|
}
|
||||||
|
else if (auto* array = v.getArray())
|
||||||
|
{
|
||||||
|
const auto index = [&]() -> size_t
|
||||||
|
{
|
||||||
|
if (unescaped == "-")
|
||||||
|
return (size_t) array->size();
|
||||||
|
|
||||||
|
if (unescaped == "0")
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (! unescaped.startsWith ("0"))
|
||||||
|
return (size_t) unescaped.getLargeIntValue();
|
||||||
|
|
||||||
|
return std::numeric_limits<size_t>::max();
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (tail.isEmpty())
|
||||||
|
return isPositiveAndBelow (index, array->size()) ? (*array)[int (index)] : defaultValue;
|
||||||
|
else
|
||||||
|
return getPointer ((*array)[(int) index], tail, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
bool JSONUtils::deepEqual (const var& a, const var& b)
|
bool JSONUtils::deepEqual (const var& a, const var& b)
|
||||||
{
|
{
|
||||||
const auto compareObjects = [] (const DynamicObject& x, const DynamicObject& y)
|
const auto compareObjects = [] (const DynamicObject& x, const DynamicObject& y)
|
||||||
|
|
@ -172,6 +291,13 @@ public:
|
||||||
, "lfoWaveform": "triangle"
|
, "lfoWaveform": "triangle"
|
||||||
, "pitchEnvelope": { "rates": [94,67,95,60], "levels": [50,50,50,50] }
|
, "pitchEnvelope": { "rates": [94,67,95,60], "levels": [50,50,50,50] }
|
||||||
})");
|
})");
|
||||||
|
|
||||||
|
expect (JSONUtils::getPointer (obj, "/name", {}) == "PIANO 4");
|
||||||
|
expect (JSONUtils::getPointer (obj, "/lfoSpeed", {}) == var (30));
|
||||||
|
expect (JSONUtils::getPointer (obj, "/pitchEnvelope/rates/1", {}) == var (67));
|
||||||
|
expect (JSONUtils::getPointer (obj, "/pitchEnvelope/levels/2", {}) == var (50));
|
||||||
|
expect (JSONUtils::getPointer (obj, "/pitchEnvelope/levels/10", {}) == var());
|
||||||
|
|
||||||
expectDeepEqual (JSONUtils::setPointer (obj, "", "hello world"), var ("hello world"));
|
expectDeepEqual (JSONUtils::setPointer (obj, "", "hello world"), var ("hello world"));
|
||||||
expectDeepEqual (JSONUtils::setPointer (obj, "/lfoWaveform/foobar", "str"), std::nullopt);
|
expectDeepEqual (JSONUtils::setPointer (obj, "/lfoWaveform/foobar", "str"), std::nullopt);
|
||||||
expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"foo":0,"bar":1})"), "/foo", 2), JSON::parse (R"({"foo":2,"bar":1})"));
|
expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"foo":0,"bar":1})"), "/foo", 2), JSON::parse (R"({"foo":2,"bar":1})"));
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,29 @@ struct JSONUtils
|
||||||
*/
|
*/
|
||||||
static std::optional<var> setPointer (const var& v, String pointer, const var& newValue);
|
static std::optional<var> setPointer (const var& v, String pointer, const var& newValue);
|
||||||
|
|
||||||
|
/** Given a JSON array/object 'v', a string representing a JSON pointer,
|
||||||
|
and a new property value 'newValue', updates 'v' where the
|
||||||
|
property or array index referenced by the pointer has been set to 'newValue'.
|
||||||
|
|
||||||
|
If the pointer cannot be followed, due to referencing missing array indices
|
||||||
|
or fields, then this returns false.
|
||||||
|
|
||||||
|
For more details, check the JSON Pointer RFC 6901:
|
||||||
|
https://datatracker.ietf.org/doc/html/rfc6901
|
||||||
|
*/
|
||||||
|
static bool updatePointer (var& v, String pointer, const var& newValue);
|
||||||
|
|
||||||
|
/** Given a JSON array/object 'v', a string representing a JSON pointer,
|
||||||
|
returns the value of the property or array index referenced by the pointer
|
||||||
|
|
||||||
|
If the pointer cannot be followed, due to referencing missing array indices
|
||||||
|
or fields, then this returns defaultValue.
|
||||||
|
|
||||||
|
For more details, check the JSON Pointer RFC 6901:
|
||||||
|
https://datatracker.ietf.org/doc/html/rfc6901
|
||||||
|
*/
|
||||||
|
static var getPointer (const var& v, String pointer, const var& defaultValue);
|
||||||
|
|
||||||
/** Converts the provided key/value pairs into a JSON object. */
|
/** Converts the provided key/value pairs into a JSON object. */
|
||||||
static var makeObject (const std::map<Identifier, var>& source);
|
static var makeObject (const std::map<Identifier, var>& source);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue