26static bool isValidXmlNameStartCharacter (
juce_wchar character)
noexcept
28 return character ==
':'
30 || (character >=
'a' && character <=
'z')
31 || (character >=
'A' && character <=
'Z')
32 || (character >= 0xc0 && character <= 0xd6)
33 || (character >= 0xd8 && character <= 0xf6)
34 || (character >= 0xf8 && character <= 0x2ff)
35 || (character >= 0x370 && character <= 0x37d)
36 || (character >= 0x37f && character <= 0x1fff)
37 || (character >= 0x200c && character <= 0x200d)
38 || (character >= 0x2070 && character <= 0x218f)
39 || (character >= 0x2c00 && character <= 0x2fef)
40 || (character >= 0x3001 && character <= 0xd7ff)
41 || (character >= 0xf900 && character <= 0xfdcf)
42 || (character >= 0xfdf0 && character <= 0xfffd)
43 || (character >= 0x10000 && character <= 0xeffff);
46static bool isValidXmlNameBodyCharacter (
juce_wchar character)
noexcept
48 return isValidXmlNameStartCharacter (character)
52 || (character >=
'0' && character <=
'9')
53 || (character >= 0x300 && character <= 0x036f)
54 || (character >= 0x203f && character <= 0x2040);
57XmlElement::XmlAttributeNode::XmlAttributeNode (
const XmlAttributeNode&
other) noexcept
63XmlElement::XmlAttributeNode::XmlAttributeNode (
const Identifier& n,
const String& v) noexcept
66 jassert (isValidXmlName (name));
72 jassert (isValidXmlName (name));
76XmlElement::XmlElement (
const String& tag)
77 : tagName (
StringPool::getGlobalPool().getPooledString (tag))
83 : tagName (
StringPool::getGlobalPool().getPooledString (tag))
89 : tagName (
StringPool::getGlobalPool().getPooledString (tag))
95 : tagName (tag.toString())
111 : tagName (
other.tagName)
113 copyChildrenAndAttributesFrom (
other);
122 tagName =
other.tagName;
123 copyChildrenAndAttributesFrom (
other);
130 : nextListItem (std::move (
other.nextListItem)),
131 firstChildElement (std::move (
other.firstChildElement)),
132 attributes (std::move (
other.attributes)),
133 tagName (std::move (
other.tagName))
141 removeAllAttributes();
142 deleteAllChildElements();
144 nextListItem = std::move (
other.nextListItem);
145 firstChildElement = std::move (
other.firstChildElement);
146 attributes = std::move (
other.attributes);
147 tagName = std::move (
other.tagName);
152void XmlElement::copyChildrenAndAttributesFrom (
const XmlElement&
other)
154 jassert (firstChildElement.get() ==
nullptr);
155 firstChildElement.addCopyOfList (
other.firstChildElement);
163 firstChildElement.deleteAll();
168namespace XmlOutputFunctions
170 namespace LegalCharLookupTable
175 enum { v = ((c >=
'a' && c <=
'z')
176 || (c >=
'A' && c <=
'Z')
177 || (c >=
'0' && c <=
'9')
178 || c ==
' ' || c ==
'.' || c ==
',' || c ==
';'
179 || c ==
':' || c ==
'-' || c ==
'(' || c ==
')'
180 || c ==
'_' || c ==
'+' || c ==
'=' || c ==
'?'
181 || c ==
'!' || c ==
'$' || c ==
'#' || c ==
'@'
182 || c ==
'[' || c ==
']' || c ==
'/' || c ==
'|'
183 || c ==
'*' || c ==
'%' || c ==
'~' || c ==
'{'
184 || c ==
'}' || c ==
'\'' || c ==
'\\')
185 ? (1 << (c & 7)) : 0 };
188 template <
int tableIndex>
197 static bool isLegal (
uint32 c)
noexcept
205 && (
legalChars[c >> 3] & (1 << (c & 7))) != 0;
211 auto t = text.getCharPointer();
215 auto character = (
uint32) t.getAndAdvance();
220 if (LegalCharLookupTable::isLegal (character))
249 static void writeSpaces (OutputStream& out,
const size_t numSpaces)
255void XmlElement::writeElementAsText (OutputStream&
outputStream,
258 const char* newLineChars)
const
272 for (
auto*
att = attributes.
get();
att !=
nullptr;
att =
att->nextListItem)
285 XmlOutputFunctions::escapeIllegalXmlChars (
outputStream,
att->value,
true);
291 if (
auto* child = firstChildElement.get())
296 for (; child !=
nullptr; child = child->nextListItem)
298 if (child->isTextElement())
300 XmlOutputFunctions::escapeIllegalXmlChars (
outputStream, child->getText(),
false);
373 output <<
"<?xml version=\"1.0\" encoding=\"";
391 output << options.
dtd;
399 writeElementAsText (output, options.
newLineChars ==
nullptr ? -1 : 0,
424 return tempFile.overwriteTargetFileWithTemporary();
434 options.lineWrapLength = lineWrapLength;
437 options.newLineChars =
nullptr;
442void XmlElement::writeToStream (OutputStream& output, StringRef
dtdToUse,
450 options.lineWrapLength = lineWrapLength;
453 options.newLineChars =
nullptr;
458bool XmlElement::writeToFile (
const File& file, StringRef
dtdToUse,
464 options.lineWrapLength = lineWrapLength;
466 return writeTo (file, options);
498 auto* e = nextListItem.get();
515 return attributes.
size();
518static const String& getEmptyStringRef() noexcept
526 if (
auto*
att = attributes[index].get())
527 return att->name.toString();
529 return getEmptyStringRef();
534 if (
auto*
att = attributes[index].get())
537 return getEmptyStringRef();
542 for (
auto*
att = attributes.
get();
att !=
nullptr;
att =
att->nextListItem)
560 return getEmptyStringRef();
582 return att->value.getDoubleValue();
591 auto firstChar = *(
att->value.getCharPointer().findEndOfWhitespace());
605 const bool ignoreCase)
const noexcept
617 if (attributes ==
nullptr)
623 for (
auto*
att = attributes.
get(); ;
att =
att->nextListItem)
631 if (
att->nextListItem ==
nullptr)
652 for (
auto*
att = &attributes;
att->
get() !=
nullptr;
att = &(
att->get()->nextListItem))
656 delete att->removeNext();
670 return firstChildElement.size();
675 return firstChildElement[index].get();
682 for (
auto* child = firstChildElement.get(); child !=
nullptr; child = child->nextListItem)
693 for (
auto* child = firstChildElement.get(); child !=
nullptr; child = child->nextListItem)
707 firstChildElement.append (
newNode);
729 firstChildElement.insertNext (
newNode);
748 delete p->replaceNext (
newNode);
776 if (
other ==
nullptr || tagName !=
other->tagName)
783 for (
auto*
att = attributes.
get();
att !=
nullptr;
att =
att->nextListItem)
785 if (!
other->compareAttribute (
att->name,
att->value))
820 auto*
thisChild = firstChildElement.get();
846 firstChildElement.deleteAll();
851 for (
auto* child = firstChildElement.get(); child !=
nullptr;)
853 auto*
nextChild = child->nextListItem.get();
855 if (child->hasTagName (name))
872 for (
auto* child = firstChildElement.get(); child !=
nullptr; child = child->nextListItem)
884void XmlElement::getChildElementsAsArray (
XmlElement**
elems)
const noexcept
886 firstChildElement.copyToArray (
elems);
889void XmlElement::reorderChildElements (XmlElement**
elems,
int num)
noexcept
892 firstChildElement = e;
894 for (
int i = 1; i < num; ++i)
896 e->nextListItem =
elems[i];
900 e->nextListItem =
nullptr;
906 return tagName.isEmpty();
909static const String juce_xmltextContentAttributeName (
"text");
934 return firstChildElement.get()->getAllSubText();
938 for (
auto* child = firstChildElement.get(); child !=
nullptr; child = child->nextListItem)
939 mem << child->getAllSubText();
947 return child->getAllSubText();
955 e->setAttribute (juce_xmltextContentAttributeName, text);
961 if (text.isEmpty() || ! isValidXmlNameStartCharacter (text.text.getAndAdvance()))
969 if (! isValidXmlNameBodyCharacter (text.text.getAndAdvance()))
981 for (
auto* child = firstChildElement.get(); child !=
nullptr;)
983 auto* next = child->nextListItem.get();
985 if (child->isTextElement())
1000 :
UnitTest (
"XmlElement", UnitTestCategories::xml)
1003 void runTest()
override
1006 beginTest (
"Float formatting");
1008 auto element = std::make_unique<XmlElement> (
"test");
1009 Identifier number (
"number");
1014 tests[1.01] =
"1.01";
1015 tests[0.76378] =
"0.76378";
1016 tests[-10] =
"-10.0";
1017 tests[10.01] =
"10.01";
1018 tests[0.0123] =
"0.0123";
1019 tests[-3.7e-27] =
"-3.7e-27";
1020 tests[1e+40] =
"1.0e40";
1021 tests[-12345678901234567.0] =
"-1.234567890123457e16";
1022 tests[192000] =
"192000.0";
1023 tests[1234567] =
"1.234567e6";
1024 tests[0.00006] =
"0.00006";
1025 tests[0.000006] =
"6.0e-6";
1027 for (
auto& test : tests)
1029 element->setAttribute (number, test.first);
1030 expectEquals (element->getStringAttribute (number), test.second);
Wraps a pointer to a null-terminated UTF-8 character string, and provides various methods to operate ...
An output stream that writes into a local file.
const Result & getStatus() const noexcept
Returns the status of the file stream.
bool openedOk() const noexcept
Returns true if the stream opened without problems.
void flush() override
If the stream is using a buffer, this will ensure it gets written out to the destination.
Represents a local file or directory.
Represents a string identifier, designed for accessing properties by name.
void deleteAll()
Iterates the list, calling the delete operator on all of its elements and leaving this pointer empty.
ObjectType * get() const noexcept
Returns the item which this pointer points to.
void addCopyOfList(const LinkedListPointer &other)
Creates copies of all the items in another list and adds them to this one.
int size() const noexcept
Returns the number of items in the list.
Writes data to an internal memory buffer, which grows as required.
The base class for streams that write data to some kind of destination.
virtual bool writeByte(char byte)
Writes a single byte to the stream.
bool failed() const noexcept
Returns true if this result indicates a failure.
A StringPool holds a set of shared strings, which reduces storage overheads and improves comparison s...
String getPooledString(const String &original)
Returns a pointer to a shared copy of the string that is passed in.
static StringPool & getGlobalPool() noexcept
Returns a shared global pool which is used for things like Identifiers, XML parsing.
A simple class for holding temporary references to a string literal or String.
String upToFirstOccurrenceOf(StringRef substringToEndWith, bool includeSubStringInResult, bool ignoreCase) const
Returns the start of this string, up to the first occurrence of a substring.
String fromLastOccurrenceOf(StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const
Returns a section of the string starting from the last occurrence of a given substring.
CharPointer_UTF8 CharPointerType
This is the character encoding type used internally to store the string.
int getIntValue() const noexcept
Reads the value of the string as a decimal number (up to 32 bits in size).
bool isNotEmpty() const noexcept
Returns true if the string contains at least one character.
Manages a temporary file, which will be deleted when this object is deleted.
This is a base class for classes that perform a unit test.
Used to build a tree of elements representing an XML document.
XmlElement * getNextElementWithTagName(StringRef requiredTagName) const
Returns the next of this element's siblings which has the specified tag name.
void setTagName(StringRef newTagName)
Changes this elements tag name.
void addTextElement(const String &text)
Appends a section of text to this element.
int getNumChildElements() const noexcept
Returns the number of sub-elements in this element.
XmlElement(const String &tagName)
Creates an XmlElement with this tag name.
void insertChildElement(XmlElement *newChildElement, int indexToInsertAt) noexcept
Inserts an element into this element's list of children.
XmlElement * getChildByName(StringRef tagNameToLookFor) const noexcept
Returns the first sub-element with a given tag-name.
bool isTextElement() const noexcept
Returns true if this element is a section of text.
void deleteAllChildElementsWithTagName(StringRef tagName) noexcept
Deletes all the child elements with a given tag name.
String toString(const TextFormat &format={}) const
Returns a text version of this XML element.
void addChildElement(XmlElement *newChildElement) noexcept
Appends an element to this element's list of children.
String getTagNameWithoutNamespace() const
Returns the part of the tag-name that follows any namespace declaration.
double getDoubleAttribute(StringRef attributeName, double defaultReturnValue=0.0) const
Returns the value of a named attribute as floating-point.
const String & getAttributeValue(int attributeIndex) const noexcept
Returns the value of one of the elements attributes.
void prependChildElement(XmlElement *newChildElement) noexcept
Inserts an element at the beginning of this element's list of children.
XmlElement * createNewChildElement(StringRef tagName)
Creates a new element with the given name and returns it, after adding it as a child element.
String getChildElementAllSubText(StringRef childTagName, const String &defaultReturnValue) const
Returns all the sub-text of a named child element.
const String & getText() const noexcept
Returns the text for a text element.
void deleteAllTextElements() noexcept
Removes all the text elements from this element.
~XmlElement() noexcept
Deleting an XmlElement will also delete all of its child elements.
String getAllSubText() const
Returns all the text from this element's child nodes.
bool compareAttribute(StringRef attributeName, StringRef stringToCompareAgainst, bool ignoreCase=false) const noexcept
Compares the value of a named attribute with a value passed-in.
void setText(const String &newText)
Sets the text in a text element.
XmlElement * findParentElementOf(const XmlElement *childToSearchFor) noexcept
Recursively searches all sub-elements of this one, looking for an element which is the direct parent ...
bool isEquivalentTo(const XmlElement *other, bool ignoreOrderOfAttributes) const noexcept
Compares two XmlElements to see if they contain the same text and attributes.
bool getBoolAttribute(StringRef attributeName, bool defaultReturnValue=false) const
Returns the value of a named attribute as a boolean.
void removeAllAttributes() noexcept
Removes all attributes from this element.
static XmlElement * createTextElement(const String &text)
Creates a text element that can be added to a parent element.
const String & getAttributeName(int attributeIndex) const noexcept
Returns the name of one of the elements attributes.
bool hasAttribute(StringRef attributeName) const noexcept
Checks whether the element contains an attribute with a certain name.
bool containsChildElement(const XmlElement *possibleChild) const noexcept
Returns true if the given element is a child of this one.
bool replaceChildElement(XmlElement *currentChildElement, XmlElement *newChildNode) noexcept
Replaces one of this element's children with another node.
void writeTo(OutputStream &output, const TextFormat &format={}) const
Writes the document to a stream as UTF-8.
XmlElement * getChildElement(int index) const noexcept
Returns the sub-element at a certain index.
XmlElement & operator=(const XmlElement &)
Creates a (deep) copy of another element.
void deleteAllChildElements() noexcept
Deletes all the child elements in the element.
bool hasTagName(StringRef possibleTagName) const noexcept
Tests whether this element has a particular tag name.
void removeAttribute(const Identifier &attributeName) noexcept
Removes a named attribute from the element.
XmlElement * getChildByAttribute(StringRef attributeName, StringRef attributeValue) const noexcept
Returns the first sub-element which has an attribute that matches the given value.
int getNumAttributes() const noexcept
Returns the number of XML attributes this element contains.
void removeChildElement(XmlElement *childToRemove, bool shouldDeleteTheChild) noexcept
Removes a child element.
static bool isValidXmlName(StringRef possibleName) noexcept
Checks if a given string is a valid XML name.
int getIntAttribute(StringRef attributeName, int defaultReturnValue=0) const
Returns the value of a named attribute as an integer.
const String & getStringAttribute(StringRef attributeName) const noexcept
Returns the value of a named attribute.
bool hasTagNameIgnoringNamespace(StringRef possibleTagName) const
Tests whether this element has a particular tag name, ignoring any XML namespace prefix.
String getNamespace() const
Returns the namespace portion of the tag-name, or an empty string if none is specified.
void setAttribute(const Identifier &attributeName, const String &newValue)
Adds a named attribute to the element.
wchar_t juce_wchar
A platform-independent 32-bit unicode character type.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
unsigned int uint32
A platform-independent 32-bit unsigned integer type.
A struct containing options for formatting the text when representing an XML element as a string.
String customEncoding
If not empty and addDefaultHeader is true, this will be set as the encoding.
TextFormat withoutHeader() const
returns a copy of this format with the addDefaultHeader flag set to false.
bool addDefaultHeader
If true, a default header will be generated; otherwise just bare XML will be emitted.
String dtd
If supplied, this DTD will be added to the document.
const char * newLineChars
Allows the newline characters to be set.
String customHeader
If supplied, this header will be used (and customEncoding & addDefaultHeader will be ignored).
TextFormat singleLine() const
returns a copy of this format with newLineChars set to nullptr.
int lineWrapLength
A maximum line length before wrapping is done.
TextFormat()
Default constructor.