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.

861 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. /*bool isLegalXmlCharSlow (const juce_wchar character) noexcept
  103. {
  104. if ((character >= 'a' && character <= 'z')
  105. || (character >= 'A' && character <= 'Z')
  106. || (character >= '0' && character <= '9'))
  107. return true;
  108. const char* t = " .,;:-()_+=?!'#@[]/\\*%~{}$|";
  109. do
  110. {
  111. if (((juce_wchar) (uint8) *t) == character)
  112. return true;
  113. }
  114. while (*++t != 0);
  115. return false;
  116. }
  117. void generateLegalCharConstants()
  118. {
  119. uint8 n[32] = { 0 };
  120. for (int i = 0; i < 256; ++i)
  121. if (isLegalXmlCharSlow (i))
  122. n[i >> 3] |= (1 << (i & 7));
  123. String s;
  124. for (int i = 0; i < 32; ++i)
  125. s << (int) n[i] << ", ";
  126. DBG (s);
  127. }*/
  128. 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. 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. 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. writeSpaces (outputStream, indentationLevel);
  180. if (! isTextElement())
  181. {
  182. outputStream.writeByte ('<');
  183. outputStream << tagName;
  184. {
  185. const int attIndent = indentationLevel + tagName.length() + 1;
  186. int lineLen = 0;
  187. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  188. {
  189. if (lineLen > lineWrapLength && indentationLevel >= 0)
  190. {
  191. outputStream << newLine;
  192. writeSpaces (outputStream, attIndent);
  193. lineLen = 0;
  194. }
  195. const int64 startPos = outputStream.getPosition();
  196. outputStream.writeByte (' ');
  197. outputStream << att->name;
  198. outputStream.write ("=\"", 2);
  199. escapeIllegalXmlChars (outputStream, att->value, true);
  200. outputStream.writeByte ('"');
  201. lineLen += (int) (outputStream.getPosition() - startPos);
  202. }
  203. }
  204. if (firstChildElement != nullptr)
  205. {
  206. outputStream.writeByte ('>');
  207. XmlElement* child = firstChildElement;
  208. bool lastWasTextNode = false;
  209. while (child != nullptr)
  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. child = child->getNextElement();
  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. XmlElement* child = firstChildElement;
  470. while (child != nullptr)
  471. {
  472. if (child->hasTagName (childName))
  473. break;
  474. child = child->nextListItem;
  475. }
  476. return child;
  477. }
  478. void XmlElement::addChildElement (XmlElement* const newNode) noexcept
  479. {
  480. if (newNode != nullptr)
  481. firstChildElement.append (newNode);
  482. }
  483. void XmlElement::insertChildElement (XmlElement* const newNode,
  484. int indexToInsertAt) noexcept
  485. {
  486. if (newNode != nullptr)
  487. {
  488. removeChildElement (newNode, false);
  489. firstChildElement.insertAtIndex (indexToInsertAt, newNode);
  490. }
  491. }
  492. XmlElement* XmlElement::createNewChildElement (const String& childTagName)
  493. {
  494. XmlElement* const newElement = new XmlElement (childTagName);
  495. addChildElement (newElement);
  496. return newElement;
  497. }
  498. bool XmlElement::replaceChildElement (XmlElement* const currentChildElement,
  499. XmlElement* const newNode) noexcept
  500. {
  501. if (newNode != nullptr)
  502. {
  503. LinkedListPointer<XmlElement>* const p = firstChildElement.findPointerTo (currentChildElement);
  504. if (p != nullptr)
  505. {
  506. if (currentChildElement != newNode)
  507. delete p->replaceNext (newNode);
  508. return true;
  509. }
  510. }
  511. return false;
  512. }
  513. void XmlElement::removeChildElement (XmlElement* const childToRemove,
  514. const bool shouldDeleteTheChild) noexcept
  515. {
  516. if (childToRemove != nullptr)
  517. {
  518. firstChildElement.remove (childToRemove);
  519. if (shouldDeleteTheChild)
  520. delete childToRemove;
  521. }
  522. }
  523. bool XmlElement::isEquivalentTo (const XmlElement* const other,
  524. const bool ignoreOrderOfAttributes) const noexcept
  525. {
  526. if (this != other)
  527. {
  528. if (other == nullptr || tagName != other->tagName)
  529. return false;
  530. if (ignoreOrderOfAttributes)
  531. {
  532. int totalAtts = 0;
  533. const XmlAttributeNode* att = attributes;
  534. while (att != nullptr)
  535. {
  536. if (! other->compareAttribute (att->name, att->value))
  537. return false;
  538. att = att->nextListItem;
  539. ++totalAtts;
  540. }
  541. if (totalAtts != other->getNumAttributes())
  542. return false;
  543. }
  544. else
  545. {
  546. const XmlAttributeNode* thisAtt = attributes;
  547. const XmlAttributeNode* otherAtt = other->attributes;
  548. for (;;)
  549. {
  550. if (thisAtt == nullptr || otherAtt == nullptr)
  551. {
  552. if (thisAtt == otherAtt) // both 0, so it's a match
  553. break;
  554. return false;
  555. }
  556. if (thisAtt->name != otherAtt->name
  557. || thisAtt->value != otherAtt->value)
  558. {
  559. return false;
  560. }
  561. thisAtt = thisAtt->nextListItem;
  562. otherAtt = otherAtt->nextListItem;
  563. }
  564. }
  565. const XmlElement* thisChild = firstChildElement;
  566. const XmlElement* otherChild = other->firstChildElement;
  567. for (;;)
  568. {
  569. if (thisChild == nullptr || otherChild == nullptr)
  570. {
  571. if (thisChild == otherChild) // both 0, so it's a match
  572. break;
  573. return false;
  574. }
  575. if (! thisChild->isEquivalentTo (otherChild, ignoreOrderOfAttributes))
  576. return false;
  577. thisChild = thisChild->nextListItem;
  578. otherChild = otherChild->nextListItem;
  579. }
  580. }
  581. return true;
  582. }
  583. void XmlElement::deleteAllChildElements() noexcept
  584. {
  585. firstChildElement.deleteAll();
  586. }
  587. void XmlElement::deleteAllChildElementsWithTagName (const String& name) noexcept
  588. {
  589. XmlElement* child = firstChildElement;
  590. while (child != nullptr)
  591. {
  592. XmlElement* const nextChild = child->nextListItem;
  593. if (child->hasTagName (name))
  594. removeChildElement (child, true);
  595. child = nextChild;
  596. }
  597. }
  598. bool XmlElement::containsChildElement (const XmlElement* const possibleChild) const noexcept
  599. {
  600. return firstChildElement.contains (possibleChild);
  601. }
  602. XmlElement* XmlElement::findParentElementOf (const XmlElement* const elementToLookFor) noexcept
  603. {
  604. if (this == elementToLookFor || elementToLookFor == nullptr)
  605. return nullptr;
  606. XmlElement* child = firstChildElement;
  607. while (child != nullptr)
  608. {
  609. if (elementToLookFor == child)
  610. return this;
  611. XmlElement* const found = child->findParentElementOf (elementToLookFor);
  612. if (found != nullptr)
  613. return found;
  614. child = child->nextListItem;
  615. }
  616. return nullptr;
  617. }
  618. void XmlElement::getChildElementsAsArray (XmlElement** elems) const noexcept
  619. {
  620. firstChildElement.copyToArray (elems);
  621. }
  622. void XmlElement::reorderChildElements (XmlElement** const elems, const int num) noexcept
  623. {
  624. XmlElement* e = firstChildElement = elems[0];
  625. for (int i = 1; i < num; ++i)
  626. {
  627. e->nextListItem = elems[i];
  628. e = e->nextListItem;
  629. }
  630. e->nextListItem = nullptr;
  631. }
  632. //==============================================================================
  633. bool XmlElement::isTextElement() const noexcept
  634. {
  635. return tagName.isEmpty();
  636. }
  637. static const String juce_xmltextContentAttributeName ("text");
  638. const String& XmlElement::getText() const noexcept
  639. {
  640. jassert (isTextElement()); // you're trying to get the text from an element that
  641. // isn't actually a text element.. If this contains text sub-nodes, you
  642. // probably want to use getAllSubText instead.
  643. return getStringAttribute (juce_xmltextContentAttributeName);
  644. }
  645. void XmlElement::setText (const String& newText)
  646. {
  647. if (isTextElement())
  648. setAttribute (juce_xmltextContentAttributeName, newText);
  649. else
  650. jassertfalse; // you can only change the text in a text element, not a normal one.
  651. }
  652. String XmlElement::getAllSubText() const
  653. {
  654. if (isTextElement())
  655. return getText();
  656. String result;
  657. String::Concatenator concatenator (result);
  658. const XmlElement* child = firstChildElement;
  659. while (child != nullptr)
  660. {
  661. concatenator.append (child->getAllSubText());
  662. child = child->nextListItem;
  663. }
  664. return result;
  665. }
  666. String XmlElement::getChildElementAllSubText (const String& childTagName,
  667. const String& defaultReturnValue) const
  668. {
  669. const XmlElement* const child = getChildByName (childTagName);
  670. if (child != nullptr)
  671. return child->getAllSubText();
  672. return defaultReturnValue;
  673. }
  674. XmlElement* XmlElement::createTextElement (const String& text)
  675. {
  676. XmlElement* const e = new XmlElement ((int) 0);
  677. e->setAttribute (juce_xmltextContentAttributeName, text);
  678. return e;
  679. }
  680. void XmlElement::addTextElement (const String& text)
  681. {
  682. addChildElement (createTextElement (text));
  683. }
  684. void XmlElement::deleteAllTextElements() noexcept
  685. {
  686. XmlElement* child = firstChildElement;
  687. while (child != nullptr)
  688. {
  689. XmlElement* const next = child->nextListItem;
  690. if (child->isTextElement())
  691. removeChildElement (child, true);
  692. child = next;
  693. }
  694. }
  695. END_JUCE_NAMESPACE