1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 namespace juce
27 {
28 
29 class ValueTree::SharedObject  : public ReferenceCountedObject
30 {
31 public:
32     using Ptr = ReferenceCountedObjectPtr<SharedObject>;
33 
SharedObject(const Identifier & t)34     explicit SharedObject (const Identifier& t) noexcept  : type (t) {}
35 
SharedObject(const SharedObject & other)36     SharedObject (const SharedObject& other)
37         : ReferenceCountedObject(), type (other.type), properties (other.properties)
38     {
39         for (auto* c : other.children)
40         {
41             auto* child = new SharedObject (*c);
42             child->parent = this;
43             children.add (child);
44         }
45     }
46 
47     SharedObject& operator= (const SharedObject&) = delete;
48 
~SharedObject()49     ~SharedObject()
50     {
51         jassert (parent == nullptr); // this should never happen unless something isn't obeying the ref-counting!
52 
53         for (auto i = children.size(); --i >= 0;)
54         {
55             const Ptr c (children.getObjectPointerUnchecked (i));
56             c->parent = nullptr;
57             children.remove (i);
58             c->sendParentChangeMessage();
59         }
60     }
61 
getRoot()62     SharedObject& getRoot() noexcept
63     {
64         return parent == nullptr ? *this : parent->getRoot();
65     }
66 
67     template <typename Function>
callListeners(ValueTree::Listener * listenerToExclude,Function fn) const68     void callListeners (ValueTree::Listener* listenerToExclude, Function fn) const
69     {
70         auto numListeners = valueTreesWithListeners.size();
71 
72         if (numListeners == 1)
73         {
74             valueTreesWithListeners.getUnchecked (0)->listeners.callExcluding (listenerToExclude, fn);
75         }
76         else if (numListeners > 0)
77         {
78             auto listenersCopy = valueTreesWithListeners;
79 
80             for (int i = 0; i < numListeners; ++i)
81             {
82                 auto* v = listenersCopy.getUnchecked (i);
83 
84                 if (i == 0 || valueTreesWithListeners.contains (v))
85                     v->listeners.callExcluding (listenerToExclude, fn);
86             }
87         }
88     }
89 
90     template <typename Function>
callListenersForAllParents(ValueTree::Listener * listenerToExclude,Function fn) const91     void callListenersForAllParents (ValueTree::Listener* listenerToExclude, Function fn) const
92     {
93         for (auto* t = this; t != nullptr; t = t->parent)
94             t->callListeners (listenerToExclude, fn);
95     }
96 
sendPropertyChangeMessage(const Identifier & property,ValueTree::Listener * listenerToExclude=nullptr)97     void sendPropertyChangeMessage (const Identifier& property, ValueTree::Listener* listenerToExclude = nullptr)
98     {
99         ValueTree tree (*this);
100         callListenersForAllParents (listenerToExclude, [&] (Listener& l) { l.valueTreePropertyChanged (tree, property); });
101     }
102 
sendChildAddedMessage(ValueTree child)103     void sendChildAddedMessage (ValueTree child)
104     {
105         ValueTree tree (*this);
106         callListenersForAllParents (nullptr, [&] (Listener& l) { l.valueTreeChildAdded (tree, child); });
107     }
108 
sendChildRemovedMessage(ValueTree child,int index)109     void sendChildRemovedMessage (ValueTree child, int index)
110     {
111         ValueTree tree (*this);
112         callListenersForAllParents (nullptr, [=, &tree, &child] (Listener& l) { l.valueTreeChildRemoved (tree, child, index); });
113     }
114 
sendChildOrderChangedMessage(int oldIndex,int newIndex)115     void sendChildOrderChangedMessage (int oldIndex, int newIndex)
116     {
117         ValueTree tree (*this);
118         callListenersForAllParents (nullptr, [=, &tree] (Listener& l) { l.valueTreeChildOrderChanged (tree, oldIndex, newIndex); });
119     }
120 
sendParentChangeMessage()121     void sendParentChangeMessage()
122     {
123         ValueTree tree (*this);
124 
125         for (auto j = children.size(); --j >= 0;)
126             if (auto* child = children.getObjectPointer (j))
127                 child->sendParentChangeMessage();
128 
129         callListeners (nullptr, [&] (Listener& l) { l.valueTreeParentChanged (tree); });
130     }
131 
setProperty(const Identifier & name,const var & newValue,UndoManager * undoManager,ValueTree::Listener * listenerToExclude=nullptr)132     void setProperty (const Identifier& name, const var& newValue, UndoManager* undoManager,
133                       ValueTree::Listener* listenerToExclude = nullptr)
134     {
135         if (undoManager == nullptr)
136         {
137             if (properties.set (name, newValue))
138                 sendPropertyChangeMessage (name, listenerToExclude);
139         }
140         else
141         {
142             if (auto* existingValue = properties.getVarPointer (name))
143             {
144                 if (*existingValue != newValue)
145                     undoManager->perform (new SetPropertyAction (*this, name, newValue, *existingValue,
146                                                                  false, false, listenerToExclude));
147             }
148             else
149             {
150                 undoManager->perform (new SetPropertyAction (*this, name, newValue, {},
151                                                              true, false, listenerToExclude));
152             }
153         }
154     }
155 
hasProperty(const Identifier & name) const156     bool hasProperty (const Identifier& name) const noexcept
157     {
158         return properties.contains (name);
159     }
160 
removeProperty(const Identifier & name,UndoManager * undoManager)161     void removeProperty (const Identifier& name, UndoManager* undoManager)
162     {
163         if (undoManager == nullptr)
164         {
165             if (properties.remove (name))
166                 sendPropertyChangeMessage (name);
167         }
168         else
169         {
170             if (properties.contains (name))
171                 undoManager->perform (new SetPropertyAction (*this, name, {}, properties[name], false, true));
172         }
173     }
174 
removeAllProperties(UndoManager * undoManager)175     void removeAllProperties (UndoManager* undoManager)
176     {
177         if (undoManager == nullptr)
178         {
179             while (properties.size() > 0)
180             {
181                 auto name = properties.getName (properties.size() - 1);
182                 properties.remove (name);
183                 sendPropertyChangeMessage (name);
184             }
185         }
186         else
187         {
188             for (auto i = properties.size(); --i >= 0;)
189                 undoManager->perform (new SetPropertyAction (*this, properties.getName (i), {},
190                                                              properties.getValueAt (i), false, true));
191         }
192     }
193 
copyPropertiesFrom(const SharedObject & source,UndoManager * undoManager)194     void copyPropertiesFrom (const SharedObject& source, UndoManager* undoManager)
195     {
196         for (auto i = properties.size(); --i >= 0;)
197             if (! source.properties.contains (properties.getName (i)))
198                 removeProperty (properties.getName (i), undoManager);
199 
200         for (int i = 0; i < source.properties.size(); ++i)
201             setProperty (source.properties.getName (i), source.properties.getValueAt (i), undoManager);
202     }
203 
getChildWithName(const Identifier & typeToMatch) const204     ValueTree getChildWithName (const Identifier& typeToMatch) const
205     {
206         for (auto* s : children)
207             if (s->type == typeToMatch)
208                 return ValueTree (*s);
209 
210         return {};
211     }
212 
getOrCreateChildWithName(const Identifier & typeToMatch,UndoManager * undoManager)213     ValueTree getOrCreateChildWithName (const Identifier& typeToMatch, UndoManager* undoManager)
214     {
215         for (auto* s : children)
216             if (s->type == typeToMatch)
217                 return ValueTree (*s);
218 
219         auto newObject = new SharedObject (typeToMatch);
220         addChild (newObject, -1, undoManager);
221         return ValueTree (*newObject);
222     }
223 
getChildWithProperty(const Identifier & propertyName,const var & propertyValue) const224     ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const
225     {
226         for (auto* s : children)
227             if (s->properties[propertyName] == propertyValue)
228                 return ValueTree (*s);
229 
230         return {};
231     }
232 
isAChildOf(const SharedObject * possibleParent) const233     bool isAChildOf (const SharedObject* possibleParent) const noexcept
234     {
235         for (auto* p = parent; p != nullptr; p = p->parent)
236             if (p == possibleParent)
237                 return true;
238 
239         return false;
240     }
241 
indexOf(const ValueTree & child) const242     int indexOf (const ValueTree& child) const noexcept
243     {
244         return children.indexOf (child.object);
245     }
246 
addChild(SharedObject * child,int index,UndoManager * undoManager)247     void addChild (SharedObject* child, int index, UndoManager* undoManager)
248     {
249         if (child != nullptr && child->parent != this)
250         {
251             if (child != this && ! isAChildOf (child))
252             {
253                 // You should always make sure that a child is removed from its previous parent before
254                 // adding it somewhere else - otherwise, it's ambiguous as to whether a different
255                 // undomanager should be used when removing it from its current parent..
256                 jassert (child->parent == nullptr);
257 
258                 if (child->parent != nullptr)
259                 {
260                     jassert (child->parent->children.indexOf (child) >= 0);
261                     child->parent->removeChild (child->parent->children.indexOf (child), undoManager);
262                 }
263 
264                 if (undoManager == nullptr)
265                 {
266                     children.insert (index, child);
267                     child->parent = this;
268                     sendChildAddedMessage (ValueTree (*child));
269                     child->sendParentChangeMessage();
270                 }
271                 else
272                 {
273                     if (! isPositiveAndBelow (index, children.size()))
274                         index = children.size();
275 
276                     undoManager->perform (new AddOrRemoveChildAction (*this, index, child));
277                 }
278             }
279             else
280             {
281                 // You're attempting to create a recursive loop! A node
282                 // can't be a child of one of its own children!
283                 jassertfalse;
284             }
285         }
286     }
287 
removeChild(int childIndex,UndoManager * undoManager)288     void removeChild (int childIndex, UndoManager* undoManager)
289     {
290         if (auto child = Ptr (children.getObjectPointer (childIndex)))
291         {
292             if (undoManager == nullptr)
293             {
294                 children.remove (childIndex);
295                 child->parent = nullptr;
296                 sendChildRemovedMessage (ValueTree (child), childIndex);
297                 child->sendParentChangeMessage();
298             }
299             else
300             {
301                 undoManager->perform (new AddOrRemoveChildAction (*this, childIndex, {}));
302             }
303         }
304     }
305 
removeAllChildren(UndoManager * undoManager)306     void removeAllChildren (UndoManager* undoManager)
307     {
308         while (children.size() > 0)
309             removeChild (children.size() - 1, undoManager);
310     }
311 
moveChild(int currentIndex,int newIndex,UndoManager * undoManager)312     void moveChild (int currentIndex, int newIndex, UndoManager* undoManager)
313     {
314         // The source index must be a valid index!
315         jassert (isPositiveAndBelow (currentIndex, children.size()));
316 
317         if (currentIndex != newIndex
318              && isPositiveAndBelow (currentIndex, children.size()))
319         {
320             if (undoManager == nullptr)
321             {
322                 children.move (currentIndex, newIndex);
323                 sendChildOrderChangedMessage (currentIndex, newIndex);
324             }
325             else
326             {
327                 if (! isPositiveAndBelow (newIndex, children.size()))
328                     newIndex = children.size() - 1;
329 
330                 undoManager->perform (new MoveChildAction (*this, currentIndex, newIndex));
331             }
332         }
333     }
334 
reorderChildren(const OwnedArray<ValueTree> & newOrder,UndoManager * undoManager)335     void reorderChildren (const OwnedArray<ValueTree>& newOrder, UndoManager* undoManager)
336     {
337         jassert (newOrder.size() == children.size());
338 
339         for (int i = 0; i < children.size(); ++i)
340         {
341             auto* child = newOrder.getUnchecked (i)->object.get();
342 
343             if (children.getObjectPointerUnchecked (i) != child)
344             {
345                 auto oldIndex = children.indexOf (child);
346                 jassert (oldIndex >= 0);
347                 moveChild (oldIndex, i, undoManager);
348             }
349         }
350     }
351 
isEquivalentTo(const SharedObject & other) const352     bool isEquivalentTo (const SharedObject& other) const noexcept
353     {
354         if (type != other.type
355              || properties.size() != other.properties.size()
356              || children.size() != other.children.size()
357              || properties != other.properties)
358             return false;
359 
360         for (int i = 0; i < children.size(); ++i)
361             if (! children.getObjectPointerUnchecked (i)->isEquivalentTo (*other.children.getObjectPointerUnchecked (i)))
362                 return false;
363 
364         return true;
365     }
366 
createXml() const367     XmlElement* createXml() const
368     {
369         auto* xml = new XmlElement (type);
370         properties.copyToXmlAttributes (*xml);
371 
372         // (NB: it's faster to add nodes to XML elements in reverse order)
373         for (auto i = children.size(); --i >= 0;)
374             xml->prependChildElement (children.getObjectPointerUnchecked (i)->createXml());
375 
376         return xml;
377     }
378 
writeToStream(OutputStream & output) const379     void writeToStream (OutputStream& output) const
380     {
381         output.writeString (type.toString());
382         output.writeCompressedInt (properties.size());
383 
384         for (int j = 0; j < properties.size(); ++j)
385         {
386             output.writeString (properties.getName (j).toString());
387             properties.getValueAt (j).writeToStream (output);
388         }
389 
390         output.writeCompressedInt (children.size());
391 
392         for (auto* c : children)
393             writeObjectToStream (output, c);
394     }
395 
writeObjectToStream(OutputStream & output,const SharedObject * object)396     static void writeObjectToStream (OutputStream& output, const SharedObject* object)
397     {
398         if (object != nullptr)
399         {
400             object->writeToStream (output);
401         }
402         else
403         {
404             output.writeString ({});
405             output.writeCompressedInt (0);
406             output.writeCompressedInt (0);
407         }
408     }
409 
410     //==============================================================================
411     struct SetPropertyAction  : public UndoableAction
412     {
SetPropertyActionjuce::ValueTree::SharedObject::SetPropertyAction413         SetPropertyAction (Ptr targetObject, const Identifier& propertyName,
414                            const var& newVal, const var& oldVal, bool isAdding, bool isDeleting,
415                            ValueTree::Listener* listenerToExclude = nullptr)
416             : target (std::move (targetObject)),
417               name (propertyName), newValue (newVal), oldValue (oldVal),
418               isAddingNewProperty (isAdding), isDeletingProperty (isDeleting),
419               excludeListener (listenerToExclude)
420         {
421         }
422 
performjuce::ValueTree::SharedObject::SetPropertyAction423         bool perform() override
424         {
425             jassert (! (isAddingNewProperty && target->hasProperty (name)));
426 
427             if (isDeletingProperty)
428                 target->removeProperty (name, nullptr);
429             else
430                 target->setProperty (name, newValue, nullptr, excludeListener);
431 
432             return true;
433         }
434 
undojuce::ValueTree::SharedObject::SetPropertyAction435         bool undo() override
436         {
437             if (isAddingNewProperty)
438                 target->removeProperty (name, nullptr);
439             else
440                 target->setProperty (name, oldValue, nullptr);
441 
442             return true;
443         }
444 
getSizeInUnitsjuce::ValueTree::SharedObject::SetPropertyAction445         int getSizeInUnits() override
446         {
447             return (int) sizeof (*this); //xxx should be more accurate
448         }
449 
createCoalescedActionjuce::ValueTree::SharedObject::SetPropertyAction450         UndoableAction* createCoalescedAction (UndoableAction* nextAction) override
451         {
452             if (! (isAddingNewProperty || isDeletingProperty))
453             {
454                 if (auto* next = dynamic_cast<SetPropertyAction*> (nextAction))
455                     if (next->target == target && next->name == name
456                           && ! (next->isAddingNewProperty || next->isDeletingProperty))
457                         return new SetPropertyAction (*target, name, next->newValue, oldValue, false, false);
458             }
459 
460             return nullptr;
461         }
462 
463     private:
464         const Ptr target;
465         const Identifier name;
466         const var newValue;
467         var oldValue;
468         const bool isAddingNewProperty : 1, isDeletingProperty : 1;
469         ValueTree::Listener* excludeListener;
470 
471         JUCE_DECLARE_NON_COPYABLE (SetPropertyAction)
472     };
473 
474     //==============================================================================
475     struct AddOrRemoveChildAction  : public UndoableAction
476     {
AddOrRemoveChildActionjuce::ValueTree::SharedObject::AddOrRemoveChildAction477         AddOrRemoveChildAction (Ptr parentObject, int index, SharedObject* newChild)
478             : target (std::move (parentObject)),
479               child (newChild != nullptr ? newChild : target->children.getObjectPointer (index)),
480               childIndex (index),
481               isDeleting (newChild == nullptr)
482         {
483             jassert (child != nullptr);
484         }
485 
performjuce::ValueTree::SharedObject::AddOrRemoveChildAction486         bool perform() override
487         {
488             if (isDeleting)
489                 target->removeChild (childIndex, nullptr);
490             else
491                 target->addChild (child.get(), childIndex, nullptr);
492 
493             return true;
494         }
495 
undojuce::ValueTree::SharedObject::AddOrRemoveChildAction496         bool undo() override
497         {
498             if (isDeleting)
499             {
500                 target->addChild (child.get(), childIndex, nullptr);
501             }
502             else
503             {
504                 // If you hit this, it seems that your object's state is getting confused - probably
505                 // because you've interleaved some undoable and non-undoable operations?
506                 jassert (childIndex < target->children.size());
507                 target->removeChild (childIndex, nullptr);
508             }
509 
510             return true;
511         }
512 
getSizeInUnitsjuce::ValueTree::SharedObject::AddOrRemoveChildAction513         int getSizeInUnits() override
514         {
515             return (int) sizeof (*this); //xxx should be more accurate
516         }
517 
518     private:
519         const Ptr target, child;
520         const int childIndex;
521         const bool isDeleting;
522 
523         JUCE_DECLARE_NON_COPYABLE (AddOrRemoveChildAction)
524     };
525 
526     //==============================================================================
527     struct MoveChildAction  : public UndoableAction
528     {
MoveChildActionjuce::ValueTree::SharedObject::MoveChildAction529         MoveChildAction (Ptr parentObject, int fromIndex, int toIndex) noexcept
530             : parent (std::move (parentObject)), startIndex (fromIndex), endIndex (toIndex)
531         {
532         }
533 
performjuce::ValueTree::SharedObject::MoveChildAction534         bool perform() override
535         {
536             parent->moveChild (startIndex, endIndex, nullptr);
537             return true;
538         }
539 
undojuce::ValueTree::SharedObject::MoveChildAction540         bool undo() override
541         {
542             parent->moveChild (endIndex, startIndex, nullptr);
543             return true;
544         }
545 
getSizeInUnitsjuce::ValueTree::SharedObject::MoveChildAction546         int getSizeInUnits() override
547         {
548             return (int) sizeof (*this); //xxx should be more accurate
549         }
550 
createCoalescedActionjuce::ValueTree::SharedObject::MoveChildAction551         UndoableAction* createCoalescedAction (UndoableAction* nextAction) override
552         {
553             if (auto* next = dynamic_cast<MoveChildAction*> (nextAction))
554                 if (next->parent == parent && next->startIndex == endIndex)
555                     return new MoveChildAction (parent, startIndex, next->endIndex);
556 
557             return nullptr;
558         }
559 
560     private:
561         const Ptr parent;
562         const int startIndex, endIndex;
563 
564         JUCE_DECLARE_NON_COPYABLE (MoveChildAction)
565     };
566 
567     //==============================================================================
568     const Identifier type;
569     NamedValueSet properties;
570     ReferenceCountedArray<SharedObject> children;
571     SortedSet<ValueTree*> valueTreesWithListeners;
572     SharedObject* parent = nullptr;
573 
574     JUCE_LEAK_DETECTOR (SharedObject)
575 };
576 
577 //==============================================================================
ValueTree()578 ValueTree::ValueTree() noexcept
579 {
580 }
581 
JUCE_DECLARE_DEPRECATED_STATIC(const ValueTree ValueTree::invalid;)582 JUCE_DECLARE_DEPRECATED_STATIC (const ValueTree ValueTree::invalid;)
583 
584 ValueTree::ValueTree (const Identifier& type)  : object (new ValueTree::SharedObject (type))
585 {
586     jassert (type.toString().isNotEmpty()); // All objects must be given a sensible type name!
587 }
588 
ValueTree(const Identifier & type,std::initializer_list<NamedValueSet::NamedValue> properties,std::initializer_list<ValueTree> subTrees)589 ValueTree::ValueTree (const Identifier& type,
590                       std::initializer_list<NamedValueSet::NamedValue> properties,
591                       std::initializer_list<ValueTree> subTrees)
592     : ValueTree (type)
593 {
594     object->properties = NamedValueSet (std::move (properties));
595 
596     for (auto& tree : subTrees)
597         addChild (tree, -1, nullptr);
598 }
599 
ValueTree(SharedObject::Ptr so)600 ValueTree::ValueTree (SharedObject::Ptr so) noexcept  : object (std::move (so)) {}
ValueTree(SharedObject & so)601 ValueTree::ValueTree (SharedObject& so) noexcept      : object (so) {}
602 
ValueTree(const ValueTree & other)603 ValueTree::ValueTree (const ValueTree& other) noexcept  : object (other.object)
604 {
605 }
606 
operator =(const ValueTree & other)607 ValueTree& ValueTree::operator= (const ValueTree& other)
608 {
609     if (object != other.object)
610     {
611         if (listeners.isEmpty())
612         {
613             object = other.object;
614         }
615         else
616         {
617             if (object != nullptr)
618                 object->valueTreesWithListeners.removeValue (this);
619 
620             if (other.object != nullptr)
621                 other.object->valueTreesWithListeners.add (this);
622 
623             object = other.object;
624 
625             listeners.call ([this] (Listener& l) { l.valueTreeRedirected (*this); });
626         }
627     }
628 
629     return *this;
630 }
631 
ValueTree(ValueTree && other)632 ValueTree::ValueTree (ValueTree&& other) noexcept
633     : object (std::move (other.object))
634 {
635     if (object != nullptr)
636         object->valueTreesWithListeners.removeValue (&other);
637 }
638 
~ValueTree()639 ValueTree::~ValueTree()
640 {
641     if (! listeners.isEmpty() && object != nullptr)
642         object->valueTreesWithListeners.removeValue (this);
643 }
644 
operator ==(const ValueTree & other) const645 bool ValueTree::operator== (const ValueTree& other) const noexcept
646 {
647     return object == other.object;
648 }
649 
operator !=(const ValueTree & other) const650 bool ValueTree::operator!= (const ValueTree& other) const noexcept
651 {
652     return object != other.object;
653 }
654 
isEquivalentTo(const ValueTree & other) const655 bool ValueTree::isEquivalentTo (const ValueTree& other) const
656 {
657     return object == other.object
658             || (object != nullptr && other.object != nullptr
659                  && object->isEquivalentTo (*other.object));
660 }
661 
createCopy() const662 ValueTree ValueTree::createCopy() const
663 {
664     if (object != nullptr)
665         return ValueTree (*new SharedObject (*object));
666 
667     return {};
668 }
669 
copyPropertiesFrom(const ValueTree & source,UndoManager * undoManager)670 void ValueTree::copyPropertiesFrom (const ValueTree& source, UndoManager* undoManager)
671 {
672     jassert (object != nullptr || source.object == nullptr); // Trying to add properties to a null ValueTree will fail!
673 
674     if (source.object == nullptr)
675         removeAllProperties (undoManager);
676     else if (object != nullptr)
677         object->copyPropertiesFrom (*(source.object), undoManager);
678 }
679 
copyPropertiesAndChildrenFrom(const ValueTree & source,UndoManager * undoManager)680 void ValueTree::copyPropertiesAndChildrenFrom (const ValueTree& source, UndoManager* undoManager)
681 {
682     jassert (object != nullptr || source.object == nullptr); // Trying to copy to a null ValueTree will fail!
683 
684     copyPropertiesFrom (source, undoManager);
685     removeAllChildren (undoManager);
686 
687     if (object != nullptr && source.object != nullptr)
688         for (auto& child : source.object->children)
689             object->addChild (createCopyIfNotNull (child), -1, undoManager);
690 }
691 
hasType(const Identifier & typeName) const692 bool ValueTree::hasType (const Identifier& typeName) const noexcept
693 {
694     return object != nullptr && object->type == typeName;
695 }
696 
getType() const697 Identifier ValueTree::getType() const noexcept
698 {
699     return object != nullptr ? object->type : Identifier();
700 }
701 
getParent() const702 ValueTree ValueTree::getParent() const noexcept
703 {
704     if (object != nullptr)
705         if (auto p = object->parent)
706             return ValueTree (*p);
707 
708     return {};
709 }
710 
getRoot() const711 ValueTree ValueTree::getRoot() const noexcept
712 {
713     if (object != nullptr)
714         return ValueTree (object->getRoot());
715 
716     return {};
717 }
718 
getSibling(int delta) const719 ValueTree ValueTree::getSibling (int delta) const noexcept
720 {
721     if (object != nullptr)
722         if (auto* p = object->parent)
723             if (auto* c = p->children.getObjectPointer (p->indexOf (*this) + delta))
724                 return ValueTree (*c);
725 
726     return {};
727 }
728 
getNullVarRef()729 static const var& getNullVarRef() noexcept
730 {
731     static var nullVar;
732     return nullVar;
733 }
734 
operator [](const Identifier & name) const735 const var& ValueTree::operator[] (const Identifier& name) const noexcept
736 {
737     return object == nullptr ? getNullVarRef() : object->properties[name];
738 }
739 
getProperty(const Identifier & name) const740 const var& ValueTree::getProperty (const Identifier& name) const noexcept
741 {
742     return object == nullptr ? getNullVarRef() : object->properties[name];
743 }
744 
getProperty(const Identifier & name,const var & defaultReturnValue) const745 var ValueTree::getProperty (const Identifier& name, const var& defaultReturnValue) const
746 {
747     return object == nullptr ? defaultReturnValue
748                              : object->properties.getWithDefault (name, defaultReturnValue);
749 }
750 
getPropertyPointer(const Identifier & name) const751 const var* ValueTree::getPropertyPointer (const Identifier& name) const noexcept
752 {
753     return object == nullptr ? nullptr
754                              : object->properties.getVarPointer (name);
755 }
756 
setProperty(const Identifier & name,const var & newValue,UndoManager * undoManager)757 ValueTree& ValueTree::setProperty (const Identifier& name, const var& newValue, UndoManager* undoManager)
758 {
759     return setPropertyExcludingListener (nullptr, name, newValue, undoManager);
760 }
761 
setPropertyExcludingListener(Listener * listenerToExclude,const Identifier & name,const var & newValue,UndoManager * undoManager)762 ValueTree& ValueTree::setPropertyExcludingListener (Listener* listenerToExclude, const Identifier& name,
763                                                     const var& newValue, UndoManager* undoManager)
764 {
765     jassert (name.toString().isNotEmpty()); // Must have a valid property name!
766     jassert (object != nullptr); // Trying to add a property to a null ValueTree will fail!
767 
768     if (object != nullptr)
769         object->setProperty (name, newValue, undoManager, listenerToExclude);
770 
771     return *this;
772 }
773 
hasProperty(const Identifier & name) const774 bool ValueTree::hasProperty (const Identifier& name) const noexcept
775 {
776     return object != nullptr && object->hasProperty (name);
777 }
778 
removeProperty(const Identifier & name,UndoManager * undoManager)779 void ValueTree::removeProperty (const Identifier& name, UndoManager* undoManager)
780 {
781     if (object != nullptr)
782         object->removeProperty (name, undoManager);
783 }
784 
removeAllProperties(UndoManager * undoManager)785 void ValueTree::removeAllProperties (UndoManager* undoManager)
786 {
787     if (object != nullptr)
788         object->removeAllProperties (undoManager);
789 }
790 
getNumProperties() const791 int ValueTree::getNumProperties() const noexcept
792 {
793     return object == nullptr ? 0 : object->properties.size();
794 }
795 
getPropertyName(int index) const796 Identifier ValueTree::getPropertyName (int index) const noexcept
797 {
798     return object == nullptr ? Identifier()
799                              : object->properties.getName (index);
800 }
801 
getReferenceCount() const802 int ValueTree::getReferenceCount() const noexcept
803 {
804     return object != nullptr ? object->getReferenceCount() : 0;
805 }
806 
807 //==============================================================================
808 struct ValueTreePropertyValueSource  : public Value::ValueSource,
809                                        private ValueTree::Listener
810 {
ValueTreePropertyValueSourcejuce::ValueTreePropertyValueSource811     ValueTreePropertyValueSource (const ValueTree& vt, const Identifier& prop, UndoManager* um, bool sync)
812         : tree (vt), property (prop), undoManager (um), updateSynchronously (sync)
813     {
814         tree.addListener (this);
815     }
816 
~ValueTreePropertyValueSourcejuce::ValueTreePropertyValueSource817     ~ValueTreePropertyValueSource() override
818     {
819         tree.removeListener (this);
820     }
821 
getValuejuce::ValueTreePropertyValueSource822     var getValue() const override                 { return tree[property]; }
setValuejuce::ValueTreePropertyValueSource823     void setValue (const var& newValue) override  { tree.setProperty (property, newValue, undoManager); }
824 
825 private:
826     ValueTree tree;
827     const Identifier property;
828     UndoManager* const undoManager;
829     const bool updateSynchronously;
830 
valueTreePropertyChangedjuce::ValueTreePropertyValueSource831     void valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty) override
832     {
833         if (tree == changedTree && property == changedProperty)
834             sendChangeMessage (updateSynchronously);
835     }
836 
valueTreeChildAddedjuce::ValueTreePropertyValueSource837     void valueTreeChildAdded (ValueTree&, ValueTree&) override {}
valueTreeChildRemovedjuce::ValueTreePropertyValueSource838     void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override {}
valueTreeChildOrderChangedjuce::ValueTreePropertyValueSource839     void valueTreeChildOrderChanged (ValueTree&, int, int) override {}
valueTreeParentChangedjuce::ValueTreePropertyValueSource840     void valueTreeParentChanged (ValueTree&) override {}
841 
842     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreePropertyValueSource)
843 };
844 
getPropertyAsValue(const Identifier & name,UndoManager * undoManager,bool updateSynchronously)845 Value ValueTree::getPropertyAsValue (const Identifier& name, UndoManager* undoManager, bool updateSynchronously)
846 {
847     return Value (new ValueTreePropertyValueSource (*this, name, undoManager, updateSynchronously));
848 }
849 
850 //==============================================================================
getNumChildren() const851 int ValueTree::getNumChildren() const noexcept
852 {
853     return object == nullptr ? 0 : object->children.size();
854 }
855 
getChild(int index) const856 ValueTree ValueTree::getChild (int index) const
857 {
858     if (object != nullptr)
859         if (auto* c = object->children.getObjectPointer (index))
860             return ValueTree (*c);
861 
862     return {};
863 }
864 
Iterator(const ValueTree & v,bool isEnd)865 ValueTree::Iterator::Iterator (const ValueTree& v, bool isEnd)
866    : internal (v.object != nullptr ? (isEnd ? v.object->children.end() : v.object->children.begin()) : nullptr)
867 {
868 }
869 
operator ++()870 ValueTree::Iterator& ValueTree::Iterator::operator++()
871 {
872     internal = static_cast<SharedObject**> (internal) + 1;
873     return *this;
874 }
875 
operator ==(const Iterator & other) const876 bool ValueTree::Iterator::operator== (const Iterator& other) const  { return internal == other.internal; }
operator !=(const Iterator & other) const877 bool ValueTree::Iterator::operator!= (const Iterator& other) const  { return internal != other.internal; }
878 
operator *() const879 ValueTree ValueTree::Iterator::operator*() const
880 {
881     return ValueTree (SharedObject::Ptr (*static_cast<SharedObject**> (internal)));
882 }
883 
begin() const884 ValueTree::Iterator ValueTree::begin() const noexcept   { return Iterator (*this, false); }
end() const885 ValueTree::Iterator ValueTree::end() const noexcept     { return Iterator (*this, true); }
886 
getChildWithName(const Identifier & type) const887 ValueTree ValueTree::getChildWithName (const Identifier& type) const
888 {
889     return object != nullptr ? object->getChildWithName (type) : ValueTree();
890 }
891 
getOrCreateChildWithName(const Identifier & type,UndoManager * undoManager)892 ValueTree ValueTree::getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager)
893 {
894     return object != nullptr ? object->getOrCreateChildWithName (type, undoManager) : ValueTree();
895 }
896 
getChildWithProperty(const Identifier & propertyName,const var & propertyValue) const897 ValueTree ValueTree::getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const
898 {
899     return object != nullptr ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree();
900 }
901 
isAChildOf(const ValueTree & possibleParent) const902 bool ValueTree::isAChildOf (const ValueTree& possibleParent) const noexcept
903 {
904     return object != nullptr && object->isAChildOf (possibleParent.object.get());
905 }
906 
indexOf(const ValueTree & child) const907 int ValueTree::indexOf (const ValueTree& child) const noexcept
908 {
909     return object != nullptr ? object->indexOf (child) : -1;
910 }
911 
addChild(const ValueTree & child,int index,UndoManager * undoManager)912 void ValueTree::addChild (const ValueTree& child, int index, UndoManager* undoManager)
913 {
914     jassert (object != nullptr); // Trying to add a child to a null ValueTree!
915 
916     if (object != nullptr)
917         object->addChild (child.object.get(), index, undoManager);
918 }
919 
appendChild(const ValueTree & child,UndoManager * undoManager)920 void ValueTree::appendChild (const ValueTree& child, UndoManager* undoManager)
921 {
922     addChild (child, -1, undoManager);
923 }
924 
removeChild(int childIndex,UndoManager * undoManager)925 void ValueTree::removeChild (int childIndex, UndoManager* undoManager)
926 {
927     if (object != nullptr)
928         object->removeChild (childIndex, undoManager);
929 }
930 
removeChild(const ValueTree & child,UndoManager * undoManager)931 void ValueTree::removeChild (const ValueTree& child, UndoManager* undoManager)
932 {
933     if (object != nullptr)
934         object->removeChild (object->children.indexOf (child.object), undoManager);
935 }
936 
removeAllChildren(UndoManager * undoManager)937 void ValueTree::removeAllChildren (UndoManager* undoManager)
938 {
939     if (object != nullptr)
940         object->removeAllChildren (undoManager);
941 }
942 
moveChild(int currentIndex,int newIndex,UndoManager * undoManager)943 void ValueTree::moveChild (int currentIndex, int newIndex, UndoManager* undoManager)
944 {
945     if (object != nullptr)
946         object->moveChild (currentIndex, newIndex, undoManager);
947 }
948 
949 //==============================================================================
createListOfChildren(OwnedArray<ValueTree> & list) const950 void ValueTree::createListOfChildren (OwnedArray<ValueTree>& list) const
951 {
952     jassert (object != nullptr);
953 
954     for (auto* o : object->children)
955     {
956         jassert (o != nullptr);
957         list.add (new ValueTree (*o));
958     }
959 }
960 
reorderChildren(const OwnedArray<ValueTree> & newOrder,UndoManager * undoManager)961 void ValueTree::reorderChildren (const OwnedArray<ValueTree>& newOrder, UndoManager* undoManager)
962 {
963     jassert (object != nullptr);
964     object->reorderChildren (newOrder, undoManager);
965 }
966 
967 //==============================================================================
addListener(Listener * listener)968 void ValueTree::addListener (Listener* listener)
969 {
970     if (listener != nullptr)
971     {
972         if (listeners.isEmpty() && object != nullptr)
973             object->valueTreesWithListeners.add (this);
974 
975         listeners.add (listener);
976     }
977 }
978 
removeListener(Listener * listener)979 void ValueTree::removeListener (Listener* listener)
980 {
981     listeners.remove (listener);
982 
983     if (listeners.isEmpty() && object != nullptr)
984         object->valueTreesWithListeners.removeValue (this);
985 }
986 
sendPropertyChangeMessage(const Identifier & property)987 void ValueTree::sendPropertyChangeMessage (const Identifier& property)
988 {
989     if (object != nullptr)
990         object->sendPropertyChangeMessage (property);
991 }
992 
993 //==============================================================================
createXml() const994 std::unique_ptr<XmlElement> ValueTree::createXml() const
995 {
996     return std::unique_ptr<XmlElement> (object != nullptr ? object->createXml() : nullptr);
997 }
998 
fromXml(const XmlElement & xml)999 ValueTree ValueTree::fromXml (const XmlElement& xml)
1000 {
1001     if (! xml.isTextElement())
1002     {
1003         ValueTree v (xml.getTagName());
1004         v.object->properties.setFromXmlAttributes (xml);
1005 
1006         forEachXmlChildElement (xml, e)
1007             v.appendChild (fromXml (*e), nullptr);
1008 
1009         return v;
1010     }
1011 
1012     // ValueTrees don't have any equivalent to XML text elements!
1013     jassertfalse;
1014     return {};
1015 }
1016 
fromXml(const String & xmlText)1017 ValueTree ValueTree::fromXml (const String& xmlText)
1018 {
1019     if (auto xml = parseXML (xmlText))
1020         return fromXml (*xml);
1021 
1022     return {};
1023 }
1024 
toXmlString(const XmlElement::TextFormat & format) const1025 String ValueTree::toXmlString (const XmlElement::TextFormat& format) const
1026 {
1027     if (auto xml = createXml())
1028         return xml->toString (format);
1029 
1030     return {};
1031 }
1032 
1033 //==============================================================================
writeToStream(OutputStream & output) const1034 void ValueTree::writeToStream (OutputStream& output) const
1035 {
1036     SharedObject::writeObjectToStream (output, object.get());
1037 }
1038 
readFromStream(InputStream & input)1039 ValueTree ValueTree::readFromStream (InputStream& input)
1040 {
1041     auto type = input.readString();
1042 
1043     if (type.isEmpty())
1044         return {};
1045 
1046     ValueTree v (type);
1047 
1048     auto numProps = input.readCompressedInt();
1049 
1050     if (numProps < 0)
1051     {
1052         jassertfalse;  // trying to read corrupted data!
1053         return v;
1054     }
1055 
1056     for (int i = 0; i < numProps; ++i)
1057     {
1058         auto name = input.readString();
1059 
1060         if (name.isNotEmpty())
1061             v.object->properties.set (name, var::readFromStream (input));
1062         else
1063             jassertfalse;  // trying to read corrupted data!
1064     }
1065 
1066     auto numChildren = input.readCompressedInt();
1067     v.object->children.ensureStorageAllocated (numChildren);
1068 
1069     for (int i = 0; i < numChildren; ++i)
1070     {
1071         auto child = readFromStream (input);
1072 
1073         if (! child.isValid())
1074             return v;
1075 
1076         v.object->children.add (child.object);
1077         child.object->parent = v.object.get();
1078     }
1079 
1080     return v;
1081 }
1082 
readFromData(const void * data,size_t numBytes)1083 ValueTree ValueTree::readFromData (const void* data, size_t numBytes)
1084 {
1085     MemoryInputStream in (data, numBytes, false);
1086     return readFromStream (in);
1087 }
1088 
readFromGZIPData(const void * data,size_t numBytes)1089 ValueTree ValueTree::readFromGZIPData (const void* data, size_t numBytes)
1090 {
1091     MemoryInputStream in (data, numBytes, false);
1092     GZIPDecompressorInputStream gzipStream (in);
1093     return readFromStream (gzipStream);
1094 }
1095 
valueTreePropertyChanged(ValueTree &,const Identifier &)1096 void ValueTree::Listener::valueTreePropertyChanged   (ValueTree&, const Identifier&) {}
valueTreeChildAdded(ValueTree &,ValueTree &)1097 void ValueTree::Listener::valueTreeChildAdded        (ValueTree&, ValueTree&)        {}
valueTreeChildRemoved(ValueTree &,ValueTree &,int)1098 void ValueTree::Listener::valueTreeChildRemoved      (ValueTree&, ValueTree&, int)   {}
valueTreeChildOrderChanged(ValueTree &,int,int)1099 void ValueTree::Listener::valueTreeChildOrderChanged (ValueTree&, int, int)          {}
valueTreeParentChanged(ValueTree &)1100 void ValueTree::Listener::valueTreeParentChanged     (ValueTree&)                    {}
valueTreeRedirected(ValueTree &)1101 void ValueTree::Listener::valueTreeRedirected        (ValueTree&)                    {}
1102 
1103 
1104 //==============================================================================
1105 //==============================================================================
1106 #if JUCE_UNIT_TESTS
1107 
1108 class ValueTreeTests  : public UnitTest
1109 {
1110 public:
ValueTreeTests()1111     ValueTreeTests()
1112         : UnitTest ("ValueTrees", UnitTestCategories::values)
1113     {}
1114 
createRandomIdentifier(Random & r)1115     static String createRandomIdentifier (Random& r)
1116     {
1117         char buffer[50] = { 0 };
1118         const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:";
1119 
1120         for (int i = 1 + r.nextInt (numElementsInArray (buffer) - 2); --i >= 0;)
1121             buffer[i] = chars[r.nextInt (sizeof (chars) - 1)];
1122 
1123         String result (buffer);
1124 
1125         if (! XmlElement::isValidXmlName (result))
1126             result = createRandomIdentifier (r);
1127 
1128         return result;
1129     }
1130 
createRandomWideCharString(Random & r)1131     static String createRandomWideCharString (Random& r)
1132     {
1133         juce_wchar buffer[50] = { 0 };
1134 
1135         for (int i = r.nextInt (numElementsInArray (buffer) - 1); --i >= 0;)
1136         {
1137             if (r.nextBool())
1138             {
1139                 do
1140                 {
1141                     buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1));
1142                 }
1143                 while (! CharPointer_UTF16::canRepresent (buffer[i]));
1144             }
1145             else
1146                 buffer[i] = (juce_wchar) (1 + r.nextInt (0x7e));
1147         }
1148 
1149         return CharPointer_UTF32 (buffer);
1150     }
1151 
createRandomTree(UndoManager * undoManager,int depth,Random & r)1152     static ValueTree createRandomTree (UndoManager* undoManager, int depth, Random& r)
1153     {
1154         ValueTree v (createRandomIdentifier (r));
1155 
1156         for (int i = r.nextInt (10); --i >= 0;)
1157         {
1158             switch (r.nextInt (5))
1159             {
1160                 case 0: v.setProperty (createRandomIdentifier (r), createRandomWideCharString (r), undoManager); break;
1161                 case 1: v.setProperty (createRandomIdentifier (r), r.nextInt(), undoManager); break;
1162                 case 2: if (depth < 5) v.addChild (createRandomTree (undoManager, depth + 1, r), r.nextInt (v.getNumChildren() + 1), undoManager); break;
1163                 case 3: v.setProperty (createRandomIdentifier (r), r.nextBool(), undoManager); break;
1164                 case 4: v.setProperty (createRandomIdentifier (r), r.nextDouble(), undoManager); break;
1165                 default: break;
1166             }
1167         }
1168 
1169         return v;
1170     }
1171 
runTest()1172     void runTest() override
1173     {
1174         {
1175             beginTest ("ValueTree");
1176 
1177             auto r = getRandom();
1178 
1179             for (int i = 10; --i >= 0;)
1180             {
1181                 MemoryOutputStream mo;
1182                 auto v1 = createRandomTree (nullptr, 0, r);
1183                 v1.writeToStream (mo);
1184 
1185                 MemoryInputStream mi (mo.getData(), mo.getDataSize(), false);
1186                 auto v2 = ValueTree::readFromStream (mi);
1187                 expect (v1.isEquivalentTo (v2));
1188 
1189                 MemoryOutputStream zipped;
1190                 {
1191                     GZIPCompressorOutputStream zippedOut (zipped);
1192                     v1.writeToStream (zippedOut);
1193                 }
1194                 expect (v1.isEquivalentTo (ValueTree::readFromGZIPData (zipped.getData(), zipped.getDataSize())));
1195 
1196                 auto xml1 = v1.createXml();
1197                 auto xml2 = v2.createCopy().createXml();
1198                 expect (xml1->isEquivalentTo (xml2.get(), false));
1199 
1200                 auto v4 = v2.createCopy();
1201                 expect (v1.isEquivalentTo (v4));
1202             }
1203         }
1204 
1205         {
1206             beginTest ("Float formatting");
1207 
1208             ValueTree testVT ("Test");
1209             Identifier number ("number");
1210 
1211             std::map<double, String> tests;
1212             tests[1] = "1.0";
1213             tests[1.1] = "1.1";
1214             tests[1.01] = "1.01";
1215             tests[0.76378] = "0.76378";
1216             tests[-10] = "-10.0";
1217             tests[10.01] = "10.01";
1218             tests[0.0123] = "0.0123";
1219             tests[-3.7e-27] = "-3.7e-27";
1220             tests[1e+40] = "1.0e40";
1221             tests[-12345678901234567.0] = "-1.234567890123457e16";
1222             tests[192000] = "192000.0";
1223             tests[1234567] = "1.234567e6";
1224             tests[0.00006] = "0.00006";
1225             tests[0.000006] = "6.0e-6";
1226 
1227             for (auto& test : tests)
1228             {
1229                 testVT.setProperty (number, test.first, nullptr);
1230                 auto lines = StringArray::fromLines (testVT.toXmlString());
1231                 lines.removeEmptyStrings();
1232                 auto numLines = lines.size();
1233                 expect (numLines > 1);
1234                 expectEquals (lines[numLines - 1], "<Test number=\"" + test.second + "\"/>");
1235             }
1236         }
1237     }
1238 };
1239 
1240 static ValueTreeTests valueTreeTests;
1241 
1242 #endif
1243 
1244 } // namespace juce
1245