From 030489f0865a982375f2bcf3b8d7a80e724a18f9 Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Wed, 3 Nov 2010 19:46:10 +0000 Subject: [PATCH] Added a couple of handy methods XmlDocument::parse(). --- .../Source/Project/jucer_Project.cpp | 3 +- src/audio/midi/juce_MidiFile.cpp | 14 +- src/audio/processors/juce_AudioProcessor.cpp | 8 +- src/containers/juce_PropertySet.cpp | 4 +- .../controls/juce_TableHeaderComponent.cpp | 4 +- src/io/network/juce_URL.cpp | 3 +- src/native/linux/juce_linux_Fonts.cpp | 3 +- src/text/juce_XmlDocument.cpp | 88 ++++++----- src/text/juce_XmlDocument.h | 39 +++-- src/text/juce_XmlElement.cpp | 144 +++++------------- src/text/juce_XmlElement.h | 2 + src/utilities/juce_PropertiesFile.cpp | 3 +- 12 files changed, 136 insertions(+), 179 deletions(-) diff --git a/extras/Jucer (experimental)/Source/Project/jucer_Project.cpp b/extras/Jucer (experimental)/Source/Project/jucer_Project.cpp index 3431535ac4..188fd32967 100644 --- a/extras/Jucer (experimental)/Source/Project/jucer_Project.cpp +++ b/extras/Jucer (experimental)/Source/Project/jucer_Project.cpp @@ -155,8 +155,7 @@ void Project::setMissingDefaultValues() //============================================================================== const String Project::loadDocument (const File& file) { - XmlDocument doc (file); - ScopedPointer xml (doc.getDocumentElement()); + ScopedPointer xml (XmlDocument::parse (file)); if (xml == 0 || ! xml->hasTagName (Tags::projectRoot.toString())) return "Not a valid Jucer project!"; diff --git a/src/audio/midi/juce_MidiFile.cpp b/src/audio/midi/juce_MidiFile.cpp index 31b4cab7fa..4618aaf3bd 100644 --- a/src/audio/midi/juce_MidiFile.cpp +++ b/src/audio/midi/juce_MidiFile.cpp @@ -34,7 +34,7 @@ BEGIN_JUCE_NAMESPACE //============================================================================== namespace MidiFileHelpers { - static void writeVariableLengthInt (OutputStream& out, unsigned int v) + void writeVariableLengthInt (OutputStream& out, unsigned int v) { unsigned int buffer = v & 0x7F; @@ -55,7 +55,7 @@ namespace MidiFileHelpers } } - static bool parseMidiHeader (const uint8* &data, short& timeFormat, short& fileType, short& numberOfTracks) throw() + bool parseMidiHeader (const uint8* &data, short& timeFormat, short& fileType, short& numberOfTracks) throw() { unsigned int ch = (int) ByteOrder::bigEndianInt (data); data += 4; @@ -97,9 +97,9 @@ namespace MidiFileHelpers return true; } - static double convertTicksToSeconds (const double time, - const MidiMessageSequence& tempoEvents, - const int timeFormat) + double convertTicksToSeconds (const double time, + const MidiMessageSequence& tempoEvents, + const int timeFormat) { if (timeFormat > 0) { @@ -184,7 +184,6 @@ namespace MidiFileHelpers } } }; - } //============================================================================== @@ -409,8 +408,7 @@ bool MidiFile::writeTo (OutputStream& out) return true; } -void MidiFile::writeTrack (OutputStream& mainOut, - const int trackNum) +void MidiFile::writeTrack (OutputStream& mainOut, const int trackNum) { MemoryOutputStream out; diff --git a/src/audio/processors/juce_AudioProcessor.cpp b/src/audio/processors/juce_AudioProcessor.cpp index 9dd7d0e82a..7be1abc5ce 100644 --- a/src/audio/processors/juce_AudioProcessor.cpp +++ b/src/audio/processors/juce_AudioProcessor.cpp @@ -285,12 +285,8 @@ XmlElement* AudioProcessor::getXmlFromBinary (const void* data, const int stringLength = (int) ByteOrder::littleEndianInt (addBytesToPointer (data, 4)); if (stringLength > 0) - { - XmlDocument doc (String::fromUTF8 (static_cast (data) + 8, - jmin ((sizeInBytes - 8), stringLength))); - - return doc.getDocumentElement(); - } + return XmlDocument::parse (String::fromUTF8 (static_cast (data) + 8, + jmin ((sizeInBytes - 8), stringLength))); } return 0; diff --git a/src/containers/juce_PropertySet.cpp b/src/containers/juce_PropertySet.cpp index c68a474a35..cda75d0f72 100644 --- a/src/containers/juce_PropertySet.cpp +++ b/src/containers/juce_PropertySet.cpp @@ -127,9 +127,7 @@ bool PropertySet::getBoolValue (const String& keyName, XmlElement* PropertySet::getXmlValue (const String& keyName) const { - XmlDocument doc (getValue (keyName)); - - return doc.getDocumentElement(); + return XmlDocument::parse (getValue (keyName)); } void PropertySet::setValue (const String& keyName, const var& v) diff --git a/src/gui/components/controls/juce_TableHeaderComponent.cpp b/src/gui/components/controls/juce_TableHeaderComponent.cpp index dcdba880e9..0b3990ed75 100644 --- a/src/gui/components/controls/juce_TableHeaderComponent.cpp +++ b/src/gui/components/controls/juce_TableHeaderComponent.cpp @@ -460,9 +460,7 @@ const String TableHeaderComponent::toString() const void TableHeaderComponent::restoreFromString (const String& storedVersion) { - XmlDocument doc (storedVersion); - ScopedPointer storedXml (doc.getDocumentElement()); - + ScopedPointer storedXml (XmlDocument::parse (storedVersion)); int index = 0; if (storedXml != 0 && storedXml->hasTagName ("TABLELAYOUT")) diff --git a/src/io/network/juce_URL.cpp b/src/io/network/juce_URL.cpp index 0285d8dc3f..e8710e7d55 100644 --- a/src/io/network/juce_URL.cpp +++ b/src/io/network/juce_URL.cpp @@ -466,8 +466,7 @@ const String URL::readEntireTextStream (const bool usePostCommand) const XmlElement* URL::readEntireXmlStream (const bool usePostCommand) const { - XmlDocument doc (readEntireTextStream (usePostCommand)); - return doc.getDocumentElement(); + return XmlDocument::parse (readEntireTextStream (usePostCommand)); } //============================================================================== diff --git a/src/native/linux/juce_linux_Fonts.cpp b/src/native/linux/juce_linux_Fonts.cpp index 011211538f..29e28d83d4 100644 --- a/src/native/linux/juce_linux_Fonts.cpp +++ b/src/native/linux/juce_linux_Fonts.cpp @@ -108,8 +108,7 @@ public: if (fontDirs.size() == 0) { - XmlDocument fontsConfig (File ("/etc/fonts/fonts.conf")); - const ScopedPointer fontsInfo (fontsConfig.getDocumentElement()); + const ScopedPointer fontsInfo (XmlDocument::parse (File ("/etc/fonts/fonts.conf"))); if (fontsInfo != 0) { diff --git a/src/text/juce_XmlDocument.cpp b/src/text/juce_XmlDocument.cpp index 782014c86c..38aacb782b 100644 --- a/src/text/juce_XmlDocument.cpp +++ b/src/text/juce_XmlDocument.cpp @@ -40,15 +40,27 @@ XmlDocument::XmlDocument (const String& documentText) } XmlDocument::XmlDocument (const File& file) - : ignoreEmptyTextElements (true) + : ignoreEmptyTextElements (true), + inputSource (new FileInputSource (file)) { - inputSource = new FileInputSource (file); } XmlDocument::~XmlDocument() { } +XmlElement* XmlDocument::parse (const File& file) +{ + XmlDocument doc (file); + return doc.getDocumentElement(); +} + +XmlElement* XmlDocument::parse (const String& xmlData) +{ + XmlDocument doc (xmlData); + return doc.getDocumentElement(); +} + void XmlDocument::setInputSource (InputSource* const newSource) throw() { inputSource = newSource; @@ -59,16 +71,36 @@ void XmlDocument::setEmptyTextElementsIgnored (const bool shouldBeIgnored) throw ignoreEmptyTextElements = shouldBeIgnored; } -bool XmlDocument::isXmlIdentifierCharSlow (const juce_wchar c) throw() +namespace XmlIdentifierChars { - return CharacterFunctions::isLetterOrDigit (c) - || c == '_' || c == '-' || c == ':' || c == '.'; -} + bool isIdentifierCharSlow (const juce_wchar c) throw() + { + return CharacterFunctions::isLetterOrDigit (c) + || c == '_' || c == '-' || c == ':' || c == '.'; + } -inline bool XmlDocument::isXmlIdentifierChar (const juce_wchar c) const throw() -{ - return (c > 0 && c <= 127) ? identifierLookupTable [(int) c] - : isXmlIdentifierCharSlow (c); + bool isIdentifierChar (const juce_wchar c) throw() + { + static const uint32 legalChars[] = { 0, 0x7ff6000, 0x87fffffe, 0x7fffffe, 0 }; + + return (c < numElementsInArray (legalChars) * 32) ? ((legalChars [c >> 5] & (1 << (c & 31))) != 0) + : isIdentifierCharSlow (c); + } + + /*static void generateIdentifierCharConstants() + { + uint32 n[8]; + zerostruct (n); + for (int i = 0; i < 256; ++i) + if (isIdentifierCharSlow (i)) + n[i >> 5] |= (1 << (i & 31)); + + String s; + for (int i = 0; i < 8; ++i) + s << "0x" << String::toHexString ((int) n[i]) << ", "; + + DBG (s); + }*/ } XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentElement) @@ -96,9 +128,6 @@ XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentEle outOfData = false; needToLoadDTD = true; - for (int i = 0; i < 128; ++i) - identifierLookupTable[i] = isXmlIdentifierCharSlow ((juce_wchar) i); - if (textToParse.isEmpty()) { lastError = "not enough input"; @@ -150,14 +179,10 @@ const String XmlDocument::getFileContents (const String& filename) const juce_wchar XmlDocument::readNextChar() throw() { if (*input != 0) - { return *input++; - } - else - { - outOfData = true; - return 0; - } + + outOfData = true; + return 0; } int XmlDocument::findNextTokenLength() throw() @@ -165,7 +190,7 @@ int XmlDocument::findNextTokenLength() throw() int len = 0; juce_wchar c = *input; - while (isXmlIdentifierChar (c)) + while (XmlIdentifierChars::isIdentifierChar (c)) c = input [++len]; return len; @@ -371,7 +396,7 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) } // get an attribute.. - if (isXmlIdentifierChar (c)) + if (XmlIdentifierChars::isIdentifierChar (c)) { const int attNameLen = findNextTokenLength(); @@ -711,20 +736,11 @@ void XmlDocument::readEntity (String& result) const String XmlDocument::expandEntity (const String& ent) { - if (ent.equalsIgnoreCase ("amp")) - return String::charToString ('&'); - - if (ent.equalsIgnoreCase ("quot")) - return String::charToString ('"'); - - if (ent.equalsIgnoreCase ("apos")) - return String::charToString ('\''); - - if (ent.equalsIgnoreCase ("lt")) - return String::charToString ('<'); - - if (ent.equalsIgnoreCase ("gt")) - return String::charToString ('>'); + if (ent.equalsIgnoreCase ("amp")) return String::charToString ('&'); + if (ent.equalsIgnoreCase ("quot")) return String::charToString ('"'); + if (ent.equalsIgnoreCase ("apos")) return String::charToString ('\''); + if (ent.equalsIgnoreCase ("lt")) return String::charToString ('<'); + if (ent.equalsIgnoreCase ("gt")) return String::charToString ('>'); if (ent[0] == '#') { diff --git a/src/text/juce_XmlDocument.h b/src/text/juce_XmlDocument.h index 1698cb64aa..adffb4dc49 100644 --- a/src/text/juce_XmlDocument.h +++ b/src/text/juce_XmlDocument.h @@ -57,6 +57,16 @@ @endcode + Or you can use the static helper methods for quick parsing.. + + @code + XmlElement* xml = XmlDocument::parse (myXmlFile); + + if (xml != 0 && xml->hasTagName ("foobar")) + { + ...etc + @endcode + @see XmlElement */ class JUCE_API XmlDocument @@ -64,28 +74,28 @@ class JUCE_API XmlDocument public: //============================================================================== /** Creates an XmlDocument from the xml text. - - The text doesn't actually get parsed until the getDocumentElement() method is - called. + The text doesn't actually get parsed until the getDocumentElement() method is called. */ XmlDocument (const String& documentText); /** Creates an XmlDocument from a file. - - The text doesn't actually get parsed until the getDocumentElement() method is - called. + The text doesn't actually get parsed until the getDocumentElement() method is called. */ XmlDocument (const File& file); /** Destructor. */ ~XmlDocument(); + //============================================================================== /** Creates an XmlElement object to represent the main document node. This method will do the actual parsing of the text, and if there's a parse error, it may returns 0 (and you can find out the error using the getLastParseError() method). + See also the parse() methods, which provide a shorthand way to quickly + parse a file or string. + @param onlyReadOuterDocumentElement if true, the parser will only read the first section of the file, and will only return the outer document element - this @@ -126,6 +136,20 @@ public: */ void setEmptyTextElementsIgnored (bool shouldBeIgnored) throw(); + //============================================================================== + /** A handy static method that parses a file. + This is a shortcut for creating an XmlDocument object and calling getDocumentElement() on it. + @returns a new XmlElement which the caller will need to delete, or null if there was an error. + */ + static XmlElement* parse (const File& file); + + /** A handy static method that parses some XML data. + This is a shortcut for creating an XmlDocument object and calling getDocumentElement() on it. + @returns a new XmlElement which the caller will need to delete, or null if there was an error. + */ + static XmlElement* parse (const String& xmlData); + + //============================================================================== juce_UseDebuggingNewOperator @@ -134,7 +158,6 @@ private: const juce_wchar* input; bool outOfData, errorOccurred; - bool identifierLookupTable [128]; String lastError, dtdText; StringArray tokenisedDTD; bool needToLoadDTD, ignoreEmptyTextElements; @@ -149,8 +172,6 @@ private: int findNextTokenLength() throw(); void readQuotedString (String& result); void readEntity (String& result); - static bool isXmlIdentifierCharSlow (juce_wchar c) throw(); - bool isXmlIdentifierChar (juce_wchar c) const throw(); const String getFileContents (const String& filename) const; const String expandEntity (const String& entity); diff --git a/src/text/juce_XmlElement.cpp b/src/text/juce_XmlElement.cpp index 864af30ef6..2d4bdc3e94 100644 --- a/src/text/juce_XmlElement.cpp +++ b/src/text/juce_XmlElement.cpp @@ -47,6 +47,16 @@ XmlElement::XmlAttributeNode::XmlAttributeNode (const String& name_, const Strin value (value_), next (0) { + #if JUCE_DEBUG + // this checks whether the attribute name string contains any illegals characters.. + for (const juce_wchar* t = name; *t != 0; ++t) + jassert (CharacterFunctions::isLetterOrDigit (*t) || *t == '_' || *t == '-' || *t == ':'); + #endif +} + +inline bool XmlElement::XmlAttributeNode::hasName (const String& nameToMatch) const throw() +{ + return name.equalsIgnoreCase (nameToMatch); } //============================================================================== @@ -220,21 +230,13 @@ namespace XmlOutputFunctions case '<': outputStream << "<"; break; case '\n': - if (changeNewLines) - outputStream << " "; - else - outputStream << (char) character; - - break; - case '\r': - if (changeNewLines) - outputStream << " "; - else + if (! changeNewLines) + { outputStream << (char) character; - - break; - + break; + } + // Note: deliberate fall-through here! default: outputStream << "&#" << ((int) (unsigned int) character) << ';'; break; @@ -282,8 +284,7 @@ void XmlElement::writeElementAsText (OutputStream& outputStream, const int attIndent = indentationLevel + tagName.length() + 1; int lineLen = 0; - const XmlAttributeNode* att = attributes; - while (att != 0) + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) { if (lineLen > lineWrapLength && indentationLevel >= 0) { @@ -299,8 +300,6 @@ void XmlElement::writeElementAsText (OutputStream& outputStream, escapeIllegalXmlChars (outputStream, att->value, true); outputStream.writeByte ('"'); lineLen += (int) (outputStream.getPosition() - startPos); - - att = att->next; } } @@ -460,29 +459,23 @@ XmlElement* XmlElement::getNextElementWithTagName (const String& requiredTagName //============================================================================== int XmlElement::getNumAttributes() const throw() { - const XmlAttributeNode* att = attributes; int count = 0; - while (att != 0) - { - att = att->next; + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) ++count; - } return count; } const String& XmlElement::getAttributeName (const int index) const throw() { - const XmlAttributeNode* att = attributes; int count = 0; - while (att != 0) + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) { if (count == index) return att->name; - att = att->next; ++count; } @@ -491,15 +484,13 @@ const String& XmlElement::getAttributeName (const int index) const throw() const String& XmlElement::getAttributeValue (const int index) const throw() { - const XmlAttributeNode* att = attributes; int count = 0; - while (att != 0) + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) { if (count == index) return att->value; - att = att->next; ++count; } @@ -508,87 +499,55 @@ const String& XmlElement::getAttributeValue (const int index) const throw() bool XmlElement::hasAttribute (const String& attributeName) const throw() { - const XmlAttributeNode* att = attributes; - - while (att != 0) - { - if (att->name.equalsIgnoreCase (attributeName)) + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) + if (att->hasName (attributeName)) return true; - att = att->next; - } - return false; } //============================================================================== const String& XmlElement::getStringAttribute (const String& attributeName) const throw() { - const XmlAttributeNode* att = attributes; - - while (att != 0) - { - if (att->name.equalsIgnoreCase (attributeName)) + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) + if (att->hasName (attributeName)) return att->value; - att = att->next; - } - return String::empty; } const String XmlElement::getStringAttribute (const String& attributeName, const String& defaultReturnValue) const { - const XmlAttributeNode* att = attributes; - - while (att != 0) - { - if (att->name.equalsIgnoreCase (attributeName)) + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) + if (att->hasName (attributeName)) return att->value; - att = att->next; - } - return defaultReturnValue; } int XmlElement::getIntAttribute (const String& attributeName, const int defaultReturnValue) const { - const XmlAttributeNode* att = attributes; - - while (att != 0) - { - if (att->name.equalsIgnoreCase (attributeName)) + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) + if (att->hasName (attributeName)) return att->value.getIntValue(); - att = att->next; - } - return defaultReturnValue; } double XmlElement::getDoubleAttribute (const String& attributeName, const double defaultReturnValue) const { - const XmlAttributeNode* att = attributes; - - while (att != 0) - { - if (att->name.equalsIgnoreCase (attributeName)) + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) + if (att->hasName (attributeName)) return att->value.getDoubleValue(); - att = att->next; - } - return defaultReturnValue; } bool XmlElement::getBoolAttribute (const String& attributeName, const bool defaultReturnValue) const { - const XmlAttributeNode* att = attributes; - - while (att != 0) + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) { - if (att->name.equalsIgnoreCase (attributeName)) + if (att->hasName (attributeName)) { juce_wchar firstChar = att->value[0]; @@ -601,8 +560,6 @@ bool XmlElement::getBoolAttribute (const String& attributeName, const bool defau || firstChar == 'T' || firstChar == 'Y'; } - - att = att->next; } return defaultReturnValue; @@ -612,20 +569,10 @@ bool XmlElement::compareAttribute (const String& attributeName, const String& stringToCompareAgainst, const bool ignoreCase) const throw() { - const XmlAttributeNode* att = attributes; - - while (att != 0) - { - if (att->name.equalsIgnoreCase (attributeName)) - { - if (ignoreCase) - return att->value.equalsIgnoreCase (stringToCompareAgainst); - else - return att->value == stringToCompareAgainst; - } - - att = att->next; - } + for (const XmlAttributeNode* att = attributes; att != 0; att = att->next) + if (att->hasName (attributeName)) + return ignoreCase ? att->value.equalsIgnoreCase (stringToCompareAgainst) + : att->value == stringToCompareAgainst; return false; } @@ -633,19 +580,6 @@ bool XmlElement::compareAttribute (const String& attributeName, //============================================================================== void XmlElement::setAttribute (const String& attributeName, const String& value) { -#if JUCE_DEBUG - // check the identifier being passed in is legal.. - const juce_wchar* t = attributeName; - while (*t != 0) - { - jassert (CharacterFunctions::isLetterOrDigit (*t) - || *t == '_' - || *t == '-' - || *t == ':'); - ++t; - } -#endif - if (attributes == 0) { attributes = new XmlAttributeNode (attributeName, value); @@ -656,7 +590,7 @@ void XmlElement::setAttribute (const String& attributeName, const String& value) for (;;) { - if (att->name.equalsIgnoreCase (attributeName)) + if (att->hasName (attributeName)) { att->value = value; break; @@ -684,12 +618,11 @@ void XmlElement::setAttribute (const String& attributeName, const double number) void XmlElement::removeAttribute (const String& attributeName) throw() { - XmlAttributeNode* att = attributes; XmlAttributeNode* lastAtt = 0; - while (att != 0) + for (XmlAttributeNode* att = attributes; att != 0; att = att->next) { - if (att->name.equalsIgnoreCase (attributeName)) + if (att->hasName (attributeName)) { if (lastAtt == 0) attributes = att->next; @@ -701,7 +634,6 @@ void XmlElement::removeAttribute (const String& attributeName) throw() } lastAtt = att; - att = att->next; } } diff --git a/src/text/juce_XmlElement.h b/src/text/juce_XmlElement.h index 76846d62dc..deb58c864b 100644 --- a/src/text/juce_XmlElement.h +++ b/src/text/juce_XmlElement.h @@ -704,6 +704,8 @@ private: String name, value; XmlAttributeNode* next; + bool hasName (const String& name) const throw(); + private: XmlAttributeNode& operator= (const XmlAttributeNode&); }; diff --git a/src/utilities/juce_PropertiesFile.cpp b/src/utilities/juce_PropertiesFile.cpp index 2e6a006eac..29f914f2fb 100644 --- a/src/utilities/juce_PropertiesFile.cpp +++ b/src/utilities/juce_PropertiesFile.cpp @@ -198,8 +198,7 @@ bool PropertiesFile::save() e->setAttribute (PropertyFileConstants::nameAttribute, getAllProperties().getAllKeys() [i]); // if the value seems to contain xml, store it as such.. - XmlDocument xmlContent (getAllProperties().getAllValues() [i]); - XmlElement* const childElement = xmlContent.getDocumentElement(); + XmlElement* const childElement = XmlDocument::parse (getAllProperties().getAllValues() [i]); if (childElement != 0) e->addChildElement (childElement);