mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-09 23:34:20 +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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -262,6 +262,8 @@ public:
|
|||
var getProperty (const Identifier& propertyName, const var& defaultReturnValue) const;
|
||||
/** Returns true if this variant is an object and if it has the given property. */
|
||||
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. */
|
||||
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 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
|
||||
|
|
|
|||
|
|
@ -124,6 +124,125 @@ std::optional<var> JSONUtils::setPointer (const var& v,
|
|||
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)
|
||||
{
|
||||
const auto compareObjects = [] (const DynamicObject& x, const DynamicObject& y)
|
||||
|
|
@ -172,6 +291,13 @@ public:
|
|||
, "lfoWaveform": "triangle"
|
||||
, "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, "/lfoWaveform/foobar", "str"), std::nullopt);
|
||||
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);
|
||||
|
||||
/** 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. */
|
||||
static var makeObject (const std::map<Identifier, var>& source);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue