The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

841 lines
25KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) noexcept
  19. : name (other.name),
  20. value (other.value)
  21. {
  22. }
  23. XmlElement::XmlAttributeNode::XmlAttributeNode (const String& name_, const String& value_) noexcept
  24. : name (name_),
  25. value (value_)
  26. {
  27. #if JUCE_DEBUG
  28. // this checks whether the attribute name string contains any illegal characters..
  29. for (String::CharPointerType t (name.getCharPointer()); ! t.isEmpty(); ++t)
  30. jassert (t.isLetterOrDigit() || *t == '_' || *t == '-' || *t == ':');
  31. #endif
  32. }
  33. inline bool XmlElement::XmlAttributeNode::hasName (const String& nameToMatch) const noexcept
  34. {
  35. return name.equalsIgnoreCase (nameToMatch);
  36. }
  37. //==============================================================================
  38. XmlElement::XmlElement (const String& tagName_) noexcept
  39. : tagName (tagName_)
  40. {
  41. // the tag name mustn't be empty, or it'll look like a text element!
  42. jassert (tagName_.containsNonWhitespaceChars())
  43. // The tag can't contain spaces or other characters that would create invalid XML!
  44. jassert (! tagName_.containsAnyOf (" <>/&"));
  45. }
  46. XmlElement::XmlElement (int /*dummy*/) noexcept
  47. {
  48. }
  49. XmlElement::XmlElement (const XmlElement& other)
  50. : tagName (other.tagName)
  51. {
  52. copyChildrenAndAttributesFrom (other);
  53. }
  54. XmlElement& XmlElement::operator= (const XmlElement& other)
  55. {
  56. if (this != &other)
  57. {
  58. removeAllAttributes();
  59. deleteAllChildElements();
  60. tagName = other.tagName;
  61. copyChildrenAndAttributesFrom (other);
  62. }
  63. return *this;
  64. }
  65. #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
  66. XmlElement::XmlElement (XmlElement&& other) noexcept
  67. : nextListItem (static_cast <LinkedListPointer <XmlElement>&&> (other.nextListItem)),
  68. firstChildElement (static_cast <LinkedListPointer <XmlElement>&&> (other.firstChildElement)),
  69. attributes (static_cast <LinkedListPointer <XmlAttributeNode>&&> (other.attributes)),
  70. tagName (static_cast <String&&> (other.tagName))
  71. {
  72. }
  73. XmlElement& XmlElement::operator= (XmlElement&& other) noexcept
  74. {
  75. jassert (this != &other); // hopefully the compiler should make this situation impossible!
  76. removeAllAttributes();
  77. deleteAllChildElements();
  78. nextListItem = static_cast <LinkedListPointer <XmlElement>&&> (other.nextListItem);
  79. firstChildElement = static_cast <LinkedListPointer <XmlElement>&&> (other.firstChildElement);
  80. attributes = static_cast <LinkedListPointer <XmlAttributeNode>&&> (other.attributes);
  81. tagName = static_cast <String&&> (other.tagName);
  82. return *this;
  83. }
  84. #endif
  85. void XmlElement::copyChildrenAndAttributesFrom (const XmlElement& other)
  86. {
  87. jassert (firstChildElement.get() == nullptr);
  88. firstChildElement.addCopyOfList (other.firstChildElement);
  89. jassert (attributes.get() == nullptr);
  90. attributes.addCopyOfList (other.attributes);
  91. }
  92. XmlElement::~XmlElement() noexcept
  93. {
  94. firstChildElement.deleteAll();
  95. attributes.deleteAll();
  96. }
  97. //==============================================================================
  98. namespace XmlOutputFunctions
  99. {
  100. #if 0 // (These functions are just used to generate the lookup table used below)
  101. bool isLegalXmlCharSlow (const juce_wchar character) noexcept
  102. {
  103. if ((character >= 'a' && character <= 'z')
  104. || (character >= 'A' && character <= 'Z')
  105. || (character >= '0' && character <= '9'))
  106. return true;
  107. const char* t = " .,;:-()_+=?!'#@[]/\\*%~{}$|";
  108. do
  109. {
  110. if (((juce_wchar) (uint8) *t) == character)
  111. return true;
  112. }
  113. while (*++t != 0);
  114. return false;
  115. }
  116. void generateLegalCharLookupTable()
  117. {
  118. uint8 n[32] = { 0 };
  119. for (int i = 0; i < 256; ++i)
  120. if (isLegalXmlCharSlow (i))
  121. n[i >> 3] |= (1 << (i & 7));
  122. String s;
  123. for (int i = 0; i < 32; ++i)
  124. s << (int) n[i] << ", ";
  125. DBG (s);
  126. }
  127. #endif
  128. static bool isLegalXmlChar (const uint32 c) noexcept
  129. {
  130. static const unsigned char legalChars[] = { 0, 0, 0, 0, 187, 255, 255, 175, 255, 255, 255, 191, 254, 255, 255, 127 };
  131. return c < sizeof (legalChars) * 8
  132. && (legalChars [c >> 3] & (1 << (c & 7))) != 0;
  133. }
  134. static void escapeIllegalXmlChars (OutputStream& outputStream, const String& text, const bool changeNewLines)
  135. {
  136. String::CharPointerType t (text.getCharPointer());
  137. for (;;)
  138. {
  139. const uint32 character = (uint32) t.getAndAdvance();
  140. if (character == 0)
  141. break;
  142. if (isLegalXmlChar (character))
  143. {
  144. outputStream << (char) character;
  145. }
  146. else
  147. {
  148. switch (character)
  149. {
  150. case '&': outputStream << "&amp;"; break;
  151. case '"': outputStream << "&quot;"; break;
  152. case '>': outputStream << "&gt;"; break;
  153. case '<': outputStream << "&lt;"; break;
  154. case '\n':
  155. case '\r':
  156. if (! changeNewLines)
  157. {
  158. outputStream << (char) character;
  159. break;
  160. }
  161. // Note: deliberate fall-through here!
  162. default:
  163. outputStream << "&#" << ((int) character) << ';';
  164. break;
  165. }
  166. }
  167. }
  168. }
  169. static void writeSpaces (OutputStream& out, const int numSpaces)
  170. {
  171. out.writeRepeatedByte (' ', numSpaces);
  172. }
  173. }
  174. void XmlElement::writeElementAsText (OutputStream& outputStream,
  175. const int indentationLevel,
  176. const int lineWrapLength) const
  177. {
  178. using namespace XmlOutputFunctions;
  179. if (indentationLevel >= 0)
  180. writeSpaces (outputStream, indentationLevel);
  181. if (! isTextElement())
  182. {
  183. outputStream.writeByte ('<');
  184. outputStream << tagName;
  185. {
  186. const int attIndent = indentationLevel + tagName.length() + 1;
  187. int lineLen = 0;
  188. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  189. {
  190. if (lineLen > lineWrapLength && indentationLevel >= 0)
  191. {
  192. outputStream << newLine;
  193. writeSpaces (outputStream, attIndent);
  194. lineLen = 0;
  195. }
  196. const int64 startPos = outputStream.getPosition();
  197. outputStream.writeByte (' ');
  198. outputStream << att->name;
  199. outputStream.write ("=\"", 2);
  200. escapeIllegalXmlChars (outputStream, att->value, true);
  201. outputStream.writeByte ('"');
  202. lineLen += (int) (outputStream.getPosition() - startPos);
  203. }
  204. }
  205. if (firstChildElement != nullptr)
  206. {
  207. outputStream.writeByte ('>');
  208. bool lastWasTextNode = false;
  209. for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem)
  210. {
  211. if (child->isTextElement())
  212. {
  213. escapeIllegalXmlChars (outputStream, child->getText(), false);
  214. lastWasTextNode = true;
  215. }
  216. else
  217. {
  218. if (indentationLevel >= 0 && ! lastWasTextNode)
  219. outputStream << newLine;
  220. child->writeElementAsText (outputStream,
  221. lastWasTextNode ? 0 : (indentationLevel + (indentationLevel >= 0 ? 2 : 0)), lineWrapLength);
  222. lastWasTextNode = false;
  223. }
  224. }
  225. if (indentationLevel >= 0 && ! lastWasTextNode)
  226. {
  227. outputStream << newLine;
  228. writeSpaces (outputStream, indentationLevel);
  229. }
  230. outputStream.write ("</", 2);
  231. outputStream << tagName;
  232. outputStream.writeByte ('>');
  233. }
  234. else
  235. {
  236. outputStream.write ("/>", 2);
  237. }
  238. }
  239. else
  240. {
  241. escapeIllegalXmlChars (outputStream, getText(), false);
  242. }
  243. }
  244. String XmlElement::createDocument (const String& dtdToUse,
  245. const bool allOnOneLine,
  246. const bool includeXmlHeader,
  247. const String& encodingType,
  248. const int lineWrapLength) const
  249. {
  250. MemoryOutputStream mem (2048);
  251. writeToStream (mem, dtdToUse, allOnOneLine, includeXmlHeader, encodingType, lineWrapLength);
  252. return mem.toUTF8();
  253. }
  254. void XmlElement::writeToStream (OutputStream& output,
  255. const String& dtdToUse,
  256. const bool allOnOneLine,
  257. const bool includeXmlHeader,
  258. const String& encodingType,
  259. const int lineWrapLength) const
  260. {
  261. using namespace XmlOutputFunctions;
  262. if (includeXmlHeader)
  263. {
  264. output << "<?xml version=\"1.0\" encoding=\"" << encodingType << "\"?>";
  265. if (allOnOneLine)
  266. output.writeByte (' ');
  267. else
  268. output << newLine << newLine;
  269. }
  270. if (dtdToUse.isNotEmpty())
  271. {
  272. output << dtdToUse;
  273. if (allOnOneLine)
  274. output.writeByte (' ');
  275. else
  276. output << newLine;
  277. }
  278. writeElementAsText (output, allOnOneLine ? -1 : 0, lineWrapLength);
  279. if (! allOnOneLine)
  280. output << newLine;
  281. }
  282. bool XmlElement::writeToFile (const File& file,
  283. const String& dtdToUse,
  284. const String& encodingType,
  285. const int lineWrapLength) const
  286. {
  287. if (file.hasWriteAccess())
  288. {
  289. TemporaryFile tempFile (file);
  290. ScopedPointer <FileOutputStream> out (tempFile.getFile().createOutputStream());
  291. if (out != nullptr)
  292. {
  293. writeToStream (*out, dtdToUse, false, true, encodingType, lineWrapLength);
  294. out = nullptr;
  295. return tempFile.overwriteTargetFileWithTemporary();
  296. }
  297. }
  298. return false;
  299. }
  300. //==============================================================================
  301. bool XmlElement::hasTagName (const String& tagNameWanted) const noexcept
  302. {
  303. #if JUCE_DEBUG
  304. // if debugging, check that the case is actually the same, because
  305. // valid xml is case-sensitive, and although this lets it pass, it's
  306. // better not to..
  307. if (tagName.equalsIgnoreCase (tagNameWanted))
  308. {
  309. jassert (tagName == tagNameWanted);
  310. return true;
  311. }
  312. else
  313. {
  314. return false;
  315. }
  316. #else
  317. return tagName.equalsIgnoreCase (tagNameWanted);
  318. #endif
  319. }
  320. XmlElement* XmlElement::getNextElementWithTagName (const String& requiredTagName) const
  321. {
  322. XmlElement* e = nextListItem;
  323. while (e != nullptr && ! e->hasTagName (requiredTagName))
  324. e = e->nextListItem;
  325. return e;
  326. }
  327. //==============================================================================
  328. int XmlElement::getNumAttributes() const noexcept
  329. {
  330. return attributes.size();
  331. }
  332. const String& XmlElement::getAttributeName (const int index) const noexcept
  333. {
  334. const XmlAttributeNode* const att = attributes [index];
  335. return att != nullptr ? att->name : String::empty;
  336. }
  337. const String& XmlElement::getAttributeValue (const int index) const noexcept
  338. {
  339. const XmlAttributeNode* const att = attributes [index];
  340. return att != nullptr ? att->value : String::empty;
  341. }
  342. bool XmlElement::hasAttribute (const String& attributeName) const noexcept
  343. {
  344. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  345. if (att->hasName (attributeName))
  346. return true;
  347. return false;
  348. }
  349. //==============================================================================
  350. const String& XmlElement::getStringAttribute (const String& attributeName) const noexcept
  351. {
  352. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  353. if (att->hasName (attributeName))
  354. return att->value;
  355. return String::empty;
  356. }
  357. String XmlElement::getStringAttribute (const String& attributeName, const String& defaultReturnValue) const
  358. {
  359. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  360. if (att->hasName (attributeName))
  361. return att->value;
  362. return defaultReturnValue;
  363. }
  364. int XmlElement::getIntAttribute (const String& attributeName, const int defaultReturnValue) const
  365. {
  366. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  367. if (att->hasName (attributeName))
  368. return att->value.getIntValue();
  369. return defaultReturnValue;
  370. }
  371. double XmlElement::getDoubleAttribute (const String& attributeName, const double defaultReturnValue) const
  372. {
  373. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  374. if (att->hasName (attributeName))
  375. return att->value.getDoubleValue();
  376. return defaultReturnValue;
  377. }
  378. bool XmlElement::getBoolAttribute (const String& attributeName, const bool defaultReturnValue) const
  379. {
  380. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  381. {
  382. if (att->hasName (attributeName))
  383. {
  384. juce_wchar firstChar = att->value[0];
  385. if (CharacterFunctions::isWhitespace (firstChar))
  386. firstChar = att->value.trimStart() [0];
  387. return firstChar == '1'
  388. || firstChar == 't'
  389. || firstChar == 'y'
  390. || firstChar == 'T'
  391. || firstChar == 'Y';
  392. }
  393. }
  394. return defaultReturnValue;
  395. }
  396. bool XmlElement::compareAttribute (const String& attributeName,
  397. const String& stringToCompareAgainst,
  398. const bool ignoreCase) const noexcept
  399. {
  400. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  401. if (att->hasName (attributeName))
  402. return ignoreCase ? att->value.equalsIgnoreCase (stringToCompareAgainst)
  403. : att->value == stringToCompareAgainst;
  404. return false;
  405. }
  406. //==============================================================================
  407. void XmlElement::setAttribute (const String& attributeName, const String& value)
  408. {
  409. if (attributes == nullptr)
  410. {
  411. attributes = new XmlAttributeNode (attributeName, value);
  412. }
  413. else
  414. {
  415. XmlAttributeNode* att = attributes;
  416. for (;;)
  417. {
  418. if (att->hasName (attributeName))
  419. {
  420. att->value = value;
  421. break;
  422. }
  423. else if (att->nextListItem == nullptr)
  424. {
  425. att->nextListItem = new XmlAttributeNode (attributeName, value);
  426. break;
  427. }
  428. att = att->nextListItem;
  429. }
  430. }
  431. }
  432. void XmlElement::setAttribute (const String& attributeName, const int number)
  433. {
  434. setAttribute (attributeName, String (number));
  435. }
  436. void XmlElement::setAttribute (const String& attributeName, const double number)
  437. {
  438. setAttribute (attributeName, String (number));
  439. }
  440. void XmlElement::removeAttribute (const String& attributeName) noexcept
  441. {
  442. LinkedListPointer<XmlAttributeNode>* att = &attributes;
  443. while (att->get() != nullptr)
  444. {
  445. if (att->get()->hasName (attributeName))
  446. {
  447. delete att->removeNext();
  448. break;
  449. }
  450. att = &(att->get()->nextListItem);
  451. }
  452. }
  453. void XmlElement::removeAllAttributes() noexcept
  454. {
  455. attributes.deleteAll();
  456. }
  457. //==============================================================================
  458. int XmlElement::getNumChildElements() const noexcept
  459. {
  460. return firstChildElement.size();
  461. }
  462. XmlElement* XmlElement::getChildElement (const int index) const noexcept
  463. {
  464. return firstChildElement [index].get();
  465. }
  466. XmlElement* XmlElement::getChildByName (const String& childName) const noexcept
  467. {
  468. for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem)
  469. if (child->hasTagName (childName))
  470. return child;
  471. return nullptr;
  472. }
  473. void XmlElement::addChildElement (XmlElement* const newNode) noexcept
  474. {
  475. if (newNode != nullptr)
  476. firstChildElement.append (newNode);
  477. }
  478. void XmlElement::insertChildElement (XmlElement* const newNode,
  479. int indexToInsertAt) noexcept
  480. {
  481. if (newNode != nullptr)
  482. {
  483. removeChildElement (newNode, false);
  484. firstChildElement.insertAtIndex (indexToInsertAt, newNode);
  485. }
  486. }
  487. XmlElement* XmlElement::createNewChildElement (const String& childTagName)
  488. {
  489. XmlElement* const newElement = new XmlElement (childTagName);
  490. addChildElement (newElement);
  491. return newElement;
  492. }
  493. bool XmlElement::replaceChildElement (XmlElement* const currentChildElement,
  494. XmlElement* const newNode) noexcept
  495. {
  496. if (newNode != nullptr)
  497. {
  498. LinkedListPointer<XmlElement>* const p = firstChildElement.findPointerTo (currentChildElement);
  499. if (p != nullptr)
  500. {
  501. if (currentChildElement != newNode)
  502. delete p->replaceNext (newNode);
  503. return true;
  504. }
  505. }
  506. return false;
  507. }
  508. void XmlElement::removeChildElement (XmlElement* const childToRemove,
  509. const bool shouldDeleteTheChild) noexcept
  510. {
  511. if (childToRemove != nullptr)
  512. {
  513. firstChildElement.remove (childToRemove);
  514. if (shouldDeleteTheChild)
  515. delete childToRemove;
  516. }
  517. }
  518. bool XmlElement::isEquivalentTo (const XmlElement* const other,
  519. const bool ignoreOrderOfAttributes) const noexcept
  520. {
  521. if (this != other)
  522. {
  523. if (other == nullptr || tagName != other->tagName)
  524. return false;
  525. if (ignoreOrderOfAttributes)
  526. {
  527. int totalAtts = 0;
  528. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  529. {
  530. if (! other->compareAttribute (att->name, att->value))
  531. return false;
  532. ++totalAtts;
  533. }
  534. if (totalAtts != other->getNumAttributes())
  535. return false;
  536. }
  537. else
  538. {
  539. const XmlAttributeNode* thisAtt = attributes;
  540. const XmlAttributeNode* otherAtt = other->attributes;
  541. for (;;)
  542. {
  543. if (thisAtt == nullptr || otherAtt == nullptr)
  544. {
  545. if (thisAtt == otherAtt) // both 0, so it's a match
  546. break;
  547. return false;
  548. }
  549. if (thisAtt->name != otherAtt->name
  550. || thisAtt->value != otherAtt->value)
  551. {
  552. return false;
  553. }
  554. thisAtt = thisAtt->nextListItem;
  555. otherAtt = otherAtt->nextListItem;
  556. }
  557. }
  558. const XmlElement* thisChild = firstChildElement;
  559. const XmlElement* otherChild = other->firstChildElement;
  560. for (;;)
  561. {
  562. if (thisChild == nullptr || otherChild == nullptr)
  563. {
  564. if (thisChild == otherChild) // both 0, so it's a match
  565. break;
  566. return false;
  567. }
  568. if (! thisChild->isEquivalentTo (otherChild, ignoreOrderOfAttributes))
  569. return false;
  570. thisChild = thisChild->nextListItem;
  571. otherChild = otherChild->nextListItem;
  572. }
  573. }
  574. return true;
  575. }
  576. void XmlElement::deleteAllChildElements() noexcept
  577. {
  578. firstChildElement.deleteAll();
  579. }
  580. void XmlElement::deleteAllChildElementsWithTagName (const String& name) noexcept
  581. {
  582. XmlElement* child = firstChildElement;
  583. while (child != nullptr)
  584. {
  585. XmlElement* const nextChild = child->nextListItem;
  586. if (child->hasTagName (name))
  587. removeChildElement (child, true);
  588. child = nextChild;
  589. }
  590. }
  591. bool XmlElement::containsChildElement (const XmlElement* const possibleChild) const noexcept
  592. {
  593. return firstChildElement.contains (possibleChild);
  594. }
  595. XmlElement* XmlElement::findParentElementOf (const XmlElement* const elementToLookFor) noexcept
  596. {
  597. if (this == elementToLookFor || elementToLookFor == nullptr)
  598. return nullptr;
  599. for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem)
  600. {
  601. if (elementToLookFor == child)
  602. return this;
  603. XmlElement* const found = child->findParentElementOf (elementToLookFor);
  604. if (found != nullptr)
  605. return found;
  606. }
  607. return nullptr;
  608. }
  609. void XmlElement::getChildElementsAsArray (XmlElement** elems) const noexcept
  610. {
  611. firstChildElement.copyToArray (elems);
  612. }
  613. void XmlElement::reorderChildElements (XmlElement** const elems, const int num) noexcept
  614. {
  615. XmlElement* e = firstChildElement = elems[0];
  616. for (int i = 1; i < num; ++i)
  617. {
  618. e->nextListItem = elems[i];
  619. e = e->nextListItem;
  620. }
  621. e->nextListItem = nullptr;
  622. }
  623. //==============================================================================
  624. bool XmlElement::isTextElement() const noexcept
  625. {
  626. return tagName.isEmpty();
  627. }
  628. static const String juce_xmltextContentAttributeName ("text");
  629. const String& XmlElement::getText() const noexcept
  630. {
  631. jassert (isTextElement()); // you're trying to get the text from an element that
  632. // isn't actually a text element.. If this contains text sub-nodes, you
  633. // probably want to use getAllSubText instead.
  634. return getStringAttribute (juce_xmltextContentAttributeName);
  635. }
  636. void XmlElement::setText (const String& newText)
  637. {
  638. if (isTextElement())
  639. setAttribute (juce_xmltextContentAttributeName, newText);
  640. else
  641. jassertfalse; // you can only change the text in a text element, not a normal one.
  642. }
  643. String XmlElement::getAllSubText() const
  644. {
  645. if (isTextElement())
  646. return getText();
  647. MemoryOutputStream mem (1024);
  648. for (const XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem)
  649. mem << child->getAllSubText();
  650. return mem.toString();
  651. }
  652. String XmlElement::getChildElementAllSubText (const String& childTagName,
  653. const String& defaultReturnValue) const
  654. {
  655. const XmlElement* const child = getChildByName (childTagName);
  656. if (child != nullptr)
  657. return child->getAllSubText();
  658. return defaultReturnValue;
  659. }
  660. XmlElement* XmlElement::createTextElement (const String& text)
  661. {
  662. XmlElement* const e = new XmlElement ((int) 0);
  663. e->setAttribute (juce_xmltextContentAttributeName, text);
  664. return e;
  665. }
  666. void XmlElement::addTextElement (const String& text)
  667. {
  668. addChildElement (createTextElement (text));
  669. }
  670. void XmlElement::deleteAllTextElements() noexcept
  671. {
  672. XmlElement* child = firstChildElement;
  673. while (child != nullptr)
  674. {
  675. XmlElement* const next = child->nextListItem;
  676. if (child->isTextElement())
  677. removeChildElement (child, true);
  678. child = next;
  679. }
  680. }