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.

1155 lines
31KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-10 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. #include "../core/juce_StandardHeader.h"
  19. BEGIN_JUCE_NAMESPACE
  20. #include "juce_XmlElement.h"
  21. #include "../io/streams/juce_MemoryOutputStream.h"
  22. #include "../io/files/juce_TemporaryFile.h"
  23. #include "../threads/juce_Thread.h"
  24. #include "../containers/juce_ScopedPointer.h"
  25. //==============================================================================
  26. XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) throw()
  27. : name (other.name),
  28. value (other.value),
  29. next (0)
  30. {
  31. }
  32. XmlElement::XmlAttributeNode::XmlAttributeNode (const String& name_, const String& value_) throw()
  33. : name (name_),
  34. value (value_),
  35. next (0)
  36. {
  37. }
  38. //==============================================================================
  39. XmlElement::XmlElement (const String& tagName_) throw()
  40. : tagName (tagName_),
  41. firstChildElement (0),
  42. nextElement (0),
  43. attributes (0)
  44. {
  45. // the tag name mustn't be empty, or it'll look like a text element!
  46. jassert (tagName_.containsNonWhitespaceChars())
  47. // The tag can't contain spaces or other characters that would create invalid XML!
  48. jassert (! tagName_.containsAnyOf (" <>/&"));
  49. }
  50. XmlElement::XmlElement (int /*dummy*/) throw()
  51. : firstChildElement (0),
  52. nextElement (0),
  53. attributes (0)
  54. {
  55. }
  56. XmlElement::XmlElement (const XmlElement& other)
  57. : tagName (other.tagName),
  58. firstChildElement (0),
  59. nextElement (0),
  60. attributes (0)
  61. {
  62. copyChildrenAndAttributesFrom (other);
  63. }
  64. XmlElement& XmlElement::operator= (const XmlElement& other)
  65. {
  66. if (this != &other)
  67. {
  68. removeAllAttributes();
  69. deleteAllChildElements();
  70. tagName = other.tagName;
  71. copyChildrenAndAttributesFrom (other);
  72. }
  73. return *this;
  74. }
  75. void XmlElement::copyChildrenAndAttributesFrom (const XmlElement& other)
  76. {
  77. XmlElement* child = other.firstChildElement;
  78. XmlElement* lastChild = 0;
  79. while (child != 0)
  80. {
  81. XmlElement* const copiedChild = new XmlElement (*child);
  82. if (lastChild != 0)
  83. lastChild->nextElement = copiedChild;
  84. else
  85. firstChildElement = copiedChild;
  86. lastChild = copiedChild;
  87. child = child->nextElement;
  88. }
  89. const XmlAttributeNode* att = other.attributes;
  90. XmlAttributeNode* lastAtt = 0;
  91. while (att != 0)
  92. {
  93. XmlAttributeNode* const newAtt = new XmlAttributeNode (*att);
  94. if (lastAtt != 0)
  95. lastAtt->next = newAtt;
  96. else
  97. attributes = newAtt;
  98. lastAtt = newAtt;
  99. att = att->next;
  100. }
  101. }
  102. XmlElement::~XmlElement() throw()
  103. {
  104. XmlElement* child = firstChildElement;
  105. while (child != 0)
  106. {
  107. XmlElement* const nextChild = child->nextElement;
  108. delete child;
  109. child = nextChild;
  110. }
  111. XmlAttributeNode* att = attributes;
  112. while (att != 0)
  113. {
  114. XmlAttributeNode* const nextAtt = att->next;
  115. delete att;
  116. att = nextAtt;
  117. }
  118. }
  119. //==============================================================================
  120. namespace XmlOutputFunctions
  121. {
  122. /*static bool isLegalXmlCharSlow (const juce_wchar character) throw()
  123. {
  124. if ((character >= 'a' && character <= 'z')
  125. || (character >= 'A' && character <= 'Z')
  126. || (character >= '0' && character <= '9'))
  127. return true;
  128. const char* t = " .,;:-()_+=?!'#@[]/\\*%~{}$|";
  129. do
  130. {
  131. if (((juce_wchar) (uint8) *t) == character)
  132. return true;
  133. }
  134. while (*++t != 0);
  135. return false;
  136. }
  137. static void generateLegalCharConstants()
  138. {
  139. uint8 n[32];
  140. zerostruct (n);
  141. for (int i = 0; i < 256; ++i)
  142. if (isLegalXmlCharSlow (i))
  143. n[i >> 3] |= (1 << (i & 7));
  144. String s;
  145. for (int i = 0; i < 32; ++i)
  146. s << (int) n[i] << ", ";
  147. DBG (s);
  148. }*/
  149. static bool isLegalXmlChar (const uint32 c) throw()
  150. {
  151. static const unsigned char legalChars[] = { 0, 0, 0, 0, 187, 255, 255, 175, 255, 255, 255, 191, 254, 255, 255, 127 };
  152. return c < sizeof (legalChars) * 8
  153. && (legalChars [c >> 3] & (1 << (c & 7))) != 0;
  154. }
  155. static void escapeIllegalXmlChars (OutputStream& outputStream, const String& text, const bool changeNewLines)
  156. {
  157. const juce_wchar* t = text;
  158. for (;;)
  159. {
  160. const juce_wchar character = *t++;
  161. if (character == 0)
  162. {
  163. break;
  164. }
  165. else if (isLegalXmlChar ((uint32) character))
  166. {
  167. outputStream << (char) character;
  168. }
  169. else
  170. {
  171. switch (character)
  172. {
  173. case '&': outputStream << "&amp;"; break;
  174. case '"': outputStream << "&quot;"; break;
  175. case '>': outputStream << "&gt;"; break;
  176. case '<': outputStream << "&lt;"; break;
  177. case '\n':
  178. if (changeNewLines)
  179. outputStream << "&#10;";
  180. else
  181. outputStream << (char) character;
  182. break;
  183. case '\r':
  184. if (changeNewLines)
  185. outputStream << "&#13;";
  186. else
  187. outputStream << (char) character;
  188. break;
  189. default:
  190. outputStream << "&#" << ((int) (unsigned int) character) << ';';
  191. break;
  192. }
  193. }
  194. }
  195. }
  196. static void writeSpaces (OutputStream& out, int numSpaces)
  197. {
  198. if (numSpaces > 0)
  199. {
  200. const char* const blanks = " ";
  201. const int blankSize = (int) sizeof (blanks) - 1;
  202. while (numSpaces > blankSize)
  203. {
  204. out.write (blanks, blankSize);
  205. numSpaces -= blankSize;
  206. }
  207. out.write (blanks, numSpaces);
  208. }
  209. }
  210. }
  211. void XmlElement::writeElementAsText (OutputStream& outputStream,
  212. const int indentationLevel,
  213. const int lineWrapLength) const
  214. {
  215. using namespace XmlOutputFunctions;
  216. writeSpaces (outputStream, indentationLevel);
  217. if (! isTextElement())
  218. {
  219. outputStream.writeByte ('<');
  220. outputStream << tagName;
  221. const int attIndent = indentationLevel + tagName.length() + 1;
  222. int lineLen = 0;
  223. const XmlAttributeNode* att = attributes;
  224. while (att != 0)
  225. {
  226. if (lineLen > lineWrapLength && indentationLevel >= 0)
  227. {
  228. outputStream.write ("\r\n", 2);
  229. writeSpaces (outputStream, attIndent);
  230. lineLen = 0;
  231. }
  232. const int64 startPos = outputStream.getPosition();
  233. outputStream.writeByte (' ');
  234. outputStream << att->name;
  235. outputStream.write ("=\"", 2);
  236. escapeIllegalXmlChars (outputStream, att->value, true);
  237. outputStream.writeByte ('"');
  238. lineLen += (int) (outputStream.getPosition() - startPos);
  239. att = att->next;
  240. }
  241. if (firstChildElement != 0)
  242. {
  243. XmlElement* child = firstChildElement;
  244. if (child->nextElement == 0 && child->isTextElement())
  245. {
  246. outputStream.writeByte ('>');
  247. escapeIllegalXmlChars (outputStream, child->getText(), false);
  248. }
  249. else
  250. {
  251. if (indentationLevel >= 0)
  252. outputStream.write (">\r\n", 3);
  253. else
  254. outputStream.writeByte ('>');
  255. bool lastWasTextNode = false;
  256. while (child != 0)
  257. {
  258. if (child->isTextElement())
  259. {
  260. if ((! lastWasTextNode) && (indentationLevel >= 0))
  261. writeSpaces (outputStream, indentationLevel + 2);
  262. escapeIllegalXmlChars (outputStream, child->getText(), false);
  263. lastWasTextNode = true;
  264. }
  265. else
  266. {
  267. if (indentationLevel >= 0)
  268. {
  269. if (lastWasTextNode)
  270. outputStream.write ("\r\n", 2);
  271. child->writeElementAsText (outputStream, indentationLevel + 2, lineWrapLength);
  272. }
  273. else
  274. {
  275. child->writeElementAsText (outputStream, indentationLevel, lineWrapLength);
  276. }
  277. lastWasTextNode = false;
  278. }
  279. child = child->nextElement;
  280. }
  281. if (indentationLevel >= 0)
  282. {
  283. if (lastWasTextNode)
  284. outputStream.write ("\r\n", 2);
  285. writeSpaces (outputStream, indentationLevel);
  286. }
  287. }
  288. outputStream.write ("</", 2);
  289. outputStream << tagName;
  290. if (indentationLevel >= 0)
  291. outputStream.write (">\r\n", 3);
  292. else
  293. outputStream.writeByte ('>');
  294. }
  295. else
  296. {
  297. if (indentationLevel >= 0)
  298. outputStream.write ("/>\r\n", 4);
  299. else
  300. outputStream.write ("/>", 2);
  301. }
  302. }
  303. else
  304. {
  305. if (indentationLevel >= 0)
  306. writeSpaces (outputStream, indentationLevel + 2);
  307. escapeIllegalXmlChars (outputStream, getText(), false);
  308. }
  309. }
  310. const String XmlElement::createDocument (const String& dtdToUse,
  311. const bool allOnOneLine,
  312. const bool includeXmlHeader,
  313. const String& encodingType,
  314. const int lineWrapLength) const
  315. {
  316. MemoryOutputStream mem (2048, 4096);
  317. writeToStream (mem, dtdToUse, allOnOneLine, includeXmlHeader, encodingType, lineWrapLength);
  318. return mem.toUTF8();
  319. }
  320. void XmlElement::writeToStream (OutputStream& output,
  321. const String& dtdToUse,
  322. const bool allOnOneLine,
  323. const bool includeXmlHeader,
  324. const String& encodingType,
  325. const int lineWrapLength) const
  326. {
  327. if (includeXmlHeader)
  328. output << "<?xml version=\"1.0\" encoding=\"" << encodingType
  329. << (allOnOneLine ? "\"?> " : "\"?>\r\n\r\n");
  330. if (dtdToUse.isNotEmpty())
  331. output << dtdToUse << (allOnOneLine ? " " : "\r\n");
  332. writeElementAsText (output, allOnOneLine ? -1 : 0, lineWrapLength);
  333. }
  334. bool XmlElement::writeToFile (const File& file,
  335. const String& dtdToUse,
  336. const String& encodingType,
  337. const int lineWrapLength) const
  338. {
  339. if (file.hasWriteAccess())
  340. {
  341. TemporaryFile tempFile (file);
  342. ScopedPointer <FileOutputStream> out (tempFile.getFile().createOutputStream());
  343. if (out != 0)
  344. {
  345. writeToStream (*out, dtdToUse, false, true, encodingType, lineWrapLength);
  346. out = 0;
  347. return tempFile.overwriteTargetFileWithTemporary();
  348. }
  349. }
  350. return false;
  351. }
  352. //==============================================================================
  353. bool XmlElement::hasTagName (const String& tagNameWanted) const throw()
  354. {
  355. #if JUCE_DEBUG
  356. // if debugging, check that the case is actually the same, because
  357. // valid xml is case-sensitive, and although this lets it pass, it's
  358. // better not to..
  359. if (tagName.equalsIgnoreCase (tagNameWanted))
  360. {
  361. jassert (tagName == tagNameWanted);
  362. return true;
  363. }
  364. else
  365. {
  366. return false;
  367. }
  368. #else
  369. return tagName.equalsIgnoreCase (tagNameWanted);
  370. #endif
  371. }
  372. XmlElement* XmlElement::getNextElementWithTagName (const String& requiredTagName) const
  373. {
  374. XmlElement* e = nextElement;
  375. while (e != 0 && ! e->hasTagName (requiredTagName))
  376. e = e->nextElement;
  377. return e;
  378. }
  379. //==============================================================================
  380. int XmlElement::getNumAttributes() const throw()
  381. {
  382. const XmlAttributeNode* att = attributes;
  383. int count = 0;
  384. while (att != 0)
  385. {
  386. att = att->next;
  387. ++count;
  388. }
  389. return count;
  390. }
  391. const String& XmlElement::getAttributeName (const int index) const throw()
  392. {
  393. const XmlAttributeNode* att = attributes;
  394. int count = 0;
  395. while (att != 0)
  396. {
  397. if (count == index)
  398. return att->name;
  399. att = att->next;
  400. ++count;
  401. }
  402. return String::empty;
  403. }
  404. const String& XmlElement::getAttributeValue (const int index) const throw()
  405. {
  406. const XmlAttributeNode* att = attributes;
  407. int count = 0;
  408. while (att != 0)
  409. {
  410. if (count == index)
  411. return att->value;
  412. att = att->next;
  413. ++count;
  414. }
  415. return String::empty;
  416. }
  417. bool XmlElement::hasAttribute (const String& attributeName) const throw()
  418. {
  419. const XmlAttributeNode* att = attributes;
  420. while (att != 0)
  421. {
  422. if (att->name.equalsIgnoreCase (attributeName))
  423. return true;
  424. att = att->next;
  425. }
  426. return false;
  427. }
  428. //==============================================================================
  429. const String& XmlElement::getStringAttribute (const String& attributeName) const throw()
  430. {
  431. const XmlAttributeNode* att = attributes;
  432. while (att != 0)
  433. {
  434. if (att->name.equalsIgnoreCase (attributeName))
  435. return att->value;
  436. att = att->next;
  437. }
  438. return String::empty;
  439. }
  440. const String XmlElement::getStringAttribute (const String& attributeName, const String& defaultReturnValue) const
  441. {
  442. const XmlAttributeNode* att = attributes;
  443. while (att != 0)
  444. {
  445. if (att->name.equalsIgnoreCase (attributeName))
  446. return att->value;
  447. att = att->next;
  448. }
  449. return defaultReturnValue;
  450. }
  451. int XmlElement::getIntAttribute (const String& attributeName, const int defaultReturnValue) const
  452. {
  453. const XmlAttributeNode* att = attributes;
  454. while (att != 0)
  455. {
  456. if (att->name.equalsIgnoreCase (attributeName))
  457. return att->value.getIntValue();
  458. att = att->next;
  459. }
  460. return defaultReturnValue;
  461. }
  462. double XmlElement::getDoubleAttribute (const String& attributeName, const double defaultReturnValue) const
  463. {
  464. const XmlAttributeNode* att = attributes;
  465. while (att != 0)
  466. {
  467. if (att->name.equalsIgnoreCase (attributeName))
  468. return att->value.getDoubleValue();
  469. att = att->next;
  470. }
  471. return defaultReturnValue;
  472. }
  473. bool XmlElement::getBoolAttribute (const String& attributeName, const bool defaultReturnValue) const
  474. {
  475. const XmlAttributeNode* att = attributes;
  476. while (att != 0)
  477. {
  478. if (att->name.equalsIgnoreCase (attributeName))
  479. {
  480. juce_wchar firstChar = att->value[0];
  481. if (CharacterFunctions::isWhitespace (firstChar))
  482. firstChar = att->value.trimStart() [0];
  483. return firstChar == '1'
  484. || firstChar == 't'
  485. || firstChar == 'y'
  486. || firstChar == 'T'
  487. || firstChar == 'Y';
  488. }
  489. att = att->next;
  490. }
  491. return defaultReturnValue;
  492. }
  493. bool XmlElement::compareAttribute (const String& attributeName,
  494. const String& stringToCompareAgainst,
  495. const bool ignoreCase) const throw()
  496. {
  497. const XmlAttributeNode* att = attributes;
  498. while (att != 0)
  499. {
  500. if (att->name.equalsIgnoreCase (attributeName))
  501. {
  502. if (ignoreCase)
  503. return att->value.equalsIgnoreCase (stringToCompareAgainst);
  504. else
  505. return att->value == stringToCompareAgainst;
  506. }
  507. att = att->next;
  508. }
  509. return false;
  510. }
  511. //==============================================================================
  512. void XmlElement::setAttribute (const String& attributeName, const String& value)
  513. {
  514. #if JUCE_DEBUG
  515. // check the identifier being passed in is legal..
  516. const juce_wchar* t = attributeName;
  517. while (*t != 0)
  518. {
  519. jassert (CharacterFunctions::isLetterOrDigit (*t)
  520. || *t == '_'
  521. || *t == '-'
  522. || *t == ':');
  523. ++t;
  524. }
  525. #endif
  526. if (attributes == 0)
  527. {
  528. attributes = new XmlAttributeNode (attributeName, value);
  529. }
  530. else
  531. {
  532. XmlAttributeNode* att = attributes;
  533. for (;;)
  534. {
  535. if (att->name.equalsIgnoreCase (attributeName))
  536. {
  537. att->value = value;
  538. break;
  539. }
  540. else if (att->next == 0)
  541. {
  542. att->next = new XmlAttributeNode (attributeName, value);
  543. break;
  544. }
  545. att = att->next;
  546. }
  547. }
  548. }
  549. void XmlElement::setAttribute (const String& attributeName, const int number)
  550. {
  551. setAttribute (attributeName, String (number));
  552. }
  553. void XmlElement::setAttribute (const String& attributeName, const double number)
  554. {
  555. setAttribute (attributeName, String (number));
  556. }
  557. void XmlElement::removeAttribute (const String& attributeName) throw()
  558. {
  559. XmlAttributeNode* att = attributes;
  560. XmlAttributeNode* lastAtt = 0;
  561. while (att != 0)
  562. {
  563. if (att->name.equalsIgnoreCase (attributeName))
  564. {
  565. if (lastAtt == 0)
  566. attributes = att->next;
  567. else
  568. lastAtt->next = att->next;
  569. delete att;
  570. break;
  571. }
  572. lastAtt = att;
  573. att = att->next;
  574. }
  575. }
  576. void XmlElement::removeAllAttributes() throw()
  577. {
  578. while (attributes != 0)
  579. {
  580. XmlAttributeNode* const nextAtt = attributes->next;
  581. delete attributes;
  582. attributes = nextAtt;
  583. }
  584. }
  585. //==============================================================================
  586. int XmlElement::getNumChildElements() const throw()
  587. {
  588. int count = 0;
  589. const XmlElement* child = firstChildElement;
  590. while (child != 0)
  591. {
  592. ++count;
  593. child = child->nextElement;
  594. }
  595. return count;
  596. }
  597. XmlElement* XmlElement::getChildElement (const int index) const throw()
  598. {
  599. int count = 0;
  600. XmlElement* child = firstChildElement;
  601. while (child != 0 && count < index)
  602. {
  603. child = child->nextElement;
  604. ++count;
  605. }
  606. return child;
  607. }
  608. XmlElement* XmlElement::getChildByName (const String& childName) const throw()
  609. {
  610. XmlElement* child = firstChildElement;
  611. while (child != 0)
  612. {
  613. if (child->hasTagName (childName))
  614. break;
  615. child = child->nextElement;
  616. }
  617. return child;
  618. }
  619. void XmlElement::addChildElement (XmlElement* const newNode) throw()
  620. {
  621. if (newNode != 0)
  622. {
  623. if (firstChildElement == 0)
  624. {
  625. firstChildElement = newNode;
  626. }
  627. else
  628. {
  629. XmlElement* child = firstChildElement;
  630. while (child->nextElement != 0)
  631. child = child->nextElement;
  632. child->nextElement = newNode;
  633. // if this is non-zero, then something's probably
  634. // gone wrong..
  635. jassert (newNode->nextElement == 0);
  636. }
  637. }
  638. }
  639. void XmlElement::insertChildElement (XmlElement* const newNode,
  640. int indexToInsertAt) throw()
  641. {
  642. if (newNode != 0)
  643. {
  644. removeChildElement (newNode, false);
  645. if (indexToInsertAt == 0)
  646. {
  647. newNode->nextElement = firstChildElement;
  648. firstChildElement = newNode;
  649. }
  650. else
  651. {
  652. if (firstChildElement == 0)
  653. {
  654. firstChildElement = newNode;
  655. }
  656. else
  657. {
  658. if (indexToInsertAt < 0)
  659. indexToInsertAt = std::numeric_limits<int>::max();
  660. XmlElement* child = firstChildElement;
  661. while (child->nextElement != 0 && --indexToInsertAt > 0)
  662. child = child->nextElement;
  663. newNode->nextElement = child->nextElement;
  664. child->nextElement = newNode;
  665. }
  666. }
  667. }
  668. }
  669. XmlElement* XmlElement::createNewChildElement (const String& childTagName)
  670. {
  671. XmlElement* const newElement = new XmlElement (childTagName);
  672. addChildElement (newElement);
  673. return newElement;
  674. }
  675. bool XmlElement::replaceChildElement (XmlElement* const currentChildElement,
  676. XmlElement* const newNode) throw()
  677. {
  678. if (newNode != 0)
  679. {
  680. XmlElement* child = firstChildElement;
  681. XmlElement* previousNode = 0;
  682. while (child != 0)
  683. {
  684. if (child == currentChildElement)
  685. {
  686. if (child != newNode)
  687. {
  688. if (previousNode == 0)
  689. firstChildElement = newNode;
  690. else
  691. previousNode->nextElement = newNode;
  692. newNode->nextElement = child->nextElement;
  693. delete child;
  694. }
  695. return true;
  696. }
  697. previousNode = child;
  698. child = child->nextElement;
  699. }
  700. }
  701. return false;
  702. }
  703. void XmlElement::removeChildElement (XmlElement* const childToRemove,
  704. const bool shouldDeleteTheChild) throw()
  705. {
  706. if (childToRemove != 0)
  707. {
  708. if (firstChildElement == childToRemove)
  709. {
  710. firstChildElement = childToRemove->nextElement;
  711. childToRemove->nextElement = 0;
  712. }
  713. else
  714. {
  715. XmlElement* child = firstChildElement;
  716. XmlElement* last = 0;
  717. while (child != 0)
  718. {
  719. if (child == childToRemove)
  720. {
  721. if (last == 0)
  722. firstChildElement = child->nextElement;
  723. else
  724. last->nextElement = child->nextElement;
  725. childToRemove->nextElement = 0;
  726. break;
  727. }
  728. last = child;
  729. child = child->nextElement;
  730. }
  731. }
  732. if (shouldDeleteTheChild)
  733. delete childToRemove;
  734. }
  735. }
  736. bool XmlElement::isEquivalentTo (const XmlElement* const other,
  737. const bool ignoreOrderOfAttributes) const throw()
  738. {
  739. if (this != other)
  740. {
  741. if (other == 0 || tagName != other->tagName)
  742. {
  743. return false;
  744. }
  745. if (ignoreOrderOfAttributes)
  746. {
  747. int totalAtts = 0;
  748. const XmlAttributeNode* att = attributes;
  749. while (att != 0)
  750. {
  751. if (! other->compareAttribute (att->name, att->value))
  752. return false;
  753. att = att->next;
  754. ++totalAtts;
  755. }
  756. if (totalAtts != other->getNumAttributes())
  757. return false;
  758. }
  759. else
  760. {
  761. const XmlAttributeNode* thisAtt = attributes;
  762. const XmlAttributeNode* otherAtt = other->attributes;
  763. for (;;)
  764. {
  765. if (thisAtt == 0 || otherAtt == 0)
  766. {
  767. if (thisAtt == otherAtt) // both 0, so it's a match
  768. break;
  769. return false;
  770. }
  771. if (thisAtt->name != otherAtt->name
  772. || thisAtt->value != otherAtt->value)
  773. {
  774. return false;
  775. }
  776. thisAtt = thisAtt->next;
  777. otherAtt = otherAtt->next;
  778. }
  779. }
  780. const XmlElement* thisChild = firstChildElement;
  781. const XmlElement* otherChild = other->firstChildElement;
  782. for (;;)
  783. {
  784. if (thisChild == 0 || otherChild == 0)
  785. {
  786. if (thisChild == otherChild) // both 0, so it's a match
  787. break;
  788. return false;
  789. }
  790. if (! thisChild->isEquivalentTo (otherChild, ignoreOrderOfAttributes))
  791. return false;
  792. thisChild = thisChild->nextElement;
  793. otherChild = otherChild->nextElement;
  794. }
  795. }
  796. return true;
  797. }
  798. void XmlElement::deleteAllChildElements() throw()
  799. {
  800. while (firstChildElement != 0)
  801. {
  802. XmlElement* const nextChild = firstChildElement->nextElement;
  803. delete firstChildElement;
  804. firstChildElement = nextChild;
  805. }
  806. }
  807. void XmlElement::deleteAllChildElementsWithTagName (const String& name) throw()
  808. {
  809. XmlElement* child = firstChildElement;
  810. while (child != 0)
  811. {
  812. if (child->hasTagName (name))
  813. {
  814. XmlElement* const nextChild = child->nextElement;
  815. removeChildElement (child, true);
  816. child = nextChild;
  817. }
  818. else
  819. {
  820. child = child->nextElement;
  821. }
  822. }
  823. }
  824. bool XmlElement::containsChildElement (const XmlElement* const possibleChild) const throw()
  825. {
  826. const XmlElement* child = firstChildElement;
  827. while (child != 0)
  828. {
  829. if (child == possibleChild)
  830. return true;
  831. child = child->nextElement;
  832. }
  833. return false;
  834. }
  835. XmlElement* XmlElement::findParentElementOf (const XmlElement* const elementToLookFor) throw()
  836. {
  837. if (this == elementToLookFor || elementToLookFor == 0)
  838. return 0;
  839. XmlElement* child = firstChildElement;
  840. while (child != 0)
  841. {
  842. if (elementToLookFor == child)
  843. return this;
  844. XmlElement* const found = child->findParentElementOf (elementToLookFor);
  845. if (found != 0)
  846. return found;
  847. child = child->nextElement;
  848. }
  849. return 0;
  850. }
  851. void XmlElement::getChildElementsAsArray (XmlElement** elems) const throw()
  852. {
  853. XmlElement* e = firstChildElement;
  854. while (e != 0)
  855. {
  856. *elems++ = e;
  857. e = e->nextElement;
  858. }
  859. }
  860. void XmlElement::reorderChildElements (XmlElement** const elems, const int num) throw()
  861. {
  862. XmlElement* e = firstChildElement = elems[0];
  863. for (int i = 1; i < num; ++i)
  864. {
  865. e->nextElement = elems[i];
  866. e = e->nextElement;
  867. }
  868. e->nextElement = 0;
  869. }
  870. //==============================================================================
  871. bool XmlElement::isTextElement() const throw()
  872. {
  873. return tagName.isEmpty();
  874. }
  875. static const juce_wchar* const juce_xmltextContentAttributeName = L"text";
  876. const String& XmlElement::getText() const throw()
  877. {
  878. jassert (isTextElement()); // you're trying to get the text from an element that
  879. // isn't actually a text element.. If this contains text sub-nodes, you
  880. // probably want to use getAllSubText instead.
  881. return getStringAttribute (juce_xmltextContentAttributeName);
  882. }
  883. void XmlElement::setText (const String& newText)
  884. {
  885. if (isTextElement())
  886. setAttribute (juce_xmltextContentAttributeName, newText);
  887. else
  888. jassertfalse; // you can only change the text in a text element, not a normal one.
  889. }
  890. const String XmlElement::getAllSubText() const
  891. {
  892. String result;
  893. String::Concatenator concatenator (result);
  894. const XmlElement* child = firstChildElement;
  895. while (child != 0)
  896. {
  897. if (child->isTextElement())
  898. concatenator.append (child->getText());
  899. child = child->nextElement;
  900. }
  901. return result;
  902. }
  903. const String XmlElement::getChildElementAllSubText (const String& childTagName,
  904. const String& defaultReturnValue) const
  905. {
  906. const XmlElement* const child = getChildByName (childTagName);
  907. if (child != 0)
  908. return child->getAllSubText();
  909. return defaultReturnValue;
  910. }
  911. XmlElement* XmlElement::createTextElement (const String& text)
  912. {
  913. XmlElement* const e = new XmlElement ((int) 0);
  914. e->setAttribute (juce_xmltextContentAttributeName, text);
  915. return e;
  916. }
  917. void XmlElement::addTextElement (const String& text)
  918. {
  919. addChildElement (createTextElement (text));
  920. }
  921. void XmlElement::deleteAllTextElements() throw()
  922. {
  923. XmlElement* child = firstChildElement;
  924. while (child != 0)
  925. {
  926. XmlElement* const next = child->nextElement;
  927. if (child->isTextElement())
  928. removeChildElement (child, true);
  929. child = next;
  930. }
  931. }
  932. END_JUCE_NAMESPACE