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