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.

830 lines
25KB

  1. /*
  2. ==============================================================================
  3. This file is part of the juce_core module of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission to use, copy, modify, and/or distribute this software for any purpose with
  6. or without fee is hereby granted, provided that the above copyright notice and this
  7. permission notice appear in all copies.
  8. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
  9. TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
  10. NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  11. DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  12. IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  13. CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. ------------------------------------------------------------------------------
  15. NOTE! This permissive ISC license applies ONLY to files within the juce_core module!
  16. All other JUCE modules are covered by a dual GPL/commercial license, so if you are
  17. using any other modules, be sure to check that you also comply with their license.
  18. For more details, visit www.juce.com
  19. ==============================================================================
  20. */
  21. XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) noexcept
  22. : name (other.name),
  23. value (other.value)
  24. {
  25. }
  26. XmlElement::XmlAttributeNode::XmlAttributeNode (const String& n, const String& v) noexcept
  27. : name (n), value (v)
  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& tag) noexcept
  41. : tagName (tag)
  42. {
  43. // the tag name mustn't be empty, or it'll look like a text element!
  44. jassert (tag.containsNonWhitespaceChars())
  45. // The tag can't contain spaces or other characters that would create invalid XML!
  46. jassert (! tag.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. static 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. static 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. static 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. if (indentationLevel >= 0)
  182. writeSpaces (outputStream, indentationLevel);
  183. if (! isTextElement())
  184. {
  185. outputStream.writeByte ('<');
  186. outputStream << tagName;
  187. {
  188. const int attIndent = indentationLevel + tagName.length() + 1;
  189. int lineLen = 0;
  190. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  191. {
  192. if (lineLen > lineWrapLength && indentationLevel >= 0)
  193. {
  194. outputStream << newLine;
  195. writeSpaces (outputStream, attIndent);
  196. lineLen = 0;
  197. }
  198. const int64 startPos = outputStream.getPosition();
  199. outputStream.writeByte (' ');
  200. outputStream << att->name;
  201. outputStream.write ("=\"", 2);
  202. escapeIllegalXmlChars (outputStream, att->value, true);
  203. outputStream.writeByte ('"');
  204. lineLen += (int) (outputStream.getPosition() - startPos);
  205. }
  206. }
  207. if (firstChildElement != nullptr)
  208. {
  209. outputStream.writeByte ('>');
  210. bool lastWasTextNode = false;
  211. for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem)
  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. }
  227. if (indentationLevel >= 0 && ! lastWasTextNode)
  228. {
  229. outputStream << newLine;
  230. writeSpaces (outputStream, indentationLevel);
  231. }
  232. outputStream.write ("</", 2);
  233. outputStream << tagName;
  234. outputStream.writeByte ('>');
  235. }
  236. else
  237. {
  238. outputStream.write ("/>", 2);
  239. }
  240. }
  241. else
  242. {
  243. escapeIllegalXmlChars (outputStream, getText(), false);
  244. }
  245. }
  246. String XmlElement::createDocument (const String& dtdToUse,
  247. const bool allOnOneLine,
  248. const bool includeXmlHeader,
  249. const String& encodingType,
  250. const int lineWrapLength) const
  251. {
  252. MemoryOutputStream mem (2048);
  253. writeToStream (mem, dtdToUse, allOnOneLine, includeXmlHeader, encodingType, lineWrapLength);
  254. return mem.toUTF8();
  255. }
  256. void XmlElement::writeToStream (OutputStream& output,
  257. const String& dtdToUse,
  258. const bool allOnOneLine,
  259. const bool includeXmlHeader,
  260. const String& encodingType,
  261. const int lineWrapLength) const
  262. {
  263. using namespace XmlOutputFunctions;
  264. if (includeXmlHeader)
  265. {
  266. output << "<?xml version=\"1.0\" encoding=\"" << encodingType << "\"?>";
  267. if (allOnOneLine)
  268. output.writeByte (' ');
  269. else
  270. output << newLine << newLine;
  271. }
  272. if (dtdToUse.isNotEmpty())
  273. {
  274. output << dtdToUse;
  275. if (allOnOneLine)
  276. output.writeByte (' ');
  277. else
  278. output << newLine;
  279. }
  280. writeElementAsText (output, allOnOneLine ? -1 : 0, lineWrapLength);
  281. if (! allOnOneLine)
  282. output << newLine;
  283. }
  284. bool XmlElement::writeToFile (const File& file,
  285. const String& dtdToUse,
  286. const String& encodingType,
  287. const int lineWrapLength) const
  288. {
  289. TemporaryFile tempFile (file);
  290. {
  291. FileOutputStream out (tempFile.getFile());
  292. if (! out.openedOk())
  293. return false;
  294. writeToStream (out, dtdToUse, false, true, encodingType, lineWrapLength);
  295. }
  296. return tempFile.overwriteTargetFileWithTemporary();
  297. }
  298. //==============================================================================
  299. bool XmlElement::hasTagName (const String& possibleTagName) const noexcept
  300. {
  301. const bool matches = tagName.equalsIgnoreCase (possibleTagName);
  302. // XML tags should be case-sensitive, so although this method allows a
  303. // case-insensitive match to pass, you should try to avoid this.
  304. jassert ((! matches) || tagName == possibleTagName);
  305. return matches;
  306. }
  307. String XmlElement::getNamespace() const
  308. {
  309. return tagName.upToFirstOccurrenceOf (":", false, false);
  310. }
  311. String XmlElement::getTagNameWithoutNamespace() const
  312. {
  313. return tagName.fromLastOccurrenceOf (":", false, false);
  314. }
  315. bool XmlElement::hasTagNameIgnoringNamespace (const String& possibleTagName) const
  316. {
  317. return hasTagName (possibleTagName) || getTagNameWithoutNamespace() == possibleTagName;
  318. }
  319. XmlElement* XmlElement::getNextElementWithTagName (const String& requiredTagName) const
  320. {
  321. XmlElement* e = nextListItem;
  322. while (e != nullptr && ! e->hasTagName (requiredTagName))
  323. e = e->nextListItem;
  324. return e;
  325. }
  326. //==============================================================================
  327. int XmlElement::getNumAttributes() const noexcept
  328. {
  329. return attributes.size();
  330. }
  331. const String& XmlElement::getAttributeName (const int index) const noexcept
  332. {
  333. const XmlAttributeNode* const att = attributes [index];
  334. return att != nullptr ? att->name : String::empty;
  335. }
  336. const String& XmlElement::getAttributeValue (const int index) const noexcept
  337. {
  338. const XmlAttributeNode* const att = attributes [index];
  339. return att != nullptr ? att->value : String::empty;
  340. }
  341. bool XmlElement::hasAttribute (const String& attributeName) const noexcept
  342. {
  343. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  344. if (att->hasName (attributeName))
  345. return true;
  346. return false;
  347. }
  348. //==============================================================================
  349. const String& XmlElement::getStringAttribute (const String& attributeName) const noexcept
  350. {
  351. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  352. if (att->hasName (attributeName))
  353. return att->value;
  354. return String::empty;
  355. }
  356. String XmlElement::getStringAttribute (const String& attributeName, const String& defaultReturnValue) const
  357. {
  358. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  359. if (att->hasName (attributeName))
  360. return att->value;
  361. return defaultReturnValue;
  362. }
  363. int XmlElement::getIntAttribute (const String& attributeName, const int defaultReturnValue) const
  364. {
  365. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  366. if (att->hasName (attributeName))
  367. return att->value.getIntValue();
  368. return defaultReturnValue;
  369. }
  370. double XmlElement::getDoubleAttribute (const String& attributeName, const double defaultReturnValue) const
  371. {
  372. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  373. if (att->hasName (attributeName))
  374. return att->value.getDoubleValue();
  375. return defaultReturnValue;
  376. }
  377. bool XmlElement::getBoolAttribute (const String& attributeName, const bool defaultReturnValue) const
  378. {
  379. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  380. {
  381. if (att->hasName (attributeName))
  382. {
  383. juce_wchar firstChar = att->value[0];
  384. if (CharacterFunctions::isWhitespace (firstChar))
  385. firstChar = att->value.trimStart() [0];
  386. return firstChar == '1'
  387. || firstChar == 't'
  388. || firstChar == 'y'
  389. || firstChar == 'T'
  390. || firstChar == 'Y';
  391. }
  392. }
  393. return defaultReturnValue;
  394. }
  395. bool XmlElement::compareAttribute (const String& attributeName,
  396. const String& stringToCompareAgainst,
  397. const bool ignoreCase) const noexcept
  398. {
  399. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  400. if (att->hasName (attributeName))
  401. return ignoreCase ? att->value.equalsIgnoreCase (stringToCompareAgainst)
  402. : att->value == stringToCompareAgainst;
  403. return false;
  404. }
  405. //==============================================================================
  406. void XmlElement::setAttribute (const String& attributeName, const String& value)
  407. {
  408. if (attributes == nullptr)
  409. {
  410. attributes = new XmlAttributeNode (attributeName, value);
  411. }
  412. else
  413. {
  414. for (XmlAttributeNode* att = attributes; ; att = att->nextListItem)
  415. {
  416. if (att->hasName (attributeName))
  417. {
  418. att->value = value;
  419. break;
  420. }
  421. if (att->nextListItem == nullptr)
  422. {
  423. att->nextListItem = new XmlAttributeNode (attributeName, value);
  424. break;
  425. }
  426. }
  427. }
  428. }
  429. void XmlElement::setAttribute (const String& attributeName, const int number)
  430. {
  431. setAttribute (attributeName, String (number));
  432. }
  433. void XmlElement::setAttribute (const String& attributeName, const double number)
  434. {
  435. setAttribute (attributeName, String (number));
  436. }
  437. void XmlElement::removeAttribute (const String& attributeName) noexcept
  438. {
  439. for (LinkedListPointer<XmlAttributeNode>* att = &attributes;
  440. att->get() != nullptr;
  441. att = &(att->get()->nextListItem))
  442. {
  443. if (att->get()->hasName (attributeName))
  444. {
  445. delete att->removeNext();
  446. break;
  447. }
  448. }
  449. }
  450. void XmlElement::removeAllAttributes() noexcept
  451. {
  452. attributes.deleteAll();
  453. }
  454. //==============================================================================
  455. int XmlElement::getNumChildElements() const noexcept
  456. {
  457. return firstChildElement.size();
  458. }
  459. XmlElement* XmlElement::getChildElement (const int index) const noexcept
  460. {
  461. return firstChildElement [index].get();
  462. }
  463. XmlElement* XmlElement::getChildByName (const String& childName) const noexcept
  464. {
  465. for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem)
  466. if (child->hasTagName (childName))
  467. return child;
  468. return nullptr;
  469. }
  470. void XmlElement::addChildElement (XmlElement* const newNode) noexcept
  471. {
  472. if (newNode != nullptr)
  473. firstChildElement.append (newNode);
  474. }
  475. void XmlElement::insertChildElement (XmlElement* const newNode,
  476. int indexToInsertAt) noexcept
  477. {
  478. if (newNode != nullptr)
  479. {
  480. removeChildElement (newNode, false);
  481. firstChildElement.insertAtIndex (indexToInsertAt, newNode);
  482. }
  483. }
  484. XmlElement* XmlElement::createNewChildElement (const String& childTagName)
  485. {
  486. XmlElement* const newElement = new XmlElement (childTagName);
  487. addChildElement (newElement);
  488. return newElement;
  489. }
  490. bool XmlElement::replaceChildElement (XmlElement* const currentChildElement,
  491. XmlElement* const newNode) noexcept
  492. {
  493. if (newNode != nullptr)
  494. {
  495. if (LinkedListPointer<XmlElement>* const p = firstChildElement.findPointerTo (currentChildElement))
  496. {
  497. if (currentChildElement != newNode)
  498. delete p->replaceNext (newNode);
  499. return true;
  500. }
  501. }
  502. return false;
  503. }
  504. void XmlElement::removeChildElement (XmlElement* const childToRemove,
  505. const bool shouldDeleteTheChild) noexcept
  506. {
  507. if (childToRemove != nullptr)
  508. {
  509. firstChildElement.remove (childToRemove);
  510. if (shouldDeleteTheChild)
  511. delete childToRemove;
  512. }
  513. }
  514. bool XmlElement::isEquivalentTo (const XmlElement* const other,
  515. const bool ignoreOrderOfAttributes) const noexcept
  516. {
  517. if (this != other)
  518. {
  519. if (other == nullptr || tagName != other->tagName)
  520. return false;
  521. if (ignoreOrderOfAttributes)
  522. {
  523. int totalAtts = 0;
  524. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  525. {
  526. if (! other->compareAttribute (att->name, att->value))
  527. return false;
  528. ++totalAtts;
  529. }
  530. if (totalAtts != other->getNumAttributes())
  531. return false;
  532. }
  533. else
  534. {
  535. const XmlAttributeNode* thisAtt = attributes;
  536. const XmlAttributeNode* otherAtt = other->attributes;
  537. for (;;)
  538. {
  539. if (thisAtt == nullptr || otherAtt == nullptr)
  540. {
  541. if (thisAtt == otherAtt) // both 0, so it's a match
  542. break;
  543. return false;
  544. }
  545. if (thisAtt->name != otherAtt->name
  546. || thisAtt->value != otherAtt->value)
  547. {
  548. return false;
  549. }
  550. thisAtt = thisAtt->nextListItem;
  551. otherAtt = otherAtt->nextListItem;
  552. }
  553. }
  554. const XmlElement* thisChild = firstChildElement;
  555. const XmlElement* otherChild = other->firstChildElement;
  556. for (;;)
  557. {
  558. if (thisChild == nullptr || otherChild == nullptr)
  559. {
  560. if (thisChild == otherChild) // both 0, so it's a match
  561. break;
  562. return false;
  563. }
  564. if (! thisChild->isEquivalentTo (otherChild, ignoreOrderOfAttributes))
  565. return false;
  566. thisChild = thisChild->nextListItem;
  567. otherChild = otherChild->nextListItem;
  568. }
  569. }
  570. return true;
  571. }
  572. void XmlElement::deleteAllChildElements() noexcept
  573. {
  574. firstChildElement.deleteAll();
  575. }
  576. void XmlElement::deleteAllChildElementsWithTagName (const String& name) noexcept
  577. {
  578. for (XmlElement* child = firstChildElement; child != nullptr;)
  579. {
  580. XmlElement* const nextChild = child->nextListItem;
  581. if (child->hasTagName (name))
  582. removeChildElement (child, true);
  583. child = nextChild;
  584. }
  585. }
  586. bool XmlElement::containsChildElement (const XmlElement* const possibleChild) const noexcept
  587. {
  588. return firstChildElement.contains (possibleChild);
  589. }
  590. XmlElement* XmlElement::findParentElementOf (const XmlElement* const elementToLookFor) noexcept
  591. {
  592. if (this == elementToLookFor || elementToLookFor == nullptr)
  593. return nullptr;
  594. for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem)
  595. {
  596. if (elementToLookFor == child)
  597. return this;
  598. if (XmlElement* const found = child->findParentElementOf (elementToLookFor))
  599. return found;
  600. }
  601. return nullptr;
  602. }
  603. void XmlElement::getChildElementsAsArray (XmlElement** elems) const noexcept
  604. {
  605. firstChildElement.copyToArray (elems);
  606. }
  607. void XmlElement::reorderChildElements (XmlElement** const elems, const int num) noexcept
  608. {
  609. XmlElement* e = firstChildElement = elems[0];
  610. for (int i = 1; i < num; ++i)
  611. {
  612. e->nextListItem = elems[i];
  613. e = e->nextListItem;
  614. }
  615. e->nextListItem = nullptr;
  616. }
  617. //==============================================================================
  618. bool XmlElement::isTextElement() const noexcept
  619. {
  620. return tagName.isEmpty();
  621. }
  622. static const String juce_xmltextContentAttributeName ("text");
  623. const String& XmlElement::getText() const noexcept
  624. {
  625. jassert (isTextElement()); // you're trying to get the text from an element that
  626. // isn't actually a text element.. If this contains text sub-nodes, you
  627. // probably want to use getAllSubText instead.
  628. return getStringAttribute (juce_xmltextContentAttributeName);
  629. }
  630. void XmlElement::setText (const String& newText)
  631. {
  632. if (isTextElement())
  633. setAttribute (juce_xmltextContentAttributeName, newText);
  634. else
  635. jassertfalse; // you can only change the text in a text element, not a normal one.
  636. }
  637. String XmlElement::getAllSubText() const
  638. {
  639. if (isTextElement())
  640. return getText();
  641. MemoryOutputStream mem (1024);
  642. for (const XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem)
  643. mem << child->getAllSubText();
  644. return mem.toString();
  645. }
  646. String XmlElement::getChildElementAllSubText (const String& childTagName,
  647. const String& defaultReturnValue) const
  648. {
  649. if (const XmlElement* const child = getChildByName (childTagName))
  650. return child->getAllSubText();
  651. return defaultReturnValue;
  652. }
  653. XmlElement* XmlElement::createTextElement (const String& text)
  654. {
  655. XmlElement* const e = new XmlElement ((int) 0);
  656. e->setAttribute (juce_xmltextContentAttributeName, text);
  657. return e;
  658. }
  659. void XmlElement::addTextElement (const String& text)
  660. {
  661. addChildElement (createTextElement (text));
  662. }
  663. void XmlElement::deleteAllTextElements() noexcept
  664. {
  665. for (XmlElement* child = firstChildElement; child != nullptr;)
  666. {
  667. XmlElement* const next = child->nextListItem;
  668. if (child->isTextElement())
  669. removeChildElement (child, true);
  670. child = next;
  671. }
  672. }