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.

862 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. if (this != &other)
  78. {
  79. removeAllAttributes();
  80. deleteAllChildElements();
  81. nextListItem = static_cast <LinkedListPointer <XmlElement>&&> (other.nextListItem);
  82. firstChildElement = static_cast <LinkedListPointer <XmlElement>&&> (other.firstChildElement);
  83. attributes = static_cast <LinkedListPointer <XmlAttributeNode>&&> (other.attributes);
  84. tagName = static_cast <String&&> (other.tagName);
  85. }
  86. return *this;
  87. }
  88. #endif
  89. void XmlElement::copyChildrenAndAttributesFrom (const XmlElement& other)
  90. {
  91. jassert (firstChildElement.get() == nullptr);
  92. firstChildElement.addCopyOfList (other.firstChildElement);
  93. jassert (attributes.get() == nullptr);
  94. attributes.addCopyOfList (other.attributes);
  95. }
  96. XmlElement::~XmlElement() noexcept
  97. {
  98. firstChildElement.deleteAll();
  99. attributes.deleteAll();
  100. }
  101. //==============================================================================
  102. namespace XmlOutputFunctions
  103. {
  104. /*bool isLegalXmlCharSlow (const juce_wchar character) noexcept
  105. {
  106. if ((character >= 'a' && character <= 'z')
  107. || (character >= 'A' && character <= 'Z')
  108. || (character >= '0' && character <= '9'))
  109. return true;
  110. const char* t = " .,;:-()_+=?!'#@[]/\\*%~{}$|";
  111. do
  112. {
  113. if (((juce_wchar) (uint8) *t) == character)
  114. return true;
  115. }
  116. while (*++t != 0);
  117. return false;
  118. }
  119. void generateLegalCharConstants()
  120. {
  121. uint8 n[32] = { 0 };
  122. for (int i = 0; i < 256; ++i)
  123. if (isLegalXmlCharSlow (i))
  124. n[i >> 3] |= (1 << (i & 7));
  125. String s;
  126. for (int i = 0; i < 32; ++i)
  127. s << (int) n[i] << ", ";
  128. DBG (s);
  129. }*/
  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. XmlElement* child = firstChildElement;
  210. bool lastWasTextNode = false;
  211. while (child != nullptr)
  212. {
  213. if (child->isTextElement())
  214. {
  215. escapeIllegalXmlChars (outputStream, child->getText(), false);
  216. lastWasTextNode = true;
  217. }
  218. else
  219. {
  220. if (indentationLevel >= 0 && ! lastWasTextNode)
  221. outputStream << newLine;
  222. child->writeElementAsText (outputStream,
  223. lastWasTextNode ? 0 : (indentationLevel + (indentationLevel >= 0 ? 2 : 0)), lineWrapLength);
  224. lastWasTextNode = false;
  225. }
  226. child = child->getNextElement();
  227. }
  228. if (indentationLevel >= 0 && ! lastWasTextNode)
  229. {
  230. outputStream << newLine;
  231. writeSpaces (outputStream, indentationLevel);
  232. }
  233. outputStream.write ("</", 2);
  234. outputStream << tagName;
  235. outputStream.writeByte ('>');
  236. }
  237. else
  238. {
  239. outputStream.write ("/>", 2);
  240. }
  241. }
  242. else
  243. {
  244. escapeIllegalXmlChars (outputStream, getText(), false);
  245. }
  246. }
  247. String XmlElement::createDocument (const String& dtdToUse,
  248. const bool allOnOneLine,
  249. const bool includeXmlHeader,
  250. const String& encodingType,
  251. const int lineWrapLength) const
  252. {
  253. MemoryOutputStream mem (2048);
  254. writeToStream (mem, dtdToUse, allOnOneLine, includeXmlHeader, encodingType, lineWrapLength);
  255. return mem.toUTF8();
  256. }
  257. void XmlElement::writeToStream (OutputStream& output,
  258. const String& dtdToUse,
  259. const bool allOnOneLine,
  260. const bool includeXmlHeader,
  261. const String& encodingType,
  262. const int lineWrapLength) const
  263. {
  264. using namespace XmlOutputFunctions;
  265. if (includeXmlHeader)
  266. {
  267. output << "<?xml version=\"1.0\" encoding=\"" << encodingType << "\"?>";
  268. if (allOnOneLine)
  269. output.writeByte (' ');
  270. else
  271. output << newLine << newLine;
  272. }
  273. if (dtdToUse.isNotEmpty())
  274. {
  275. output << dtdToUse;
  276. if (allOnOneLine)
  277. output.writeByte (' ');
  278. else
  279. output << newLine;
  280. }
  281. writeElementAsText (output, allOnOneLine ? -1 : 0, lineWrapLength);
  282. if (! allOnOneLine)
  283. output << newLine;
  284. }
  285. bool XmlElement::writeToFile (const File& file,
  286. const String& dtdToUse,
  287. const String& encodingType,
  288. const int lineWrapLength) const
  289. {
  290. if (file.hasWriteAccess())
  291. {
  292. TemporaryFile tempFile (file);
  293. ScopedPointer <FileOutputStream> out (tempFile.getFile().createOutputStream());
  294. if (out != nullptr)
  295. {
  296. writeToStream (*out, dtdToUse, false, true, encodingType, lineWrapLength);
  297. out = nullptr;
  298. return tempFile.overwriteTargetFileWithTemporary();
  299. }
  300. }
  301. return false;
  302. }
  303. //==============================================================================
  304. bool XmlElement::hasTagName (const String& tagNameWanted) const noexcept
  305. {
  306. #if JUCE_DEBUG
  307. // if debugging, check that the case is actually the same, because
  308. // valid xml is case-sensitive, and although this lets it pass, it's
  309. // better not to..
  310. if (tagName.equalsIgnoreCase (tagNameWanted))
  311. {
  312. jassert (tagName == tagNameWanted);
  313. return true;
  314. }
  315. else
  316. {
  317. return false;
  318. }
  319. #else
  320. return tagName.equalsIgnoreCase (tagNameWanted);
  321. #endif
  322. }
  323. XmlElement* XmlElement::getNextElementWithTagName (const String& requiredTagName) const
  324. {
  325. XmlElement* e = nextListItem;
  326. while (e != nullptr && ! e->hasTagName (requiredTagName))
  327. e = e->nextListItem;
  328. return e;
  329. }
  330. //==============================================================================
  331. int XmlElement::getNumAttributes() const noexcept
  332. {
  333. return attributes.size();
  334. }
  335. const String& XmlElement::getAttributeName (const int index) const noexcept
  336. {
  337. const XmlAttributeNode* const att = attributes [index];
  338. return att != nullptr ? att->name : String::empty;
  339. }
  340. const String& XmlElement::getAttributeValue (const int index) const noexcept
  341. {
  342. const XmlAttributeNode* const att = attributes [index];
  343. return att != nullptr ? att->value : String::empty;
  344. }
  345. bool XmlElement::hasAttribute (const String& attributeName) const noexcept
  346. {
  347. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  348. if (att->hasName (attributeName))
  349. return true;
  350. return false;
  351. }
  352. //==============================================================================
  353. const String& XmlElement::getStringAttribute (const String& attributeName) const noexcept
  354. {
  355. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  356. if (att->hasName (attributeName))
  357. return att->value;
  358. return String::empty;
  359. }
  360. String XmlElement::getStringAttribute (const String& attributeName, const String& defaultReturnValue) const
  361. {
  362. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  363. if (att->hasName (attributeName))
  364. return att->value;
  365. return defaultReturnValue;
  366. }
  367. int XmlElement::getIntAttribute (const String& attributeName, const int defaultReturnValue) const
  368. {
  369. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  370. if (att->hasName (attributeName))
  371. return att->value.getIntValue();
  372. return defaultReturnValue;
  373. }
  374. double XmlElement::getDoubleAttribute (const String& attributeName, const double defaultReturnValue) const
  375. {
  376. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  377. if (att->hasName (attributeName))
  378. return att->value.getDoubleValue();
  379. return defaultReturnValue;
  380. }
  381. bool XmlElement::getBoolAttribute (const String& attributeName, const bool defaultReturnValue) const
  382. {
  383. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  384. {
  385. if (att->hasName (attributeName))
  386. {
  387. juce_wchar firstChar = att->value[0];
  388. if (CharacterFunctions::isWhitespace (firstChar))
  389. firstChar = att->value.trimStart() [0];
  390. return firstChar == '1'
  391. || firstChar == 't'
  392. || firstChar == 'y'
  393. || firstChar == 'T'
  394. || firstChar == 'Y';
  395. }
  396. }
  397. return defaultReturnValue;
  398. }
  399. bool XmlElement::compareAttribute (const String& attributeName,
  400. const String& stringToCompareAgainst,
  401. const bool ignoreCase) const noexcept
  402. {
  403. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  404. if (att->hasName (attributeName))
  405. return ignoreCase ? att->value.equalsIgnoreCase (stringToCompareAgainst)
  406. : att->value == stringToCompareAgainst;
  407. return false;
  408. }
  409. //==============================================================================
  410. void XmlElement::setAttribute (const String& attributeName, const String& value)
  411. {
  412. if (attributes == nullptr)
  413. {
  414. attributes = new XmlAttributeNode (attributeName, value);
  415. }
  416. else
  417. {
  418. XmlAttributeNode* att = attributes;
  419. for (;;)
  420. {
  421. if (att->hasName (attributeName))
  422. {
  423. att->value = value;
  424. break;
  425. }
  426. else if (att->nextListItem == nullptr)
  427. {
  428. att->nextListItem = new XmlAttributeNode (attributeName, value);
  429. break;
  430. }
  431. att = att->nextListItem;
  432. }
  433. }
  434. }
  435. void XmlElement::setAttribute (const String& attributeName, const int number)
  436. {
  437. setAttribute (attributeName, String (number));
  438. }
  439. void XmlElement::setAttribute (const String& attributeName, const double number)
  440. {
  441. setAttribute (attributeName, String (number));
  442. }
  443. void XmlElement::removeAttribute (const String& attributeName) noexcept
  444. {
  445. LinkedListPointer<XmlAttributeNode>* att = &attributes;
  446. while (att->get() != nullptr)
  447. {
  448. if (att->get()->hasName (attributeName))
  449. {
  450. delete att->removeNext();
  451. break;
  452. }
  453. att = &(att->get()->nextListItem);
  454. }
  455. }
  456. void XmlElement::removeAllAttributes() noexcept
  457. {
  458. attributes.deleteAll();
  459. }
  460. //==============================================================================
  461. int XmlElement::getNumChildElements() const noexcept
  462. {
  463. return firstChildElement.size();
  464. }
  465. XmlElement* XmlElement::getChildElement (const int index) const noexcept
  466. {
  467. return firstChildElement [index].get();
  468. }
  469. XmlElement* XmlElement::getChildByName (const String& childName) const noexcept
  470. {
  471. XmlElement* child = firstChildElement;
  472. while (child != nullptr)
  473. {
  474. if (child->hasTagName (childName))
  475. break;
  476. child = child->nextListItem;
  477. }
  478. return child;
  479. }
  480. void XmlElement::addChildElement (XmlElement* const newNode) noexcept
  481. {
  482. if (newNode != nullptr)
  483. firstChildElement.append (newNode);
  484. }
  485. void XmlElement::insertChildElement (XmlElement* const newNode,
  486. int indexToInsertAt) noexcept
  487. {
  488. if (newNode != nullptr)
  489. {
  490. removeChildElement (newNode, false);
  491. firstChildElement.insertAtIndex (indexToInsertAt, newNode);
  492. }
  493. }
  494. XmlElement* XmlElement::createNewChildElement (const String& childTagName)
  495. {
  496. XmlElement* const newElement = new XmlElement (childTagName);
  497. addChildElement (newElement);
  498. return newElement;
  499. }
  500. bool XmlElement::replaceChildElement (XmlElement* const currentChildElement,
  501. XmlElement* const newNode) noexcept
  502. {
  503. if (newNode != nullptr)
  504. {
  505. LinkedListPointer<XmlElement>* const p = firstChildElement.findPointerTo (currentChildElement);
  506. if (p != nullptr)
  507. {
  508. if (currentChildElement != newNode)
  509. delete p->replaceNext (newNode);
  510. return true;
  511. }
  512. }
  513. return false;
  514. }
  515. void XmlElement::removeChildElement (XmlElement* const childToRemove,
  516. const bool shouldDeleteTheChild) noexcept
  517. {
  518. if (childToRemove != nullptr)
  519. {
  520. firstChildElement.remove (childToRemove);
  521. if (shouldDeleteTheChild)
  522. delete childToRemove;
  523. }
  524. }
  525. bool XmlElement::isEquivalentTo (const XmlElement* const other,
  526. const bool ignoreOrderOfAttributes) const noexcept
  527. {
  528. if (this != other)
  529. {
  530. if (other == nullptr || tagName != other->tagName)
  531. return false;
  532. if (ignoreOrderOfAttributes)
  533. {
  534. int totalAtts = 0;
  535. const XmlAttributeNode* att = attributes;
  536. while (att != nullptr)
  537. {
  538. if (! other->compareAttribute (att->name, att->value))
  539. return false;
  540. att = att->nextListItem;
  541. ++totalAtts;
  542. }
  543. if (totalAtts != other->getNumAttributes())
  544. return false;
  545. }
  546. else
  547. {
  548. const XmlAttributeNode* thisAtt = attributes;
  549. const XmlAttributeNode* otherAtt = other->attributes;
  550. for (;;)
  551. {
  552. if (thisAtt == nullptr || otherAtt == nullptr)
  553. {
  554. if (thisAtt == otherAtt) // both 0, so it's a match
  555. break;
  556. return false;
  557. }
  558. if (thisAtt->name != otherAtt->name
  559. || thisAtt->value != otherAtt->value)
  560. {
  561. return false;
  562. }
  563. thisAtt = thisAtt->nextListItem;
  564. otherAtt = otherAtt->nextListItem;
  565. }
  566. }
  567. const XmlElement* thisChild = firstChildElement;
  568. const XmlElement* otherChild = other->firstChildElement;
  569. for (;;)
  570. {
  571. if (thisChild == nullptr || otherChild == nullptr)
  572. {
  573. if (thisChild == otherChild) // both 0, so it's a match
  574. break;
  575. return false;
  576. }
  577. if (! thisChild->isEquivalentTo (otherChild, ignoreOrderOfAttributes))
  578. return false;
  579. thisChild = thisChild->nextListItem;
  580. otherChild = otherChild->nextListItem;
  581. }
  582. }
  583. return true;
  584. }
  585. void XmlElement::deleteAllChildElements() noexcept
  586. {
  587. firstChildElement.deleteAll();
  588. }
  589. void XmlElement::deleteAllChildElementsWithTagName (const String& name) noexcept
  590. {
  591. XmlElement* child = firstChildElement;
  592. while (child != nullptr)
  593. {
  594. XmlElement* const nextChild = child->nextListItem;
  595. if (child->hasTagName (name))
  596. removeChildElement (child, true);
  597. child = nextChild;
  598. }
  599. }
  600. bool XmlElement::containsChildElement (const XmlElement* const possibleChild) const noexcept
  601. {
  602. return firstChildElement.contains (possibleChild);
  603. }
  604. XmlElement* XmlElement::findParentElementOf (const XmlElement* const elementToLookFor) noexcept
  605. {
  606. if (this == elementToLookFor || elementToLookFor == nullptr)
  607. return nullptr;
  608. XmlElement* child = firstChildElement;
  609. while (child != nullptr)
  610. {
  611. if (elementToLookFor == child)
  612. return this;
  613. XmlElement* const found = child->findParentElementOf (elementToLookFor);
  614. if (found != nullptr)
  615. return found;
  616. child = child->nextListItem;
  617. }
  618. return nullptr;
  619. }
  620. void XmlElement::getChildElementsAsArray (XmlElement** elems) const noexcept
  621. {
  622. firstChildElement.copyToArray (elems);
  623. }
  624. void XmlElement::reorderChildElements (XmlElement** const elems, const int num) noexcept
  625. {
  626. XmlElement* e = firstChildElement = elems[0];
  627. for (int i = 1; i < num; ++i)
  628. {
  629. e->nextListItem = elems[i];
  630. e = e->nextListItem;
  631. }
  632. e->nextListItem = nullptr;
  633. }
  634. //==============================================================================
  635. bool XmlElement::isTextElement() const noexcept
  636. {
  637. return tagName.isEmpty();
  638. }
  639. static const String juce_xmltextContentAttributeName ("text");
  640. const String& XmlElement::getText() const noexcept
  641. {
  642. jassert (isTextElement()); // you're trying to get the text from an element that
  643. // isn't actually a text element.. If this contains text sub-nodes, you
  644. // probably want to use getAllSubText instead.
  645. return getStringAttribute (juce_xmltextContentAttributeName);
  646. }
  647. void XmlElement::setText (const String& newText)
  648. {
  649. if (isTextElement())
  650. setAttribute (juce_xmltextContentAttributeName, newText);
  651. else
  652. jassertfalse; // you can only change the text in a text element, not a normal one.
  653. }
  654. String XmlElement::getAllSubText() const
  655. {
  656. if (isTextElement())
  657. return getText();
  658. String result;
  659. String::Concatenator concatenator (result);
  660. const XmlElement* child = firstChildElement;
  661. while (child != nullptr)
  662. {
  663. concatenator.append (child->getAllSubText());
  664. child = child->nextListItem;
  665. }
  666. return result;
  667. }
  668. String XmlElement::getChildElementAllSubText (const String& childTagName,
  669. const String& defaultReturnValue) const
  670. {
  671. const XmlElement* const child = getChildByName (childTagName);
  672. if (child != nullptr)
  673. return child->getAllSubText();
  674. return defaultReturnValue;
  675. }
  676. XmlElement* XmlElement::createTextElement (const String& text)
  677. {
  678. XmlElement* const e = new XmlElement ((int) 0);
  679. e->setAttribute (juce_xmltextContentAttributeName, text);
  680. return e;
  681. }
  682. void XmlElement::addTextElement (const String& text)
  683. {
  684. addChildElement (createTextElement (text));
  685. }
  686. void XmlElement::deleteAllTextElements() noexcept
  687. {
  688. XmlElement* child = firstChildElement;
  689. while (child != nullptr)
  690. {
  691. XmlElement* const next = child->nextListItem;
  692. if (child->isTextElement())
  693. removeChildElement (child, true);
  694. child = next;
  695. }
  696. }
  697. END_JUCE_NAMESPACE