OpenShot Library | libopenshot-audio  0.1.9
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 
23 namespace juce
24 {
25 
26 inline 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 inline 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 
57 XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) noexcept
58  : name (other.name),
59  value (other.value)
60 {
61 }
62 
63 XmlElement::XmlAttributeNode::XmlAttributeNode (const Identifier& n, const String& v) noexcept
64  : name (n), value (v)
65 {
66  jassert (isValidXmlName (name));
67 }
68 
69 XmlElement::XmlAttributeNode::XmlAttributeNode (String::CharPointerType nameStart, String::CharPointerType nameEnd)
70  : name (nameStart, nameEnd)
71 {
72  jassert (isValidXmlName (name));
73 }
74 
75 //==============================================================================
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 
89  : tagName (StringPool::getGlobalPool().getPooledString (tag))
90 {
91  jassert (isValidXmlName (tagName));
92 }
93 
95  : tagName (tag.toString())
96 {
97  jassert (isValidXmlName (tagName));
98 }
99 
101  : tagName (StringPool::getGlobalPool().getPooledString (tagNameStart, tagNameEnd))
102 {
103  jassert (isValidXmlName (tagName));
104 }
105 
106 XmlElement::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 
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 
162 {
163  firstChildElement.deleteAll();
164  attributes.deleteAll();
165 }
166 
167 //==============================================================================
168 namespace XmlOutputFunctions
169 {
170  #if 0 // (These functions are just used to generate the lookup table used below)
171  bool isLegalXmlCharSlow (const juce_wchar character) noexcept
172  {
173  if ((character >= 'a' && character <= 'z')
174  || (character >= 'A' && character <= 'Z')
175  || (character >= '0' && character <= '9'))
176  return true;
177 
178  const char* t = " .,;:-()_+=?!'#@[]/\\*%~{}$|";
179 
180  do
181  {
182  if (((juce_wchar) (uint8) *t) == character)
183  return true;
184  }
185  while (*++t != 0);
186 
187  return false;
188  }
189 
190  void generateLegalCharLookupTable()
191  {
192  uint8 n[32] = { 0 };
193  for (int i = 0; i < 256; ++i)
194  if (isLegalXmlCharSlow (i))
195  n[i >> 3] |= (1 << (i & 7));
196 
197  String s;
198  for (int i = 0; i < 32; ++i)
199  s << (int) n[i] << ", ";
200 
201  DBG (s);
202  }
203  #endif
204 
205  static bool isLegalXmlChar (const uint32 c) noexcept
206  {
207  static const unsigned char legalChars[] = { 0, 0, 0, 0, 187, 255, 255, 175, 255,
208  255, 255, 191, 254, 255, 255, 127 };
209  return c < sizeof (legalChars) * 8
210  && (legalChars [c >> 3] & (1 << (c & 7))) != 0;
211  }
212 
213  static void escapeIllegalXmlChars (OutputStream& outputStream, const String& text, const bool changeNewLines)
214  {
215  auto t = text.getCharPointer();
216 
217  for (;;)
218  {
219  auto character = (uint32) t.getAndAdvance();
220 
221  if (character == 0)
222  break;
223 
224  if (isLegalXmlChar (character))
225  {
226  outputStream << (char) character;
227  }
228  else
229  {
230  switch (character)
231  {
232  case '&': outputStream << "&amp;"; break;
233  case '"': outputStream << "&quot;"; break;
234  case '>': outputStream << "&gt;"; break;
235  case '<': outputStream << "&lt;"; break;
236 
237  case '\n':
238  case '\r':
239  if (! changeNewLines)
240  {
241  outputStream << (char) character;
242  break;
243  }
244  // Note: Deliberate fall-through here!
245  default:
246  outputStream << "&#" << ((int) character) << ';';
247  break;
248  }
249  }
250  }
251  }
252 
253  static void writeSpaces (OutputStream& out, const size_t numSpaces)
254  {
255  out.writeRepeatedByte (' ', numSpaces);
256  }
257 }
258 
259 void XmlElement::writeElementAsText (OutputStream& outputStream,
260  const int indentationLevel,
261  const int lineWrapLength) const
262 {
263  using namespace XmlOutputFunctions;
264 
265  if (indentationLevel >= 0)
266  writeSpaces (outputStream, (size_t) indentationLevel);
267 
268  if (! isTextElement())
269  {
270  outputStream.writeByte ('<');
271  outputStream << tagName;
272 
273  {
274  auto attIndent = (size_t) (indentationLevel + tagName.length() + 1);
275  int lineLen = 0;
276 
277  for (auto* att = attributes.get(); att != nullptr; att = att->nextListItem)
278  {
279  if (lineLen > lineWrapLength && indentationLevel >= 0)
280  {
281  outputStream << newLine;
282  writeSpaces (outputStream, attIndent);
283  lineLen = 0;
284  }
285 
286  auto startPos = outputStream.getPosition();
287  outputStream.writeByte (' ');
288  outputStream << att->name;
289  outputStream.write ("=\"", 2);
290  escapeIllegalXmlChars (outputStream, att->value, true);
291  outputStream.writeByte ('"');
292  lineLen += (int) (outputStream.getPosition() - startPos);
293  }
294  }
295 
296  if (auto* child = firstChildElement.get())
297  {
298  outputStream.writeByte ('>');
299  bool lastWasTextNode = false;
300 
301  for (; child != nullptr; child = child->nextListItem)
302  {
303  if (child->isTextElement())
304  {
305  escapeIllegalXmlChars (outputStream, child->getText(), false);
306  lastWasTextNode = true;
307  }
308  else
309  {
310  if (indentationLevel >= 0 && ! lastWasTextNode)
311  outputStream << newLine;
312 
313  child->writeElementAsText (outputStream,
314  lastWasTextNode ? 0 : (indentationLevel + (indentationLevel >= 0 ? 2 : 0)), lineWrapLength);
315  lastWasTextNode = false;
316  }
317  }
318 
319  if (indentationLevel >= 0 && ! lastWasTextNode)
320  {
321  outputStream << newLine;
322  writeSpaces (outputStream, (size_t) indentationLevel);
323  }
324 
325  outputStream.write ("</", 2);
326  outputStream << tagName;
327  outputStream.writeByte ('>');
328  }
329  else
330  {
331  outputStream.write ("/>", 2);
332  }
333  }
334  else
335  {
336  escapeIllegalXmlChars (outputStream, getText(), false);
337  }
338 }
339 
341  const bool allOnOneLine,
342  const bool includeXmlHeader,
343  StringRef encodingType,
344  const int lineWrapLength) const
345 {
346  MemoryOutputStream mem (2048);
347  writeToStream (mem, dtdToUse, allOnOneLine, includeXmlHeader, encodingType, lineWrapLength);
348 
349  return mem.toUTF8();
350 }
351 
353  bool allOnOneLine, bool includeXmlHeader,
354  StringRef encodingType, int lineWrapLength) const
355 {
356  using namespace XmlOutputFunctions;
357 
358  if (includeXmlHeader)
359  {
360  output << "<?xml version=\"1.0\" encoding=\"" << encodingType << "\"?>";
361 
362  if (allOnOneLine)
363  output.writeByte (' ');
364  else
365  output << newLine << newLine;
366  }
367 
368  if (dtdToUse.isNotEmpty())
369  {
370  output << dtdToUse;
371 
372  if (allOnOneLine)
373  output.writeByte (' ');
374  else
375  output << newLine;
376  }
377 
378  writeElementAsText (output, allOnOneLine ? -1 : 0, lineWrapLength);
379 
380  if (! allOnOneLine)
381  output << newLine;
382 }
383 
384 bool XmlElement::writeToFile (const File& file, StringRef dtdToUse,
385  StringRef encodingType, int lineWrapLength) const
386 {
387  TemporaryFile tempFile (file);
388 
389  {
390  FileOutputStream out (tempFile.getFile());
391 
392  if (! out.openedOk())
393  return false;
394 
395  writeToStream (out, dtdToUse, false, true, encodingType, lineWrapLength);
396  out.flush(); // (called explicitly to force an fsync on posix)
397 
398  if (out.getStatus().failed())
399  return false;
400  }
401 
402  return tempFile.overwriteTargetFileWithTemporary();
403 }
404 
405 //==============================================================================
406 bool XmlElement::hasTagName (StringRef possibleTagName) const noexcept
407 {
408  const bool matches = tagName.equalsIgnoreCase (possibleTagName);
409 
410  // XML tags should be case-sensitive, so although this method allows a
411  // case-insensitive match to pass, you should try to avoid this.
412  jassert ((! matches) || tagName == possibleTagName);
413 
414  return matches;
415 }
416 
418 {
419  return tagName.upToFirstOccurrenceOf (":", false, false);
420 }
421 
423 {
424  return tagName.fromLastOccurrenceOf (":", false, false);
425 }
426 
428 {
429  return hasTagName (possibleTagName) || getTagNameWithoutNamespace() == possibleTagName;
430 }
431 
433 {
434  auto* e = nextListItem.get();
435 
436  while (e != nullptr && ! e->hasTagName (requiredTagName))
437  e = e->nextListItem;
438 
439  return e;
440 }
441 
443 {
444  jassert (isValidXmlName (newTagName));
445  tagName = StringPool::getGlobalPool().getPooledString (newTagName);
446 }
447 
448 //==============================================================================
449 int XmlElement::getNumAttributes() const noexcept
450 {
451  return attributes.size();
452 }
453 
454 static const String& getEmptyStringRef() noexcept
455 {
456  static String empty;
457  return empty;
458 }
459 
460 const String& XmlElement::getAttributeName (const int index) const noexcept
461 {
462  if (auto* att = attributes[index].get())
463  return att->name.toString();
464 
465  return getEmptyStringRef();
466 }
467 
468 const String& XmlElement::getAttributeValue (const int index) const noexcept
469 {
470  if (auto* att = attributes[index].get())
471  return att->value;
472 
473  return getEmptyStringRef();
474 }
475 
476 XmlElement::XmlAttributeNode* XmlElement::getAttribute (StringRef attributeName) const noexcept
477 {
478  for (auto* att = attributes.get(); att != nullptr; att = att->nextListItem)
479  if (att->name == attributeName)
480  return att;
481 
482  return nullptr;
483 }
484 
485 bool XmlElement::hasAttribute (StringRef attributeName) const noexcept
486 {
487  return getAttribute (attributeName) != nullptr;
488 }
489 
490 //==============================================================================
491 const String& XmlElement::getStringAttribute (StringRef attributeName) const noexcept
492 {
493  if (auto* att = getAttribute (attributeName))
494  return att->value;
495 
496  return getEmptyStringRef();
497 }
498 
499 String XmlElement::getStringAttribute (StringRef attributeName, const String& defaultReturnValue) const
500 {
501  if (auto* att = getAttribute (attributeName))
502  return att->value;
503 
504  return defaultReturnValue;
505 }
506 
507 int XmlElement::getIntAttribute (StringRef attributeName, const int defaultReturnValue) const
508 {
509  if (auto* att = getAttribute (attributeName))
510  return att->value.getIntValue();
511 
512  return defaultReturnValue;
513 }
514 
515 double XmlElement::getDoubleAttribute (StringRef attributeName, const double defaultReturnValue) const
516 {
517  if (auto* att = getAttribute (attributeName))
518  return att->value.getDoubleValue();
519 
520  return defaultReturnValue;
521 }
522 
523 bool XmlElement::getBoolAttribute (StringRef attributeName, const bool defaultReturnValue) const
524 {
525  if (auto* att = getAttribute (attributeName))
526  {
527  auto firstChar = *(att->value.getCharPointer().findEndOfWhitespace());
528 
529  return firstChar == '1'
530  || firstChar == 't'
531  || firstChar == 'y'
532  || firstChar == 'T'
533  || firstChar == 'Y';
534  }
535 
536  return defaultReturnValue;
537 }
538 
540  StringRef stringToCompareAgainst,
541  const bool ignoreCase) const noexcept
542 {
543  if (auto* att = getAttribute (attributeName))
544  return ignoreCase ? att->value.equalsIgnoreCase (stringToCompareAgainst)
545  : att->value == stringToCompareAgainst;
546 
547  return false;
548 }
549 
550 //==============================================================================
551 void XmlElement::setAttribute (const Identifier& attributeName, const String& value)
552 {
553  if (attributes == nullptr)
554  {
555  attributes = new XmlAttributeNode (attributeName, value);
556  }
557  else
558  {
559  for (auto* att = attributes.get(); ; att = att->nextListItem)
560  {
561  if (att->name == attributeName)
562  {
563  att->value = value;
564  break;
565  }
566 
567  if (att->nextListItem == nullptr)
568  {
569  att->nextListItem = new XmlAttributeNode (attributeName, value);
570  break;
571  }
572  }
573  }
574 }
575 
576 void XmlElement::setAttribute (const Identifier& attributeName, const int number)
577 {
578  setAttribute (attributeName, String (number));
579 }
580 
581 void XmlElement::setAttribute (const Identifier& attributeName, const double number)
582 {
583  setAttribute (attributeName, serialiseDouble (number));
584 }
585 
586 void XmlElement::removeAttribute (const Identifier& attributeName) noexcept
587 {
588  for (auto* att = &attributes; att->get() != nullptr; att = &(att->get()->nextListItem))
589  {
590  if (att->get()->name == attributeName)
591  {
592  delete att->removeNext();
593  break;
594  }
595  }
596 }
597 
599 {
600  attributes.deleteAll();
601 }
602 
603 //==============================================================================
605 {
606  return firstChildElement.size();
607 }
608 
609 XmlElement* XmlElement::getChildElement (const int index) const noexcept
610 {
611  return firstChildElement [index].get();
612 }
613 
615 {
616  jassert (! childName.isEmpty());
617 
618  for (auto* child = firstChildElement.get(); child != nullptr; child = child->nextListItem)
619  if (child->hasTagName (childName))
620  return child;
621 
622  return nullptr;
623 }
624 
625 XmlElement* XmlElement::getChildByAttribute (StringRef attributeName, StringRef attributeValue) const noexcept
626 {
627  jassert (! attributeName.isEmpty());
628 
629  for (auto* child = firstChildElement.get(); child != nullptr; child = child->nextListItem)
630  if (child->compareAttribute (attributeName, attributeValue))
631  return child;
632 
633  return nullptr;
634 }
635 
636 void XmlElement::addChildElement (XmlElement* const newNode) noexcept
637 {
638  if (newNode != nullptr)
639  {
640  // The element being added must not be a child of another node!
641  jassert (newNode->nextListItem == nullptr);
642 
643  firstChildElement.append (newNode);
644  }
645 }
646 
647 void XmlElement::insertChildElement (XmlElement* const newNode, int indexToInsertAt) noexcept
648 {
649  if (newNode != nullptr)
650  {
651  // The element being added must not be a child of another node!
652  jassert (newNode->nextListItem == nullptr);
653 
654  firstChildElement.insertAtIndex (indexToInsertAt, newNode);
655  }
656 }
657 
659 {
660  if (newNode != nullptr)
661  {
662  // The element being added must not be a child of another node!
663  jassert (newNode->nextListItem == nullptr);
664 
665  firstChildElement.insertNext (newNode);
666  }
667 }
668 
670 {
671  auto newElement = new XmlElement (childTagName);
672  addChildElement (newElement);
673  return newElement;
674 }
675 
676 bool XmlElement::replaceChildElement (XmlElement* const currentChildElement,
677  XmlElement* const newNode) noexcept
678 {
679  if (newNode != nullptr)
680  {
681  if (auto* p = firstChildElement.findPointerTo (currentChildElement))
682  {
683  if (currentChildElement != newNode)
684  delete p->replaceNext (newNode);
685 
686  return true;
687  }
688  }
689 
690  return false;
691 }
692 
693 void XmlElement::removeChildElement (XmlElement* const childToRemove,
694  const bool shouldDeleteTheChild) noexcept
695 {
696  if (childToRemove != nullptr)
697  {
698  jassert (containsChildElement (childToRemove));
699 
700  firstChildElement.remove (childToRemove);
701 
702  if (shouldDeleteTheChild)
703  delete childToRemove;
704  }
705 }
706 
707 bool XmlElement::isEquivalentTo (const XmlElement* const other,
708  const bool ignoreOrderOfAttributes) const noexcept
709 {
710  if (this != other)
711  {
712  if (other == nullptr || tagName != other->tagName)
713  return false;
714 
715  if (ignoreOrderOfAttributes)
716  {
717  int totalAtts = 0;
718 
719  for (auto* att = attributes.get(); att != nullptr; att = att->nextListItem)
720  {
721  if (! other->compareAttribute (att->name, att->value))
722  return false;
723 
724  ++totalAtts;
725  }
726 
727  if (totalAtts != other->getNumAttributes())
728  return false;
729  }
730  else
731  {
732  auto* thisAtt = attributes.get();
733  auto* otherAtt = other->attributes.get();
734 
735  for (;;)
736  {
737  if (thisAtt == nullptr || otherAtt == nullptr)
738  {
739  if (thisAtt == otherAtt) // both nullptr, so it's a match
740  break;
741 
742  return false;
743  }
744 
745  if (thisAtt->name != otherAtt->name
746  || thisAtt->value != otherAtt->value)
747  {
748  return false;
749  }
750 
751  thisAtt = thisAtt->nextListItem;
752  otherAtt = otherAtt->nextListItem;
753  }
754  }
755 
756  auto* thisChild = firstChildElement.get();
757  auto* otherChild = other->firstChildElement.get();
758 
759  for (;;)
760  {
761  if (thisChild == nullptr || otherChild == nullptr)
762  {
763  if (thisChild == otherChild) // both 0, so it's a match
764  break;
765 
766  return false;
767  }
768 
769  if (! thisChild->isEquivalentTo (otherChild, ignoreOrderOfAttributes))
770  return false;
771 
772  thisChild = thisChild->nextListItem;
773  otherChild = otherChild->nextListItem;
774  }
775  }
776 
777  return true;
778 }
779 
781 {
782  firstChildElement.deleteAll();
783 }
784 
786 {
787  for (auto* child = firstChildElement.get(); child != nullptr;)
788  {
789  auto* nextChild = child->nextListItem.get();
790 
791  if (child->hasTagName (name))
792  removeChildElement (child, true);
793 
794  child = nextChild;
795  }
796 }
797 
798 bool XmlElement::containsChildElement (const XmlElement* const possibleChild) const noexcept
799 {
800  return firstChildElement.contains (possibleChild);
801 }
802 
803 XmlElement* XmlElement::findParentElementOf (const XmlElement* const elementToLookFor) noexcept
804 {
805  if (this == elementToLookFor || elementToLookFor == nullptr)
806  return nullptr;
807 
808  for (auto* child = firstChildElement.get(); child != nullptr; child = child->nextListItem)
809  {
810  if (elementToLookFor == child)
811  return this;
812 
813  if (auto* found = child->findParentElementOf (elementToLookFor))
814  return found;
815  }
816 
817  return nullptr;
818 }
819 
820 void XmlElement::getChildElementsAsArray (XmlElement** elems) const noexcept
821 {
822  firstChildElement.copyToArray (elems);
823 }
824 
825 void XmlElement::reorderChildElements (XmlElement** elems, int num) noexcept
826 {
827  auto* e = elems[0];
828  firstChildElement = e;
829 
830  for (int i = 1; i < num; ++i)
831  {
832  e->nextListItem = elems[i];
833  e = e->nextListItem;
834  }
835 
836  e->nextListItem = nullptr;
837 }
838 
839 //==============================================================================
840 bool XmlElement::isTextElement() const noexcept
841 {
842  return tagName.isEmpty();
843 }
844 
845 static const String juce_xmltextContentAttributeName ("text");
846 
847 const String& XmlElement::getText() const noexcept
848 {
849  jassert (isTextElement()); // you're trying to get the text from an element that
850  // isn't actually a text element.. If this contains text sub-nodes, you
851  // probably want to use getAllSubText instead.
852 
853  return getStringAttribute (juce_xmltextContentAttributeName);
854 }
855 
856 void XmlElement::setText (const String& newText)
857 {
858  if (isTextElement())
859  setAttribute (juce_xmltextContentAttributeName, newText);
860  else
861  jassertfalse; // you can only change the text in a text element, not a normal one.
862 }
863 
865 {
866  if (isTextElement())
867  return getText();
868 
869  if (getNumChildElements() == 1)
870  return firstChildElement.get()->getAllSubText();
871 
872  MemoryOutputStream mem (1024);
873 
874  for (auto* child = firstChildElement.get(); child != nullptr; child = child->nextListItem)
875  mem << child->getAllSubText();
876 
877  return mem.toUTF8();
878 }
879 
880 String XmlElement::getChildElementAllSubText (StringRef childTagName, const String& defaultReturnValue) const
881 {
882  if (auto* child = getChildByName (childTagName))
883  return child->getAllSubText();
884 
885  return defaultReturnValue;
886 }
887 
889 {
890  auto e = new XmlElement ((int) 0);
891  e->setAttribute (juce_xmltextContentAttributeName, text);
892  return e;
893 }
894 
896 {
897  if (text.isEmpty() || ! isValidXmlNameStartCharacter (text.text.getAndAdvance()))
898  return false;
899 
900  for (;;)
901  {
902  if (text.isEmpty())
903  return true;
904 
905  if (! isValidXmlNameBodyCharacter (text.text.getAndAdvance()))
906  return false;
907  }
908 }
909 
911 {
913 }
914 
916 {
917  for (auto* child = firstChildElement.get(); child != nullptr;)
918  {
919  auto* next = child->nextListItem.get();
920 
921  if (child->isTextElement())
922  removeChildElement (child, true);
923 
924  child = next;
925  }
926 }
927 
928 //==============================================================================
929 #if JUCE_UNIT_TESTS
930 
931 class XmlElementTests : public UnitTest
932 {
933 public:
934  XmlElementTests() : UnitTest ("XmlElement", "XML") {}
935 
936  void runTest() override
937  {
938  {
939  beginTest ("Float formatting");
940 
941  auto element = std::make_unique<XmlElement> ("test");
942  Identifier number ("number");
943 
944  std::map<double, String> tests;
945  tests[1] = "1.0";
946  tests[1.1] = "1.1";
947  tests[1.01] = "1.01";
948  tests[0.76378] = "0.76378";
949  tests[-10] = "-10.0";
950  tests[10.01] = "10.01";
951  tests[0.0123] = "0.0123";
952  tests[-3.7e-27] = "-3.7e-27";
953  tests[1e+40] = "1.0e40";
954  tests[-12345678901234567.0] = "-1.234567890123457e16";
955  tests[192000] = "192000.0";
956  tests[1234567] = "1.234567e6";
957  tests[0.00006] = "0.00006";
958  tests[0.000006] = "6.0e-6";
959 
960  for (auto& test : tests)
961  {
962  element->setAttribute (number, test.first);
963  expectEquals (element->getStringAttribute (number), test.second);
964  }
965  }
966  }
967 };
968 
969 static XmlElementTests xmlElementTests;
970 
971 #endif
972 
973 } // namespace juce
const String & getText() const noexcept
Returns the text for a text element.
Manages a temporary file, which will be deleted when this object is deleted.
bool hasTagName(StringRef possibleTagName) const noexcept
Tests whether this element has a particular tag name.
void deleteAllChildElementsWithTagName(StringRef tagName) noexcept
Deletes all the child elements with a given tag name.
virtual bool write(const void *dataToWrite, size_t numberOfBytes)=0
Writes a block of data to the stream.
void deleteAll()
Iterates the list, calling the delete operator on all of its elements and leaving this pointer empty...
bool overwriteTargetFileWithTemporary() const
Tries to move the temporary file to overwrite the target file that was specified in the constructor...
void removeAttribute(const Identifier &attributeName) noexcept
Removes a named attribute from the element.
const File & getFile() const noexcept
Returns the temporary file.
virtual bool writeByte(char byte)
Writes a single byte to the stream.
XmlElement & operator=(const XmlElement &)
Creates a (deep) copy of another element.
~XmlElement() noexcept
Deleting an XmlElement will also delete all of its child elements.
Represents a string identifier, designed for accessing properties by name.
XmlElement * getChildElement(int index) const noexcept
Returns the sub-element at a certain index.
A simple class for holding temporary references to a string literal or String.
static XmlElement * createTextElement(const String &text)
Creates a text element that can be added to a parent element.
bool hasTagNameIgnoringNamespace(StringRef possibleTagName) const
Tests whether this element has a particular tag name, ignoring any XML namespace prefix.
static StringPool & getGlobalPool() noexcept
Returns a shared global pool which is used for things like Identifiers, XML parsing.
const String & getAttributeValue(int attributeIndex) const noexcept
Returns the value of one of the elements attributes.
XmlElement * getChildByName(StringRef tagNameToLookFor) const noexcept
Returns the first sub-element with a given tag-name.
bool isNotEmpty() const noexcept
Returns true if the string is not empty.
Used to build a tree of elements representing an XML document.
CharPointerType getCharPointer() const noexcept
Returns the character pointer currently being used to store this string.
Definition: juce_String.h:1200
void addChildElement(XmlElement *newChildElement) noexcept
Appends an element to this element&#39;s list of children.
The JUCE String class!
Definition: juce_String.h:42
int getIntAttribute(StringRef attributeName, int defaultReturnValue=0) const
Returns the value of a named attribute as an integer.
const String & getAttributeName(int attributeIndex) const noexcept
Returns the name of one of the elements attributes.
bool hasAttribute(StringRef attributeName) const noexcept
Checks whether the element contains an attribute with a certain name.
int getNumChildElements() const noexcept
Returns the number of sub-elements in this element.
This is a base class for classes that perform a unit test.
Definition: juce_UnitTest.h:73
XmlElement * createNewChildElement(StringRef tagName)
Creates a new element with the given name and returns it, after adding it as a child element...
String fromLastOccurrenceOf(StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const
Returns a section of the string starting from the last occurrence of a given substring.
XmlElement(const String &tagName)
Creates an XmlElement with this tag name.
String getAllSubText() const
Returns all the text from this element&#39;s child nodes.
ObjectType * get() const noexcept
Returns the item which this pointer points to.
bool isEmpty() const noexcept
Returns true if the string is empty.
void setAttribute(const Identifier &attributeName, const String &newValue)
Adds a named attribute to the element.
A StringPool holds a set of shared strings, which reduces storage overheads and improves comparison s...
void setTagName(StringRef newTagName)
Changes this elements tag name.
juce_wchar getAndAdvance() noexcept
Returns the character that this pointer is currently pointing to, and then advances the pointer to po...
double getDoubleAttribute(StringRef attributeName, double defaultReturnValue=0.0) const
Returns the value of a named attribute as floating-point.
void addCopyOfList(const LinkedListPointer &other)
Creates copies of all the items in another list and adds them to this one.
int getNumAttributes() const noexcept
Returns the number of XML attributes this element contains.
void addTextElement(const String &text)
Appends a section of text to this element.
bool writeToFile(const File &destinationFile, StringRef dtdToUse, StringRef encodingType="UTF-8", int lineWrapLength=60) const
Writes the element to a file as an XML document.
void removeAllAttributes() noexcept
Removes all attributes from this element.
bool compareAttribute(StringRef attributeName, StringRef stringToCompareAgainst, bool ignoreCase=false) const noexcept
Compares the value of a named attribute with a value passed-in.
Represents a local file or directory.
Definition: juce_File.h:44
The base class for streams that write data to some kind of destination.
CharPointer_UTF8 CharPointerType
This is the character encoding type used internally to store the string.
Definition: juce_String.h:164
static bool isValidXmlName(StringRef possibleName) noexcept
Checks if a given string is a valid XML name.
String getTagNameWithoutNamespace() const
Returns the part of the tag-name that follows any namespace declaration.
void deleteAllChildElements() noexcept
Deletes all the child elements in the element.
String createDocument(StringRef dtdToUse, bool allOnOneLine=false, bool includeXmlHeader=true, StringRef encodingType="UTF-8", int lineWrapLength=60) const
Returns an XML text document that represents this element.
String upToFirstOccurrenceOf(StringRef substringToEndWith, bool includeSubStringInResult, bool ignoreCase) const
Returns the start of this string, up to the first occurrence of a substring.
void writeToStream(OutputStream &output, StringRef dtdToUse, bool allOnOneLine=false, bool includeXmlHeader=true, StringRef encodingType="UTF-8", int lineWrapLength=60) const
Writes the document to a stream as UTF-8.
XmlElement * getNextElementWithTagName(StringRef requiredTagName) const
Returns the next of this element&#39;s siblings which has the specified tag name.
bool containsChildElement(const XmlElement *possibleChild) const noexcept
Returns true if the given element is a child of this one.
XmlElement * getChildByAttribute(StringRef attributeName, StringRef attributeValue) const noexcept
Returns the first sub-element which has an attribute that matches the given value.
An output stream that writes into a local file.
const String & getStringAttribute(StringRef attributeName) const noexcept
Returns the value of a named attribute.
virtual bool writeRepeatedByte(uint8 byte, size_t numTimesToRepeat)
Writes a byte to the output stream a given number of times.
XmlElement * findParentElementOf(const XmlElement *childToSearchFor) noexcept
Recursively searches all sub-elements of this one, looking for an element which is the direct parent ...
void deleteAllTextElements() noexcept
Removes all the text elements from this element.
void setText(const String &newText)
Sets the text in a text element.
String getPooledString(const String &original)
Returns a pointer to a shared copy of the string that is passed in.
bool isEquivalentTo(const XmlElement *other, bool ignoreOrderOfAttributes) const noexcept
Compares two XmlElements to see if they contain the same text and attributes.
String toUTF8() const
Returns a String created from the (UTF8) data that has been written to the stream.
bool isTextElement() const noexcept
Returns true if this element is a section of text.
Writes data to an internal memory buffer, which grows as required.
void removeChildElement(XmlElement *childToRemove, bool shouldDeleteTheChild) noexcept
Removes a child element.
virtual int64 getPosition()=0
Returns the stream&#39;s current position.
bool getBoolAttribute(StringRef attributeName, bool defaultReturnValue=false) const
Returns the value of a named attribute as a boolean.
String getNamespace() const
Returns the namespace portion of the tag-name, or an empty string if none is specified.
void insertChildElement(XmlElement *newChildElement, int indexToInsertAt) noexcept
Inserts an element into this element&#39;s list of children.
bool replaceChildElement(XmlElement *currentChildElement, XmlElement *newChildNode) noexcept
Replaces one of this element&#39;s children with another node.
String getChildElementAllSubText(StringRef childTagName, const String &defaultReturnValue) const
Returns all the sub-text of a named child element.
void prependChildElement(XmlElement *newChildElement) noexcept
Inserts an element at the beginning of this element&#39;s list of children.
Wraps a pointer to a null-terminated UTF-8 character string, and provides various methods to operate ...