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.

716 lines
29KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 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. #ifndef __JUCE_XMLELEMENT_JUCEHEADER__
  19. #define __JUCE_XMLELEMENT_JUCEHEADER__
  20. #include "juce_String.h"
  21. #include "../io/streams/juce_OutputStream.h"
  22. #include "../io/files/juce_File.h"
  23. //==============================================================================
  24. /** A handy macro to make it easy to iterate all the child elements in an XmlElement.
  25. The parentXmlElement should be a reference to the parent XML, and the childElementVariableName
  26. will be the name of a pointer to each child element.
  27. E.g. @code
  28. XmlElement* myParentXml = createSomeKindOfXmlDocument();
  29. forEachXmlChildElement (*myParentXml, child)
  30. {
  31. if (child->hasTagName ("FOO"))
  32. doSomethingWithXmlElement (child);
  33. }
  34. @endcode
  35. @see forEachXmlChildElementWithTagName
  36. */
  37. #define forEachXmlChildElement(parentXmlElement, childElementVariableName) \
  38. \
  39. for (XmlElement* childElementVariableName = (parentXmlElement).getFirstChildElement(); \
  40. childElementVariableName != 0; \
  41. childElementVariableName = childElementVariableName->getNextElement())
  42. /** A macro that makes it easy to iterate all the child elements of an XmlElement
  43. which have a specified tag.
  44. This does the same job as the forEachXmlChildElement macro, but only for those
  45. elements that have a particular tag name.
  46. The parentXmlElement should be a reference to the parent XML, and the childElementVariableName
  47. will be the name of a pointer to each child element. The requiredTagName is the
  48. tag name to match.
  49. E.g. @code
  50. XmlElement* myParentXml = createSomeKindOfXmlDocument();
  51. forEachXmlChildElementWithTagName (*myParentXml, child, "MYTAG")
  52. {
  53. // the child object is now guaranteed to be a <MYTAG> element..
  54. doSomethingWithMYTAGElement (child);
  55. }
  56. @endcode
  57. @see forEachXmlChildElement
  58. */
  59. #define forEachXmlChildElementWithTagName(parentXmlElement, childElementVariableName, requiredTagName) \
  60. \
  61. for (XmlElement* childElementVariableName = (parentXmlElement).getChildByName (requiredTagName); \
  62. childElementVariableName != 0; \
  63. childElementVariableName = childElementVariableName->getNextElementWithTagName (requiredTagName))
  64. //==============================================================================
  65. /** Used to build a tree of elements representing an XML document.
  66. An XML document can be parsed into a tree of XmlElements, each of which
  67. represents an XML tag structure, and which may itself contain other
  68. nested elements.
  69. An XmlElement can also be converted back into a text document, and has
  70. lots of useful methods for manipulating its attributes and sub-elements,
  71. so XmlElements can actually be used as a handy general-purpose data
  72. structure.
  73. Here's an example of parsing some elements: @code
  74. // check we're looking at the right kind of document..
  75. if (myElement->hasTagName ("ANIMALS"))
  76. {
  77. // now we'll iterate its sub-elements looking for 'giraffe' elements..
  78. forEachXmlChildElement (*myElement, e)
  79. {
  80. if (e->hasTagName ("GIRAFFE"))
  81. {
  82. // found a giraffe, so use some of its attributes..
  83. String giraffeName = e->getStringAttribute ("name");
  84. int giraffeAge = e->getIntAttribute ("age");
  85. bool isFriendly = e->getBoolAttribute ("friendly");
  86. }
  87. }
  88. }
  89. @endcode
  90. And here's an example of how to create an XML document from scratch: @code
  91. // create an outer node called "ANIMALS"
  92. XmlElement animalsList ("ANIMALS");
  93. for (int i = 0; i < numAnimals; ++i)
  94. {
  95. // create an inner element..
  96. XmlElement* giraffe = new XmlElement ("GIRAFFE");
  97. giraffe->setAttribute ("name", "nigel");
  98. giraffe->setAttribute ("age", 10);
  99. giraffe->setAttribute ("friendly", true);
  100. // ..and add our new element to the parent node
  101. animalsList.addChildElement (giraffe);
  102. }
  103. // now we can turn the whole thing into a text document..
  104. String myXmlDoc = animalsList.createDocument (String::empty);
  105. @endcode
  106. @see XmlDocument
  107. */
  108. class JUCE_API XmlElement
  109. {
  110. public:
  111. //==============================================================================
  112. /** Creates an XmlElement with this tag name. */
  113. explicit XmlElement (const String& tagName) throw();
  114. /** Creates a (deep) copy of another element. */
  115. XmlElement (const XmlElement& other) throw();
  116. /** Creates a (deep) copy of another element. */
  117. XmlElement& operator= (const XmlElement& other) throw();
  118. /** Deleting an XmlElement will also delete all its child elements. */
  119. ~XmlElement() throw();
  120. //==============================================================================
  121. /** Compares two XmlElements to see if they contain the same text and attiributes.
  122. The elements are only considered equivalent if they contain the same attiributes
  123. with the same values, and have the same sub-nodes.
  124. @param other the other element to compare to
  125. @param ignoreOrderOfAttributes if true, this means that two elements with the
  126. same attributes in a different order will be
  127. considered the same; if false, the attributes must
  128. be in the same order as well
  129. */
  130. bool isEquivalentTo (const XmlElement* other,
  131. bool ignoreOrderOfAttributes) const throw();
  132. //==============================================================================
  133. /** Returns an XML text document that represents this element.
  134. The string returned can be parsed to recreate the same XmlElement that
  135. was used to create it.
  136. @param dtdToUse the DTD to add to the document
  137. @param allOnOneLine if true, this means that the document will not contain any
  138. linefeeds, so it'll be smaller but not very easy to read.
  139. @param includeXmlHeader whether to add the "<?xml version..etc" line at the start of the
  140. document
  141. @param encodingType the character encoding format string to put into the xml
  142. header
  143. @param lineWrapLength the line length that will be used before items get placed on
  144. a new line. This isn't an absolute maximum length, it just
  145. determines how lists of attributes get broken up
  146. @see writeToStream, writeToFile
  147. */
  148. const String createDocument (const String& dtdToUse,
  149. bool allOnOneLine = false,
  150. bool includeXmlHeader = true,
  151. const String& encodingType = "UTF-8",
  152. int lineWrapLength = 60) const;
  153. /** Writes the document to a stream as UTF-8.
  154. @param output the stream to write to
  155. @param dtdToUse the DTD to add to the document
  156. @param allOnOneLine if true, this means that the document will not contain any
  157. linefeeds, so it'll be smaller but not very easy to read.
  158. @param includeXmlHeader whether to add the "<?xml version..etc" line at the start of the
  159. document
  160. @param encodingType the character encoding format string to put into the xml
  161. header
  162. @param lineWrapLength the line length that will be used before items get placed on
  163. a new line. This isn't an absolute maximum length, it just
  164. determines how lists of attributes get broken up
  165. @see writeToFile, createDocument
  166. */
  167. void writeToStream (OutputStream& output,
  168. const String& dtdToUse,
  169. bool allOnOneLine = false,
  170. bool includeXmlHeader = true,
  171. const String& encodingType = "UTF-8",
  172. int lineWrapLength = 60) const;
  173. /** Writes the element to a file as an XML document.
  174. To improve safety in case something goes wrong while writing the file, this
  175. will actually write the document to a new temporary file in the same
  176. directory as the destination file, and if this succeeds, it will rename this
  177. new file as the destination file (overwriting any existing file that was there).
  178. @param destinationFile the file to write to. If this already exists, it will be
  179. overwritten.
  180. @param dtdToUse the DTD to add to the document
  181. @param encodingType the character encoding format string to put into the xml
  182. header
  183. @param lineWrapLength the line length that will be used before items get placed on
  184. a new line. This isn't an absolute maximum length, it just
  185. determines how lists of attributes get broken up
  186. @returns true if the file is written successfully; false if something goes wrong
  187. in the process
  188. @see createDocument
  189. */
  190. bool writeToFile (const File& destinationFile,
  191. const String& dtdToUse,
  192. const String& encodingType = "UTF-8",
  193. int lineWrapLength = 60) const;
  194. //==============================================================================
  195. /** Returns this element's tag type name.
  196. E.g. for an element such as \<MOOSE legs="4" antlers="2">, this would return
  197. "MOOSE".
  198. @see hasTagName
  199. */
  200. inline const String& getTagName() const throw() { return tagName; }
  201. /** Tests whether this element has a particular tag name.
  202. @param possibleTagName the tag name you're comparing it with
  203. @see getTagName
  204. */
  205. bool hasTagName (const String& possibleTagName) const throw();
  206. //==============================================================================
  207. /** Returns the number of XML attributes this element contains.
  208. E.g. for an element such as \<MOOSE legs="4" antlers="2">, this would
  209. return 2.
  210. */
  211. int getNumAttributes() const throw();
  212. /** Returns the name of one of the elements attributes.
  213. E.g. for an element such as \<MOOSE legs="4" antlers="2">, then
  214. getAttributeName(1) would return "antlers".
  215. @see getAttributeValue, getStringAttribute
  216. */
  217. const String& getAttributeName (int attributeIndex) const throw();
  218. /** Returns the value of one of the elements attributes.
  219. E.g. for an element such as \<MOOSE legs="4" antlers="2">, then
  220. getAttributeName(1) would return "2".
  221. @see getAttributeName, getStringAttribute
  222. */
  223. const String& getAttributeValue (int attributeIndex) const throw();
  224. //==============================================================================
  225. // Attribute-handling methods..
  226. /** Checks whether the element contains an attribute with a certain name. */
  227. bool hasAttribute (const String& attributeName) const throw();
  228. /** Returns the value of a named attribute.
  229. @param attributeName the name of the attribute to look up
  230. */
  231. const String& getStringAttribute (const String& attributeName) const throw();
  232. /** Returns the value of a named attribute.
  233. @param attributeName the name of the attribute to look up
  234. @param defaultReturnValue a value to return if the element doesn't have an attribute
  235. with this name
  236. */
  237. const String getStringAttribute (const String& attributeName,
  238. const String& defaultReturnValue) const;
  239. /** Compares the value of a named attribute with a value passed-in.
  240. @param attributeName the name of the attribute to look up
  241. @param stringToCompareAgainst the value to compare it with
  242. @param ignoreCase whether the comparison should be case-insensitive
  243. @returns true if the value of the attribute is the same as the string passed-in;
  244. false if it's different (or if no such attribute exists)
  245. */
  246. bool compareAttribute (const String& attributeName,
  247. const String& stringToCompareAgainst,
  248. bool ignoreCase = false) const throw();
  249. /** Returns the value of a named attribute as an integer.
  250. This will try to find the attribute and convert it to an integer (using
  251. the String::getIntValue() method).
  252. @param attributeName the name of the attribute to look up
  253. @param defaultReturnValue a value to return if the element doesn't have an attribute
  254. with this name
  255. @see setAttribute
  256. */
  257. int getIntAttribute (const String& attributeName,
  258. int defaultReturnValue = 0) const;
  259. /** Returns the value of a named attribute as floating-point.
  260. This will try to find the attribute and convert it to an integer (using
  261. the String::getDoubleValue() method).
  262. @param attributeName the name of the attribute to look up
  263. @param defaultReturnValue a value to return if the element doesn't have an attribute
  264. with this name
  265. @see setAttribute
  266. */
  267. double getDoubleAttribute (const String& attributeName,
  268. double defaultReturnValue = 0.0) const;
  269. /** Returns the value of a named attribute as a boolean.
  270. This will try to find the attribute and interpret it as a boolean. To do this,
  271. it'll return true if the value is "1", "true", "y", etc, or false for other
  272. values.
  273. @param attributeName the name of the attribute to look up
  274. @param defaultReturnValue a value to return if the element doesn't have an attribute
  275. with this name
  276. */
  277. bool getBoolAttribute (const String& attributeName,
  278. bool defaultReturnValue = false) const;
  279. /** Adds a named attribute to the element.
  280. If the element already contains an attribute with this name, it's value will
  281. be updated to the new value. If there's no such attribute yet, a new one will
  282. be added.
  283. Note that there are other setAttribute() methods that take integers,
  284. doubles, etc. to make it easy to store numbers.
  285. @param attributeName the name of the attribute to set
  286. @param newValue the value to set it to
  287. @see removeAttribute
  288. */
  289. void setAttribute (const String& attributeName,
  290. const String& newValue);
  291. /** Adds a named attribute to the element, setting it to an integer value.
  292. If the element already contains an attribute with this name, it's value will
  293. be updated to the new value. If there's no such attribute yet, a new one will
  294. be added.
  295. Note that there are other setAttribute() methods that take integers,
  296. doubles, etc. to make it easy to store numbers.
  297. @param attributeName the name of the attribute to set
  298. @param newValue the value to set it to
  299. */
  300. void setAttribute (const String& attributeName,
  301. int newValue);
  302. /** Adds a named attribute to the element, setting it to a floating-point value.
  303. If the element already contains an attribute with this name, it's value will
  304. be updated to the new value. If there's no such attribute yet, a new one will
  305. be added.
  306. Note that there are other setAttribute() methods that take integers,
  307. doubles, etc. to make it easy to store numbers.
  308. @param attributeName the name of the attribute to set
  309. @param newValue the value to set it to
  310. */
  311. void setAttribute (const String& attributeName,
  312. double newValue);
  313. /** Removes a named attribute from the element.
  314. @param attributeName the name of the attribute to remove
  315. @see removeAllAttributes
  316. */
  317. void removeAttribute (const String& attributeName) throw();
  318. /** Removes all attributes from this element.
  319. */
  320. void removeAllAttributes() throw();
  321. //==============================================================================
  322. // Child element methods..
  323. /** Returns the first of this element's sub-elements.
  324. see getNextElement() for an example of how to iterate the sub-elements.
  325. @see forEachXmlChildElement
  326. */
  327. XmlElement* getFirstChildElement() const throw() { return firstChildElement; }
  328. /** Returns the next of this element's siblings.
  329. This can be used for iterating an element's sub-elements, e.g.
  330. @code
  331. XmlElement* child = myXmlDocument->getFirstChildElement();
  332. while (child != 0)
  333. {
  334. ...do stuff with this child..
  335. child = child->getNextElement();
  336. }
  337. @endcode
  338. Note that when iterating the child elements, some of them might be
  339. text elements as well as XML tags - use isTextElement() to work this
  340. out.
  341. Also, it's much easier and neater to use this method indirectly via the
  342. forEachXmlChildElement macro.
  343. @returns the sibling element that follows this one, or zero if this is the last
  344. element in its parent
  345. @see getNextElement, isTextElement, forEachXmlChildElement
  346. */
  347. inline XmlElement* getNextElement() const throw() { return nextElement; }
  348. /** Returns the next of this element's siblings which has the specified tag
  349. name.
  350. This is like getNextElement(), but will scan through the list until it
  351. finds an element with the given tag name.
  352. @see getNextElement, forEachXmlChildElementWithTagName
  353. */
  354. XmlElement* getNextElementWithTagName (const String& requiredTagName) const;
  355. /** Returns the number of sub-elements in this element.
  356. @see getChildElement
  357. */
  358. int getNumChildElements() const throw();
  359. /** Returns the sub-element at a certain index.
  360. It's not very efficient to iterate the sub-elements by index - see
  361. getNextElement() for an example of how best to iterate.
  362. @returns the n'th child of this element, or 0 if the index is out-of-range
  363. @see getNextElement, isTextElement, getChildByName
  364. */
  365. XmlElement* getChildElement (int index) const throw();
  366. /** Returns the first sub-element with a given tag-name.
  367. @param tagNameToLookFor the tag name of the element you want to find
  368. @returns the first element with this tag name, or 0 if none is found
  369. @see getNextElement, isTextElement, getChildElement
  370. */
  371. XmlElement* getChildByName (const String& tagNameToLookFor) const throw();
  372. //==============================================================================
  373. /** Appends an element to this element's list of children.
  374. Child elements are deleted automatically when their parent is deleted, so
  375. make sure the object that you pass in will not be deleted by anything else,
  376. and make sure it's not already the child of another element.
  377. @see getFirstChildElement, getNextElement, getNumChildElements,
  378. getChildElement, removeChildElement
  379. */
  380. void addChildElement (XmlElement* const newChildElement) throw();
  381. /** Inserts an element into this element's list of children.
  382. Child elements are deleted automatically when their parent is deleted, so
  383. make sure the object that you pass in will not be deleted by anything else,
  384. and make sure it's not already the child of another element.
  385. @param newChildNode the element to add
  386. @param indexToInsertAt the index at which to insert the new element - if this is
  387. below zero, it will be added to the end of the list
  388. @see addChildElement, insertChildElement
  389. */
  390. void insertChildElement (XmlElement* newChildNode,
  391. int indexToInsertAt) throw();
  392. /** Creates a new element with the given name and returns it, after adding it
  393. as a child element.
  394. This is a handy method that means that instead of writing this:
  395. @code
  396. XmlElement* newElement = new XmlElement ("foobar");
  397. myParentElement->addChildElement (newElement);
  398. @endcode
  399. ..you could just write this:
  400. @code
  401. XmlElement* newElement = myParentElement->createNewChildElement ("foobar");
  402. @endcode
  403. */
  404. XmlElement* createNewChildElement (const String& tagName);
  405. /** Replaces one of this element's children with another node.
  406. If the current element passed-in isn't actually a child of this element,
  407. this will return false and the new one won't be added. Otherwise, the
  408. existing element will be deleted, replaced with the new one, and it
  409. will return true.
  410. */
  411. bool replaceChildElement (XmlElement* currentChildElement,
  412. XmlElement* newChildNode) throw();
  413. /** Removes a child element.
  414. @param childToRemove the child to look for and remove
  415. @param shouldDeleteTheChild if true, the child will be deleted, if false it'll
  416. just remove it
  417. */
  418. void removeChildElement (XmlElement* childToRemove,
  419. bool shouldDeleteTheChild) throw();
  420. /** Deletes all the child elements in the element.
  421. @see removeChildElement, deleteAllChildElementsWithTagName
  422. */
  423. void deleteAllChildElements() throw();
  424. /** Deletes all the child elements with a given tag name.
  425. @see removeChildElement
  426. */
  427. void deleteAllChildElementsWithTagName (const String& tagName) throw();
  428. /** Returns true if the given element is a child of this one. */
  429. bool containsChildElement (const XmlElement* const possibleChild) const throw();
  430. /** Recursively searches all sub-elements to find one that contains the specified
  431. child element.
  432. */
  433. XmlElement* findParentElementOf (const XmlElement* elementToLookFor) throw();
  434. //==============================================================================
  435. /** Sorts the child elements using a comparator.
  436. This will use a comparator object to sort the elements into order. The object
  437. passed must have a method of the form:
  438. @code
  439. int compareElements (const XmlElement* first, const XmlElement* second);
  440. @endcode
  441. ..and this method must return:
  442. - a value of < 0 if the first comes before the second
  443. - a value of 0 if the two objects are equivalent
  444. - a value of > 0 if the second comes before the first
  445. To improve performance, the compareElements() method can be declared as static or const.
  446. @param comparator the comparator to use for comparing elements.
  447. @param retainOrderOfEquivalentItems if this is true, then items
  448. which the comparator says are equivalent will be
  449. kept in the order in which they currently appear
  450. in the array. This is slower to perform, but may
  451. be important in some cases. If it's false, a faster
  452. algorithm is used, but equivalent elements may be
  453. rearranged.
  454. */
  455. template <class ElementComparator>
  456. void sortChildElements (ElementComparator& comparator,
  457. const bool retainOrderOfEquivalentItems = false) throw()
  458. {
  459. const int num = getNumChildElements();
  460. if (num > 1)
  461. {
  462. HeapBlock <XmlElement*> elems (num);
  463. getChildElementsAsArray (elems);
  464. sortArray (comparator, (XmlElement**) elems, 0, num - 1, retainOrderOfEquivalentItems);
  465. reorderChildElements (elems, num);
  466. }
  467. }
  468. //==============================================================================
  469. /** Returns true if this element is a section of text.
  470. Elements can either be an XML tag element or a secton of text, so this
  471. is used to find out what kind of element this one is.
  472. @see getAllText, addTextElement, deleteAllTextElements
  473. */
  474. bool isTextElement() const throw();
  475. /** Returns the text for a text element.
  476. Note that if you have an element like this:
  477. @code<xyz>hello</xyz>@endcode
  478. then calling getText on the "xyz" element won't return "hello", because that is
  479. actually stored in a special text sub-element inside the xyz element. To get the
  480. "hello" string, you could either call getText on the (unnamed) sub-element, or
  481. use getAllSubText() to do this automatically.
  482. @see isTextElement, getAllSubText, getChildElementAllSubText
  483. */
  484. const String getText() const throw();
  485. /** Sets the text in a text element.
  486. Note that this is only a valid call if this element is a text element. If it's
  487. not, then no action will be performed.
  488. */
  489. void setText (const String& newText) throw();
  490. /** Returns all the text from this element's child nodes.
  491. This iterates all the child elements and when it finds text elements,
  492. it concatenates their text into a big string which it returns.
  493. E.g. @code<xyz> hello <x></x> there </xyz>@endcode
  494. if you called getAllSubText on the "xyz" element, it'd return "hello there".
  495. @see isTextElement, getChildElementAllSubText, getText, addTextElement
  496. */
  497. const String getAllSubText() const throw();
  498. /** Returns all the sub-text of a named child element.
  499. If there is a child element with the given tag name, this will return
  500. all of its sub-text (by calling getAllSubText() on it). If there is
  501. no such child element, this will return the default string passed-in.
  502. @see getAllSubText
  503. */
  504. const String getChildElementAllSubText (const String& childTagName,
  505. const String& defaultReturnValue) const throw();
  506. /** Appends a section of text to this element.
  507. @see isTextElement, getText, getAllSubText
  508. */
  509. void addTextElement (const String& text) throw();
  510. /** Removes all the text elements from this element.
  511. @see isTextElement, getText, getAllSubText, addTextElement
  512. */
  513. void deleteAllTextElements() throw();
  514. /** Creates a text element that can be added to a parent element.
  515. */
  516. static XmlElement* createTextElement (const String& text) throw();
  517. //==============================================================================
  518. juce_UseDebuggingNewOperator
  519. private:
  520. friend class XmlDocument;
  521. String tagName;
  522. XmlElement* firstChildElement;
  523. XmlElement* nextElement;
  524. struct XmlAttributeNode
  525. {
  526. XmlAttributeNode (const XmlAttributeNode& other) throw();
  527. XmlAttributeNode (const String& name, const String& value) throw();
  528. String name, value;
  529. XmlAttributeNode* next;
  530. private:
  531. XmlAttributeNode& operator= (const XmlAttributeNode&);
  532. };
  533. XmlAttributeNode* attributes;
  534. XmlElement (int) throw();
  535. void copyChildrenAndAttributesFrom (const XmlElement& other) throw();
  536. void writeElementAsText (OutputStream& out, int indentationLevel, int lineWrapLength) const;
  537. void getChildElementsAsArray (XmlElement**) const throw();
  538. void reorderChildElements (XmlElement** const, const int) throw();
  539. };
  540. #endif // __JUCE_XMLELEMENT_JUCEHEADER__