OpenShot Audio Library | OpenShotAudio 0.4.0

Loading...
Searching...
No Matches
juce_XmlElement.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23 namespace juce
24{
25
26 static bool isValidXmlNameStartCharacter (juce_wchar character) noexcept
27{
28 return character == ':'
29 || character == '_'
30 || (character >= 'a' && character <= 'z')
31 || (character >= 'A' && character <= 'Z')
32 || (character >= 0xc0 && character <= 0xd6)
33 || (character >= 0xd8 && character <= 0xf6)
34 || (character >= 0xf8 && character <= 0x2ff)
35 || (character >= 0x370 && character <= 0x37d)
36 || (character >= 0x37f && character <= 0x1fff)
37 || (character >= 0x200c && character <= 0x200d)
38 || (character >= 0x2070 && character <= 0x218f)
39 || (character >= 0x2c00 && character <= 0x2fef)
40 || (character >= 0x3001 && character <= 0xd7ff)
41 || (character >= 0xf900 && character <= 0xfdcf)
42 || (character >= 0xfdf0 && character <= 0xfffd)
43 || (character >= 0x10000 && character <= 0xeffff);
44}
45
46 static bool isValidXmlNameBodyCharacter (juce_wchar character) noexcept
47{
48 return isValidXmlNameStartCharacter (character)
49 || character == '-'
50 || character == '.'
51 || character == 0xb7
52 || (character >= '0' && character <= '9')
53 || (character >= 0x300 && character <= 0x036f)
54 || (character >= 0x203f && character <= 0x2040);
55}
56
57XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) noexcept
58 : name (other.name),
59 value (other.value)
60{
61}
62
63XmlElement::XmlAttributeNode::XmlAttributeNode (const Identifier& n, const String& v) noexcept
64 : name (n), value (v)
65{
66 jassert (isValidXmlName (name));
67}
68
69XmlElement::XmlAttributeNode::XmlAttributeNode (String::CharPointerType nameStart, String::CharPointerType nameEnd)
70 : name (nameStart, nameEnd)
71{
72 jassert (isValidXmlName (name));
73}
74
75//==============================================================================
76XmlElement::XmlElement (const String& tag)
77 : tagName (StringPool::getGlobalPool().getPooledString (tag))
78{
79 jassert (isValidXmlName (tagName));
80}
81
82 XmlElement::XmlElement (const char* tag)
83 : tagName (StringPool::getGlobalPool().getPooledString (tag))
84{
85 jassert (isValidXmlName (tagName));
86}
87
88 XmlElement::XmlElement (StringRef tag)
89 : tagName (StringPool::getGlobalPool().getPooledString (tag))
90{
91 jassert (isValidXmlName (tagName));
92}
93
94 XmlElement::XmlElement (const Identifier& tag)
95 : tagName (tag.toString())
96{
97 jassert (isValidXmlName (tagName));
98}
99
100 XmlElement::XmlElement (String::CharPointerType tagNameStart, String::CharPointerType tagNameEnd)
101 : tagName (StringPool::getGlobalPool().getPooledString (tagNameStart, tagNameEnd))
102{
103 jassert (isValidXmlName (tagName));
104}
105
106 XmlElement::XmlElement (int /*dummy*/) noexcept
107{
108}
109
110 XmlElement::XmlElement (const XmlElement& other)
111 : tagName (other.tagName)
112{
113 copyChildrenAndAttributesFrom (other);
114}
115
116 XmlElement& XmlElement::operator= (const XmlElement& other)
117{
118 if (this != &other)
119 {
120 removeAllAttributes();
121 deleteAllChildElements();
122 tagName = other.tagName;
123 copyChildrenAndAttributesFrom (other);
124 }
125
126 return *this;
127}
128
129 XmlElement::XmlElement (XmlElement&& other) noexcept
130 : nextListItem (std::move (other.nextListItem)),
131 firstChildElement (std::move (other.firstChildElement)),
132 attributes (std::move (other.attributes)),
133 tagName (std::move (other.tagName))
134{
135}
136
137 XmlElement& XmlElement::operator= (XmlElement&& other) noexcept
138{
139 jassert (this != &other); // hopefully the compiler should make this situation impossible!
140
141 removeAllAttributes();
142 deleteAllChildElements();
143
144 nextListItem = std::move (other.nextListItem);
145 firstChildElement = std::move (other.firstChildElement);
146 attributes = std::move (other.attributes);
147 tagName = std::move (other.tagName);
148
149 return *this;
150}
151
152 void XmlElement::copyChildrenAndAttributesFrom (const XmlElement& other)
153{
154 jassert (firstChildElement.get() == nullptr);
155 firstChildElement.addCopyOfList (other.firstChildElement);
156
157 jassert (attributes.get() == nullptr);
158 attributes.addCopyOfList (other.attributes);
159}
160
161 XmlElement::~XmlElement() noexcept
162{
163 firstChildElement.deleteAll();
164 attributes.deleteAll();
165}
166
167//==============================================================================
168 namespace XmlOutputFunctions
169{
170 namespace LegalCharLookupTable
171 {
172 template <int c>
173 struct Bit
174 {
175 enum { v = ((c >= 'a' && c <= 'z')
176 || (c >= 'A' && c <= 'Z')
177 || (c >= '0' && c <= '9')
178 || c == ' ' || c == '.' || c == ',' || c == ';'
179 || c == ':' || c == '-' || c == '(' || c == ')'
180 || c == '_' || c == '+' || c == '=' || c == '?'
181 || c == '!' || c == '$' || c == '#' || c == '@'
182 || c == '[' || c == ']' || c == '/' || c == '|'
183 || c == '*' || c == '%' || c == '~' || c == '{'
184 || c == '}' || c == '\'' || c == '\\')
185 ? (1 << (c & 7)) : 0 };
186 };
187
188 template <int tableIndex>
189 struct Byte
190 {
191 enum { v = (int) Bit<tableIndex * 8 + 0>::v | (int) Bit<tableIndex * 8 + 1>::v
192 | (int) Bit<tableIndex * 8 + 2>::v | (int) Bit<tableIndex * 8 + 3>::v
193 | (int) Bit<tableIndex * 8 + 4>::v | (int) Bit<tableIndex * 8 + 5>::v
194 | (int) Bit<tableIndex * 8 + 6>::v | (int) Bit<tableIndex * 8 + 7>::v };
195 };
196
197 static bool isLegal (uint32 c) noexcept
198 {
199 static const unsigned char legalChars[] = { Byte< 0>::v, Byte< 1>::v, Byte< 2>::v, Byte< 3>::v,
200 Byte< 4>::v, Byte< 5>::v, Byte< 6>::v, Byte< 7>::v,
201 Byte< 8>::v, Byte< 9>::v, Byte<10>::v, Byte<11>::v,
202 Byte<12>::v, Byte<13>::v, Byte<14>::v, Byte<15>::v };
203
204 return c < sizeof (legalChars) * 8
205 && (legalChars[c >> 3] & (1 << (c & 7))) != 0;
206 }
207 }
208
209 static void escapeIllegalXmlChars (OutputStream& outputStream, const String& text, bool changeNewLines)
210 {
211 auto t = text.getCharPointer();
212
213 for (;;)
214 {
215 auto character = (uint32) t.getAndAdvance();
216
217 if (character == 0)
218 break;
219
220 if (LegalCharLookupTable::isLegal (character))
221 {
222 outputStream << (char) character;
223 }
224 else
225 {
226 switch (character)
227 {
228 case '&': outputStream << "&amp;"; break;
229 case '"': outputStream << "&quot;"; break;
230 case '>': outputStream << "&gt;"; break;
231 case '<': outputStream << "&lt;"; break;
232
233 case '\n':
234 case '\r':
235 if (! changeNewLines)
236 {
237 outputStream << (char) character;
238 break;
239 }
240 JUCE_FALLTHROUGH
241 default:
242 outputStream << "&#" << ((int) character) << ';';
243 break;
244 }
245 }
246 }
247 }
248
249 static void writeSpaces (OutputStream& out, const size_t numSpaces)
250 {
251 out.writeRepeatedByte (' ', numSpaces);
252 }
253}
254
255 void XmlElement::writeElementAsText (OutputStream& outputStream,
256 int indentationLevel,
257 int lineWrapLength,
258 const char* newLineChars) const
259{
260 if (indentationLevel >= 0)
261 XmlOutputFunctions::writeSpaces (outputStream, (size_t) indentationLevel);
262
263 if (! isTextElement())
264 {
265 outputStream.writeByte ('<');
266 outputStream << tagName;
267
268 {
269 auto attIndent = (size_t) (indentationLevel + tagName.length() + 1);
270 int lineLen = 0;
271
272 for (auto* att = attributes.get(); att != nullptr; att = att->nextListItem)
273 {
274 if (lineLen > lineWrapLength && indentationLevel >= 0)
275 {
276 outputStream << newLineChars;
277 XmlOutputFunctions::writeSpaces (outputStream, attIndent);
278 lineLen = 0;
279 }
280
281 auto startPos = outputStream.getPosition();
282 outputStream.writeByte (' ');
283 outputStream << att->name;
284 outputStream.write ("=\"", 2);
285 XmlOutputFunctions::escapeIllegalXmlChars (outputStream, att->value, true);
286 outputStream.writeByte ('"');
287 lineLen += (int) (outputStream.getPosition() - startPos);
288 }
289 }
290
291 if (auto* child = firstChildElement.get())
292 {
293 outputStream.writeByte ('>');
294 bool lastWasTextNode = false;
295
296 for (; child != nullptr; child = child->nextListItem)
297 {
298 if (child->isTextElement())
299 {
300 XmlOutputFunctions::escapeIllegalXmlChars (outputStream, child->getText(), false);
301 lastWasTextNode = true;
302 }
303 else
304 {
305 if (indentationLevel >= 0 && ! lastWasTextNode)
306 outputStream << newLineChars;
307
308 child->writeElementAsText (outputStream,
309 lastWasTextNode ? 0 : (indentationLevel + (indentationLevel >= 0 ? 2 : 0)), lineWrapLength,
310 newLineChars);
311 lastWasTextNode = false;
312 }
313 }
314
315 if (indentationLevel >= 0 && ! lastWasTextNode)
316 {
317 outputStream << newLineChars;
318 XmlOutputFunctions::writeSpaces (outputStream, (size_t) indentationLevel);
319 }
320
321 outputStream.write ("</", 2);
322 outputStream << tagName;
323 outputStream.writeByte ('>');
324 }
325 else
326 {
327 outputStream.write ("/>", 2);
328 }
329 }
330 else
331 {
332 XmlOutputFunctions::escapeIllegalXmlChars (outputStream, getText(), false);
333 }
334}
335
336 XmlElement::TextFormat::TextFormat() {}
337
338 XmlElement::TextFormat XmlElement::TextFormat::singleLine() const
339{
340 auto f = *this;
341 f.newLineChars = nullptr;
342 return f;
343}
344
345 XmlElement::TextFormat XmlElement::TextFormat::withoutHeader() const
346{
347 auto f = *this;
348 f.addDefaultHeader = false;
349 return f;
350}
351
352 String XmlElement::toString (const TextFormat& options) const
353{
354 MemoryOutputStream mem (2048);
355 writeTo (mem, options);
356 return mem.toUTF8();
357}
358
359 void XmlElement::writeTo (OutputStream& output, const TextFormat& options) const
360{
361 if (options.customHeader.isNotEmpty())
362 {
363 output << options.customHeader;
364
365 if (options.newLineChars == nullptr)
366 output.writeByte (' ');
367 else
368 output << options.newLineChars
369 << options.newLineChars;
370 }
371 else if (options.addDefaultHeader)
372 {
373 output << "<?xml version=\"1.0\" encoding=\"";
374
375 if (options.customEncoding.isNotEmpty())
376 output << options.customEncoding;
377 else
378 output << "UTF-8";
379
380 output << "\"?>";
381
382 if (options.newLineChars == nullptr)
383 output.writeByte (' ');
384 else
385 output << options.newLineChars
386 << options.newLineChars;
387 }
388
389 if (options.dtd.isNotEmpty())
390 {
391 output << options.dtd;
392
393 if (options.newLineChars == nullptr)
394 output.writeByte (' ');
395 else
396 output << options.newLineChars;
397 }
398
399 writeElementAsText (output, options.newLineChars == nullptr ? -1 : 0,
400 options.lineWrapLength,
401 options.newLineChars);
402
403 if (options.newLineChars != nullptr)
404 output << options.newLineChars;
405}
406
407 bool XmlElement::writeTo (const File& destinationFile, const TextFormat& options) const
408{
409 TemporaryFile tempFile (destinationFile);
410
411 {
412 FileOutputStream out (tempFile.getFile());
413
414 if (! out.openedOk())
415 return false;
416
417 writeTo (out, options);
418 out.flush(); // (called explicitly to force an fsync on posix)
419
420 if (out.getStatus().failed())
421 return false;
422 }
423
424 return tempFile.overwriteTargetFileWithTemporary();
425}
426
427 String XmlElement::createDocument (StringRef dtdToUse, bool allOnOneLine, bool includeXmlHeader,
428 StringRef encodingType, int lineWrapLength) const
429{
430 TextFormat options;
431 options.dtd = dtdToUse;
432 options.customEncoding = encodingType;
433 options.addDefaultHeader = includeXmlHeader;
434 options.lineWrapLength = lineWrapLength;
435
436 if (allOnOneLine)
437 options.newLineChars = nullptr;
438
439 return toString (options);
440}
441
442 void XmlElement::writeToStream (OutputStream& output, StringRef dtdToUse,
443 bool allOnOneLine, bool includeXmlHeader,
444 StringRef encodingType, int lineWrapLength) const
445{
446 TextFormat options;
447 options.dtd = dtdToUse;
448 options.customEncoding = encodingType;
449 options.addDefaultHeader = includeXmlHeader;
450 options.lineWrapLength = lineWrapLength;
451
452 if (allOnOneLine)
453 options.newLineChars = nullptr;
454
455 writeTo (output, options);
456}
457
458 bool XmlElement::writeToFile (const File& file, StringRef dtdToUse,
459 StringRef encodingType, int lineWrapLength) const
460{
461 TextFormat options;
462 options.dtd = dtdToUse;
463 options.customEncoding = encodingType;
464 options.lineWrapLength = lineWrapLength;
465
466 return writeTo (file, options);
467}
468
469//==============================================================================
470 bool XmlElement::hasTagName (StringRef possibleTagName) const noexcept
471{
472 const bool matches = tagName.equalsIgnoreCase (possibleTagName);
473
474 // XML tags should be case-sensitive, so although this method allows a
475 // case-insensitive match to pass, you should try to avoid this.
476 jassert ((! matches) || tagName == possibleTagName);
477
478 return matches;
479}
480
481 String XmlElement::getNamespace() const
482{
483 return tagName.upToFirstOccurrenceOf (":", false, false);
484}
485
486 String XmlElement::getTagNameWithoutNamespace() const
487{
488 return tagName.fromLastOccurrenceOf (":", false, false);
489}
490
491 bool XmlElement::hasTagNameIgnoringNamespace (StringRef possibleTagName) const
492{
493 return hasTagName (possibleTagName) || getTagNameWithoutNamespace() == possibleTagName;
494}
495
496 XmlElement* XmlElement::getNextElementWithTagName (StringRef requiredTagName) const
497{
498 auto* e = nextListItem.get();
499
500 while (e != nullptr && ! e->hasTagName (requiredTagName))
501 e = e->nextListItem;
502
503 return e;
504}
505
506 void XmlElement::setTagName (StringRef newTagName)
507{
508 jassert (isValidXmlName (newTagName));
509 tagName = StringPool::getGlobalPool().getPooledString (newTagName);
510}
511
512//==============================================================================
513 int XmlElement::getNumAttributes() const noexcept
514{
515 return attributes.size();
516}
517
518 static const String& getEmptyStringRef() noexcept
519{
520 static String empty;
521 return empty;
522}
523
524 const String& XmlElement::getAttributeName (const int index) const noexcept
525{
526 if (auto* att = attributes[index].get())
527 return att->name.toString();
528
529 return getEmptyStringRef();
530}
531
532 const String& XmlElement::getAttributeValue (const int index) const noexcept
533{
534 if (auto* att = attributes[index].get())
535 return att->value;
536
537 return getEmptyStringRef();
538}
539
540XmlElement::XmlAttributeNode* XmlElement::getAttribute (StringRef attributeName) const noexcept
541{
542 for (auto* att = attributes.get(); att != nullptr; att = att->nextListItem)
543 if (att->name == attributeName)
544 return att;
545
546 return nullptr;
547}
548
549 bool XmlElement::hasAttribute (StringRef attributeName) const noexcept
550{
551 return getAttribute (attributeName) != nullptr;
552}
553
554//==============================================================================
555 const String& XmlElement::getStringAttribute (StringRef attributeName) const noexcept
556{
557 if (auto* att = getAttribute (attributeName))
558 return att->value;
559
560 return getEmptyStringRef();
561}
562
563 String XmlElement::getStringAttribute (StringRef attributeName, const String& defaultReturnValue) const
564{
565 if (auto* att = getAttribute (attributeName))
566 return att->value;
567
568 return defaultReturnValue;
569}
570
571 int XmlElement::getIntAttribute (StringRef attributeName, const int defaultReturnValue) const
572{
573 if (auto* att = getAttribute (attributeName))
574 return att->value.getIntValue();
575
576 return defaultReturnValue;
577}
578
579 double XmlElement::getDoubleAttribute (StringRef attributeName, const double defaultReturnValue) const
580{
581 if (auto* att = getAttribute (attributeName))
582 return att->value.getDoubleValue();
583
584 return defaultReturnValue;
585}
586
587 bool XmlElement::getBoolAttribute (StringRef attributeName, const bool defaultReturnValue) const
588{
589 if (auto* att = getAttribute (attributeName))
590 {
591 auto firstChar = *(att->value.getCharPointer().findEndOfWhitespace());
592
593 return firstChar == '1'
594 || firstChar == 't'
595 || firstChar == 'y'
596 || firstChar == 'T'
597 || firstChar == 'Y';
598 }
599
600 return defaultReturnValue;
601}
602
603 bool XmlElement::compareAttribute (StringRef attributeName,
604 StringRef stringToCompareAgainst,
605 const bool ignoreCase) const noexcept
606{
607 if (auto* att = getAttribute (attributeName))
608 return ignoreCase ? att->value.equalsIgnoreCase (stringToCompareAgainst)
609 : att->value == stringToCompareAgainst;
610
611 return false;
612}
613
614//==============================================================================
615 void XmlElement::setAttribute (const Identifier& attributeName, const String& value)
616{
617 if (attributes == nullptr)
618 {
619 attributes = new XmlAttributeNode (attributeName, value);
620 }
621 else
622 {
623 for (auto* att = attributes.get(); ; att = att->nextListItem)
624 {
625 if (att->name == attributeName)
626 {
627 att->value = value;
628 break;
629 }
630
631 if (att->nextListItem == nullptr)
632 {
633 att->nextListItem = new XmlAttributeNode (attributeName, value);
634 break;
635 }
636 }
637 }
638}
639
640 void XmlElement::setAttribute (const Identifier& attributeName, const int number)
641{
642 setAttribute (attributeName, String (number));
643}
644
645 void XmlElement::setAttribute (const Identifier& attributeName, const double number)
646{
647 setAttribute (attributeName, serialiseDouble (number));
648}
649
650 void XmlElement::removeAttribute (const Identifier& attributeName) noexcept
651{
652 for (auto* att = &attributes; att->get() != nullptr; att = &(att->get()->nextListItem))
653 {
654 if (att->get()->name == attributeName)
655 {
656 delete att->removeNext();
657 break;
658 }
659 }
660}
661
662 void XmlElement::removeAllAttributes() noexcept
663{
664 attributes.deleteAll();
665}
666
667//==============================================================================
668 int XmlElement::getNumChildElements() const noexcept
669{
670 return firstChildElement.size();
671}
672
673 XmlElement* XmlElement::getChildElement (const int index) const noexcept
674{
675 return firstChildElement[index].get();
676}
677
678 XmlElement* XmlElement::getChildByName (StringRef childName) const noexcept
679{
680 jassert (! childName.isEmpty());
681
682 for (auto* child = firstChildElement.get(); child != nullptr; child = child->nextListItem)
683 if (child->hasTagName (childName))
684 return child;
685
686 return nullptr;
687}
688
689 XmlElement* XmlElement::getChildByAttribute (StringRef attributeName, StringRef attributeValue) const noexcept
690{
691 jassert (! attributeName.isEmpty());
692
693 for (auto* child = firstChildElement.get(); child != nullptr; child = child->nextListItem)
694 if (child->compareAttribute (attributeName, attributeValue))
695 return child;
696
697 return nullptr;
698}
699
700 void XmlElement::addChildElement (XmlElement* const newNode) noexcept
701{
702 if (newNode != nullptr)
703 {
704 // The element being added must not be a child of another node!
705 jassert (newNode->nextListItem == nullptr);
706
707 firstChildElement.append (newNode);
708 }
709}
710
711 void XmlElement::insertChildElement (XmlElement* const newNode, int indexToInsertAt) noexcept
712{
713 if (newNode != nullptr)
714 {
715 // The element being added must not be a child of another node!
716 jassert (newNode->nextListItem == nullptr);
717
718 firstChildElement.insertAtIndex (indexToInsertAt, newNode);
719 }
720}
721
722 void XmlElement::prependChildElement (XmlElement* newNode) noexcept
723{
724 if (newNode != nullptr)
725 {
726 // The element being added must not be a child of another node!
727 jassert (newNode->nextListItem == nullptr);
728
729 firstChildElement.insertNext (newNode);
730 }
731}
732
733 XmlElement* XmlElement::createNewChildElement (StringRef childTagName)
734{
735 auto newElement = new XmlElement (childTagName);
736 addChildElement (newElement);
737 return newElement;
738}
739
740 bool XmlElement::replaceChildElement (XmlElement* const currentChildElement,
741 XmlElement* const newNode) noexcept
742{
743 if (newNode != nullptr)
744 {
745 if (auto* p = firstChildElement.findPointerTo (currentChildElement))
746 {
747 if (currentChildElement != newNode)
748 delete p->replaceNext (newNode);
749
750 return true;
751 }
752 }
753
754 return false;
755}
756
757 void XmlElement::removeChildElement (XmlElement* const childToRemove,
758 const bool shouldDeleteTheChild) noexcept
759{
760 if (childToRemove != nullptr)
761 {
762 jassert (containsChildElement (childToRemove));
763
764 firstChildElement.remove (childToRemove);
765
766 if (shouldDeleteTheChild)
767 delete childToRemove;
768 }
769}
770
771 bool XmlElement::isEquivalentTo (const XmlElement* const other,
772 const bool ignoreOrderOfAttributes) const noexcept
773{
774 if (this != other)
775 {
776 if (other == nullptr || tagName != other->tagName)
777 return false;
778
779 if (ignoreOrderOfAttributes)
780 {
781 int totalAtts = 0;
782
783 for (auto* att = attributes.get(); att != nullptr; att = att->nextListItem)
784 {
785 if (! other->compareAttribute (att->name, att->value))
786 return false;
787
788 ++totalAtts;
789 }
790
791 if (totalAtts != other->getNumAttributes())
792 return false;
793 }
794 else
795 {
796 auto* thisAtt = attributes.get();
797 auto* otherAtt = other->attributes.get();
798
799 for (;;)
800 {
801 if (thisAtt == nullptr || otherAtt == nullptr)
802 {
803 if (thisAtt == otherAtt) // both nullptr, so it's a match
804 break;
805
806 return false;
807 }
808
809 if (thisAtt->name != otherAtt->name
810 || thisAtt->value != otherAtt->value)
811 {
812 return false;
813 }
814
815 thisAtt = thisAtt->nextListItem;
816 otherAtt = otherAtt->nextListItem;
817 }
818 }
819
820 auto* thisChild = firstChildElement.get();
821 auto* otherChild = other->firstChildElement.get();
822
823 for (;;)
824 {
825 if (thisChild == nullptr || otherChild == nullptr)
826 {
827 if (thisChild == otherChild) // both 0, so it's a match
828 break;
829
830 return false;
831 }
832
833 if (! thisChild->isEquivalentTo (otherChild, ignoreOrderOfAttributes))
834 return false;
835
836 thisChild = thisChild->nextListItem;
837 otherChild = otherChild->nextListItem;
838 }
839 }
840
841 return true;
842}
843
844 void XmlElement::deleteAllChildElements() noexcept
845{
846 firstChildElement.deleteAll();
847}
848
849 void XmlElement::deleteAllChildElementsWithTagName (StringRef name) noexcept
850{
851 for (auto* child = firstChildElement.get(); child != nullptr;)
852 {
853 auto* nextChild = child->nextListItem.get();
854
855 if (child->hasTagName (name))
856 removeChildElement (child, true);
857
858 child = nextChild;
859 }
860}
861
862 bool XmlElement::containsChildElement (const XmlElement* const possibleChild) const noexcept
863{
864 return firstChildElement.contains (possibleChild);
865}
866
867 XmlElement* XmlElement::findParentElementOf (const XmlElement* const elementToLookFor) noexcept
868{
869 if (this == elementToLookFor || elementToLookFor == nullptr)
870 return nullptr;
871
872 for (auto* child = firstChildElement.get(); child != nullptr; child = child->nextListItem)
873 {
874 if (elementToLookFor == child)
875 return this;
876
877 if (auto* found = child->findParentElementOf (elementToLookFor))
878 return found;
879 }
880
881 return nullptr;
882}
883
884 void XmlElement::getChildElementsAsArray (XmlElement** elems) const noexcept
885{
886 firstChildElement.copyToArray (elems);
887}
888
889 void XmlElement::reorderChildElements (XmlElement** elems, int num) noexcept
890{
891 auto* e = elems[0];
892 firstChildElement = e;
893
894 for (int i = 1; i < num; ++i)
895 {
896 e->nextListItem = elems[i];
897 e = e->nextListItem;
898 }
899
900 e->nextListItem = nullptr;
901}
902
903//==============================================================================
904 bool XmlElement::isTextElement() const noexcept
905{
906 return tagName.isEmpty();
907}
908
909 static const String juce_xmltextContentAttributeName ("text");
910
911 const String& XmlElement::getText() const noexcept
912{
913 jassert (isTextElement()); // you're trying to get the text from an element that
914 // isn't actually a text element.. If this contains text sub-nodes, you
915 // probably want to use getAllSubText instead.
916
917 return getStringAttribute (juce_xmltextContentAttributeName);
918}
919
920 void XmlElement::setText (const String& newText)
921{
922 if (isTextElement())
923 setAttribute (juce_xmltextContentAttributeName, newText);
924 else
925 jassertfalse; // you can only change the text in a text element, not a normal one.
926}
927
928 String XmlElement::getAllSubText() const
929{
930 if (isTextElement())
931 return getText();
932
933 if (getNumChildElements() == 1)
934 return firstChildElement.get()->getAllSubText();
935
936 MemoryOutputStream mem (1024);
937
938 for (auto* child = firstChildElement.get(); child != nullptr; child = child->nextListItem)
939 mem << child->getAllSubText();
940
941 return mem.toUTF8();
942}
943
944 String XmlElement::getChildElementAllSubText (StringRef childTagName, const String& defaultReturnValue) const
945{
946 if (auto* child = getChildByName (childTagName))
947 return child->getAllSubText();
948
949 return defaultReturnValue;
950}
951
952 XmlElement* XmlElement::createTextElement (const String& text)
953{
954 auto e = new XmlElement ((int) 0);
955 e->setAttribute (juce_xmltextContentAttributeName, text);
956 return e;
957}
958
959 bool XmlElement::isValidXmlName (StringRef text) noexcept
960{
961 if (text.isEmpty() || ! isValidXmlNameStartCharacter (text.text.getAndAdvance()))
962 return false;
963
964 for (;;)
965 {
966 if (text.isEmpty())
967 return true;
968
969 if (! isValidXmlNameBodyCharacter (text.text.getAndAdvance()))
970 return false;
971 }
972}
973
974 void XmlElement::addTextElement (const String& text)
975{
976 addChildElement (createTextElement (text));
977}
978
979 void XmlElement::deleteAllTextElements() noexcept
980{
981 for (auto* child = firstChildElement.get(); child != nullptr;)
982 {
983 auto* next = child->nextListItem.get();
984
985 if (child->isTextElement())
986 removeChildElement (child, true);
987
988 child = next;
989 }
990}
991
992//==============================================================================
993//==============================================================================
994#if JUCE_UNIT_TESTS
995
996 class XmlElementTests final : public UnitTest
997{
998 public:
999 XmlElementTests()
1000 : UnitTest ("XmlElement", UnitTestCategories::xml)
1001 {}
1002
1003 void runTest() override
1004 {
1005 {
1006 beginTest ("Float formatting");
1007
1008 auto element = std::make_unique<XmlElement> ("test");
1009 Identifier number ("number");
1010
1011 std::map<double, String> tests;
1012 tests[1] = "1.0";
1013 tests[1.1] = "1.1";
1014 tests[1.01] = "1.01";
1015 tests[0.76378] = "0.76378";
1016 tests[-10] = "-10.0";
1017 tests[10.01] = "10.01";
1018 tests[0.0123] = "0.0123";
1019 tests[-3.7e-27] = "-3.7e-27";
1020 tests[1e+40] = "1.0e40";
1021 tests[-12345678901234567.0] = "-1.234567890123457e16";
1022 tests[192000] = "192000.0";
1023 tests[1234567] = "1.234567e6";
1024 tests[0.00006] = "0.00006";
1025 tests[0.000006] = "6.0e-6";
1026
1027 for (auto& test : tests)
1028 {
1029 element->setAttribute (number, test.first);
1030 expectEquals (element->getStringAttribute (number), test.second);
1031 }
1032 }
1033 }
1034};
1035
1036 static XmlElementTests xmlElementTests;
1037
1038#endif
1039
1040} // namespace juce
juce::FileOutputStream
Definition juce_FileOutputStream.h:35
juce::FileOutputStream::getStatus
const Result & getStatus() const noexcept
Definition juce_FileOutputStream.h:81
juce::FileOutputStream::openedOk
bool openedOk() const noexcept
Definition juce_FileOutputStream.h:91
juce::FileOutputStream::flush
void flush() override
Definition juce_FileOutputStream.cpp:70
juce::File
Definition juce_File.h:45
juce::Identifier
Definition juce_Identifier.h:39
juce::LinkedListPointer::deleteAll
void deleteAll()
Definition juce_LinkedListPointer.h:280
juce::LinkedListPointer::get
ObjectType * get() const noexcept
Definition juce_LinkedListPointer.h:102
juce::LinkedListPointer::addCopyOfList
void addCopyOfList(const LinkedListPointer &other)
Definition juce_LinkedListPointer.h:240
juce::LinkedListPointer::size
int size() const noexcept
Definition juce_LinkedListPointer.h:128
juce::MemoryOutputStream
Definition juce_MemoryOutputStream.h:36
juce::MemoryOutputStream::toUTF8
String toUTF8() const
Definition juce_MemoryOutputStream.cpp:189
juce::OutputStream
Definition juce_OutputStream.h:38
juce::OutputStream::writeByte
virtual bool writeByte(char byte)
Definition juce_OutputStream.cpp:83
juce::Result::failed
bool failed() const noexcept
Definition juce_Result.cpp:77
juce::StringPool
Definition juce_StringPool.h:40
juce::StringPool::getPooledString
String getPooledString(const String &original)
Definition juce_StringPool.cpp:130
juce::StringPool::getGlobalPool
static StringPool & getGlobalPool() noexcept
Definition juce_StringPool.cpp:158
juce::StringRef
Definition juce_StringRef.h:62
juce::String
Definition juce_String.h:53
juce::String::upToFirstOccurrenceOf
String upToFirstOccurrenceOf(StringRef substringToEndWith, bool includeSubStringInResult, bool ignoreCase) const
Definition juce_String.cpp:1585
juce::String::fromLastOccurrenceOf
String fromLastOccurrenceOf(StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const
Definition juce_String.cpp:1575
juce::String::getIntValue
int getIntValue() const noexcept
Definition juce_String.cpp:1871
juce::String::isNotEmpty
bool isNotEmpty() const noexcept
Definition juce_String.h:316
juce::TemporaryFile
Definition juce_TemporaryFile.h:65
juce::TemporaryFile::overwriteTargetFileWithTemporary
bool overwriteTargetFileWithTemporary() const
Definition juce_TemporaryFile.cpp:93
juce::TemporaryFile::getFile
const File & getFile() const noexcept
Definition juce_TemporaryFile.h:126
juce::UnitTest
Definition juce_UnitTest.h:70
juce::XmlElement
Definition juce_XmlElement.h:83
juce::XmlElement::getNextElementWithTagName
XmlElement * getNextElementWithTagName(StringRef requiredTagName) const
Definition juce_XmlElement.cpp:496
juce::XmlElement::setTagName
void setTagName(StringRef newTagName)
Definition juce_XmlElement.cpp:506
juce::XmlElement::addTextElement
void addTextElement(const String &text)
Definition juce_XmlElement.cpp:974
juce::XmlElement::getNumChildElements
int getNumChildElements() const noexcept
Definition juce_XmlElement.cpp:668
juce::XmlElement::XmlElement
XmlElement(const String &tagName)
Definition juce_XmlElement.cpp:76
juce::XmlElement::insertChildElement
void insertChildElement(XmlElement *newChildElement, int indexToInsertAt) noexcept
Definition juce_XmlElement.cpp:711
juce::XmlElement::getChildByName
XmlElement * getChildByName(StringRef tagNameToLookFor) const noexcept
Definition juce_XmlElement.cpp:678
juce::XmlElement::isTextElement
bool isTextElement() const noexcept
Definition juce_XmlElement.cpp:904
juce::XmlElement::deleteAllChildElementsWithTagName
void deleteAllChildElementsWithTagName(StringRef tagName) noexcept
Definition juce_XmlElement.cpp:849
juce::XmlElement::toString
String toString(const TextFormat &format={}) const
Definition juce_XmlElement.cpp:352
juce::XmlElement::addChildElement
void addChildElement(XmlElement *newChildElement) noexcept
Definition juce_XmlElement.cpp:700
juce::XmlElement::getTagNameWithoutNamespace
String getTagNameWithoutNamespace() const
Definition juce_XmlElement.cpp:486
juce::XmlElement::getDoubleAttribute
double getDoubleAttribute(StringRef attributeName, double defaultReturnValue=0.0) const
Definition juce_XmlElement.cpp:579
juce::XmlElement::getAttributeValue
const String & getAttributeValue(int attributeIndex) const noexcept
Definition juce_XmlElement.cpp:532
juce::XmlElement::prependChildElement
void prependChildElement(XmlElement *newChildElement) noexcept
Definition juce_XmlElement.cpp:722
juce::XmlElement::createNewChildElement
XmlElement * createNewChildElement(StringRef tagName)
Definition juce_XmlElement.cpp:733
juce::XmlElement::getChildElementAllSubText
String getChildElementAllSubText(StringRef childTagName, const String &defaultReturnValue) const
Definition juce_XmlElement.cpp:944
juce::XmlElement::getText
const String & getText() const noexcept
Definition juce_XmlElement.cpp:911
juce::XmlElement::deleteAllTextElements
void deleteAllTextElements() noexcept
Definition juce_XmlElement.cpp:979
juce::XmlElement::~XmlElement
~XmlElement() noexcept
Definition juce_XmlElement.cpp:161
juce::XmlElement::getAllSubText
String getAllSubText() const
Definition juce_XmlElement.cpp:928
juce::XmlElement::compareAttribute
bool compareAttribute(StringRef attributeName, StringRef stringToCompareAgainst, bool ignoreCase=false) const noexcept
Definition juce_XmlElement.cpp:603
juce::XmlElement::setText
void setText(const String &newText)
Definition juce_XmlElement.cpp:920
juce::XmlElement::findParentElementOf
XmlElement * findParentElementOf(const XmlElement *childToSearchFor) noexcept
Definition juce_XmlElement.cpp:867
juce::XmlElement::isEquivalentTo
bool isEquivalentTo(const XmlElement *other, bool ignoreOrderOfAttributes) const noexcept
Definition juce_XmlElement.cpp:771
juce::XmlElement::getBoolAttribute
bool getBoolAttribute(StringRef attributeName, bool defaultReturnValue=false) const
Definition juce_XmlElement.cpp:587
juce::XmlElement::removeAllAttributes
void removeAllAttributes() noexcept
Definition juce_XmlElement.cpp:662
juce::XmlElement::createTextElement
static XmlElement * createTextElement(const String &text)
Definition juce_XmlElement.cpp:952
juce::XmlElement::getAttributeName
const String & getAttributeName(int attributeIndex) const noexcept
Definition juce_XmlElement.cpp:524
juce::XmlElement::hasAttribute
bool hasAttribute(StringRef attributeName) const noexcept
Definition juce_XmlElement.cpp:549
juce::XmlElement::containsChildElement
bool containsChildElement(const XmlElement *possibleChild) const noexcept
Definition juce_XmlElement.cpp:862
juce::XmlElement::replaceChildElement
bool replaceChildElement(XmlElement *currentChildElement, XmlElement *newChildNode) noexcept
Definition juce_XmlElement.cpp:740
juce::XmlElement::writeTo
void writeTo(OutputStream &output, const TextFormat &format={}) const
Definition juce_XmlElement.cpp:359
juce::XmlElement::getChildElement
XmlElement * getChildElement(int index) const noexcept
Definition juce_XmlElement.cpp:673
juce::XmlElement::operator=
XmlElement & operator=(const XmlElement &)
Definition juce_XmlElement.cpp:116
juce::XmlElement::deleteAllChildElements
void deleteAllChildElements() noexcept
Definition juce_XmlElement.cpp:844
juce::XmlElement::hasTagName
bool hasTagName(StringRef possibleTagName) const noexcept
Definition juce_XmlElement.cpp:470
juce::XmlElement::removeAttribute
void removeAttribute(const Identifier &attributeName) noexcept
Definition juce_XmlElement.cpp:650
juce::XmlElement::getChildByAttribute
XmlElement * getChildByAttribute(StringRef attributeName, StringRef attributeValue) const noexcept
Definition juce_XmlElement.cpp:689
juce::XmlElement::getNumAttributes
int getNumAttributes() const noexcept
Definition juce_XmlElement.cpp:513
juce::XmlElement::removeChildElement
void removeChildElement(XmlElement *childToRemove, bool shouldDeleteTheChild) noexcept
Definition juce_XmlElement.cpp:757
juce::XmlElement::isValidXmlName
static bool isValidXmlName(StringRef possibleName) noexcept
Definition juce_XmlElement.cpp:959
juce::XmlElement::getIntAttribute
int getIntAttribute(StringRef attributeName, int defaultReturnValue=0) const
Definition juce_XmlElement.cpp:571
juce::XmlElement::getStringAttribute
const String & getStringAttribute(StringRef attributeName) const noexcept
Definition juce_XmlElement.cpp:555
juce::XmlElement::hasTagNameIgnoringNamespace
bool hasTagNameIgnoringNamespace(StringRef possibleTagName) const
Definition juce_XmlElement.cpp:491
juce::XmlElement::getNamespace
String getNamespace() const
Definition juce_XmlElement.cpp:481
juce::XmlElement::setAttribute
void setAttribute(const Identifier &attributeName, const String &newValue)
Definition juce_XmlElement.cpp:615
juce::XmlElement::TextFormat
Definition juce_XmlElement.h:136
juce::XmlElement::TextFormat::customEncoding
String customEncoding
Definition juce_XmlElement.h:142
juce::XmlElement::TextFormat::withoutHeader
TextFormat withoutHeader() const
Definition juce_XmlElement.cpp:345
juce::XmlElement::TextFormat::addDefaultHeader
bool addDefaultHeader
Definition juce_XmlElement.h:143
juce::XmlElement::TextFormat::dtd
String dtd
Definition juce_XmlElement.h:140
juce::XmlElement::TextFormat::newLineChars
const char * newLineChars
Definition juce_XmlElement.h:145
juce::XmlElement::TextFormat::customHeader
String customHeader
Definition juce_XmlElement.h:141
juce::XmlElement::TextFormat::singleLine
TextFormat singleLine() const
Definition juce_XmlElement.cpp:338
juce::XmlElement::TextFormat::lineWrapLength
int lineWrapLength
Definition juce_XmlElement.h:144
juce::XmlElement::TextFormat::TextFormat
TextFormat()
Definition juce_XmlElement.cpp:336