Loading...
Searching...
No Matches
juce_XmlElement.cpp
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
static bool isValidXmlNameStartCharacter (juce_wchar character) noexcept
{
return character == ':'
|| character == '_'
|| (character >= 'a' && character <= 'z')
|| (character >= 'A' && character <= 'Z')
|| (character >= 0xc0 && character <= 0xd6)
|| (character >= 0xd8 && character <= 0xf6)
|| (character >= 0xf8 && character <= 0x2ff)
|| (character >= 0x370 && character <= 0x37d)
|| (character >= 0x37f && character <= 0x1fff)
|| (character >= 0x200c && character <= 0x200d)
|| (character >= 0x2070 && character <= 0x218f)
|| (character >= 0x2c00 && character <= 0x2fef)
|| (character >= 0x3001 && character <= 0xd7ff)
|| (character >= 0xf900 && character <= 0xfdcf)
|| (character >= 0xfdf0 && character <= 0xfffd)
|| (character >= 0x10000 && character <= 0xeffff);
}
static bool isValidXmlNameBodyCharacter (juce_wchar character) noexcept
{
return isValidXmlNameStartCharacter (character)
|| character == '-'
|| character == '.'
|| character == 0xb7
|| (character >= '0' && character <= '9')
|| (character >= 0x300 && character <= 0x036f)
|| (character >= 0x203f && character <= 0x2040);
}
XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) noexcept
: name (other.name),
value (other.value)
{
}
XmlElement::XmlAttributeNode::XmlAttributeNode (const Identifier& n, const String& v) noexcept
: name (n), value (v)
{
jassert (isValidXmlName (name));
}
XmlElement::XmlAttributeNode::XmlAttributeNode (String::CharPointerType nameStart, String::CharPointerType nameEnd)
: name (nameStart, nameEnd)
{
jassert (isValidXmlName (name));
}
//==============================================================================
XmlElement::XmlElement (const String& tag)
: tagName (StringPool::getGlobalPool().getPooledString (tag))
{
jassert (isValidXmlName (tagName));
}
XmlElement::XmlElement (const char* tag)
: tagName (StringPool::getGlobalPool().getPooledString (tag))
{
jassert (isValidXmlName (tagName));
}
XmlElement::XmlElement (StringRef tag)
: tagName (StringPool::getGlobalPool().getPooledString (tag))
{
jassert (isValidXmlName (tagName));
}
XmlElement::XmlElement (const Identifier& tag)
: tagName (tag.toString())
{
jassert (isValidXmlName (tagName));
}
XmlElement::XmlElement (String::CharPointerType tagNameStart, String::CharPointerType tagNameEnd)
: tagName (StringPool::getGlobalPool().getPooledString (tagNameStart, tagNameEnd))
{
jassert (isValidXmlName (tagName));
}
XmlElement::XmlElement (int /*dummy*/) noexcept
{
}
XmlElement::XmlElement (const XmlElement& other)
: tagName (other.tagName)
{
copyChildrenAndAttributesFrom (other);
}
XmlElement& XmlElement::operator= (const XmlElement& other)
{
if (this != &other)
{
removeAllAttributes();
deleteAllChildElements();
tagName = other.tagName;
copyChildrenAndAttributesFrom (other);
}
return *this;
}
XmlElement::XmlElement (XmlElement&& other) noexcept
: nextListItem (std::move (other.nextListItem)),
firstChildElement (std::move (other.firstChildElement)),
attributes (std::move (other.attributes)),
tagName (std::move (other.tagName))
{
}
XmlElement& XmlElement::operator= (XmlElement&& other) noexcept
{
jassert (this != &other); // hopefully the compiler should make this situation impossible!
removeAllAttributes();
deleteAllChildElements();
nextListItem = std::move (other.nextListItem);
firstChildElement = std::move (other.firstChildElement);
attributes = std::move (other.attributes);
tagName = std::move (other.tagName);
return *this;
}
void XmlElement::copyChildrenAndAttributesFrom (const XmlElement& other)
{
jassert (firstChildElement.get() == nullptr);
firstChildElement.addCopyOfList (other.firstChildElement);
jassert (attributes.get() == nullptr);
attributes.addCopyOfList (other.attributes);
}
XmlElement::~XmlElement() noexcept
{
firstChildElement.deleteAll();
attributes.deleteAll();
}
//==============================================================================
namespace XmlOutputFunctions
{
namespace LegalCharLookupTable
{
template <int c>
struct Bit
{
enum { v = ((c >= 'a' && c <= 'z')
|| (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9')
|| c == ' ' || c == '.' || c == ',' || c == ';'
|| c == ':' || c == '-' || c == '(' || c == ')'
|| c == '_' || c == '+' || c == '=' || c == '?'
|| c == '!' || c == '$' || c == '#' || c == '@'
|| c == '[' || c == ']' || c == '/' || c == '|'
|| c == '*' || c == '%' || c == '~' || c == '{'
|| c == '}' || c == '\'' || c == '\\')
? (1 << (c & 7)) : 0 };
};
template <int tableIndex>
struct Byte
{
enum { v = (int) Bit<tableIndex * 8 + 0>::v | (int) Bit<tableIndex * 8 + 1>::v
| (int) Bit<tableIndex * 8 + 2>::v | (int) Bit<tableIndex * 8 + 3>::v
| (int) Bit<tableIndex * 8 + 4>::v | (int) Bit<tableIndex * 8 + 5>::v
| (int) Bit<tableIndex * 8 + 6>::v | (int) Bit<tableIndex * 8 + 7>::v };
};
static bool isLegal (uint32 c) noexcept
{
static const unsigned char legalChars[] = { Byte< 0>::v, Byte< 1>::v, Byte< 2>::v, Byte< 3>::v,
Byte< 4>::v, Byte< 5>::v, Byte< 6>::v, Byte< 7>::v,
Byte< 8>::v, Byte< 9>::v, Byte<10>::v, Byte<11>::v,
Byte<12>::v, Byte<13>::v, Byte<14>::v, Byte<15>::v };
return c < sizeof (legalChars) * 8
&& (legalChars[c >> 3] & (1 << (c & 7))) != 0;
}
}
static void escapeIllegalXmlChars (OutputStream& outputStream, const String& text, bool changeNewLines)
{
auto t = text.getCharPointer();
for (;;)
{
auto character = (uint32) t.getAndAdvance();
if (character == 0)
break;
if (LegalCharLookupTable::isLegal (character))
{
outputStream << (char) character;
}
else
{
switch (character)
{
case '&': outputStream << "&amp;"; break;
case '"': outputStream << "&quot;"; break;
case '>': outputStream << "&gt;"; break;
case '<': outputStream << "&lt;"; break;
case '\n':
case '\r':
if (! changeNewLines)
{
outputStream << (char) character;
break;
}
JUCE_FALLTHROUGH
default:
outputStream << "&#" << ((int) character) << ';';
break;
}
}
}
}
static void writeSpaces (OutputStream& out, const size_t numSpaces)
{
out.writeRepeatedByte (' ', numSpaces);
}
}
void XmlElement::writeElementAsText (OutputStream& outputStream,
int indentationLevel,
int lineWrapLength,
const char* newLineChars) const
{
if (indentationLevel >= 0)
XmlOutputFunctions::writeSpaces (outputStream, (size_t) indentationLevel);
if (! isTextElement())
{
outputStream.writeByte ('<');
outputStream << tagName;
{
auto attIndent = (size_t) (indentationLevel + tagName.length() + 1);
int lineLen = 0;
for (auto* att = attributes.get(); att != nullptr; att = att->nextListItem)
{
if (lineLen > lineWrapLength && indentationLevel >= 0)
{
outputStream << newLineChars;
XmlOutputFunctions::writeSpaces (outputStream, attIndent);
lineLen = 0;
}
auto startPos = outputStream.getPosition();
outputStream.writeByte (' ');
outputStream << att->name;
outputStream.write ("=\"", 2);
XmlOutputFunctions::escapeIllegalXmlChars (outputStream, att->value, true);
outputStream.writeByte ('"');
lineLen += (int) (outputStream.getPosition() - startPos);
}
}
if (auto* child = firstChildElement.get())
{
outputStream.writeByte ('>');
bool lastWasTextNode = false;
for (; child != nullptr; child = child->nextListItem)
{
if (child->isTextElement())
{
XmlOutputFunctions::escapeIllegalXmlChars (outputStream, child->getText(), false);
lastWasTextNode = true;
}
else
{
if (indentationLevel >= 0 && ! lastWasTextNode)
outputStream << newLineChars;
child->writeElementAsText (outputStream,
lastWasTextNode ? 0 : (indentationLevel + (indentationLevel >= 0 ? 2 : 0)), lineWrapLength,
newLineChars);
lastWasTextNode = false;
}
}
if (indentationLevel >= 0 && ! lastWasTextNode)
{
outputStream << newLineChars;
XmlOutputFunctions::writeSpaces (outputStream, (size_t) indentationLevel);
}
outputStream.write ("</", 2);
outputStream << tagName;
outputStream.writeByte ('>');
}
else
{
outputStream.write ("/>", 2);
}
}
else
{
XmlOutputFunctions::escapeIllegalXmlChars (outputStream, getText(), false);
}
}
XmlElement::TextFormat::TextFormat() {}
XmlElement::TextFormat XmlElement::TextFormat::singleLine() const
{
auto f = *this;
f.newLineChars = nullptr;
return f;
}
XmlElement::TextFormat XmlElement::TextFormat::withoutHeader() const
{
auto f = *this;
f.addDefaultHeader = false;
return f;
}
String XmlElement::toString (const TextFormat& options) const
{
MemoryOutputStream mem (2048);
writeTo (mem, options);
return mem.toUTF8();
}
void XmlElement::writeTo (OutputStream& output, const TextFormat& options) const
{
if (options.customHeader.isNotEmpty())
{
output << options.customHeader;
if (options.newLineChars == nullptr)
output.writeByte (' ');
else
output << options.newLineChars
<< options.newLineChars;
}
else if (options.addDefaultHeader)
{
output << "<?xml version=\"1.0\" encoding=\"";
if (options.customEncoding.isNotEmpty())
output << options.customEncoding;
else
output << "UTF-8";
output << "\"?>";
if (options.newLineChars == nullptr)
output.writeByte (' ');
else
output << options.newLineChars
<< options.newLineChars;
}
if (options.dtd.isNotEmpty())
{
output << options.dtd;
if (options.newLineChars == nullptr)
output.writeByte (' ');
else
output << options.newLineChars;
}
writeElementAsText (output, options.newLineChars == nullptr ? -1 : 0,
options.lineWrapLength,
options.newLineChars);
if (options.newLineChars != nullptr)
output << options.newLineChars;
}
bool XmlElement::writeTo (const File& destinationFile, const TextFormat& options) const
{
TemporaryFile tempFile (destinationFile);
{
FileOutputStream out (tempFile.getFile());
if (! out.openedOk())
return false;
writeTo (out, options);
out.flush(); // (called explicitly to force an fsync on posix)
if (out.getStatus().failed())
return false;
}
return tempFile.overwriteTargetFileWithTemporary();
}
String XmlElement::createDocument (StringRef dtdToUse, bool allOnOneLine, bool includeXmlHeader,
StringRef encodingType, int lineWrapLength) const
{
TextFormat options;
options.dtd = dtdToUse;
options.customEncoding = encodingType;
options.addDefaultHeader = includeXmlHeader;
options.lineWrapLength = lineWrapLength;
if (allOnOneLine)
options.newLineChars = nullptr;
return toString (options);
}
void XmlElement::writeToStream (OutputStream& output, StringRef dtdToUse,
bool allOnOneLine, bool includeXmlHeader,
StringRef encodingType, int lineWrapLength) const
{
TextFormat options;
options.dtd = dtdToUse;
options.customEncoding = encodingType;
options.addDefaultHeader = includeXmlHeader;
options.lineWrapLength = lineWrapLength;
if (allOnOneLine)
options.newLineChars = nullptr;
writeTo (output, options);
}
bool XmlElement::writeToFile (const File& file, StringRef dtdToUse,
StringRef encodingType, int lineWrapLength) const
{
TextFormat options;
options.dtd = dtdToUse;
options.customEncoding = encodingType;
options.lineWrapLength = lineWrapLength;
return writeTo (file, options);
}
//==============================================================================
bool XmlElement::hasTagName (StringRef possibleTagName) const noexcept
{
const bool matches = tagName.equalsIgnoreCase (possibleTagName);
// XML tags should be case-sensitive, so although this method allows a
// case-insensitive match to pass, you should try to avoid this.
jassert ((! matches) || tagName == possibleTagName);
return matches;
}
String XmlElement::getNamespace() const
{
return tagName.upToFirstOccurrenceOf (":", false, false);
}
String XmlElement::getTagNameWithoutNamespace() const
{
return tagName.fromLastOccurrenceOf (":", false, false);
}
bool XmlElement::hasTagNameIgnoringNamespace (StringRef possibleTagName) const
{
return hasTagName (possibleTagName) || getTagNameWithoutNamespace() == possibleTagName;
}
XmlElement* XmlElement::getNextElementWithTagName (StringRef requiredTagName) const
{
auto* e = nextListItem.get();
while (e != nullptr && ! e->hasTagName (requiredTagName))
e = e->nextListItem;
return e;
}
void XmlElement::setTagName (StringRef newTagName)
{
jassert (isValidXmlName (newTagName));
tagName = StringPool::getGlobalPool().getPooledString (newTagName);
}
//==============================================================================
int XmlElement::getNumAttributes() const noexcept
{
return attributes.size();
}
static const String& getEmptyStringRef() noexcept
{
static String empty;
return empty;
}
const String& XmlElement::getAttributeName (const int index) const noexcept
{
if (auto* att = attributes[index].get())
return att->name.toString();
return getEmptyStringRef();
}
const String& XmlElement::getAttributeValue (const int index) const noexcept
{
if (auto* att = attributes[index].get())
return att->value;
return getEmptyStringRef();
}
XmlElement::XmlAttributeNode* XmlElement::getAttribute (StringRef attributeName) const noexcept
{
for (auto* att = attributes.get(); att != nullptr; att = att->nextListItem)
if (att->name == attributeName)
return att;
return nullptr;
}
bool XmlElement::hasAttribute (StringRef attributeName) const noexcept
{
return getAttribute (attributeName) != nullptr;
}
//==============================================================================
const String& XmlElement::getStringAttribute (StringRef attributeName) const noexcept
{
if (auto* att = getAttribute (attributeName))
return att->value;
return getEmptyStringRef();
}
String XmlElement::getStringAttribute (StringRef attributeName, const String& defaultReturnValue) const
{
if (auto* att = getAttribute (attributeName))
return att->value;
return defaultReturnValue;
}
int XmlElement::getIntAttribute (StringRef attributeName, const int defaultReturnValue) const
{
if (auto* att = getAttribute (attributeName))
return att->value.getIntValue();
return defaultReturnValue;
}
double XmlElement::getDoubleAttribute (StringRef attributeName, const double defaultReturnValue) const
{
if (auto* att = getAttribute (attributeName))
return att->value.getDoubleValue();
return defaultReturnValue;
}
bool XmlElement::getBoolAttribute (StringRef attributeName, const bool defaultReturnValue) const
{
if (auto* att = getAttribute (attributeName))
{
auto firstChar = *(att->value.getCharPointer().findEndOfWhitespace());
return firstChar == '1'
|| firstChar == 't'
|| firstChar == 'y'
|| firstChar == 'T'
|| firstChar == 'Y';
}
return defaultReturnValue;
}
bool XmlElement::compareAttribute (StringRef attributeName,
StringRef stringToCompareAgainst,
const bool ignoreCase) const noexcept
{
if (auto* att = getAttribute (attributeName))
return ignoreCase ? att->value.equalsIgnoreCase (stringToCompareAgainst)
: att->value == stringToCompareAgainst;
return false;
}
//==============================================================================
void XmlElement::setAttribute (const Identifier& attributeName, const String& value)
{
if (attributes == nullptr)
{
attributes = new XmlAttributeNode (attributeName, value);
}
else
{
for (auto* att = attributes.get(); ; att = att->nextListItem)
{
if (att->name == attributeName)
{
att->value = value;
break;
}
if (att->nextListItem == nullptr)
{
att->nextListItem = new XmlAttributeNode (attributeName, value);
break;
}
}
}
}
void XmlElement::setAttribute (const Identifier& attributeName, const int number)
{
setAttribute (attributeName, String (number));
}
void XmlElement::setAttribute (const Identifier& attributeName, const double number)
{
setAttribute (attributeName, serialiseDouble (number));
}
void XmlElement::removeAttribute (const Identifier& attributeName) noexcept
{
for (auto* att = &attributes; att->get() != nullptr; att = &(att->get()->nextListItem))
{
if (att->get()->name == attributeName)
{
delete att->removeNext();
break;
}
}
}
void XmlElement::removeAllAttributes() noexcept
{
attributes.deleteAll();
}
//==============================================================================
int XmlElement::getNumChildElements() const noexcept
{
return firstChildElement.size();
}
XmlElement* XmlElement::getChildElement (const int index) const noexcept
{
return firstChildElement[index].get();
}
XmlElement* XmlElement::getChildByName (StringRef childName) const noexcept
{
jassert (! childName.isEmpty());
for (auto* child = firstChildElement.get(); child != nullptr; child = child->nextListItem)
if (child->hasTagName (childName))
return child;
return nullptr;
}
XmlElement* XmlElement::getChildByAttribute (StringRef attributeName, StringRef attributeValue) const noexcept
{
jassert (! attributeName.isEmpty());
for (auto* child = firstChildElement.get(); child != nullptr; child = child->nextListItem)
if (child->compareAttribute (attributeName, attributeValue))
return child;
return nullptr;
}
void XmlElement::addChildElement (XmlElement* const newNode) noexcept
{
if (newNode != nullptr)
{
// The element being added must not be a child of another node!
jassert (newNode->nextListItem == nullptr);
firstChildElement.append (newNode);
}
}
void XmlElement::insertChildElement (XmlElement* const newNode, int indexToInsertAt) noexcept
{
if (newNode != nullptr)
{
// The element being added must not be a child of another node!
jassert (newNode->nextListItem == nullptr);
firstChildElement.insertAtIndex (indexToInsertAt, newNode);
}
}
void XmlElement::prependChildElement (XmlElement* newNode) noexcept
{
if (newNode != nullptr)
{
// The element being added must not be a child of another node!
jassert (newNode->nextListItem == nullptr);
firstChildElement.insertNext (newNode);
}
}
XmlElement* XmlElement::createNewChildElement (StringRef childTagName)
{
auto newElement = new XmlElement (childTagName);
addChildElement (newElement);
return newElement;
}
bool XmlElement::replaceChildElement (XmlElement* const currentChildElement,
XmlElement* const newNode) noexcept
{
if (newNode != nullptr)
{
if (auto* p = firstChildElement.findPointerTo (currentChildElement))
{
if (currentChildElement != newNode)
delete p->replaceNext (newNode);
return true;
}
}
return false;
}
void XmlElement::removeChildElement (XmlElement* const childToRemove,
const bool shouldDeleteTheChild) noexcept
{
if (childToRemove != nullptr)
{
jassert (containsChildElement (childToRemove));
firstChildElement.remove (childToRemove);
if (shouldDeleteTheChild)
delete childToRemove;
}
}
bool XmlElement::isEquivalentTo (const XmlElement* const other,
const bool ignoreOrderOfAttributes) const noexcept
{
if (this != other)
{
if (other == nullptr || tagName != other->tagName)
return false;
if (ignoreOrderOfAttributes)
{
int totalAtts = 0;
for (auto* att = attributes.get(); att != nullptr; att = att->nextListItem)
{
if (! other->compareAttribute (att->name, att->value))
return false;
++totalAtts;
}
if (totalAtts != other->getNumAttributes())
return false;
}
else
{
auto* thisAtt = attributes.get();
auto* otherAtt = other->attributes.get();
for (;;)
{
if (thisAtt == nullptr || otherAtt == nullptr)
{
if (thisAtt == otherAtt) // both nullptr, so it's a match
break;
return false;
}
if (thisAtt->name != otherAtt->name
|| thisAtt->value != otherAtt->value)
{
return false;
}
thisAtt = thisAtt->nextListItem;
otherAtt = otherAtt->nextListItem;
}
}
auto* thisChild = firstChildElement.get();
auto* otherChild = other->firstChildElement.get();
for (;;)
{
if (thisChild == nullptr || otherChild == nullptr)
{
if (thisChild == otherChild) // both 0, so it's a match
break;
return false;
}
if (! thisChild->isEquivalentTo (otherChild, ignoreOrderOfAttributes))
return false;
thisChild = thisChild->nextListItem;
otherChild = otherChild->nextListItem;
}
}
return true;
}
void XmlElement::deleteAllChildElements() noexcept
{
firstChildElement.deleteAll();
}
void XmlElement::deleteAllChildElementsWithTagName (StringRef name) noexcept
{
for (auto* child = firstChildElement.get(); child != nullptr;)
{
auto* nextChild = child->nextListItem.get();
if (child->hasTagName (name))
removeChildElement (child, true);
child = nextChild;
}
}
bool XmlElement::containsChildElement (const XmlElement* const possibleChild) const noexcept
{
return firstChildElement.contains (possibleChild);
}
XmlElement* XmlElement::findParentElementOf (const XmlElement* const elementToLookFor) noexcept
{
if (this == elementToLookFor || elementToLookFor == nullptr)
return nullptr;
for (auto* child = firstChildElement.get(); child != nullptr; child = child->nextListItem)
{
if (elementToLookFor == child)
return this;
if (auto* found = child->findParentElementOf (elementToLookFor))
return found;
}
return nullptr;
}
void XmlElement::getChildElementsAsArray (XmlElement** elems) const noexcept
{
firstChildElement.copyToArray (elems);
}
void XmlElement::reorderChildElements (XmlElement** elems, int num) noexcept
{
auto* e = elems[0];
firstChildElement = e;
for (int i = 1; i < num; ++i)
{
e->nextListItem = elems[i];
e = e->nextListItem;
}
e->nextListItem = nullptr;
}
//==============================================================================
bool XmlElement::isTextElement() const noexcept
{
return tagName.isEmpty();
}
static const String juce_xmltextContentAttributeName ("text");
const String& XmlElement::getText() const noexcept
{
jassert (isTextElement()); // you're trying to get the text from an element that
// isn't actually a text element.. If this contains text sub-nodes, you
// probably want to use getAllSubText instead.
return getStringAttribute (juce_xmltextContentAttributeName);
}
void XmlElement::setText (const String& newText)
{
if (isTextElement())
setAttribute (juce_xmltextContentAttributeName, newText);
else
jassertfalse; // you can only change the text in a text element, not a normal one.
}
String XmlElement::getAllSubText() const
{
if (isTextElement())
return getText();
if (getNumChildElements() == 1)
return firstChildElement.get()->getAllSubText();
MemoryOutputStream mem (1024);
for (auto* child = firstChildElement.get(); child != nullptr; child = child->nextListItem)
mem << child->getAllSubText();
return mem.toUTF8();
}
String XmlElement::getChildElementAllSubText (StringRef childTagName, const String& defaultReturnValue) const
{
if (auto* child = getChildByName (childTagName))
return child->getAllSubText();
return defaultReturnValue;
}
XmlElement* XmlElement::createTextElement (const String& text)
{
auto e = new XmlElement ((int) 0);
e->setAttribute (juce_xmltextContentAttributeName, text);
return e;
}
bool XmlElement::isValidXmlName (StringRef text) noexcept
{
if (text.isEmpty() || ! isValidXmlNameStartCharacter (text.text.getAndAdvance()))
return false;
for (;;)
{
if (text.isEmpty())
return true;
if (! isValidXmlNameBodyCharacter (text.text.getAndAdvance()))
return false;
}
}
void XmlElement::addTextElement (const String& text)
{
addChildElement (createTextElement (text));
}
void XmlElement::deleteAllTextElements() noexcept
{
for (auto* child = firstChildElement.get(); child != nullptr;)
{
auto* next = child->nextListItem.get();
if (child->isTextElement())
removeChildElement (child, true);
child = next;
}
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class XmlElementTests final : public UnitTest
{
public:
XmlElementTests()
: UnitTest ("XmlElement", UnitTestCategories::xml)
{}
void runTest() override
{
{
beginTest ("Float formatting");
auto element = std::make_unique<XmlElement> ("test");
Identifier number ("number");
std::map<double, String> tests;
tests[1] = "1.0";
tests[1.1] = "1.1";
tests[1.01] = "1.01";
tests[0.76378] = "0.76378";
tests[-10] = "-10.0";
tests[10.01] = "10.01";
tests[0.0123] = "0.0123";
tests[-3.7e-27] = "-3.7e-27";
tests[1e+40] = "1.0e40";
tests[-12345678901234567.0] = "-1.234567890123457e16";
tests[192000] = "192000.0";
tests[1234567] = "1.234567e6";
tests[0.00006] = "0.00006";
tests[0.000006] = "6.0e-6";
for (auto& test : tests)
{
element->setAttribute (number, test.first);
expectEquals (element->getStringAttribute (number), test.second);
}
}
}
};
static XmlElementTests xmlElementTests;
#endif
} // namespace juce
juce::FileOutputStream
Definition juce_FileOutputStream.h:35
juce::FileOutputStream::getStatus
const Result & getStatus() const noexcept
Definition juce_FileOutputStream.h:81
juce::FileOutputStream::openedOk
bool openedOk() const noexcept
Definition juce_FileOutputStream.h:91
juce::FileOutputStream::flush
void flush() override
Definition juce_FileOutputStream.cpp:70
juce::File
Definition juce_File.h:45
juce::Identifier
Definition juce_Identifier.h:39
juce::LinkedListPointer::get
ObjectType * get() const noexcept
Definition juce_LinkedListPointer.h:102
juce::LinkedListPointer::addCopyOfList
void addCopyOfList(const LinkedListPointer &other)
Definition juce_LinkedListPointer.h:240
juce::MemoryOutputStream
Definition juce_MemoryOutputStream.h:36
juce::MemoryOutputStream::toUTF8
String toUTF8() const
Definition juce_MemoryOutputStream.cpp:189
juce::OutputStream
Definition juce_OutputStream.h:38
juce::OutputStream::writeByte
virtual bool writeByte(char byte)
Definition juce_OutputStream.cpp:83
juce::Result::failed
bool failed() const noexcept
Definition juce_Result.cpp:77
juce::StringPool
Definition juce_StringPool.h:40
juce::StringPool::getPooledString
String getPooledString(const String &original)
Definition juce_StringPool.cpp:130
juce::StringPool::getGlobalPool
static StringPool & getGlobalPool() noexcept
Definition juce_StringPool.cpp:158
juce::StringRef
Definition juce_StringRef.h:62
juce::String
Definition juce_String.h:53
juce::String::upToFirstOccurrenceOf
String upToFirstOccurrenceOf(StringRef substringToEndWith, bool includeSubStringInResult, bool ignoreCase) const
Definition juce_String.cpp:1585
juce::String::fromLastOccurrenceOf
String fromLastOccurrenceOf(StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const
Definition juce_String.cpp:1575
juce::String::getIntValue
int getIntValue() const noexcept
Definition juce_String.cpp:1871
juce::String::isNotEmpty
bool isNotEmpty() const noexcept
Definition juce_String.h:316
juce::TemporaryFile
Definition juce_TemporaryFile.h:65
juce::TemporaryFile::overwriteTargetFileWithTemporary
bool overwriteTargetFileWithTemporary() const
Definition juce_TemporaryFile.cpp:93
juce::TemporaryFile::getFile
const File & getFile() const noexcept
Definition juce_TemporaryFile.h:126
juce::UnitTest
Definition juce_UnitTest.h:70
juce::XmlElement
Definition juce_XmlElement.h:83
juce::XmlElement::getNextElementWithTagName
XmlElement * getNextElementWithTagName(StringRef requiredTagName) const
Definition juce_XmlElement.cpp:496
juce::XmlElement::setTagName
void setTagName(StringRef newTagName)
Definition juce_XmlElement.cpp:506
juce::XmlElement::addTextElement
void addTextElement(const String &text)
Definition juce_XmlElement.cpp:974
juce::XmlElement::getNumChildElements
int getNumChildElements() const noexcept
Definition juce_XmlElement.cpp:668
juce::XmlElement::XmlElement
XmlElement(const String &tagName)
Definition juce_XmlElement.cpp:76
juce::XmlElement::insertChildElement
void insertChildElement(XmlElement *newChildElement, int indexToInsertAt) noexcept
Definition juce_XmlElement.cpp:711
juce::XmlElement::getChildByName
XmlElement * getChildByName(StringRef tagNameToLookFor) const noexcept
Definition juce_XmlElement.cpp:678
juce::XmlElement::isTextElement
bool isTextElement() const noexcept
Definition juce_XmlElement.cpp:904
juce::XmlElement::deleteAllChildElementsWithTagName
void deleteAllChildElementsWithTagName(StringRef tagName) noexcept
Definition juce_XmlElement.cpp:849
juce::XmlElement::toString
String toString(const TextFormat &format={}) const
Definition juce_XmlElement.cpp:352
juce::XmlElement::addChildElement
void addChildElement(XmlElement *newChildElement) noexcept
Definition juce_XmlElement.cpp:700
juce::XmlElement::getTagNameWithoutNamespace
String getTagNameWithoutNamespace() const
Definition juce_XmlElement.cpp:486
juce::XmlElement::getDoubleAttribute
double getDoubleAttribute(StringRef attributeName, double defaultReturnValue=0.0) const
Definition juce_XmlElement.cpp:579
juce::XmlElement::getAttributeValue
const String & getAttributeValue(int attributeIndex) const noexcept
Definition juce_XmlElement.cpp:532
juce::XmlElement::prependChildElement
void prependChildElement(XmlElement *newChildElement) noexcept
Definition juce_XmlElement.cpp:722
juce::XmlElement::createNewChildElement
XmlElement * createNewChildElement(StringRef tagName)
Definition juce_XmlElement.cpp:733
juce::XmlElement::getChildElementAllSubText
String getChildElementAllSubText(StringRef childTagName, const String &defaultReturnValue) const
Definition juce_XmlElement.cpp:944
juce::XmlElement::getText
const String & getText() const noexcept
Definition juce_XmlElement.cpp:911
juce::XmlElement::deleteAllTextElements
void deleteAllTextElements() noexcept
Definition juce_XmlElement.cpp:979
juce::XmlElement::~XmlElement
~XmlElement() noexcept
Definition juce_XmlElement.cpp:161
juce::XmlElement::getAllSubText
String getAllSubText() const
Definition juce_XmlElement.cpp:928
juce::XmlElement::compareAttribute
bool compareAttribute(StringRef attributeName, StringRef stringToCompareAgainst, bool ignoreCase=false) const noexcept
Definition juce_XmlElement.cpp:603
juce::XmlElement::setText
void setText(const String &newText)
Definition juce_XmlElement.cpp:920
juce::XmlElement::findParentElementOf
XmlElement * findParentElementOf(const XmlElement *childToSearchFor) noexcept
Definition juce_XmlElement.cpp:867
juce::XmlElement::isEquivalentTo
bool isEquivalentTo(const XmlElement *other, bool ignoreOrderOfAttributes) const noexcept
Definition juce_XmlElement.cpp:771
juce::XmlElement::getBoolAttribute
bool getBoolAttribute(StringRef attributeName, bool defaultReturnValue=false) const
Definition juce_XmlElement.cpp:587
juce::XmlElement::removeAllAttributes
void removeAllAttributes() noexcept
Definition juce_XmlElement.cpp:662
juce::XmlElement::createTextElement
static XmlElement * createTextElement(const String &text)
Definition juce_XmlElement.cpp:952
juce::XmlElement::getAttributeName
const String & getAttributeName(int attributeIndex) const noexcept
Definition juce_XmlElement.cpp:524
juce::XmlElement::hasAttribute
bool hasAttribute(StringRef attributeName) const noexcept
Definition juce_XmlElement.cpp:549
juce::XmlElement::containsChildElement
bool containsChildElement(const XmlElement *possibleChild) const noexcept
Definition juce_XmlElement.cpp:862
juce::XmlElement::replaceChildElement
bool replaceChildElement(XmlElement *currentChildElement, XmlElement *newChildNode) noexcept
Definition juce_XmlElement.cpp:740
juce::XmlElement::writeTo
void writeTo(OutputStream &output, const TextFormat &format={}) const
Definition juce_XmlElement.cpp:359
juce::XmlElement::getChildElement
XmlElement * getChildElement(int index) const noexcept
Definition juce_XmlElement.cpp:673
juce::XmlElement::operator=
XmlElement & operator=(const XmlElement &)
Definition juce_XmlElement.cpp:116
juce::XmlElement::deleteAllChildElements
void deleteAllChildElements() noexcept
Definition juce_XmlElement.cpp:844
juce::XmlElement::hasTagName
bool hasTagName(StringRef possibleTagName) const noexcept
Definition juce_XmlElement.cpp:470
juce::XmlElement::removeAttribute
void removeAttribute(const Identifier &attributeName) noexcept
Definition juce_XmlElement.cpp:650
juce::XmlElement::getChildByAttribute
XmlElement * getChildByAttribute(StringRef attributeName, StringRef attributeValue) const noexcept
Definition juce_XmlElement.cpp:689
juce::XmlElement::getNumAttributes
int getNumAttributes() const noexcept
Definition juce_XmlElement.cpp:513
juce::XmlElement::removeChildElement
void removeChildElement(XmlElement *childToRemove, bool shouldDeleteTheChild) noexcept
Definition juce_XmlElement.cpp:757
juce::XmlElement::isValidXmlName
static bool isValidXmlName(StringRef possibleName) noexcept
Definition juce_XmlElement.cpp:959
juce::XmlElement::getIntAttribute
int getIntAttribute(StringRef attributeName, int defaultReturnValue=0) const
Definition juce_XmlElement.cpp:571
juce::XmlElement::getStringAttribute
const String & getStringAttribute(StringRef attributeName) const noexcept
Definition juce_XmlElement.cpp:555
juce::XmlElement::hasTagNameIgnoringNamespace
bool hasTagNameIgnoringNamespace(StringRef possibleTagName) const
Definition juce_XmlElement.cpp:491
juce::XmlElement::getNamespace
String getNamespace() const
Definition juce_XmlElement.cpp:481
juce::XmlElement::setAttribute
void setAttribute(const Identifier &attributeName, const String &newValue)
Definition juce_XmlElement.cpp:615
juce::XmlElement::TextFormat
Definition juce_XmlElement.h:136
juce::XmlElement::TextFormat::customEncoding
String customEncoding
Definition juce_XmlElement.h:142
juce::XmlElement::TextFormat::withoutHeader
TextFormat withoutHeader() const
Definition juce_XmlElement.cpp:345
juce::XmlElement::TextFormat::addDefaultHeader
bool addDefaultHeader
Definition juce_XmlElement.h:143
juce::XmlElement::TextFormat::dtd
String dtd
Definition juce_XmlElement.h:140
juce::XmlElement::TextFormat::newLineChars
const char * newLineChars
Definition juce_XmlElement.h:145
juce::XmlElement::TextFormat::customHeader
String customHeader
Definition juce_XmlElement.h:141
juce::XmlElement::TextFormat::singleLine
TextFormat singleLine() const
Definition juce_XmlElement.cpp:338
juce::XmlElement::TextFormat::lineWrapLength
int lineWrapLength
Definition juce_XmlElement.h:144
juce::XmlElement::TextFormat::TextFormat
TextFormat()
Definition juce_XmlElement.cpp:336