text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

class ValueTree::SharedObject  : public ReferenceCountedObject
{
public:
    typedef ReferenceCountedObjectPtr<SharedObject> Ptr;

    explicit SharedObject (const Identifier& t) noexcept  : type (t)
    {
    }

    SharedObject (const SharedObject& other)
        : ReferenceCountedObject(), type (other.type), properties (other.properties)
    {
        for (int i = 0; i < other.children.size(); ++i)
        {
            auto child = new SharedObject (*other.children.getObjectPointerUnchecked(i));
            child->parent = this;
            children.add (child);
        }
    }

    ~SharedObject()
    {
        jassert (parent == nullptr); // this should never happen unless something isn't obeying the ref-counting!

        for (int i = children.size(); --i >= 0;)
        {
            const Ptr c (children.getObjectPointerUnchecked(i));
            c->parent = nullptr;
            children.remove (i);
            c->sendParentChangeMessage();
        }
    }

    SharedObject* getRoot() noexcept
    {
        return parent == nullptr ? this : parent->getRoot();
    }

    template <typename Function>
    void callListeners (Function fn) const
    {
        auto numListeners = valueTreesWithListeners.size();

        if (numListeners == 1)
        {
            fn (valueTreesWithListeners.getUnchecked(0)->listeners);
        }
        else if (numListeners > 0)
        {
            auto listenersCopy = valueTreesWithListeners;

            for (int i = 0; i < numListeners; ++i)
            {
                auto* v = listenersCopy.getUnchecked(i);

                if (i == 0 || valueTreesWithListeners.contains (v))
                    fn (v->listeners);
            }
        }
    }

    template <typename Function>
    void callListenersForAllParents (Function fn) const
    {
        for (auto* t = this; t != nullptr; t = t->parent)
            t->callListeners (fn);
    }

    void sendPropertyChangeMessage (const Identifier& property, ValueTree::Listener* listenerToExclude = nullptr)
    {
        ValueTree tree (this);

        callListenersForAllParents ([&] (ListenerList<Listener>& list) { list.callExcluding (listenerToExclude, &ValueTree::Listener::valueTreePropertyChanged, tree, property); });
    }

    void sendChildAddedMessage (ValueTree child)
    {
        ValueTree tree (this);
        callListenersForAllParents ([&] (ListenerList<Listener>& list) { list.call (&ValueTree::Listener::valueTreeChildAdded, tree, child); });
    }

    void sendChildRemovedMessage (ValueTree child, int index)
    {
        ValueTree tree (this);
        callListenersForAllParents ([=, &tree, &child] (ListenerList<Listener>& list) { list.call (&ValueTree::Listener::valueTreeChildRemoved, tree, child, index); });
    }

    void sendChildOrderChangedMessage (int oldIndex, int newIndex)
    {
        ValueTree tree (this);
        callListenersForAllParents ([=, &tree] (ListenerList<Listener>& list) { list.call (&ValueTree::Listener::valueTreeChildOrderChanged, tree, oldIndex, newIndex); });
    }

    void sendParentChangeMessage()
    {
        ValueTree tree (this);

        for (int j = children.size(); --j >= 0;)
            if (auto* child = children.getObjectPointer (j))
                child->sendParentChangeMessage();

        callListeners ([&] (ListenerList<Listener>& list) { list.call (&ValueTree::Listener::valueTreeParentChanged, tree); });
    }

    void setProperty (const Identifier& name, const var& newValue, UndoManager* const undoManager,
                      ValueTree::Listener* listenerToExclude = nullptr)
    {
        if (undoManager == nullptr)
        {
            if (properties.set (name, newValue))
                sendPropertyChangeMessage (name, listenerToExclude);
        }
        else
        {
            if (auto* existingValue = properties.getVarPointer (name))
            {
                if (*existingValue != newValue)
                    undoManager->perform (new SetPropertyAction (this, name, newValue, *existingValue, false, false, listenerToExclude));
            }
            else
            {
                undoManager->perform (new SetPropertyAction (this, name, newValue, {}, true, false, listenerToExclude));
            }
        }
    }

    bool hasProperty (const Identifier& name) const noexcept
    {
        return properties.contains (name);
    }

    void removeProperty (const Identifier& name, UndoManager* const undoManager)
    {
        if (undoManager == nullptr)
        {
            if (properties.remove (name))
                sendPropertyChangeMessage (name);
        }
        else
        {
            if (properties.contains (name))
                undoManager->perform (new SetPropertyAction (this, name, {}, properties [name], false, true));
        }
    }

    void removeAllProperties (UndoManager* const undoManager)
    {
        if (undoManager == nullptr)
        {
            while (properties.size() > 0)
            {
                auto name = properties.getName (properties.size() - 1);
                properties.remove (name);
                sendPropertyChangeMessage (name);
            }
        }
        else
        {
            for (int i = properties.size(); --i >= 0;)
                undoManager->perform (new SetPropertyAction (this, properties.getName(i), {},
                                                             properties.getValueAt(i), false, true));
        }
    }

    void copyPropertiesFrom (const SharedObject& source, UndoManager* const undoManager)
    {
        for (int i = properties.size(); --i >= 0;)
            if (! source.properties.contains (properties.getName (i)))
                removeProperty (properties.getName (i), undoManager);

        for (int i = 0; i < source.properties.size(); ++i)
            setProperty (source.properties.getName(i), source.properties.getValueAt(i), undoManager);
    }

    ValueTree getChildWithName (const Identifier& typeToMatch) const
    {
        for (auto* s : children)
            if (s->type == typeToMatch)
                return ValueTree (s);

        return {};
    }

    ValueTree getOrCreateChildWithName (const Identifier& typeToMatch, UndoManager* undoManager)
    {
        for (auto* s : children)
            if (s->type == typeToMatch)
                return ValueTree (s);

        auto newObject = new SharedObject (typeToMatch);
        addChild (newObject, -1, undoManager);
        return ValueTree (newObject);

    }

    ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const
    {
        for (auto* s : children)
            if (s->properties[propertyName] == propertyValue)
                return ValueTree (s);

        return {};
    }

    bool isAChildOf (const SharedObject* const possibleParent) const noexcept
    {
        for (auto* p = parent; p != nullptr; p = p->parent)
            if (p == possibleParent)
                return true;

        return false;
    }

    int indexOf (const ValueTree& child) const noexcept
    {
        return children.indexOf (child.object);
    }

    void addChild (SharedObject* child, int index, UndoManager* const undoManager)
    {
        if (child != nullptr && child->parent != this)
        {
            if (child != this && ! isAChildOf (child))
            {
                // You should always make sure that a child is removed from its previous parent before
                // adding it somewhere else - otherwise, it's ambiguous as to whether a different
                // undomanager should be used when removing it from its current parent..
                jassert (child->parent == nullptr);

                if (child->parent != nullptr)
                {
                    jassert (child->parent->children.indexOf (child) >= 0);
                    child->parent->removeChild (child->parent->children.indexOf (child), undoManager);
                }

                if (undoManager == nullptr)
                {
                    children.insert (index, child);
                    child->parent = this;
                    sendChildAddedMessage (ValueTree (child));
                    child->sendParentChangeMessage();
                }
                else
                {
                    if (! isPositiveAndBelow (index, children.size()))
                        index = children.size();

                    undoManager->perform (new AddOrRemoveChildAction (this, index, child));
                }
            }
            else
            {
                // You're attempting to create a recursive loop! A node
                // can't be a child of one of its own children!
                jassertfalse;
            }
        }
    }

    void removeChild (const int childIndex, UndoManager* const undoManager)
    {
        if (const Ptr child = children.getObjectPointer (childIndex))
        {
            if (undoManager == nullptr)
            {
                children.remove (childIndex);
                child->parent = nullptr;
                sendChildRemovedMessage (ValueTree (child), childIndex);
                child->sendParentChangeMessage();
            }
            else
            {
                undoManager->perform (new AddOrRemoveChildAction (this, childIndex, nullptr));
            }
        }
    }

    void removeAllChildren (UndoManager* const undoManager)
    {
        while (children.size() > 0)
            removeChild (children.size() - 1, undoManager);
    }

    void moveChild (int currentIndex, int newIndex, UndoManager* undoManager)
    {
        // The source index must be a valid index!
        jassert (isPositiveAndBelow (currentIndex, children.size()));

        if (currentIndex != newIndex
             && isPositiveAndBelow (currentIndex, children.size()))
        {
            if (undoManager == nullptr)
            {
                children.move (currentIndex, newIndex);
                sendChildOrderChangedMessage (currentIndex, newIndex);
            }
            else
            {
                if (! isPositiveAndBelow (newIndex, children.size()))
                    newIndex = children.size() - 1;

                undoManager->perform (new MoveChildAction (this, currentIndex, newIndex));
            }
        }
    }

    void reorderChildren (const OwnedArray<ValueTree>& newOrder, UndoManager* undoManager)
    {
        jassert (newOrder.size() == children.size());

        for (int i = 0; i < children.size(); ++i)
        {
            auto* child = newOrder.getUnchecked(i)->object.get();

            if (children.getObjectPointerUnchecked (i) != child)
            {
                const int oldIndex = children.indexOf (child);
                jassert (oldIndex >= 0);
                moveChild (oldIndex, i, undoManager);
            }
        }
    }

    bool isEquivalentTo (const SharedObject& other) const noexcept
    {
        if (type != other.type
             || properties.size() != other.properties.size()
             || children.size() != other.children.size()
             || properties != other.properties)
            return false;

        for (int i = 0; i < children.size(); ++i)
            if (! children.getObjectPointerUnchecked(i)->isEquivalentTo (*other.children.getObjectPointerUnchecked(i)))
                return false;

        return true;
    }

    XmlElement* createXml() const
    {
        auto xml = new XmlElement (type);
        properties.copyToXmlAttributes (*xml);

        // (NB: it's faster to add nodes to XML elements in reverse order)
        for (int i = children.size(); --i >= 0;)
            xml->prependChildElement (children.getObjectPointerUnchecked(i)->createXml());

        return xml;
    }

    void writeToStream (OutputStream& output) const
    {
        output.writeString (type.toString());
        output.writeCompressedInt (properties.size());

        for (int j = 0; j < properties.size(); ++j)
        {
            output.writeString (properties.getName (j).toString());
            properties.getValueAt(j).writeToStream (output);
        }

        output.writeCompressedInt (children.size());

        for (int i = 0; i < children.size(); ++i)
            writeObjectToStream (output, children.getObjectPointerUnchecked(i));
    }

    static void writeObjectToStream (OutputStream& output, const SharedObject* const object)
    {
        if (object != nullptr)
        {
            object->writeToStream (output);
        }
        else
        {
            output.writeString (String());
            output.writeCompressedInt (0);
            output.writeCompressedInt (0);
        }
    }

    //==============================================================================
    struct SetPropertyAction  : public UndoableAction
    {
        SetPropertyAction (SharedObject* const so, const Identifier& propertyName,
                           const var& newVal, const var& oldVal, bool isAdding, bool isDeleting,
                           ValueTree::Listener* listenerToExclude = nullptr)
            : target (so), name (propertyName), newValue (newVal), oldValue (oldVal),
              isAddingNewProperty (isAdding), isDeletingProperty (isDeleting),
              excludeListener (listenerToExclude)
        {
        }

        bool perform() override
        {
            jassert (! (isAddingNewProperty && target->hasProperty (name)));

            if (isDeletingProperty)
                target->removeProperty (name, nullptr);
            else
                target->setProperty (name, newValue, nullptr, excludeListener);

            return true;
        }

        bool undo() override
        {
            if (isAddingNewProperty)
                target->removeProperty (name, nullptr);
            else
                target->setProperty (name, oldValue, nullptr);

            return true;
        }

        int getSizeInUnits() override
        {
            return (int) sizeof (*this); //xxx should be more accurate
        }

        UndoableAction* createCoalescedAction (UndoableAction* nextAction) override
        {
            if (! (isAddingNewProperty || isDeletingProperty))
            {
                if (SetPropertyAction* const next = dynamic_cast<SetPropertyAction*> (nextAction))
                    if (next->target == target && next->name == name
                          && ! (next->isAddingNewProperty || next->isDeletingProperty))
                        return new SetPropertyAction (target, name, next->newValue, oldValue, false, false);
            }

            return nullptr;
        }

    private:
        const Ptr target;
        const Identifier name;
        const var newValue;
        var oldValue;
        const bool isAddingNewProperty : 1, isDeletingProperty : 1;
        ValueTree::Listener* excludeListener;

        JUCE_DECLARE_NON_COPYABLE (SetPropertyAction)
    };

    //==============================================================================
    struct AddOrRemoveChildAction  : public UndoableAction
    {
        AddOrRemoveChildAction (SharedObject* parentObject, int index, SharedObject* newChild)
            : target (parentObject),
              child (newChild != nullptr ? newChild : parentObject->children.getObjectPointer (index)),
              childIndex (index),
              isDeleting (newChild == nullptr)
        {
            jassert (child != nullptr);
        }

        bool perform() override
        {
            if (isDeleting)
                target->removeChild (childIndex, nullptr);
            else
                target->addChild (child, childIndex, nullptr);

            return true;
        }

        bool undo() override
        {
            if (isDeleting)
            {
                target->addChild (child, childIndex, nullptr);
            }
            else
            {
                // If you hit this, it seems that your object's state is getting confused - probably
                // because you've interleaved some undoable and non-undoable operations?
                jassert (childIndex < target->children.size());
                target->removeChild (childIndex, nullptr);
            }

            return true;
        }

        int getSizeInUnits() override
        {
            return (int) sizeof (*this); //xxx should be more accurate
        }

    private:
        const Ptr target, child;
        const int childIndex;
        const bool isDeleting;

        JUCE_DECLARE_NON_COPYABLE (AddOrRemoveChildAction)
    };

    //==============================================================================
    struct MoveChildAction  : public UndoableAction
    {
        MoveChildAction (SharedObject* parentObject, int fromIndex, int toIndex) noexcept
            : parent (parentObject), startIndex (fromIndex), endIndex (toIndex)
        {
        }

        bool perform() override
        {
            parent->moveChild (startIndex, endIndex, nullptr);
            return true;
        }

        bool undo() override
        {
            parent->moveChild (endIndex, startIndex, nullptr);
            return true;
        }

        int getSizeInUnits() override
        {
            return (int) sizeof (*this); //xxx should be more accurate
        }

        UndoableAction* createCoalescedAction (UndoableAction* nextAction) override
        {
            if (auto* next = dynamic_cast<MoveChildAction*> (nextAction))
                if (next->parent == parent && next->startIndex == endIndex)
                    return new MoveChildAction (parent, startIndex, next->endIndex);

            return nullptr;
        }

    private:
        const Ptr parent;
        const int startIndex, endIndex;

        JUCE_DECLARE_NON_COPYABLE (MoveChildAction)
    };

    //==============================================================================
    const Identifier type;
    NamedValueSet properties;
    ReferenceCountedArray<SharedObject> children;
    SortedSet<ValueTree*> valueTreesWithListeners;
    SharedObject* parent = nullptr;

private:
    SharedObject& operator= (const SharedObject&);
    JUCE_LEAK_DETECTOR (SharedObject)
};

//==============================================================================
ValueTree::ValueTree() noexcept
{
}

#if JUCE_ALLOW_STATIC_NULL_VARIABLES
const ValueTree ValueTree::invalid;
#endif

ValueTree::ValueTree (const Identifier& type)  : object (new ValueTree::SharedObject (type))
{
    jassert (type.toString().isNotEmpty()); // All objects must be given a sensible type name!
}

ValueTree::ValueTree (SharedObject* so) noexcept  : object (so)
{
}

ValueTree::ValueTree (const ValueTree& other) noexcept  : object (other.object)
{
}

ValueTree& ValueTree::operator= (const ValueTree& other)
{
    if (object != other.object)
    {
        if (listeners.isEmpty())
        {
            object = other.object;
        }
        else
        {
            if (object != nullptr)
                object->valueTreesWithListeners.removeValue (this);

            if (other.object != nullptr)
                other.object->valueTreesWithListeners.add (this);

            object = other.object;

            listeners.call (&ValueTree::Listener::valueTreeRedirected, *this);
        }
    }

    return *this;
}

ValueTree::ValueTree (ValueTree&& other) noexcept
    : object (static_cast<SharedObject::Ptr&&> (other.object))
{
}

ValueTree::~ValueTree()
{
    if (listeners.size() > 0 && object != nullptr)
        object->valueTreesWithListeners.removeValue (this);
}

bool ValueTree::operator== (const ValueTree& other) const noexcept
{
    return object == other.object;
}

bool ValueTree::operator!= (const ValueTree& other) const noexcept
{
    return object != other.object;
}

bool ValueTree::isEquivalentTo (const ValueTree& other) const
{
    return object == other.object
            || (object != nullptr && other.object != nullptr
                 && object->isEquivalentTo (*other.object));
}

ValueTree ValueTree::createCopy() const
{
    return ValueTree (createCopyIfNotNull (object.get()));
}

bool ValueTree::hasType (const Identifier& typeName) const noexcept
{
    return object != nullptr && object->type == typeName;
}

Identifier ValueTree::getType() const noexcept
{
    return object != nullptr ? object->type : Identifier();
}

ValueTree ValueTree::getParent() const noexcept
{
    return ValueTree (object != nullptr ? object->parent
                                        : static_cast<SharedObject*> (nullptr));
}

ValueTree ValueTree::getRoot() const noexcept
{
    return ValueTree (object != nullptr ? object->getRoot()
                                        : static_cast<SharedObject*> (nullptr));
}

ValueTree ValueTree::getSibling (const int delta) const noexcept
{
    if (object == nullptr || object->parent == nullptr)
        return {};

    const int index = object->parent->indexOf (*this) + delta;
    return ValueTree (object->parent->children.getObjectPointer (index));
}

static const var& getNullVarRef() noexcept
{
   #if JUCE_ALLOW_STATIC_NULL_VARIABLES
    return var::null;
   #else
    static var nullVar;
    return nullVar;
   #endif
}

const var& ValueTree::operator[] (const Identifier& name) const noexcept
{
    return object == nullptr ? getNullVarRef() : object->properties[name];
}

const var& ValueTree::getProperty (const Identifier& name) const noexcept
{
    return object == nullptr ? getNullVarRef() : object->properties[name];
}

var ValueTree::getProperty (const Identifier& name, const var& defaultReturnValue) const
{
    return object == nullptr ? defaultReturnValue
                             : object->properties.getWithDefault (name, defaultReturnValue);
}

const var* ValueTree::getPropertyPointer (const Identifier& name) const noexcept
{
    return object == nullptr ? nullptr
                             : object->properties.getVarPointer (name);
}

ValueTree& ValueTree::setProperty (const Identifier& name, const var& newValue, UndoManager* undoManager)
{
    return setPropertyExcludingListener (nullptr, name, newValue, undoManager);
}

ValueTree& ValueTree::setPropertyExcludingListener (Listener* listenerToExclude, const Identifier& name, const var& newValue, UndoManager* undoManager)
{
    jassert (name.toString().isNotEmpty()); // Must have a valid property name!
    jassert (object != nullptr); // Trying to add a property to a null ValueTree will fail!

    if (object != nullptr)
        object->setProperty (name, newValue, undoManager, listenerToExclude);

    return *this;
}

bool ValueTree::hasProperty (const Identifier& name) const noexcept
{
    return object != nullptr && object->hasProperty (name);
}

void ValueTree::removeProperty (const Identifier& name, UndoManager* const undoManager)
{
    if (object != nullptr)
        object->removeProperty (name, undoManager);
}

void ValueTree::removeAllProperties (UndoManager* const undoManager)
{
    if (object != nullptr)
        object->removeAllProperties (undoManager);
}

int ValueTree::getNumProperties() const noexcept
{
    return object == nullptr ? 0 : object->properties.size();
}

Identifier ValueTree::getPropertyName (const int index) const noexcept
{
    return object == nullptr ? Identifier()
                             : object->properties.getName (index);
}

void ValueTree::copyPropertiesFrom (const ValueTree& source, UndoManager* const undoManager)
{
    jassert (object != nullptr || source.object == nullptr); // Trying to add properties to a null ValueTree will fail!

    if (source.object == nullptr)
        removeAllProperties (undoManager);
    else if (object != nullptr)
        object->copyPropertiesFrom (*(source.object), undoManager);
}

int ValueTree::getReferenceCount() const noexcept
{
    return object != nullptr ? object->getReferenceCount() : 0;
}

//==============================================================================
class ValueTreePropertyValueSource  : public Value::ValueSource,
                                      private ValueTree::Listener
{
public:
    ValueTreePropertyValueSource (const ValueTree& vt, const Identifier& prop, UndoManager* um)
        : tree (vt), property (prop), undoManager (um)
    {
        tree.addListener (this);
    }

    ~ValueTreePropertyValueSource()
    {
        tree.removeListener (this);
    }

    var getValue() const override                 { return tree [property]; }
    void setValue (const var& newValue) override  { tree.setProperty (property, newValue, undoManager); }

private:
    ValueTree tree;
    const Identifier property;
    UndoManager* const undoManager;

    void valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty) override
    {
        if (tree == changedTree && property == changedProperty)
            sendChangeMessage (false);
    }

    void valueTreeChildAdded (ValueTree&, ValueTree&) override {}
    void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override {}
    void valueTreeChildOrderChanged (ValueTree&, int, int) override {}
    void valueTreeParentChanged (ValueTree&) override {}

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreePropertyValueSource)
};

Value ValueTree::getPropertyAsValue (const Identifier& name, UndoManager* const undoManager)
{
    return Value (new ValueTreePropertyValueSource (*this, name, undoManager));
}

//==============================================================================
int ValueTree::getNumChildren() const noexcept
{
    return object == nullptr ? 0 : object->children.size();
}

ValueTree ValueTree::getChild (int index) const
{
    return ValueTree (object != nullptr ? object->children.getObjectPointer (index)
                                        : static_cast<SharedObject*> (nullptr));
}

ValueTree::Iterator::Iterator (const ValueTree& v, bool isEnd) noexcept
   : internal (v.object != nullptr ? (isEnd ? v.object->children.end() : v.object->children.begin()) : nullptr)
{
}

ValueTree::Iterator& ValueTree::Iterator::operator++() noexcept
{
    internal = static_cast<SharedObject**> (internal) + 1;
    return *this;
}

bool ValueTree::Iterator::operator!= (const Iterator& other) const noexcept
{
    return internal != other.internal;
}

ValueTree ValueTree::Iterator::operator*() const
{
    return ValueTree (*static_cast<SharedObject**> (internal));
}

ValueTree::Iterator ValueTree::begin() const noexcept   { return Iterator (*this, false); }
ValueTree::Iterator ValueTree::end() const noexcept     { return Iterator (*this, true); }

ValueTree ValueTree::getChildWithName (const Identifier& type) const
{
    return object != nullptr ? object->getChildWithName (type) : ValueTree();
}

ValueTree ValueTree::getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager)
{
    return object != nullptr ? object->getOrCreateChildWithName (type, undoManager) : ValueTree();
}

ValueTree ValueTree::getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const
{
    return object != nullptr ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree();
}

bool ValueTree::isAChildOf (const ValueTree& possibleParent) const noexcept
{
    return object != nullptr && object->isAChildOf (possibleParent.object);
}

int ValueTree::indexOf (const ValueTree& child) const noexcept
{
    return object != nullptr ? object->indexOf (child) : -1;
}

void ValueTree::addChild (const ValueTree& child, int index, UndoManager* const undoManager)
{
    jassert (object != nullptr); // Trying to add a child to a null ValueTree!

    if (object != nullptr)
        object->addChild (child.object, index, undoManager);
}

void ValueTree::removeChild (const int childIndex, UndoManager* const undoManager)
{
    if (object != nullptr)
        object->removeChild (childIndex, undoManager);
}

void ValueTree::removeChild (const ValueTree& child, UndoManager* const undoManager)
{
    if (object != nullptr)
        object->removeChild (object->children.indexOf (child.object), undoManager);
}

void ValueTree::removeAllChildren (UndoManager* const undoManager)
{
    if (object != nullptr)
        object->removeAllChildren (undoManager);
}

void ValueTree::moveChild (int currentIndex, int newIndex, UndoManager* undoManager)
{
    if (object != nullptr)
        object->moveChild (currentIndex, newIndex, undoManager);
}

//==============================================================================
void ValueTree::createListOfChildren (OwnedArray<ValueTree>& list) const
{
    jassert (object != nullptr);

    for (int i = 0; i < object->children.size(); ++i)
        list.add (new ValueTree (object->children.getObjectPointerUnchecked(i)));
}

void ValueTree::reorderChildren (const OwnedArray<ValueTree>& newOrder, UndoManager* undoManager)
{
    jassert (object != nullptr);
    object->reorderChildren (newOrder, undoManager);
}

//==============================================================================
void ValueTree::addListener (Listener* listener)
{
    if (listener != nullptr)
    {
        if (listeners.isEmpty() && object != nullptr)
            object->valueTreesWithListeners.add (this);

        listeners.add (listener);
    }
}

void ValueTree::removeListener (Listener* listener)
{
    listeners.remove (listener);

    if (listeners.isEmpty() && object != nullptr)
        object->valueTreesWithListeners.removeValue (this);
}

void ValueTree::sendPropertyChangeMessage (const Identifier& property)
{
    if (object != nullptr)
        object->sendPropertyChangeMessage (property);
}

//==============================================================================
XmlElement* ValueTree::createXml() const
{
    return object != nullptr ? object->createXml() : nullptr;
}

ValueTree ValueTree::fromXml (const XmlElement& xml)
{
    if (! xml.isTextElement())
    {
        ValueTree v (xml.getTagName());
        v.object->properties.setFromXmlAttributes (xml);

        forEachXmlChildElement (xml, e)
            v.addChild (fromXml (*e), -1, nullptr);

        return v;
    }

    // ValueTrees don't have any equivalent to XML text elements!
    jassertfalse;
    return {};
}

String ValueTree::toXmlString() const
{
    const ScopedPointer<XmlElement> xml (createXml());
    return xml != nullptr ? xml->createDocument (StringRef()) : String();
}

//==============================================================================
void ValueTree::writeToStream (OutputStream& output) const
{
    SharedObject::writeObjectToStream (output, object);
}

ValueTree ValueTree::readFromStream (InputStream& input)
{
    auto type = input.readString();

    if (type.isEmpty())
        return {};

    ValueTree v (type);

    const int numProps = input.readCompressedInt();

    if (numProps < 0)
    {
        jassertfalse;  // trying to read corrupted data!
        return v;
    }

    for (int i = 0; i < numProps; ++i)
    {
        const String name (input.readString());

        if (name.isNotEmpty())
        {
            const var value (var::readFromStream (input));
            v.object->properties.set (name, value);
        }
        else
        {
            jassertfalse;  // trying to read corrupted data!
        }
    }

    const int numChildren = input.readCompressedInt();
    v.object->children.ensureStorageAllocated (numChildren);

    for (int i = 0; i < numChildren; ++i)
    {
        ValueTree child (readFromStream (input));

        if (! child.isValid())
            return v;

        v.object->children.add (child.object);
        child.object->parent = v.object;
    }

    return v;
}

ValueTree ValueTree::readFromData (const void* const data, const size_t numBytes)
{
    MemoryInputStream in (data, numBytes, false);
    return readFromStream (in);
}

ValueTree ValueTree::readFromGZIPData (const void* const data, const size_t numBytes)
{
    MemoryInputStream in (data, numBytes, false);
    GZIPDecompressorInputStream gzipStream (in);
    return readFromStream (gzipStream);
}

void ValueTree::Listener::valueTreeRedirected (ValueTree&) {}

//==============================================================================
#if JUCE_UNIT_TESTS

class ValueTreeTests  : public UnitTest
{
public:
    ValueTreeTests() : UnitTest ("ValueTrees", "Values") {}

    static String createRandomIdentifier (Random& r)
    {
        char buffer[50] = { 0 };
        const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:";

        for (int i = 1 + r.nextInt (numElementsInArray (buffer) - 2); --i >= 0;)
            buffer[i] = chars [r.nextInt (sizeof (chars) - 1)];

        String result (buffer);

        if (! XmlElement::isValidXmlName (result))
            result = createRandomIdentifier (r);

        return result;
    }

    static String createRandomWideCharString (Random& r)
    {
        juce_wchar buffer[50] = { 0 };

        for (int i = r.nextInt (numElementsInArray (buffer) - 1); --i >= 0;)
        {
            if (r.nextBool())
            {
                do
                {
                    buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1));
                }
                while (! CharPointer_UTF16::canRepresent (buffer[i]));
            }
            else
                buffer[i] = (juce_wchar) (1 + r.nextInt (0x7e));
        }

        return CharPointer_UTF32 (buffer);
    }

    static ValueTree createRandomTree (UndoManager* undoManager, int depth, Random& r)
    {
        ValueTree v (createRandomIdentifier (r));

        for (int i = r.nextInt (10); --i >= 0;)
        {
            switch (r.nextInt (5))
            {
                case 0: v.setProperty (createRandomIdentifier (r), createRandomWideCharString (r), undoManager); break;
                case 1: v.setProperty (createRandomIdentifier (r), r.nextInt(), undoManager); break;
                case 2: if (depth < 5) v.addChild (createRandomTree (undoManager, depth + 1, r), r.nextInt (v.getNumChildren() + 1), undoManager); break;
                case 3: v.setProperty (createRandomIdentifier (r), r.nextBool(), undoManager); break;
                case 4: v.setProperty (createRandomIdentifier (r), r.nextDouble(), undoManager); break;
                default: break;
            }
        }

        return v;
    }

    void runTest() override
    {
        beginTest ("ValueTree");
        Random r = getRandom();

        for (int i = 10; --i >= 0;)
        {
            MemoryOutputStream mo;
            ValueTree v1 (createRandomTree (nullptr, 0, r));
            v1.writeToStream (mo);

            MemoryInputStream mi (mo.getData(), mo.getDataSize(), false);
            ValueTree v2 = ValueTree::readFromStream (mi);
            expect (v1.isEquivalentTo (v2));

            ScopedPointer<XmlElement> xml1 (v1.createXml());
            ScopedPointer<XmlElement> xml2 (v2.createCopy().createXml());
            expect (xml1->isEquivalentTo (xml2, false));

            ValueTree v4 = v2.createCopy();
            expect (v1.isEquivalentTo (v4));
        }
    }
};

static ValueTreeTests valueTreeTests;

#endif

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    A powerful tree structure that can be used to hold free-form data, and which can
    handle its own undo and redo behaviour.

    A ValueTree contains a list of named properties as var objects, and also holds
    any number of sub-trees.

    Create ValueTree objects on the stack, and don't be afraid to copy them around, as
    they're simply a lightweight reference to a shared data container. Creating a copy
    of another ValueTree simply creates a new reference to the same underlying object - to
    make a separate, deep copy of a tree you should explicitly call createCopy().

    Each ValueTree has a type name, in much the same way as an XmlElement has a tag name,
    and much of the structure of a ValueTree is similar to an XmlElement tree.
    You can convert a ValueTree to and from an XmlElement, and as long as the XML doesn't
    contain text elements, the conversion works well and makes a good serialisation
    format. They can also be serialised to a binary format, which is very fast and compact.

    All the methods that change data take an optional UndoManager, which will be used
    to track any changes to the object. For this to work, you have to be careful to
    consistently always use the same UndoManager for all operations to any node inside
    the tree.

    A ValueTree can only be a child of one parent at a time, so if you're moving one from
    one tree to another, be careful to always remove it first, before adding it. This
    could also mess up your undo/redo chain, so be wary! In a debug build you should hit
    assertions if you try to do anything dangerous, but there are still plenty of ways it
    could go wrong.

    Note that although the children in a tree have a fixed order, the properties are not
    guaranteed to be stored in any particular order, so don't expect that a property's index
    will correspond to the order in which the property was added, or that it will remain
    constant when other properties are added or removed.

    Listeners can be added to a ValueTree to be told when properies change and when
    nodes are added or removed.

    @see var, XmlElement
*/
class JUCE_API  ValueTree  final
{
public:
    //==============================================================================
    /** Creates an empty, invalid ValueTree.

        A ValueTree that is created with this constructor can't actually be used for anything,
        it's just a default 'null' ValueTree that can be returned to indicate some sort of failure.
        To create a real one, use the constructor that takes a string.
    */
    ValueTree() noexcept;

    /** Creates an empty ValueTree with the given type name.
        Like an XmlElement, each ValueTree node has a type, which you can access with
        getType() and hasType().
    */
    explicit ValueTree (const Identifier& type);

    /** Creates a reference to another ValueTree. */
    ValueTree (const ValueTree&) noexcept;

    /** Makes this object reference another node. */
    ValueTree& operator= (const ValueTree&);

    /** Move constructor */
    ValueTree (ValueTree&&) noexcept;

    /** Destructor. */
    ~ValueTree();

    /** Returns true if both this and the other tree node refer to the same underlying structure.
        Note that this isn't a value comparison - two independently-created trees which
        contain identical data are not considered equal.
    */
    bool operator== (const ValueTree&) const noexcept;

    /** Returns true if this and the other node refer to different underlying structures.
        Note that this isn't a value comparison - two independently-created trees which
        contain identical data are not considered equal.
    */
    bool operator!= (const ValueTree&) const noexcept;

    /** Performs a deep comparison between the properties and children of two trees.
        If all the properties and children of the two trees are the same (recursively), this
        returns true.
        The normal operator==() only checks whether two trees refer to the same shared data
        structure, so use this method if you need to do a proper value comparison.
    */
    bool isEquivalentTo (const ValueTree&) const;

    //==============================================================================
    /** Returns true if this node refers to some valid data.
        It's hard to create an invalid node, but you might get one returned, e.g. by an out-of-range
        call to getChild().
    */
    bool isValid() const noexcept                           { return object != nullptr; }

    /** Returns a deep copy of this tree and all its sub-nodes. */
    ValueTree createCopy() const;

    //==============================================================================
    /** Returns the type of this node.
        The type is specified when the ValueTree is created.
        @see hasType
    */
    Identifier getType() const noexcept;

    /** Returns true if the node has this type.
        The comparison is case-sensitive.
    */
    bool hasType (const Identifier& typeName) const noexcept;

    //==============================================================================
    /** Returns the value of a named property.
        If no such property has been set, this will return a void variant.
        You can also use operator[] to get a property.
        @see var, setProperty, getPropertyPointer, hasProperty
    */
    const var& getProperty (const Identifier& name) const noexcept;

    /** Returns the value of a named property, or the value of defaultReturnValue
        if the property doesn't exist.
        You can also use operator[] and getProperty to get a property.
        @see var, getProperty, getPropertyPointer, setProperty, hasProperty
    */
    var getProperty (const Identifier& name, const var& defaultReturnValue) const;

    /** Returns a pointer to the value of a named property, or nullptr if the property
        doesn't exist.
        @see var, getProperty, setProperty, hasProperty
    */
    const var* getPropertyPointer (const Identifier& name) const noexcept;

    /** Returns the value of a named property.
        If no such property has been set, this will return a void variant. This is the same as
        calling getProperty().
        @see getProperty
    */
    const var& operator[] (const Identifier& name) const noexcept;

    /** Changes a named property of the node.
        The name identifier must not be an empty string.
        If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
        so that this change can be undone.
        @see var, getProperty, removeProperty
        @returns a reference to the value tree, so that you can daisy-chain calls to this method.
    */
    ValueTree& setProperty (const Identifier& name, const var& newValue, UndoManager* undoManager);

    /** Returns true if the node contains a named property. */
    bool hasProperty (const Identifier& name) const noexcept;

    /** Removes a property from the node.
        If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
        so that this change can be undone.
    */
    void removeProperty (const Identifier& name, UndoManager* undoManager);

    /** Removes all properties from the node.
        If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
        so that this change can be undone.
    */
    void removeAllProperties (UndoManager* undoManager);

    /** Returns the total number of properties that the node contains.
        @see getProperty.
    */
    int getNumProperties() const noexcept;

    /** Returns the identifier of the property with a given index.
        Note that properties are not guaranteed to be stored in any particular order, so don't
        expect that the index will correspond to the order in which the property was added, or
        that it will remain constant when other properties are added or removed.
        @see getNumProperties
    */
    Identifier getPropertyName (int index) const noexcept;

    /** Returns a Value object that can be used to control and respond to one of the tree's properties.

        The Value object will maintain a reference to this tree, and will use the undo manager when
        it needs to change the value. Attaching a Value::Listener to the value object will provide
        callbacks whenever the property changes.
    */
    Value getPropertyAsValue (const Identifier& name, UndoManager* undoManager);

    /** Overwrites all the properties in this tree with the properties of the source tree.
        Any properties that already exist will be updated; and new ones will be added, and
        any that are not present in the source tree will be removed.
    */
    void copyPropertiesFrom (const ValueTree& source, UndoManager* undoManager);

    //==============================================================================
    /** Returns the number of child nodes belonging to this one.
        @see getChild
    */
    int getNumChildren() const noexcept;

    /** Returns one of this node's child nodes.
        If the index is out of range, it'll return an invalid node. (See isValid() to find out
        whether a node is valid).
    */
    ValueTree getChild (int index) const;

    /** Returns the first child node with the specified type name.
        If no such node is found, it'll return an invalid node. (See isValid() to find out
        whether a node is valid).
        @see getOrCreateChildWithName
    */
    ValueTree getChildWithName (const Identifier& type) const;

    /** Returns the first child node with the specified type name, creating and adding
        a child with this name if there wasn't already one there.

        The only time this will return an invalid object is when the object that you're calling
        the method on is itself invalid.
        @see getChildWithName
    */
    ValueTree getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager);

    /** Looks for the first child node that has the specified property value.

        This will scan the child nodes in order, until it finds one that has property that matches
        the specified value.

        If no such node is found, it'll return an invalid node. (See isValid() to find out
        whether a node is valid).
    */
    ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const;

    /** Adds a child to this node.

        Make sure that the child is removed from any former parent node before calling this, or
        you'll hit an assertion.

        If the index is < 0 or greater than the current number of child nodes, the new node will
        be added at the end of the list.

        If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
        so that this change can be undone.
    */
    void addChild (const ValueTree& child, int index, UndoManager* undoManager);

    /** Removes the specified child from this node's child-list.
        If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
        so that this change can be undone.
    */
    void removeChild (const ValueTree& child, UndoManager* undoManager);

    /** Removes a child from this node's child-list.
        If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
        so that this change can be undone.
    */
    void removeChild (int childIndex, UndoManager* undoManager);

    /** Removes all child-nodes from this node.
        If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
        so that this change can be undone.
    */
    void removeAllChildren (UndoManager* undoManager);

    /** Moves one of the children to a different index.

        This will move the child to a specified index, shuffling along any intervening
        items as required. So for example, if you have a list of { 0, 1, 2, 3, 4, 5 }, then
        calling move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }.

        @param currentIndex     the index of the item to be moved. If this isn't a
                                valid index, then nothing will be done
        @param newIndex         the index at which you'd like this item to end up. If this
                                is less than zero, the value will be moved to the end
                                of the list
        @param undoManager      the optional UndoManager to use to store this transaction
    */
    void moveChild (int currentIndex, int newIndex, UndoManager* undoManager);

    /** Returns true if this node is anywhere below the specified parent node.
        This returns true if the node is a child-of-a-child, as well as a direct child.
    */
    bool isAChildOf (const ValueTree& possibleParent) const noexcept;

    /** Returns the index of a child item in this parent.
        If the child isn't found, this returns -1.
    */
    int indexOf (const ValueTree& child) const noexcept;

    /** Returns the parent node that contains this one.
        If the node has no parent, this will return an invalid node. (See isValid() to find out
        whether a node is valid).
    */
    ValueTree getParent() const noexcept;

    /** Recusrively finds the highest-level parent node that contains this one.
        If the node has no parent, this will return itself.
    */
    ValueTree getRoot() const noexcept;

    /** Returns one of this node's siblings in its parent's child list.

        The delta specifies how far to move through the list, so a value of 1 would return the node
        that follows this one, -1 would return the node before it, 0 will return this node itself, etc.
        If the requested position is beyond the range of available nodes, this will return an empty ValueTree().
    */
    ValueTree getSibling (int delta) const noexcept;

    //==============================================================================
    struct Iterator
    {
        Iterator (const ValueTree&, bool isEnd) noexcept;
        Iterator& operator++() noexcept;

        bool operator!= (const Iterator&) const noexcept;
        ValueTree operator*() const;

    private:
        void* internal;
    };

    /** Returns a start iterator for the children in this tree. */
    Iterator begin() const noexcept;

    /** Returns an end iterator for the children in this tree. */
    Iterator end() const noexcept;

    //==============================================================================
    /** Creates an XmlElement that holds a complete image of this node and all its children.

        If this node is invalid, this may return nullptr. Otherwise, the XML that is produced can
        be used to recreate a similar node by calling fromXml().

        The caller must delete the object that is returned.

        @see fromXml
    */
    XmlElement* createXml() const;

    /** Tries to recreate a node from its XML representation.

        This isn't designed to cope with random XML data - for a sensible result, it should only
        be fed XML that was created by the createXml() method.
    */
    static ValueTree fromXml (const XmlElement& xml);

    /** This returns a string containing an XML representation of the tree.
        This is quite handy for debugging purposes, as it provides a quick way to view a tree.
    */
    String toXmlString() const;

    //==============================================================================
    /** Stores this tree (and all its children) in a binary format.

        Once written, the data can be read back with readFromStream().

        It's much faster to load/save your tree in binary form than as XML, but
        obviously isn't human-readable.
    */
    void writeToStream (OutputStream& output) const;

    /** Reloads a tree from a stream that was written with writeToStream(). */
    static ValueTree readFromStream (InputStream& input);

    /** Reloads a tree from a data block that was written with writeToStream(). */
    static ValueTree readFromData (const void* data, size_t numBytes);

    /** Reloads a tree from a data block that was written with writeToStream() and
        then zipped using GZIPCompressorOutputStream.
    */
    static ValueTree readFromGZIPData (const void* data, size_t numBytes);

    //==============================================================================
    /** Listener class for events that happen to a ValueTree.

        To get events from a ValueTree, make your class implement this interface, and use
        ValueTree::addListener() and ValueTree::removeListener() to register it.
    */
    class JUCE_API  Listener
    {
    public:
        /** Destructor. */
        virtual ~Listener() {}

        /** This method is called when a property of this node (or of one of its sub-nodes) has
            changed.

            The tree parameter indicates which tree has had its property changed, and the property
            parameter indicates the property.

            Note that when you register a listener to a tree, it will receive this callback for
            property changes in that tree, and also for any of its children, (recursively, at any depth).
            If your tree has sub-trees but you only want to know about changes to the top level tree,
            simply check the tree parameter in this callback to make sure it's the tree you're interested in.
        */
        virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged,
                                               const Identifier& property) = 0;

        /** This method is called when a child sub-tree is added.

            Note that when you register a listener to a tree, it will receive this callback for
            child changes in both that tree and any of its children, (recursively, at any depth).
            If your tree has sub-trees but you only want to know about changes to the top level tree,
            just check the parentTree parameter to make sure it's the one that you're interested in.
        */
        virtual void valueTreeChildAdded (ValueTree& parentTree,
                                          ValueTree& childWhichHasBeenAdded) = 0;

        /** This method is called when a child sub-tree is removed.

            Note that when you register a listener to a tree, it will receive this callback for
            child changes in both that tree and any of its children, (recursively, at any depth).
            If your tree has sub-trees but you only want to know about changes to the top level tree,
            just check the parentTree parameter to make sure it's the one that you're interested in.
        */
        virtual void valueTreeChildRemoved (ValueTree& parentTree,
                                            ValueTree& childWhichHasBeenRemoved,
                                            int indexFromWhichChildWasRemoved) = 0;

        /** This method is called when a tree's children have been re-shuffled.

            Note that when you register a listener to a tree, it will receive this callback for
            child changes in both that tree and any of its children, (recursively, at any depth).
            If your tree has sub-trees but you only want to know about changes to the top level tree,
            just check the parameter to make sure it's the tree that you're interested in.
        */
        virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved,
                                                 int oldIndex, int newIndex) = 0;

        /** This method is called when a tree has been added or removed from a parent node.

            This callback happens when the tree to which the listener was registered is added or
            removed from a parent. Unlike the other callbacks, it applies only to the tree to which
            the listener is registered, and not to any of its children.
        */
        virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) = 0;

        /** This method is called when a tree is made to point to a different internal shared object.
            When operator= is used to make a ValueTree refer to a different object, this callback
            will be made.
        */
        virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged);
    };

    /** Adds a listener to receive callbacks when this node is changed.

        The listener is added to this specific ValueTree object, and not to the shared
        object that it refers to. When this object is deleted, all the listeners will
        be lost, even if other references to the same ValueTree still exist. And if you
        use the operator= to make this refer to a different ValueTree, any listeners will
        begin listening to changes to the new tree instead of the old one.

        When you're adding a listener, make sure that you add it to a ValueTree instance that
        will last for as long as you need the listener. In general, you'd never want to add a
        listener to a local stack-based ValueTree, and would usually add one to a member variable.

        @see removeListener
    */
    void addListener (Listener* listener);

    /** Removes a listener that was previously added with addListener(). */
    void removeListener (Listener* listener);

    /** Changes a named property of the node, but will not notify a specified listener of the change.
        @see setProperty
    */
    ValueTree& setPropertyExcludingListener (Listener* listenerToExclude,
                                             const Identifier& name, const var& newValue,
                                             UndoManager* undoManager);

    /** Causes a property-change callback to be triggered for the specified property,
        calling any listeners that are registered.
    */
    void sendPropertyChangeMessage (const Identifier& property);

    //==============================================================================
    /** This method uses a comparator object to sort the tree's children into order.

        The object provided must have a method of the form:
        @code
        int compareElements (const ValueTree& first, const ValueTree& second);
        @endcode

        ..and this method must return:
          - a value of < 0 if the first comes before the second
          - a value of 0 if the two objects are equivalent
          - a value of > 0 if the second comes before the first

        To improve performance, the compareElements() method can be declared as static or const.

        @param comparator   the comparator to use for comparing elements.
        @param undoManager  optional UndoManager for storing the changes
        @param retainOrderOfEquivalentItems     if this is true, then items which the comparator says are
                            equivalent will be kept in the order in which they currently appear in the array.
                            This is slower to perform, but may be important in some cases. If it's false, a
                            faster algorithm is used, but equivalent elements may be rearranged.
    */
    template <typename ElementComparator>
    void sort (ElementComparator& comparator, UndoManager* undoManager, bool retainOrderOfEquivalentItems)
    {
        if (object != nullptr)
        {
            OwnedArray<ValueTree> sortedList;
            createListOfChildren (sortedList);
            ComparatorAdapter<ElementComparator> adapter (comparator);
            sortedList.sort (adapter, retainOrderOfEquivalentItems);
            reorderChildren (sortedList, undoManager);
        }
    }

   #if JUCE_ALLOW_STATIC_NULL_VARIABLES
    /** An invalid ValueTree that can be used if you need to return one as an error condition, etc.
        This invalid object is equivalent to ValueTree created with its default constructor, but
        you should always prefer to avoid it and use ValueTree() or {} instead.
    */
    static const ValueTree invalid;
   #endif

    /** Returns the total number of references to the shared underlying data structure that this
        ValueTree is using.
    */
    int getReferenceCount() const noexcept;

private:
    //==============================================================================
    JUCE_PUBLIC_IN_DLL_BUILD (class SharedObject)
    friend class SharedObject;

    ReferenceCountedObjectPtr<SharedObject> object;
    ListenerList<Listener> listeners;

    template <typename ElementComparator>
    struct ComparatorAdapter
    {
        ComparatorAdapter (ElementComparator& comp) noexcept : comparator (comp) {}

        int compareElements (const ValueTree* const first, const ValueTree* const second)
        {
            return comparator.compareElements (*first, *second);
        }

    private:
        ElementComparator& comparator;
        JUCE_DECLARE_NON_COPYABLE (ComparatorAdapter)
    };

    void createListOfChildren (OwnedArray<ValueTree>&) const;
    void reorderChildren (const OwnedArray<ValueTree>&, UndoManager*);

    explicit ValueTree (SharedObject*) noexcept;
};

} // namespace juce

repo_name: fedden/RenderMan

mime_type: text/x-c++


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/**
    Represents a shared variant value.

    A Value object contains a reference to a var object, and can get and set its value.
    Listeners can be attached to be told when the value is changed.

    The Value class is a wrapper around a shared, reference-counted underlying data
    object - this means that multiple Value objects can all refer to the same piece of
    data, allowing all of them to be notified when any of them changes it.

    When you create a Value with its default constructor, it acts as a wrapper around a
    simple var object, but by creating a Value that refers to a custom subclass of ValueSource,
    you can map the Value onto any kind of underlying data.

    Important note! The Value class is not thread-safe! If you're accessing one from
    multiple threads, then you'll need to use your own synchronisation around any code
    that accesses it.
*/
class JUCE_API  Value  final
{
public:
    //==============================================================================
    /** Creates an empty Value, containing a void var. */
    Value();

    /** Creates a Value that refers to the same value as another one.

        Note that this doesn't make a copy of the other value - both this and the other
        Value will share the same underlying value, so that when either one alters it, both
        will see it change.
    */
    Value (const Value& other);

    /** Creates a Value that is set to the specified value. */
    explicit Value (const var& initialValue);

    /** Move constructor */
    Value (Value&&) noexcept;

    /** Destructor. */
    ~Value();

    //==============================================================================
    /** Returns the current value. */
    var getValue() const;

    /** Returns the current value. */
    operator var() const;

    /** Returns the value as a string.
        This is a shortcut for "myValue.getValue().toString()".
    */
    String toString() const;

    /** Sets the current value.

        You can also use operator= to set the value.

        If there are any listeners registered, they will be notified of the
        change asynchronously.
    */
    void setValue (const var& newValue);

    /** Sets the current value.

        This is the same as calling setValue().

        If there are any listeners registered, they will be notified of the
        change asynchronously.
    */
    Value& operator= (const var& newValue);

    /** Move assignment operator */
    Value& operator= (Value&&) noexcept;

    /** Makes this object refer to the same underlying ValueSource as another one.

        Once this object has been connected to another one, changing either one
        will update the other.

        Existing listeners will still be registered after you call this method, and
        they'll continue to receive messages when the new value changes.
    */
    void referTo (const Value& valueToReferTo);

    /** Returns true if this value and the other one are references to the same value.
    */
    bool refersToSameSourceAs (const Value& other) const;

    /** Compares two values.
        This is a compare-by-value comparison, so is effectively the same as
        saying (this->getValue() == other.getValue()).
    */
    bool operator== (const Value& other) const;

    /** Compares two values.
        This is a compare-by-value comparison, so is effectively the same as
        saying (this->getValue() != other.getValue()).
    */
    bool operator!= (const Value& other) const;

    //==============================================================================
    /** Receives callbacks when a Value object changes.
        @see Value::addListener
    */
    class JUCE_API  Listener
    {
    public:
        Listener()          {}
        virtual ~Listener() {}

        /** Called when a Value object is changed.

            Note that the Value object passed as a parameter may not be exactly the same
            object that you registered the listener with - it might be a copy that refers
            to the same underlying ValueSource. To find out, you can call Value::refersToSameSourceAs().
        */
        virtual void valueChanged (Value& value) = 0;
    };

    /** Adds a listener to receive callbacks when the value changes.

        The listener is added to this specific Value object, and not to the shared
        object that it refers to. When this object is deleted, all the listeners will
        be lost, even if other references to the same Value still exist. So when you're
        adding a listener, make sure that you add it to a Value instance that will last
        for as long as you need the listener. In general, you'd never want to add a listener
        to a local stack-based Value, but more likely to one that's a member variable.

        @see removeListener
    */
    void addListener (Listener* listener);

    /** Removes a listener that was previously added with addListener(). */
    void removeListener (Listener* listener);

    //==============================================================================
    /**
        Used internally by the Value class as the base class for its shared value objects.

        The Value class is essentially a reference-counted pointer to a shared instance
        of a ValueSource object. If you're feeling adventurous, you can create your own custom
        ValueSource classes to allow Value objects to represent your own custom data items.
    */
    class JUCE_API  ValueSource   : public ReferenceCountedObject,
                                    private AsyncUpdater
    {
    public:
        ValueSource();
        virtual ~ValueSource();

        /** Returns the current value of this object. */
        virtual var getValue() const = 0;

        /** Changes the current value.
            This must also trigger a change message if the value actually changes.
        */
        virtual void setValue (const var& newValue) = 0;

        /** Delivers a change message to all the listeners that are registered with
            this value.

            If dispatchSynchronously is true, the method will call all the listeners
            before returning; otherwise it'll dispatch a message and make the call later.
        */
        void sendChangeMessage (bool dispatchSynchronously);

    protected:
        //==============================================================================
        friend class Value;
        SortedSet<Value*> valuesWithListeners;

    private:
        void handleAsyncUpdate() override;

        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueSource)
    };

    //==============================================================================
    /** Creates a Value object that uses this valueSource object as its underlying data. */
    explicit Value (ValueSource* valueSource);

    /** Returns the ValueSource that this value is referring to. */
    ValueSource& getValueSource() noexcept          { return *value; }

private:
    //==============================================================================
    friend class ValueSource;
    ReferenceCountedObjectPtr<ValueSource> value;
    ListenerList<Listener> listeners;

    void callListeners();
    void removeFromListenerList();

    // This is disallowed to avoid confusion about whether it should
    // do a by-value or by-reference copy.
    Value& operator= (const Value&) JUCE_DELETED_FUNCTION;

    // This declaration prevents accidental construction from an integer of 0,
    // which is possible in some compilers via an implicit cast to a pointer.
    explicit Value (void*) JUCE_DELETED_FUNCTION;
};

/** Writes a Value to an OutputStream as a UTF8 string. */
OutputStream& JUCE_CALLTYPE operator<< (OutputStream&, const Value&);

/** This typedef is just for compatibility with old code - newer code should use the Value::Listener class directly. */
typedef Value::Listener ValueListener;

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

namespace PropertyFileConstants
{
    JUCE_CONSTEXPR static const int magicNumber            = (int) ByteOrder::littleEndianInt ('P', 'R', 'O', 'P');
    JUCE_CONSTEXPR static const int magicNumberCompressed  = (int) ByteOrder::littleEndianInt ('C', 'P', 'R', 'P');

    JUCE_CONSTEXPR static const char* const fileTag        = "PROPERTIES";
    JUCE_CONSTEXPR static const char* const valueTag       = "VALUE";
    JUCE_CONSTEXPR static const char* const nameAttribute  = "name";
    JUCE_CONSTEXPR static const char* const valueAttribute = "val";
}

//==============================================================================
PropertiesFile::Options::Options()
    : commonToAllUsers (false),
      ignoreCaseOfKeyNames (false),
      doNotSave (false),
      millisecondsBeforeSaving (3000),
      storageFormat (PropertiesFile::storeAsXML),
      processLock (nullptr)
{
}

File PropertiesFile::Options::getDefaultFile() const
{
    // mustn't have illegal characters in this name..
    jassert (applicationName == File::createLegalFileName (applicationName));

   #if JUCE_MAC || JUCE_IOS
    File dir (commonToAllUsers ?  "/Library/"
                               : "~/Library/");

    if (osxLibrarySubFolder != "Preferences" && ! osxLibrarySubFolder.startsWith ("Application Support"))
    {
        /* The PropertiesFile class always used to put its settings files in "Library/Preferences", but Apple
           have changed their advice, and now stipulate that settings should go in "Library/Application Support".

           Because older apps would be broken by a silent change in this class's behaviour, you must now
           explicitly set the osxLibrarySubFolder value to indicate which path you want to use.

           In newer apps, you should always set this to "Application Support"
           or "Application Support/YourSubFolderName".

           If your app needs to load settings files that were created by older versions of juce and
           you want to maintain backwards-compatibility, then you can set this to "Preferences".
           But.. for better Apple-compliance, the recommended approach would be to write some code that
           finds your old settings files in ~/Library/Preferences, moves them to ~/Library/Application Support,
           and then uses the new path.
        */
        jassertfalse;

        dir = dir.getChildFile ("Application Support");
    }
    else
    {
        dir = dir.getChildFile (osxLibrarySubFolder);
    }

    if (folderName.isNotEmpty())
        dir = dir.getChildFile (folderName);

   #elif JUCE_LINUX || JUCE_ANDROID
    const File dir (File (commonToAllUsers ? "/var" : "~")
                     .getChildFile (folderName.isNotEmpty() ? folderName
                                                            : ("." + applicationName)));

   #elif JUCE_WINDOWS
    File dir (File::getSpecialLocation (commonToAllUsers ? File::commonApplicationDataDirectory
                                                         : File::userApplicationDataDirectory));

    if (dir == File())
        return {};

    dir = dir.getChildFile (folderName.isNotEmpty() ? folderName
                                                    : applicationName);
   #endif

    return (filenameSuffix.startsWithChar (L'.')
               ? dir.getChildFile (applicationName).withFileExtension (filenameSuffix)
               : dir.getChildFile (applicationName + "." + filenameSuffix));
}

//==============================================================================
PropertiesFile::PropertiesFile (const File& f, const Options& o)
    : PropertySet (o.ignoreCaseOfKeyNames),
      file (f), options (o),
      loadedOk (false), needsWriting (false)
{
    reload();
}

PropertiesFile::PropertiesFile (const Options& o)
    : PropertySet (o.ignoreCaseOfKeyNames),
      file (o.getDefaultFile()), options (o),
      loadedOk (false), needsWriting (false)
{
    reload();
}

bool PropertiesFile::reload()
{
    ProcessScopedLock pl (createProcessLock());

    if (pl != nullptr && ! pl->isLocked())
        return false; // locking failure..

    loadedOk = (! file.exists()) || loadAsBinary() || loadAsXml();
    return loadedOk;
}

PropertiesFile::~PropertiesFile()
{
    saveIfNeeded();
}

InterProcessLock::ScopedLockType* PropertiesFile::createProcessLock() const
{
    return options.processLock != nullptr ? new InterProcessLock::ScopedLockType (*options.processLock) : nullptr;
}

bool PropertiesFile::saveIfNeeded()
{
    const ScopedLock sl (getLock());
    return (! needsWriting) || save();
}

bool PropertiesFile::needsToBeSaved() const
{
    const ScopedLock sl (getLock());
    return needsWriting;
}

void PropertiesFile::setNeedsToBeSaved (const bool needsToBeSaved_)
{
    const ScopedLock sl (getLock());
    needsWriting = needsToBeSaved_;
}

bool PropertiesFile::save()
{
    const ScopedLock sl (getLock());

    stopTimer();

    if (options.doNotSave
         || file == File()
         || file.isDirectory()
         || ! file.getParentDirectory().createDirectory())
        return false;

    if (options.storageFormat == storeAsXML)
        return saveAsXml();

    return saveAsBinary();
}

bool PropertiesFile::loadAsXml()
{
    XmlDocument parser (file);
    ScopedPointer<XmlElement> doc (parser.getDocumentElement (true));

    if (doc != nullptr && doc->hasTagName (PropertyFileConstants::fileTag))
    {
        doc = parser.getDocumentElement();

        if (doc != nullptr)
        {
            forEachXmlChildElementWithTagName (*doc, e, PropertyFileConstants::valueTag)
            {
                const String name (e->getStringAttribute (PropertyFileConstants::nameAttribute));

                if (name.isNotEmpty())
                {
                    getAllProperties().set (name,
                                            e->getFirstChildElement() != nullptr
                                                ? e->getFirstChildElement()->createDocument ("", true)
                                                : e->getStringAttribute (PropertyFileConstants::valueAttribute));
                }
            }

            return true;
        }

        // must be a pretty broken XML file we're trying to parse here,
        // or a sign that this object needs an InterProcessLock,
        // or just a failure reading the file.  This last reason is why
        // we don't jassertfalse here.
    }

    return false;
}

bool PropertiesFile::saveAsXml()
{
    XmlElement doc (PropertyFileConstants::fileTag);
    const StringPairArray& props = getAllProperties();

    for (int i = 0; i < props.size(); ++i)
    {
        XmlElement* const e = doc.createNewChildElement (PropertyFileConstants::valueTag);
        e->setAttribute (PropertyFileConstants::nameAttribute, props.getAllKeys() [i]);

        // if the value seems to contain xml, store it as such..
        if (XmlElement* const childElement = XmlDocument::parse (props.getAllValues() [i]))
            e->addChildElement (childElement);
        else
            e->setAttribute (PropertyFileConstants::valueAttribute, props.getAllValues() [i]);
    }

    ProcessScopedLock pl (createProcessLock());

    if (pl != nullptr && ! pl->isLocked())
        return false; // locking failure..

    if (doc.writeToFile (file, String()))
    {
        needsWriting = false;
        return true;
    }

    return false;
}

bool PropertiesFile::loadAsBinary()
{
    FileInputStream fileStream (file);

    if (fileStream.openedOk())
    {
        const int magicNumber = fileStream.readInt();

        if (magicNumber == PropertyFileConstants::magicNumberCompressed)
        {
            SubregionStream subStream (&fileStream, 4, -1, false);
            GZIPDecompressorInputStream gzip (subStream);
            return loadAsBinary (gzip);
        }

        if (magicNumber == PropertyFileConstants::magicNumber)
            return loadAsBinary (fileStream);
    }

    return false;
}

bool PropertiesFile::loadAsBinary (InputStream& input)
{
    BufferedInputStream in (input, 2048);

    int numValues = in.readInt();

    while (--numValues >= 0 && ! in.isExhausted())
    {
        const String key (in.readString());
        const String value (in.readString());

        jassert (key.isNotEmpty());
        if (key.isNotEmpty())
            getAllProperties().set (key, value);
    }

    return true;
}

bool PropertiesFile::saveAsBinary()
{
    ProcessScopedLock pl (createProcessLock());

    if (pl != nullptr && ! pl->isLocked())
        return false; // locking failure..

    TemporaryFile tempFile (file);
    ScopedPointer<OutputStream> out (tempFile.getFile().createOutputStream());

    if (out != nullptr)
    {
        if (options.storageFormat == storeAsCompressedBinary)
        {
            out->writeInt (PropertyFileConstants::magicNumberCompressed);
            out->flush();

            out = new GZIPCompressorOutputStream (out.release(), 9, true);
        }
        else
        {
            // have you set up the storage option flags correctly?
            jassert (options.storageFormat == storeAsBinary);

            out->writeInt (PropertyFileConstants::magicNumber);
        }

        const StringPairArray& props = getAllProperties();
        const int numProperties   = props.size();
        const StringArray& keys   = props.getAllKeys();
        const StringArray& values = props.getAllValues();

        out->writeInt (numProperties);

        for (int i = 0; i < numProperties; ++i)
        {
            out->writeString (keys[i]);
            out->writeString (values[i]);
        }

        out = nullptr;

        if (tempFile.overwriteTargetFileWithTemporary())
        {
            needsWriting = false;
            return true;
        }
    }

    return false;
}

void PropertiesFile::timerCallback()
{
    saveIfNeeded();
}

void PropertiesFile::propertyChanged()
{
    sendChangeMessage();

    needsWriting = true;

    if (options.millisecondsBeforeSaving > 0)
        startTimer (options.millisecondsBeforeSaving);
    else if (options.millisecondsBeforeSaving == 0)
        saveIfNeeded();
}

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

//==============================================================================
/** Wrapper on a file that stores a list of key/value data pairs.

    Useful for storing application settings, etc. See the PropertySet class for
    the interfaces that read and write values.

    Not designed for very large amounts of data, as it keeps all the values in
    memory and writes them out to disk lazily when they are changed.

    Because this class derives from ChangeBroadcaster, ChangeListeners can be registered
    with it, and these will be signalled when a value changes.

    @see PropertySet
*/
class JUCE_API  PropertiesFile  : public PropertySet,
                                  public ChangeBroadcaster,
                                  private Timer
{
public:
    //==============================================================================
    enum StorageFormat
    {
        storeAsBinary,
        storeAsCompressedBinary,
        storeAsXML
    };

    //==============================================================================
    struct JUCE_API  Options
    {
        /** Creates an empty Options structure.
            You'll need to fill-in the data members appropriately before using this structure.
        */
        Options();

        /** The name of your application - this is used to help generate the path and filename
            at which the properties file will be stored. */
        String applicationName;

        /** The suffix to use for your properties file.
            It doesn't really matter what this is - you may want to use ".settings" or
            ".properties" or something. If the suffix includes the prefixing dot (for example
            ".settings") then the suffix of applicationName will be replaced with your suffix
            ("MyApp.exe" -> "MyApp.settings"). If your filenameSuffix does NOT include the dot,
            then the suffix will be appended to the applicationName ("MyApp.exe" ->
            "MyApp.exe.settings").
        */
        String filenameSuffix;

        /** The name of a subfolder in which you'd like your properties file to live.
            See the getDefaultFile() method for more details about how this is used.
        */
        String folderName;

        /** If you're using properties files on a Mac, you must set this value - failure to
            do so will cause a runtime assertion.

            The PropertiesFile class always used to put its settings files in "Library/Preferences", but Apple
            have changed their advice, and now stipulate that settings should go in "Library/Application Support".

            Because older apps would be broken by a silent change in this class's behaviour, you must now
            explicitly set the osxLibrarySubFolder value to indicate which path you want to use.

            In newer apps, you should always set this to "Application Support" or
            "Application Support/YourSubFolderName".

            If your app needs to load settings files that were created by older versions of juce and
            you want to maintain backwards-compatibility, then you can set this to "Preferences".
            But.. for better Apple-compliance, the recommended approach would be to write some code that
            finds your old settings files in ~/Library/Preferences, moves them to ~/Library/Application Support,
            and then uses the new path.
        */
        String osxLibrarySubFolder;

        /** If true, the file will be created in a location that's shared between users.
            The default constructor initialises this value to false.
        */
        bool commonToAllUsers;

        /** If true, this means that property names are matched in a case-insensitive manner.
            See the PropertySet constructor for more info.
            The default constructor initialises this value to false.
        */
        bool ignoreCaseOfKeyNames;

        /** If set to true, this prevents the file from being written to disk. */
        bool doNotSave;

        /** If this is zero or greater, then after a value is changed, the object will wait
            for this amount of time and then save the file. If this zero, the file will be
            written to disk immediately on being changed (which might be slow, as it'll re-write
            synchronously each time a value-change method is called). If it is less than zero,
            the file won't be saved until save() or saveIfNeeded() are explicitly called.
            The default constructor sets this to a reasonable value of a few seconds, so you
            only need to change it if you need a special case.
        */
        int millisecondsBeforeSaving;

        /** Specifies whether the file should be written as XML, binary, etc.
            The default constructor sets this to storeAsXML, so you only need to set it explicitly
            if you want to use a different format.
        */
        StorageFormat storageFormat;

        /** An optional InterprocessLock object that will be used to prevent multiple threads or
            processes from writing to the file at the same time. The PropertiesFile will keep a
            pointer to this object but will not take ownership of it - the caller is responsible for
            making sure that the lock doesn't get deleted before the PropertiesFile has been deleted.
            The default constructor initialises this value to nullptr, so you don't need to touch it
            unless you want to use a lock.
        */
        InterProcessLock* processLock;

        /** This can be called to suggest a file that should be used, based on the values
            in this structure.

            So on a Mac, this will return a file called:
            ~/Library/[osxLibrarySubFolder]/[folderName]/[applicationName].[filenameSuffix]

            On Windows it'll return something like:
            C:\\Documents and Settings\\username\\Application Data\\[folderName]\\[applicationName].[filenameSuffix]

            On Linux it'll return
            ~/[folderName]/[applicationName].[filenameSuffix]

            If the folderName variable is empty, it'll use the app name for this (or omit the
            folder name on the Mac).

            The paths will also vary depending on whether commonToAllUsers is true.
        */
        File getDefaultFile() const;
    };

    //==============================================================================
    /** Creates a PropertiesFile object.
        The file used will be chosen by calling PropertiesFile::Options::getDefaultFile()
        for the options provided. To set the file explicitly, use the other constructor.
    */
    explicit PropertiesFile (const Options& options);

    /** Creates a PropertiesFile object.
        Unlike the other constructor, this one allows you to explicitly set the file that you
        want to be used, rather than using the default one.
    */
    PropertiesFile (const File& file,
                    const Options& options);

    /** Destructor.
        When deleted, the file will first call saveIfNeeded() to flush any changes to disk.
    */
    ~PropertiesFile();

    //==============================================================================
    /** Returns true if this file was created from a valid (or non-existent) file.
        If the file failed to load correctly because it was corrupt or had insufficient
        access, this will be false.
    */
    bool isValidFile() const noexcept               { return loadedOk; }

    //==============================================================================
    /** This will flush all the values to disk if they've changed since the last
        time they were saved.

        Returns false if it fails to write to the file for some reason (maybe because
        it's read-only or the directory doesn't exist or something).

        @see save
    */
    bool saveIfNeeded();

    /** This will force a write-to-disk of the current values, regardless of whether
        anything has changed since the last save.

        Returns false if it fails to write to the file for some reason (maybe because
        it's read-only or the directory doesn't exist or something).

        @see saveIfNeeded
    */
    bool save();

    /** Returns true if the properties have been altered since the last time they were saved.
        The file is flagged as needing to be saved when you change a value, but you can
        explicitly set this flag with setNeedsToBeSaved().
    */
    bool needsToBeSaved() const;

    /** Explicitly sets the flag to indicate whether the file needs saving or not.
        @see needsToBeSaved
    */
    void setNeedsToBeSaved (bool needsToBeSaved);

    /** Attempts to reload the settings from the file. */
    bool reload();

    //==============================================================================
    /** Returns the file that's being used. */
    const File& getFile() const noexcept            { return file; }

protected:
    /** @internal */
    void propertyChanged() override;

private:
    //==============================================================================
    File file;
    Options options;
    bool loadedOk, needsWriting;

    typedef const ScopedPointer<InterProcessLock::ScopedLockType> ProcessScopedLock;
    InterProcessLock::ScopedLockType* createProcessLock() const;

    void timerCallback() override;
    bool saveAsXml();
    bool saveAsBinary();
    bool loadAsXml();
    bool loadAsBinary();
    bool loadAsBinary (InputStream&);

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertiesFile)
};

} // namespace juce


text:
/*
  ==============================================================================

   This file is part of the JUCE library.
   Copyright (c) 2017 - ROLI Ltd.

   JUCE is an open source library subject to commercial or open-source
   licensing.

   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
   27th April 2017).

   End User License Agreement: www.juce.com/juce-5-licence
   Privacy Policy: www.juce.com/juce-5-privacy-policy

   Or: You may also use this code under the terms of the GPL v3 (see
   www.gnu.org/licenses).

   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
   DISCLAIMED.

  ==============================================================================
*/

namespace juce
{

ApplicationProperties::ApplicationProperties()
    : commonSettingsAreReadOnly (0)
{
}

ApplicationProperties::~ApplicationProperties()
{
    closeFiles();
}

//==============================================================================
void ApplicationProperties::setStorageParameters (const PropertiesFile::Options& newOptions)
{
    options = newOptions;
}

//==============================================================================
void ApplicationProperties::openFiles()
{
    // You need to call setStorageParameters() before trying to get hold of the properties!
    jassert (options.applicationName.isNotEmpty());

    if (options.applicationName.isNotEmpty())
    {
        PropertiesFile::Options o (options);

        if (userProps == nullptr)
        {
            o.commonToAllUsers = false;
            userProps = new PropertiesFile (o);
        }

        if (commonProps == nullptr)
        {
            o.commonToAllUsers = true;
            commonProps = new PropertiesFile (o);
        }

        userProps->setFallbackPropertySet (commonProps);
    }
}

PropertiesFile* ApplicationProperties::getUserSettings()
{
    if (userProps == nullptr)
        openFiles();

    return userProps;
}

PropertiesFile* ApplicationProperties::getCommonSettings (const bool returnUserPropsIfReadOnly)
{
    if (commonProps == nullptr)
        openFiles();

    if (returnUserPropsIfReadOnly)
    {
        if (commonSettingsAreReadOnly == 0)
            commonSettingsAreReadOnly = commonProps->save() ? -1 : 1;

        if (commonSettingsAreReadOnly > 0)
            return userProps;
    }

    return commonProps;
}

bool ApplicationProperties::saveIfNeeded()
{
    return (userProps == nullptr || userProps->saveIfNeeded())
         && (commonProps == nullptr || commonProps->saveIfNeeded());
}

void ApplicationProperties::closeFiles()
{
    userProps = nullptr;
    commonProps = nullptr;
}

} // namespace juce