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.

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