OpenShot Audio Library | OpenShotAudio 0.3.2
Loading...
Searching...
No Matches
juce_XmlElement.cpp
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 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
23namespace juce
24{
25
26inline 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
46inline 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
83 : tagName (StringPool::getGlobalPool().getPooledString (tag))
84{
85 jassert (isValidXmlName (tagName));
86}
87
89 : tagName (StringPool::getGlobalPool().getPooledString (tag))
90{
91 jassert (isValidXmlName (tagName));
92}
93
95 : tagName (tag.toString())
96{
97 jassert (isValidXmlName (tagName));
98}
99
100XmlElement::XmlElement (String::CharPointerType tagNameStart, String::CharPointerType tagNameEnd)
101 : tagName (StringPool::getGlobalPool().getPooledString (tagNameStart, tagNameEnd))
102{
103 jassert (isValidXmlName (tagName));
104}
105
106XmlElement::XmlElement (int /*dummy*/) noexcept
107{
108}
109
111 : tagName (other.tagName)
112{
113 copyChildrenAndAttributesFrom (other);
114}
115
117{
118 if (this != &other)
119 {
122 tagName = other.tagName;
123 copyChildrenAndAttributesFrom (other);
124 }
125
126 return *this;
127}
128
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
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
152void 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
162{
163 firstChildElement.deleteAll();
164 attributes.deleteAll();
165}
166
167//==============================================================================
168namespace 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 // Note: Deliberate fall-through here!
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
255void 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
337
339{
340 auto f = *this;
341 f.newLineChars = nullptr;
342 return f;
343}
344
346{
347 auto f = *this;
348 f.addDefaultHeader = false;
349 return f;
350}
351
353{
354 MemoryOutputStream mem (2048);
355 writeTo (mem, options);
356 return mem.toUTF8();
357}
358
359void 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
407bool XmlElement::writeTo (const File& destinationFile, const TextFormat& options) const
408{
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
427String 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
442void 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
458bool 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//==============================================================================
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
482{
483 return tagName.upToFirstOccurrenceOf (":", false, false);
484}
485
487{
488 return tagName.fromLastOccurrenceOf (":", false, false);
489}
490
495
497{
498 auto* e = nextListItem.get();
499
500 while (e != nullptr && ! e->hasTagName (requiredTagName))
501 e = e->nextListItem;
502
503 return e;
504}
505
507{
508 jassert (isValidXmlName (newTagName));
509 tagName = StringPool::getGlobalPool().getPooledString (newTagName);
510}
511
512//==============================================================================
514{
515 return attributes.size();
516}
517
518static const String& getEmptyStringRef() noexcept
519{
520 static String empty;
521 return empty;
522}
523
524const 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
532const 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
550{
551 return getAttribute (attributeName) != nullptr;
552}
553
554//==============================================================================
556{
557 if (auto* att = getAttribute (attributeName))
558 return att->value;
559
560 return getEmptyStringRef();
561}
562
564{
565 if (auto* att = getAttribute (attributeName))
566 return att->value;
567
568 return defaultReturnValue;
569}
570
572{
573 if (auto* att = getAttribute (attributeName))
574 return att->value.getIntValue();
575
576 return defaultReturnValue;
577}
578
580{
581 if (auto* att = getAttribute (attributeName))
582 return att->value.getDoubleValue();
583
584 return defaultReturnValue;
585}
586
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
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//==============================================================================
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
644
646{
647 setAttribute (attributeName, serialiseDouble (number));
648}
649
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
663{
664 attributes.deleteAll();
665}
666
667//==============================================================================
669{
670 return firstChildElement.size();
671}
672
673XmlElement* XmlElement::getChildElement (const int index) const noexcept
674{
675 return firstChildElement[index].get();
676}
677
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
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
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
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
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
739
741 XmlElement* const newNode) noexcept
742{
743 if (newNode != nullptr)
744 {
745 if (auto* p = firstChildElement.findPointerTo (currentChildElement))
746 {
748 delete p->replaceNext (newNode);
749
750 return true;
751 }
752 }
753
754 return false;
755}
756
758 const bool shouldDeleteTheChild) noexcept
759{
760 if (childToRemove != nullptr)
761 {
763
764 firstChildElement.remove (childToRemove);
765
767 delete childToRemove;
768 }
769}
770
772 const bool ignoreOrderOfAttributes) const noexcept
773{
774 if (this != other)
775 {
776 if (other == nullptr || tagName != other->tagName)
777 return false;
778
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
845{
846 firstChildElement.deleteAll();
847}
848
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
863{
864 return firstChildElement.contains (possibleChild);
865}
866
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
884void XmlElement::getChildElementsAsArray (XmlElement** elems) const noexcept
885{
886 firstChildElement.copyToArray (elems);
887}
888
889void 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//==============================================================================
905{
906 return tagName.isEmpty();
907}
908
909static const String juce_xmltextContentAttributeName ("text");
910
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
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
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
945{
946 if (auto* child = getChildByName (childTagName))
947 return child->getAllSubText();
948
949 return defaultReturnValue;
950}
951
953{
954 auto e = new XmlElement ((int) 0);
955 e->setAttribute (juce_xmltextContentAttributeName, text);
956 return e;
957}
958
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
975{
977}
978
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
996class XmlElementTests : public UnitTest
997{
998public:
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
1036static XmlElementTests xmlElementTests;
1037
1038#endif
1039
1040} // namespace juce
bool isEmpty() const noexcept
Definition juce_Array.h:222
ObjectType * get() const noexcept
void addCopyOfList(const LinkedListPointer &other)
virtual bool writeByte(char byte)
static StringPool & getGlobalPool() noexcept
String upToFirstOccurrenceOf(StringRef substringToEndWith, bool includeSubStringInResult, bool ignoreCase) const
String fromLastOccurrenceOf(StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const
bool isNotEmpty() const noexcept
XmlElement * getNextElementWithTagName(StringRef requiredTagName) const
void setTagName(StringRef newTagName)
void addTextElement(const String &text)
int getNumChildElements() const noexcept
XmlElement(const String &tagName)
void insertChildElement(XmlElement *newChildElement, int indexToInsertAt) noexcept
XmlElement * getChildByName(StringRef tagNameToLookFor) const noexcept
bool isTextElement() const noexcept
void deleteAllChildElementsWithTagName(StringRef tagName) noexcept
String toString(const TextFormat &format={}) const
void addChildElement(XmlElement *newChildElement) noexcept
String getTagNameWithoutNamespace() const
double getDoubleAttribute(StringRef attributeName, double defaultReturnValue=0.0) const
const String & getAttributeValue(int attributeIndex) const noexcept
void prependChildElement(XmlElement *newChildElement) noexcept
XmlElement * createNewChildElement(StringRef tagName)
String getChildElementAllSubText(StringRef childTagName, const String &defaultReturnValue) const
const String & getText() const noexcept
void deleteAllTextElements() noexcept
String getAllSubText() const
bool compareAttribute(StringRef attributeName, StringRef stringToCompareAgainst, bool ignoreCase=false) const noexcept
void setText(const String &newText)
XmlElement * findParentElementOf(const XmlElement *childToSearchFor) noexcept
bool isEquivalentTo(const XmlElement *other, bool ignoreOrderOfAttributes) const noexcept
bool getBoolAttribute(StringRef attributeName, bool defaultReturnValue=false) const
void removeAllAttributes() noexcept
static XmlElement * createTextElement(const String &text)
const String & getAttributeName(int attributeIndex) const noexcept
bool hasAttribute(StringRef attributeName) const noexcept
bool containsChildElement(const XmlElement *possibleChild) const noexcept
bool replaceChildElement(XmlElement *currentChildElement, XmlElement *newChildNode) noexcept
void writeTo(OutputStream &output, const TextFormat &format={}) const
XmlElement * getChildElement(int index) const noexcept
XmlElement & operator=(const XmlElement &)
void deleteAllChildElements() noexcept
bool hasTagName(StringRef possibleTagName) const noexcept
void removeAttribute(const Identifier &attributeName) noexcept
XmlElement * getChildByAttribute(StringRef attributeName, StringRef attributeValue) const noexcept
int getNumAttributes() const noexcept
void removeChildElement(XmlElement *childToRemove, bool shouldDeleteTheChild) noexcept
static bool isValidXmlName(StringRef possibleName) noexcept
int getIntAttribute(StringRef attributeName, int defaultReturnValue=0) const
const String & getStringAttribute(StringRef attributeName) const noexcept
bool hasTagNameIgnoringNamespace(StringRef possibleTagName) const
String getNamespace() const
void setAttribute(const Identifier &attributeName, const String &newValue)