Audio plugin host https://kx.studio/carla
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.

946 lines
28KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2016 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of the ISC license
  6. http://www.isc.org/downloads/software-support-policy/isc-license/
  7. Permission to use, copy, modify, and/or distribute this software for any
  8. purpose with or without fee is hereby granted, provided that the above
  9. copyright notice and this permission notice appear in all copies.
  10. THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
  11. TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  12. FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
  13. OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
  14. USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  15. TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  16. OF THIS SOFTWARE.
  17. -----------------------------------------------------------------------------
  18. To release a closed-source product which uses other parts of JUCE not
  19. licensed under the ISC terms, commercial licenses are available: visit
  20. www.juce.com for more information.
  21. ==============================================================================
  22. */
  23. #include "juce_XmlElement.h"
  24. #include "../streams/juce_MemoryOutputStream.h"
  25. #include "../streams/juce_OutputStream.h"
  26. #include "../text/juce_NewLine.h"
  27. #include "../text/juce_StringPool.h"
  28. namespace water {
  29. inline bool isValidXmlNameStartCharacter (const juce_wchar character) noexcept
  30. {
  31. return character == ':'
  32. || character == '_'
  33. || (character >= 'a' && character <= 'z')
  34. || (character >= 'A' && character <= 'Z')
  35. || (character >= 0xc0 && character <= 0xd6)
  36. || (character >= 0xd8 && character <= 0xf6)
  37. || (character >= 0xf8 && character <= 0x2ff)
  38. || (character >= 0x370 && character <= 0x37d)
  39. || (character >= 0x37f && character <= 0x1fff)
  40. || (character >= 0x200c && character <= 0x200d)
  41. || (character >= 0x2070 && character <= 0x218f)
  42. || (character >= 0x2c00 && character <= 0x2fef)
  43. || (character >= 0x3001 && character <= 0xd7ff)
  44. || (character >= 0xf900 && character <= 0xfdcf)
  45. || (character >= 0xfdf0 && character <= 0xfffd)
  46. || (character >= 0x10000 && character <= 0xeffff);
  47. }
  48. inline bool isValidXmlNameBodyCharacter (const juce_wchar character) noexcept
  49. {
  50. return isValidXmlNameStartCharacter (character)
  51. || character == '-'
  52. || character == '.'
  53. || character == 0xb7
  54. || (character >= '0' && character <= '9')
  55. || (character >= 0x300 && character <= 0x036f)
  56. || (character >= 0x203f && character <= 0x2040);
  57. }
  58. XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) noexcept
  59. : name (other.name),
  60. value (other.value)
  61. {
  62. }
  63. XmlElement::XmlAttributeNode::XmlAttributeNode (const Identifier& n, const String& v) noexcept
  64. : name (n), value (v)
  65. {
  66. jassert (isValidXmlName (name));
  67. }
  68. XmlElement::XmlAttributeNode::XmlAttributeNode (String::CharPointerType nameStart, String::CharPointerType nameEnd)
  69. : name (nameStart, nameEnd)
  70. {
  71. jassert (isValidXmlName (name));
  72. }
  73. //==============================================================================
  74. XmlElement::XmlElement (const String& tag)
  75. : tagName (StringPool::getGlobalPool().getPooledString (tag))
  76. {
  77. jassert (isValidXmlName (tagName));
  78. }
  79. XmlElement::XmlElement (const char* tag)
  80. : tagName (StringPool::getGlobalPool().getPooledString (tag))
  81. {
  82. jassert (isValidXmlName (tagName));
  83. }
  84. XmlElement::XmlElement (StringRef tag)
  85. : tagName (StringPool::getGlobalPool().getPooledString (tag))
  86. {
  87. jassert (isValidXmlName (tagName));
  88. }
  89. XmlElement::XmlElement (const Identifier& tag)
  90. : tagName (tag.toString())
  91. {
  92. jassert (isValidXmlName (tagName));
  93. }
  94. XmlElement::XmlElement (String::CharPointerType tagNameStart, String::CharPointerType tagNameEnd)
  95. : tagName (StringPool::getGlobalPool().getPooledString (tagNameStart, tagNameEnd))
  96. {
  97. jassert (isValidXmlName (tagName));
  98. }
  99. XmlElement::XmlElement (int /*dummy*/) noexcept
  100. {
  101. }
  102. XmlElement::XmlElement (const XmlElement& other)
  103. : tagName (other.tagName)
  104. {
  105. copyChildrenAndAttributesFrom (other);
  106. }
  107. XmlElement& XmlElement::operator= (const XmlElement& other)
  108. {
  109. if (this != &other)
  110. {
  111. removeAllAttributes();
  112. deleteAllChildElements();
  113. tagName = other.tagName;
  114. copyChildrenAndAttributesFrom (other);
  115. }
  116. return *this;
  117. }
  118. #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
  119. XmlElement::XmlElement (XmlElement&& other) noexcept
  120. : nextListItem (static_cast<LinkedListPointer<XmlElement>&&> (other.nextListItem)),
  121. firstChildElement (static_cast<LinkedListPointer<XmlElement>&&> (other.firstChildElement)),
  122. attributes (static_cast<LinkedListPointer<XmlAttributeNode>&&> (other.attributes)),
  123. tagName (static_cast<String&&> (other.tagName))
  124. {
  125. }
  126. XmlElement& XmlElement::operator= (XmlElement&& other) noexcept
  127. {
  128. jassert (this != &other); // hopefully the compiler should make this situation impossible!
  129. removeAllAttributes();
  130. deleteAllChildElements();
  131. nextListItem = static_cast<LinkedListPointer<XmlElement>&&> (other.nextListItem);
  132. firstChildElement = static_cast<LinkedListPointer<XmlElement>&&> (other.firstChildElement);
  133. attributes = static_cast<LinkedListPointer<XmlAttributeNode>&&> (other.attributes);
  134. tagName = static_cast<String&&> (other.tagName);
  135. return *this;
  136. }
  137. #endif
  138. void XmlElement::copyChildrenAndAttributesFrom (const XmlElement& other)
  139. {
  140. jassert (firstChildElement.get() == nullptr);
  141. firstChildElement.addCopyOfList (other.firstChildElement);
  142. jassert (attributes.get() == nullptr);
  143. attributes.addCopyOfList (other.attributes);
  144. }
  145. XmlElement::~XmlElement() noexcept
  146. {
  147. firstChildElement.deleteAll();
  148. attributes.deleteAll();
  149. }
  150. //==============================================================================
  151. namespace XmlOutputFunctions
  152. {
  153. #if 0 // (These functions are just used to generate the lookup table used below)
  154. bool isLegalXmlCharSlow (const juce_wchar character) noexcept
  155. {
  156. if ((character >= 'a' && character <= 'z')
  157. || (character >= 'A' && character <= 'Z')
  158. || (character >= '0' && character <= '9'))
  159. return true;
  160. const char* t = " .,;:-()_+=?!'#@[]/\\*%~{}$|";
  161. do
  162. {
  163. if (((juce_wchar) (uint8) *t) == character)
  164. return true;
  165. }
  166. while (*++t != 0);
  167. return false;
  168. }
  169. void generateLegalCharLookupTable()
  170. {
  171. uint8 n[32] = { 0 };
  172. for (int i = 0; i < 256; ++i)
  173. if (isLegalXmlCharSlow (i))
  174. n[i >> 3] |= (1 << (i & 7));
  175. String s;
  176. for (int i = 0; i < 32; ++i)
  177. s << (int) n[i] << ", ";
  178. DBG (s);
  179. }
  180. #endif
  181. static bool isLegalXmlChar (const uint32 c) noexcept
  182. {
  183. static const unsigned char legalChars[] = { 0, 0, 0, 0, 187, 255, 255, 175, 255,
  184. 255, 255, 191, 254, 255, 255, 127 };
  185. return c < sizeof (legalChars) * 8
  186. && (legalChars [c >> 3] & (1 << (c & 7))) != 0;
  187. }
  188. static void escapeIllegalXmlChars (OutputStream& outputStream, const String& text, const bool changeNewLines)
  189. {
  190. String::CharPointerType t (text.getCharPointer());
  191. for (;;)
  192. {
  193. const uint32 character = (uint32) t.getAndAdvance();
  194. if (character == 0)
  195. break;
  196. if (isLegalXmlChar (character))
  197. {
  198. outputStream << (char) character;
  199. }
  200. else
  201. {
  202. switch (character)
  203. {
  204. case '&': outputStream << "&amp;"; break;
  205. case '"': outputStream << "&quot;"; break;
  206. case '>': outputStream << "&gt;"; break;
  207. case '<': outputStream << "&lt;"; break;
  208. case '\n':
  209. case '\r':
  210. if (! changeNewLines)
  211. {
  212. outputStream << (char) character;
  213. break;
  214. }
  215. // fall-through
  216. default:
  217. outputStream << "&#" << ((int) character) << ';';
  218. break;
  219. }
  220. }
  221. }
  222. }
  223. static void writeSpaces (OutputStream& out, const size_t numSpaces)
  224. {
  225. out.writeRepeatedByte (' ', numSpaces);
  226. }
  227. }
  228. void XmlElement::writeElementAsText (OutputStream& outputStream,
  229. const int indentationLevel,
  230. const int lineWrapLength) const
  231. {
  232. using namespace XmlOutputFunctions;
  233. if (indentationLevel >= 0)
  234. writeSpaces (outputStream, (size_t) indentationLevel);
  235. if (! isTextElement())
  236. {
  237. outputStream.writeByte ('<');
  238. outputStream << tagName;
  239. {
  240. const size_t attIndent = (size_t) (indentationLevel + tagName.length() + 1);
  241. int lineLen = 0;
  242. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  243. {
  244. if (lineLen > lineWrapLength && indentationLevel >= 0)
  245. {
  246. outputStream << newLine;
  247. writeSpaces (outputStream, attIndent);
  248. lineLen = 0;
  249. }
  250. const int64 startPos = outputStream.getPosition();
  251. outputStream.writeByte (' ');
  252. outputStream << att->name;
  253. outputStream.write ("=\"", 2);
  254. escapeIllegalXmlChars (outputStream, att->value, true);
  255. outputStream.writeByte ('"');
  256. lineLen += (int) (outputStream.getPosition() - startPos);
  257. }
  258. }
  259. if (firstChildElement != nullptr)
  260. {
  261. outputStream.writeByte ('>');
  262. bool lastWasTextNode = false;
  263. for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem)
  264. {
  265. if (child->isTextElement())
  266. {
  267. escapeIllegalXmlChars (outputStream, child->getText(), false);
  268. lastWasTextNode = true;
  269. }
  270. else
  271. {
  272. if (indentationLevel >= 0 && ! lastWasTextNode)
  273. outputStream << newLine;
  274. child->writeElementAsText (outputStream,
  275. lastWasTextNode ? 0 : (indentationLevel + (indentationLevel >= 0 ? 2 : 0)), lineWrapLength);
  276. lastWasTextNode = false;
  277. }
  278. }
  279. if (indentationLevel >= 0 && ! lastWasTextNode)
  280. {
  281. outputStream << newLine;
  282. writeSpaces (outputStream, (size_t) indentationLevel);
  283. }
  284. outputStream.write ("</", 2);
  285. outputStream << tagName;
  286. outputStream.writeByte ('>');
  287. }
  288. else
  289. {
  290. outputStream.write ("/>", 2);
  291. }
  292. }
  293. else
  294. {
  295. escapeIllegalXmlChars (outputStream, getText(), false);
  296. }
  297. }
  298. String XmlElement::createDocument (StringRef dtdToUse,
  299. const bool allOnOneLine,
  300. const bool includeXmlHeader,
  301. StringRef encodingType,
  302. const int lineWrapLength) const
  303. {
  304. MemoryOutputStream mem (2048);
  305. writeToStream (mem, dtdToUse, allOnOneLine, includeXmlHeader, encodingType, lineWrapLength);
  306. return mem.toUTF8();
  307. }
  308. void XmlElement::writeToStream (OutputStream& output,
  309. StringRef dtdToUse,
  310. const bool allOnOneLine,
  311. const bool includeXmlHeader,
  312. StringRef encodingType,
  313. const int lineWrapLength) const
  314. {
  315. using namespace XmlOutputFunctions;
  316. if (includeXmlHeader)
  317. {
  318. output << "<?xml version=\"1.0\" encoding=\"" << encodingType << "\"?>";
  319. if (allOnOneLine)
  320. output.writeByte (' ');
  321. else
  322. output << newLine << newLine;
  323. }
  324. if (dtdToUse.isNotEmpty())
  325. {
  326. output << dtdToUse;
  327. if (allOnOneLine)
  328. output.writeByte (' ');
  329. else
  330. output << newLine;
  331. }
  332. writeElementAsText (output, allOnOneLine ? -1 : 0, lineWrapLength);
  333. if (! allOnOneLine)
  334. output << newLine;
  335. }
  336. #if 0
  337. bool XmlElement::writeToFile (const File& file,
  338. StringRef dtdToUse,
  339. StringRef encodingType,
  340. const int lineWrapLength) const
  341. {
  342. TemporaryFile tempFile (file);
  343. {
  344. FileOutputStream out (tempFile.getFile());
  345. if (! out.openedOk())
  346. return false;
  347. writeToStream (out, dtdToUse, false, true, encodingType, lineWrapLength);
  348. out.flush(); // (called explicitly to force an fsync on posix)
  349. if (out.getStatus().failed())
  350. return false;
  351. }
  352. return tempFile.overwriteTargetFileWithTemporary();
  353. }
  354. #endif
  355. //==============================================================================
  356. bool XmlElement::hasTagName (StringRef possibleTagName) const noexcept
  357. {
  358. const bool matches = tagName.equalsIgnoreCase (possibleTagName);
  359. // XML tags should be case-sensitive, so although this method allows a
  360. // case-insensitive match to pass, you should try to avoid this.
  361. jassert ((! matches) || tagName == possibleTagName);
  362. return matches;
  363. }
  364. String XmlElement::getNamespace() const
  365. {
  366. return tagName.upToFirstOccurrenceOf (":", false, false);
  367. }
  368. String XmlElement::getTagNameWithoutNamespace() const
  369. {
  370. return tagName.fromLastOccurrenceOf (":", false, false);
  371. }
  372. bool XmlElement::hasTagNameIgnoringNamespace (StringRef possibleTagName) const
  373. {
  374. return hasTagName (possibleTagName) || getTagNameWithoutNamespace() == possibleTagName;
  375. }
  376. XmlElement* XmlElement::getNextElementWithTagName (StringRef requiredTagName) const
  377. {
  378. XmlElement* e = nextListItem;
  379. while (e != nullptr && ! e->hasTagName (requiredTagName))
  380. e = e->nextListItem;
  381. return e;
  382. }
  383. //==============================================================================
  384. int XmlElement::getNumAttributes() const noexcept
  385. {
  386. return attributes.size();
  387. }
  388. static const String& getEmptyStringRef() noexcept
  389. {
  390. static String empty;
  391. return empty;
  392. }
  393. const String& XmlElement::getAttributeName (const int index) const noexcept
  394. {
  395. if (const XmlAttributeNode* const att = attributes [index])
  396. return att->name.toString();
  397. return getEmptyStringRef();
  398. }
  399. const String& XmlElement::getAttributeValue (const int index) const noexcept
  400. {
  401. if (const XmlAttributeNode* const att = attributes [index])
  402. return att->value;
  403. return getEmptyStringRef();
  404. }
  405. XmlElement::XmlAttributeNode* XmlElement::getAttribute (StringRef attributeName) const noexcept
  406. {
  407. for (XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  408. if (att->name == attributeName)
  409. return att;
  410. return nullptr;
  411. }
  412. bool XmlElement::hasAttribute (StringRef attributeName) const noexcept
  413. {
  414. return getAttribute (attributeName) != nullptr;
  415. }
  416. //==============================================================================
  417. const String& XmlElement::getStringAttribute (StringRef attributeName) const noexcept
  418. {
  419. if (const XmlAttributeNode* att = getAttribute (attributeName))
  420. return att->value;
  421. return getEmptyStringRef();
  422. }
  423. String XmlElement::getStringAttribute (StringRef attributeName, const String& defaultReturnValue) const
  424. {
  425. if (const XmlAttributeNode* att = getAttribute (attributeName))
  426. return att->value;
  427. return defaultReturnValue;
  428. }
  429. int XmlElement::getIntAttribute (StringRef attributeName, const int defaultReturnValue) const
  430. {
  431. if (const XmlAttributeNode* att = getAttribute (attributeName))
  432. return att->value.getIntValue();
  433. return defaultReturnValue;
  434. }
  435. double XmlElement::getDoubleAttribute (StringRef attributeName, const double defaultReturnValue) const
  436. {
  437. if (const XmlAttributeNode* att = getAttribute (attributeName))
  438. return att->value.getDoubleValue();
  439. return defaultReturnValue;
  440. }
  441. bool XmlElement::getBoolAttribute (StringRef attributeName, const bool defaultReturnValue) const
  442. {
  443. if (const XmlAttributeNode* att = getAttribute (attributeName))
  444. {
  445. const juce_wchar firstChar = *(att->value.getCharPointer().findEndOfWhitespace());
  446. return firstChar == '1'
  447. || firstChar == 't'
  448. || firstChar == 'y'
  449. || firstChar == 'T'
  450. || firstChar == 'Y';
  451. }
  452. return defaultReturnValue;
  453. }
  454. bool XmlElement::compareAttribute (StringRef attributeName,
  455. StringRef stringToCompareAgainst,
  456. const bool ignoreCase) const noexcept
  457. {
  458. if (const XmlAttributeNode* att = getAttribute (attributeName))
  459. return ignoreCase ? att->value.equalsIgnoreCase (stringToCompareAgainst)
  460. : att->value == stringToCompareAgainst;
  461. return false;
  462. }
  463. //==============================================================================
  464. void XmlElement::setAttribute (const Identifier& attributeName, const String& value)
  465. {
  466. if (attributes == nullptr)
  467. {
  468. attributes = new XmlAttributeNode (attributeName, value);
  469. }
  470. else
  471. {
  472. for (XmlAttributeNode* att = attributes; ; att = att->nextListItem)
  473. {
  474. if (att->name == attributeName)
  475. {
  476. att->value = value;
  477. break;
  478. }
  479. if (att->nextListItem == nullptr)
  480. {
  481. att->nextListItem = new XmlAttributeNode (attributeName, value);
  482. break;
  483. }
  484. }
  485. }
  486. }
  487. void XmlElement::setAttribute (const Identifier& attributeName, const int number)
  488. {
  489. setAttribute (attributeName, String (number));
  490. }
  491. void XmlElement::setAttribute (const Identifier& attributeName, const double number)
  492. {
  493. setAttribute (attributeName, String (number, 20));
  494. }
  495. void XmlElement::removeAttribute (const Identifier& attributeName) noexcept
  496. {
  497. for (LinkedListPointer<XmlAttributeNode>* att = &attributes;
  498. att->get() != nullptr;
  499. att = &(att->get()->nextListItem))
  500. {
  501. if (att->get()->name == attributeName)
  502. {
  503. delete att->removeNext();
  504. break;
  505. }
  506. }
  507. }
  508. void XmlElement::removeAllAttributes() noexcept
  509. {
  510. attributes.deleteAll();
  511. }
  512. //==============================================================================
  513. int XmlElement::getNumChildElements() const noexcept
  514. {
  515. return firstChildElement.size();
  516. }
  517. XmlElement* XmlElement::getChildElement (const int index) const noexcept
  518. {
  519. return firstChildElement [index].get();
  520. }
  521. XmlElement* XmlElement::getChildByName (StringRef childName) const noexcept
  522. {
  523. jassert (! childName.isEmpty());
  524. for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem)
  525. if (child->hasTagName (childName))
  526. return child;
  527. return nullptr;
  528. }
  529. XmlElement* XmlElement::getChildByAttribute (StringRef attributeName, StringRef attributeValue) const noexcept
  530. {
  531. jassert (! attributeName.isEmpty());
  532. for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem)
  533. if (child->compareAttribute (attributeName, attributeValue))
  534. return child;
  535. return nullptr;
  536. }
  537. void XmlElement::addChildElement (XmlElement* const newNode) noexcept
  538. {
  539. if (newNode != nullptr)
  540. {
  541. // The element being added must not be a child of another node!
  542. jassert (newNode->nextListItem == nullptr);
  543. firstChildElement.append (newNode);
  544. }
  545. }
  546. void XmlElement::insertChildElement (XmlElement* const newNode, int indexToInsertAt) noexcept
  547. {
  548. if (newNode != nullptr)
  549. {
  550. // The element being added must not be a child of another node!
  551. jassert (newNode->nextListItem == nullptr);
  552. firstChildElement.insertAtIndex (indexToInsertAt, newNode);
  553. }
  554. }
  555. void XmlElement::prependChildElement (XmlElement* newNode) noexcept
  556. {
  557. if (newNode != nullptr)
  558. {
  559. // The element being added must not be a child of another node!
  560. jassert (newNode->nextListItem == nullptr);
  561. firstChildElement.insertNext (newNode);
  562. }
  563. }
  564. XmlElement* XmlElement::createNewChildElement (StringRef childTagName)
  565. {
  566. XmlElement* const newElement = new XmlElement (childTagName);
  567. addChildElement (newElement);
  568. return newElement;
  569. }
  570. bool XmlElement::replaceChildElement (XmlElement* const currentChildElement,
  571. XmlElement* const newNode) noexcept
  572. {
  573. if (newNode != nullptr)
  574. {
  575. if (LinkedListPointer<XmlElement>* const p = firstChildElement.findPointerTo (currentChildElement))
  576. {
  577. if (currentChildElement != newNode)
  578. delete p->replaceNext (newNode);
  579. return true;
  580. }
  581. }
  582. return false;
  583. }
  584. void XmlElement::removeChildElement (XmlElement* const childToRemove,
  585. const bool shouldDeleteTheChild) noexcept
  586. {
  587. if (childToRemove != nullptr)
  588. {
  589. firstChildElement.remove (childToRemove);
  590. if (shouldDeleteTheChild)
  591. delete childToRemove;
  592. }
  593. }
  594. bool XmlElement::isEquivalentTo (const XmlElement* const other,
  595. const bool ignoreOrderOfAttributes) const noexcept
  596. {
  597. if (this != other)
  598. {
  599. if (other == nullptr || tagName != other->tagName)
  600. return false;
  601. if (ignoreOrderOfAttributes)
  602. {
  603. int totalAtts = 0;
  604. for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem)
  605. {
  606. if (! other->compareAttribute (att->name, att->value))
  607. return false;
  608. ++totalAtts;
  609. }
  610. if (totalAtts != other->getNumAttributes())
  611. return false;
  612. }
  613. else
  614. {
  615. const XmlAttributeNode* thisAtt = attributes;
  616. const XmlAttributeNode* otherAtt = other->attributes;
  617. for (;;)
  618. {
  619. if (thisAtt == nullptr || otherAtt == nullptr)
  620. {
  621. if (thisAtt == otherAtt) // both nullptr, so it's a match
  622. break;
  623. return false;
  624. }
  625. if (thisAtt->name != otherAtt->name
  626. || thisAtt->value != otherAtt->value)
  627. {
  628. return false;
  629. }
  630. thisAtt = thisAtt->nextListItem;
  631. otherAtt = otherAtt->nextListItem;
  632. }
  633. }
  634. const XmlElement* thisChild = firstChildElement;
  635. const XmlElement* otherChild = other->firstChildElement;
  636. for (;;)
  637. {
  638. if (thisChild == nullptr || otherChild == nullptr)
  639. {
  640. if (thisChild == otherChild) // both 0, so it's a match
  641. break;
  642. return false;
  643. }
  644. if (! thisChild->isEquivalentTo (otherChild, ignoreOrderOfAttributes))
  645. return false;
  646. thisChild = thisChild->nextListItem;
  647. otherChild = otherChild->nextListItem;
  648. }
  649. }
  650. return true;
  651. }
  652. void XmlElement::deleteAllChildElements() noexcept
  653. {
  654. firstChildElement.deleteAll();
  655. }
  656. void XmlElement::deleteAllChildElementsWithTagName (StringRef name) noexcept
  657. {
  658. for (XmlElement* child = firstChildElement; child != nullptr;)
  659. {
  660. XmlElement* const nextChild = child->nextListItem;
  661. if (child->hasTagName (name))
  662. removeChildElement (child, true);
  663. child = nextChild;
  664. }
  665. }
  666. bool XmlElement::containsChildElement (const XmlElement* const possibleChild) const noexcept
  667. {
  668. return firstChildElement.contains (possibleChild);
  669. }
  670. XmlElement* XmlElement::findParentElementOf (const XmlElement* const elementToLookFor) noexcept
  671. {
  672. if (this == elementToLookFor || elementToLookFor == nullptr)
  673. return nullptr;
  674. for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem)
  675. {
  676. if (elementToLookFor == child)
  677. return this;
  678. if (XmlElement* const found = child->findParentElementOf (elementToLookFor))
  679. return found;
  680. }
  681. return nullptr;
  682. }
  683. void XmlElement::getChildElementsAsArray (XmlElement** elems) const noexcept
  684. {
  685. firstChildElement.copyToArray (elems);
  686. }
  687. void XmlElement::reorderChildElements (XmlElement** const elems, const int num) noexcept
  688. {
  689. XmlElement* e = firstChildElement = elems[0];
  690. for (int i = 1; i < num; ++i)
  691. {
  692. e->nextListItem = elems[i];
  693. e = e->nextListItem;
  694. }
  695. e->nextListItem = nullptr;
  696. }
  697. //==============================================================================
  698. bool XmlElement::isTextElement() const noexcept
  699. {
  700. return tagName.isEmpty();
  701. }
  702. static const String juce_xmltextContentAttributeName ("text");
  703. const String& XmlElement::getText() const noexcept
  704. {
  705. jassert (isTextElement()); // you're trying to get the text from an element that
  706. // isn't actually a text element.. If this contains text sub-nodes, you
  707. // probably want to use getAllSubText instead.
  708. return getStringAttribute (juce_xmltextContentAttributeName);
  709. }
  710. void XmlElement::setText (const String& newText)
  711. {
  712. if (isTextElement())
  713. setAttribute (juce_xmltextContentAttributeName, newText);
  714. else
  715. jassertfalse; // you can only change the text in a text element, not a normal one.
  716. }
  717. String XmlElement::getAllSubText() const
  718. {
  719. if (isTextElement())
  720. return getText();
  721. if (getNumChildElements() == 1)
  722. return firstChildElement.get()->getAllSubText();
  723. MemoryOutputStream mem (1024);
  724. for (const XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem)
  725. mem << child->getAllSubText();
  726. return mem.toUTF8();
  727. }
  728. String XmlElement::getChildElementAllSubText (StringRef childTagName, const String& defaultReturnValue) const
  729. {
  730. if (const XmlElement* const child = getChildByName (childTagName))
  731. return child->getAllSubText();
  732. return defaultReturnValue;
  733. }
  734. XmlElement* XmlElement::createTextElement (const String& text)
  735. {
  736. XmlElement* const e = new XmlElement ((int) 0);
  737. e->setAttribute (juce_xmltextContentAttributeName, text);
  738. return e;
  739. }
  740. bool XmlElement::isValidXmlName (StringRef text) noexcept
  741. {
  742. if (text.isEmpty() || ! isValidXmlNameStartCharacter (text.text.getAndAdvance()))
  743. return false;
  744. for (;;)
  745. {
  746. if (text.isEmpty())
  747. return true;
  748. if (! isValidXmlNameBodyCharacter (text.text.getAndAdvance()))
  749. return false;
  750. }
  751. }
  752. void XmlElement::addTextElement (const String& text)
  753. {
  754. addChildElement (createTextElement (text));
  755. }
  756. void XmlElement::deleteAllTextElements() noexcept
  757. {
  758. for (XmlElement* child = firstChildElement; child != nullptr;)
  759. {
  760. XmlElement* const next = child->nextListItem;
  761. if (child->isTextElement())
  762. removeChildElement (child, true);
  763. child = next;
  764. }
  765. }
  766. }