fix macOS build (following Projucer changes made in Windows, which removed /Applications/JUCE/modules from its headers). move JUCE headers under source control, so that Windows and macOS can both build against same version of JUCE. remove AUv3 target (I think it's an iOS thing, so it will never work with this macOS fluidsynth dylib).
This commit is contained in:
865
modules/juce_core/xml/juce_XmlDocument.cpp
Normal file
865
modules/juce_core/xml/juce_XmlDocument.cpp
Normal file
@ -0,0 +1,865 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
XmlDocument::XmlDocument (const String& text) : originalText (text) {}
|
||||
XmlDocument::XmlDocument (const File& 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* newSource) noexcept
|
||||
{
|
||||
inputSource.reset (newSource);
|
||||
}
|
||||
|
||||
void XmlDocument::setEmptyTextElementsIgnored (bool shouldBeIgnored) noexcept
|
||||
{
|
||||
ignoreEmptyTextElements = shouldBeIgnored;
|
||||
}
|
||||
|
||||
namespace XmlIdentifierChars
|
||||
{
|
||||
static bool isIdentifierCharSlow (juce_wchar c) noexcept
|
||||
{
|
||||
return CharacterFunctions::isLetterOrDigit (c)
|
||||
|| c == '_' || c == '-' || c == ':' || c == '.';
|
||||
}
|
||||
|
||||
static bool isIdentifierChar (juce_wchar c) noexcept
|
||||
{
|
||||
static const uint32 legalChars[] = { 0, 0x7ff6000, 0x87fffffe, 0x7fffffe, 0 };
|
||||
|
||||
return ((int) c < (int) numElementsInArray (legalChars) * 32) ? ((legalChars [c >> 5] & (1 << (c & 31))) != 0)
|
||||
: isIdentifierCharSlow (c);
|
||||
}
|
||||
|
||||
/*static void generateIdentifierCharConstants()
|
||||
{
|
||||
uint32 n[8] = { 0 };
|
||||
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);
|
||||
}*/
|
||||
|
||||
static String::CharPointerType findEndOfToken (String::CharPointerType p) noexcept
|
||||
{
|
||||
while (isIdentifierChar (*p))
|
||||
++p;
|
||||
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentElement)
|
||||
{
|
||||
if (originalText.isEmpty() && inputSource != nullptr)
|
||||
{
|
||||
std::unique_ptr<InputStream> in (inputSource->createInputStream());
|
||||
|
||||
if (in != nullptr)
|
||||
{
|
||||
MemoryOutputStream data;
|
||||
data.writeFromInputStream (*in, onlyReadOuterDocumentElement ? 8192 : -1);
|
||||
|
||||
#if JUCE_STRING_UTF_TYPE == 8
|
||||
if (data.getDataSize() > 2)
|
||||
{
|
||||
data.writeByte (0);
|
||||
auto* text = static_cast<const char*> (data.getData());
|
||||
|
||||
if (CharPointer_UTF16::isByteOrderMarkBigEndian (text)
|
||||
|| CharPointer_UTF16::isByteOrderMarkLittleEndian (text))
|
||||
{
|
||||
originalText = data.toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CharPointer_UTF8::isByteOrderMark (text))
|
||||
text += 3;
|
||||
|
||||
// parse the input buffer directly to avoid copying it all to a string..
|
||||
return parseDocumentElement (String::CharPointerType (text), onlyReadOuterDocumentElement);
|
||||
}
|
||||
}
|
||||
#else
|
||||
originalText = data.toString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return parseDocumentElement (originalText.getCharPointer(), onlyReadOuterDocumentElement);
|
||||
}
|
||||
|
||||
const String& XmlDocument::getLastParseError() const noexcept
|
||||
{
|
||||
return lastError;
|
||||
}
|
||||
|
||||
void XmlDocument::setLastError (const String& desc, const bool carryOn)
|
||||
{
|
||||
lastError = desc;
|
||||
errorOccurred = ! carryOn;
|
||||
}
|
||||
|
||||
String XmlDocument::getFileContents (const String& filename) const
|
||||
{
|
||||
if (inputSource != nullptr)
|
||||
{
|
||||
std::unique_ptr<InputStream> in (inputSource->createInputStreamFor (filename.trim().unquoted()));
|
||||
|
||||
if (in != nullptr)
|
||||
return in->readEntireStreamAsString();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
juce_wchar XmlDocument::readNextChar() noexcept
|
||||
{
|
||||
auto c = input.getAndAdvance();
|
||||
|
||||
if (c == 0)
|
||||
{
|
||||
outOfData = true;
|
||||
--input;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
XmlElement* XmlDocument::parseDocumentElement (String::CharPointerType textToParse,
|
||||
const bool onlyReadOuterDocumentElement)
|
||||
{
|
||||
input = textToParse;
|
||||
errorOccurred = false;
|
||||
outOfData = false;
|
||||
needToLoadDTD = true;
|
||||
|
||||
if (textToParse.isEmpty())
|
||||
{
|
||||
lastError = "not enough input";
|
||||
}
|
||||
else if (! parseHeader())
|
||||
{
|
||||
lastError = "malformed header";
|
||||
}
|
||||
else if (! parseDTD())
|
||||
{
|
||||
lastError = "malformed DTD";
|
||||
}
|
||||
else
|
||||
{
|
||||
lastError.clear();
|
||||
std::unique_ptr<XmlElement> result (readNextElement (! onlyReadOuterDocumentElement));
|
||||
|
||||
if (! errorOccurred)
|
||||
return result.release();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool XmlDocument::parseHeader()
|
||||
{
|
||||
skipNextWhiteSpace();
|
||||
|
||||
if (CharacterFunctions::compareUpTo (input, CharPointer_ASCII ("<?xml"), 5) == 0)
|
||||
{
|
||||
auto headerEnd = CharacterFunctions::find (input, CharPointer_ASCII ("?>"));
|
||||
|
||||
if (headerEnd.isEmpty())
|
||||
return false;
|
||||
|
||||
#if JUCE_DEBUG
|
||||
auto encoding = String (input, headerEnd)
|
||||
.fromFirstOccurrenceOf ("encoding", false, true)
|
||||
.fromFirstOccurrenceOf ("=", false, false)
|
||||
.fromFirstOccurrenceOf ("\"", false, false)
|
||||
.upToFirstOccurrenceOf ("\"", false, false)
|
||||
.trim();
|
||||
|
||||
/* If you load an XML document with a non-UTF encoding type, it may have been
|
||||
loaded wrongly.. Since all the files are read via the normal juce file streams,
|
||||
they're treated as UTF-8, so by the time it gets to the parser, the encoding will
|
||||
have been lost. Best plan is to stick to utf-8 or if you have specific files to
|
||||
read, use your own code to convert them to a unicode String, and pass that to the
|
||||
XML parser.
|
||||
*/
|
||||
jassert (encoding.isEmpty() || encoding.startsWithIgnoreCase ("utf-"));
|
||||
#endif
|
||||
|
||||
input = headerEnd + 2;
|
||||
skipNextWhiteSpace();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XmlDocument::parseDTD()
|
||||
{
|
||||
if (CharacterFunctions::compareUpTo (input, CharPointer_ASCII ("<!DOCTYPE"), 9) == 0)
|
||||
{
|
||||
input += 9;
|
||||
auto dtdStart = input;
|
||||
|
||||
for (int n = 1; n > 0;)
|
||||
{
|
||||
auto c = readNextChar();
|
||||
|
||||
if (outOfData)
|
||||
return false;
|
||||
|
||||
if (c == '<')
|
||||
++n;
|
||||
else if (c == '>')
|
||||
--n;
|
||||
}
|
||||
|
||||
dtdText = String (dtdStart, input - 1).trim();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void XmlDocument::skipNextWhiteSpace()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
input = input.findEndOfWhitespace();
|
||||
|
||||
if (input.isEmpty())
|
||||
{
|
||||
outOfData = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*input == '<')
|
||||
{
|
||||
if (input[1] == '!'
|
||||
&& input[2] == '-'
|
||||
&& input[3] == '-')
|
||||
{
|
||||
input += 4;
|
||||
auto closeComment = input.indexOf (CharPointer_ASCII ("-->"));
|
||||
|
||||
if (closeComment < 0)
|
||||
{
|
||||
outOfData = true;
|
||||
break;
|
||||
}
|
||||
|
||||
input += closeComment + 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (input[1] == '?')
|
||||
{
|
||||
input += 2;
|
||||
auto closeBracket = input.indexOf (CharPointer_ASCII ("?>"));
|
||||
|
||||
if (closeBracket < 0)
|
||||
{
|
||||
outOfData = true;
|
||||
break;
|
||||
}
|
||||
|
||||
input += closeBracket + 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void XmlDocument::readQuotedString (String& result)
|
||||
{
|
||||
auto quote = readNextChar();
|
||||
|
||||
while (! outOfData)
|
||||
{
|
||||
auto c = readNextChar();
|
||||
|
||||
if (c == quote)
|
||||
break;
|
||||
|
||||
--input;
|
||||
|
||||
if (c == '&')
|
||||
{
|
||||
readEntity (result);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto start = input;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto character = *input;
|
||||
|
||||
if (character == quote)
|
||||
{
|
||||
result.appendCharPointer (start, input);
|
||||
++input;
|
||||
return;
|
||||
}
|
||||
|
||||
if (character == '&')
|
||||
{
|
||||
result.appendCharPointer (start, input);
|
||||
break;
|
||||
}
|
||||
|
||||
if (character == 0)
|
||||
{
|
||||
setLastError ("unmatched quotes", false);
|
||||
outOfData = true;
|
||||
break;
|
||||
}
|
||||
|
||||
++input;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements)
|
||||
{
|
||||
XmlElement* node = nullptr;
|
||||
skipNextWhiteSpace();
|
||||
|
||||
if (outOfData)
|
||||
return nullptr;
|
||||
|
||||
if (*input == '<')
|
||||
{
|
||||
++input;
|
||||
auto endOfToken = XmlIdentifierChars::findEndOfToken (input);
|
||||
|
||||
if (endOfToken == input)
|
||||
{
|
||||
// no tag name - but allow for a gap after the '<' before giving an error
|
||||
skipNextWhiteSpace();
|
||||
endOfToken = XmlIdentifierChars::findEndOfToken (input);
|
||||
|
||||
if (endOfToken == input)
|
||||
{
|
||||
setLastError ("tag name missing", false);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
node = new XmlElement (input, endOfToken);
|
||||
input = endOfToken;
|
||||
LinkedListPointer<XmlElement::XmlAttributeNode>::Appender attributeAppender (node->attributes);
|
||||
|
||||
// look for attributes
|
||||
for (;;)
|
||||
{
|
||||
skipNextWhiteSpace();
|
||||
auto c = *input;
|
||||
|
||||
// empty tag..
|
||||
if (c == '/' && input[1] == '>')
|
||||
{
|
||||
input += 2;
|
||||
break;
|
||||
}
|
||||
|
||||
// parse the guts of the element..
|
||||
if (c == '>')
|
||||
{
|
||||
++input;
|
||||
|
||||
if (alsoParseSubElements)
|
||||
readChildElements (*node);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// get an attribute..
|
||||
if (XmlIdentifierChars::isIdentifierChar (c))
|
||||
{
|
||||
auto attNameEnd = XmlIdentifierChars::findEndOfToken (input);
|
||||
|
||||
if (attNameEnd != input)
|
||||
{
|
||||
auto attNameStart = input;
|
||||
input = attNameEnd;
|
||||
skipNextWhiteSpace();
|
||||
|
||||
if (readNextChar() == '=')
|
||||
{
|
||||
skipNextWhiteSpace();
|
||||
auto nextChar = *input;
|
||||
|
||||
if (nextChar == '"' || nextChar == '\'')
|
||||
{
|
||||
auto* newAtt = new XmlElement::XmlAttributeNode (attNameStart, attNameEnd);
|
||||
readQuotedString (newAtt->value);
|
||||
attributeAppender.append (newAtt);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setLastError ("expected '=' after attribute '"
|
||||
+ String (attNameStart, attNameEnd) + "'", false);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! outOfData)
|
||||
setLastError ("illegal character found in " + node->getTagName() + ": '" + c + "'", false);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void XmlDocument::readChildElements (XmlElement& parent)
|
||||
{
|
||||
LinkedListPointer<XmlElement>::Appender childAppender (parent.firstChildElement);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto preWhitespaceInput = input;
|
||||
skipNextWhiteSpace();
|
||||
|
||||
if (outOfData)
|
||||
{
|
||||
setLastError ("unmatched tags", false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (*input == '<')
|
||||
{
|
||||
auto c1 = input[1];
|
||||
|
||||
if (c1 == '/')
|
||||
{
|
||||
// our close tag..
|
||||
auto closeTag = input.indexOf ((juce_wchar) '>');
|
||||
|
||||
if (closeTag >= 0)
|
||||
input += closeTag + 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (c1 == '!' && CharacterFunctions::compareUpTo (input + 2, CharPointer_ASCII ("[CDATA["), 7) == 0)
|
||||
{
|
||||
input += 9;
|
||||
auto inputStart = input;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto c0 = *input;
|
||||
|
||||
if (c0 == 0)
|
||||
{
|
||||
setLastError ("unterminated CDATA section", false);
|
||||
outOfData = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (c0 == ']' && input[1] == ']' && input[2] == '>')
|
||||
{
|
||||
childAppender.append (XmlElement::createTextElement (String (inputStart, input)));
|
||||
input += 3;
|
||||
break;
|
||||
}
|
||||
|
||||
++input;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// this is some other element, so parse and add it..
|
||||
if (auto* n = readNextElement (true))
|
||||
childAppender.append (n);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else // must be a character block
|
||||
{
|
||||
input = preWhitespaceInput; // roll back to include the leading whitespace
|
||||
MemoryOutputStream textElementContent;
|
||||
bool contentShouldBeUsed = ! ignoreEmptyTextElements;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto c = *input;
|
||||
|
||||
if (c == '<')
|
||||
{
|
||||
if (input[1] == '!' && input[2] == '-' && input[3] == '-')
|
||||
{
|
||||
input += 4;
|
||||
auto closeComment = input.indexOf (CharPointer_ASCII ("-->"));
|
||||
|
||||
if (closeComment < 0)
|
||||
{
|
||||
setLastError ("unterminated comment", false);
|
||||
outOfData = true;
|
||||
return;
|
||||
}
|
||||
|
||||
input += closeComment + 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == 0)
|
||||
{
|
||||
setLastError ("unmatched tags", false);
|
||||
outOfData = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (c == '&')
|
||||
{
|
||||
String entity;
|
||||
readEntity (entity);
|
||||
|
||||
if (entity.startsWithChar ('<') && entity [1] != 0)
|
||||
{
|
||||
auto oldInput = input;
|
||||
auto oldOutOfData = outOfData;
|
||||
|
||||
input = entity.getCharPointer();
|
||||
outOfData = false;
|
||||
|
||||
while (auto* n = readNextElement (true))
|
||||
childAppender.append (n);
|
||||
|
||||
input = oldInput;
|
||||
outOfData = oldOutOfData;
|
||||
}
|
||||
else
|
||||
{
|
||||
textElementContent << entity;
|
||||
contentShouldBeUsed = contentShouldBeUsed || entity.containsNonWhitespaceChars();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (;; ++input)
|
||||
{
|
||||
auto nextChar = *input;
|
||||
|
||||
if (nextChar == '\r')
|
||||
{
|
||||
nextChar = '\n';
|
||||
|
||||
if (input[1] == '\n')
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nextChar == '<' || nextChar == '&')
|
||||
break;
|
||||
|
||||
if (nextChar == 0)
|
||||
{
|
||||
setLastError ("unmatched tags", false);
|
||||
outOfData = true;
|
||||
return;
|
||||
}
|
||||
|
||||
textElementContent.appendUTF8Char (nextChar);
|
||||
contentShouldBeUsed = contentShouldBeUsed || ! CharacterFunctions::isWhitespace (nextChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (contentShouldBeUsed)
|
||||
childAppender.append (XmlElement::createTextElement (textElementContent.toUTF8()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void XmlDocument::readEntity (String& result)
|
||||
{
|
||||
// skip over the ampersand
|
||||
++input;
|
||||
|
||||
if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("amp;"), 4) == 0)
|
||||
{
|
||||
input += 4;
|
||||
result += '&';
|
||||
}
|
||||
else if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("quot;"), 5) == 0)
|
||||
{
|
||||
input += 5;
|
||||
result += '"';
|
||||
}
|
||||
else if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("apos;"), 5) == 0)
|
||||
{
|
||||
input += 5;
|
||||
result += '\'';
|
||||
}
|
||||
else if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("lt;"), 3) == 0)
|
||||
{
|
||||
input += 3;
|
||||
result += '<';
|
||||
}
|
||||
else if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("gt;"), 3) == 0)
|
||||
{
|
||||
input += 3;
|
||||
result += '>';
|
||||
}
|
||||
else if (*input == '#')
|
||||
{
|
||||
int charCode = 0;
|
||||
++input;
|
||||
|
||||
if (*input == 'x' || *input == 'X')
|
||||
{
|
||||
++input;
|
||||
int numChars = 0;
|
||||
|
||||
while (input[0] != ';')
|
||||
{
|
||||
auto hexValue = CharacterFunctions::getHexDigitValue (input[0]);
|
||||
|
||||
if (hexValue < 0 || ++numChars > 8)
|
||||
{
|
||||
setLastError ("illegal escape sequence", true);
|
||||
break;
|
||||
}
|
||||
|
||||
charCode = (charCode << 4) | hexValue;
|
||||
++input;
|
||||
}
|
||||
|
||||
++input;
|
||||
}
|
||||
else if (input[0] >= '0' && input[0] <= '9')
|
||||
{
|
||||
int numChars = 0;
|
||||
|
||||
while (input[0] != ';')
|
||||
{
|
||||
if (++numChars > 12)
|
||||
{
|
||||
setLastError ("illegal escape sequence", true);
|
||||
break;
|
||||
}
|
||||
|
||||
charCode = charCode * 10 + ((int) input[0] - '0');
|
||||
++input;
|
||||
}
|
||||
|
||||
++input;
|
||||
}
|
||||
else
|
||||
{
|
||||
setLastError ("illegal escape sequence", true);
|
||||
result += '&';
|
||||
return;
|
||||
}
|
||||
|
||||
result << (juce_wchar) charCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto entityNameStart = input;
|
||||
auto closingSemiColon = input.indexOf ((juce_wchar) ';');
|
||||
|
||||
if (closingSemiColon < 0)
|
||||
{
|
||||
outOfData = true;
|
||||
result += '&';
|
||||
}
|
||||
else
|
||||
{
|
||||
input += closingSemiColon + 1;
|
||||
result += expandExternalEntity (String (entityNameStart, (size_t) closingSemiColon));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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[0] == '#')
|
||||
{
|
||||
auto char1 = ent[1];
|
||||
|
||||
if (char1 == 'x' || char1 == 'X')
|
||||
return String::charToString (static_cast<juce_wchar> (ent.substring (2).getHexValue32()));
|
||||
|
||||
if (char1 >= '0' && char1 <= '9')
|
||||
return String::charToString (static_cast<juce_wchar> (ent.substring (1).getIntValue()));
|
||||
|
||||
setLastError ("illegal escape sequence", false);
|
||||
return String::charToString ('&');
|
||||
}
|
||||
|
||||
return expandExternalEntity (ent);
|
||||
}
|
||||
|
||||
String XmlDocument::expandExternalEntity (const String& entity)
|
||||
{
|
||||
if (needToLoadDTD)
|
||||
{
|
||||
if (dtdText.isNotEmpty())
|
||||
{
|
||||
dtdText = dtdText.trimCharactersAtEnd (">");
|
||||
tokenisedDTD.addTokens (dtdText, true);
|
||||
|
||||
if (tokenisedDTD[tokenisedDTD.size() - 2].equalsIgnoreCase ("system")
|
||||
&& tokenisedDTD[tokenisedDTD.size() - 1].isQuotedString())
|
||||
{
|
||||
auto fn = tokenisedDTD[tokenisedDTD.size() - 1];
|
||||
|
||||
tokenisedDTD.clear();
|
||||
tokenisedDTD.addTokens (getFileContents (fn), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
tokenisedDTD.clear();
|
||||
auto openBracket = dtdText.indexOfChar ('[');
|
||||
|
||||
if (openBracket > 0)
|
||||
{
|
||||
auto closeBracket = dtdText.lastIndexOfChar (']');
|
||||
|
||||
if (closeBracket > openBracket)
|
||||
tokenisedDTD.addTokens (dtdText.substring (openBracket + 1,
|
||||
closeBracket), true);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = tokenisedDTD.size(); --i >= 0;)
|
||||
{
|
||||
if (tokenisedDTD[i].startsWithChar ('%')
|
||||
&& tokenisedDTD[i].endsWithChar (';'))
|
||||
{
|
||||
auto parsed = getParameterEntity (tokenisedDTD[i].substring (1, tokenisedDTD[i].length() - 1));
|
||||
StringArray newToks;
|
||||
newToks.addTokens (parsed, true);
|
||||
|
||||
tokenisedDTD.remove (i);
|
||||
|
||||
for (int j = newToks.size(); --j >= 0;)
|
||||
tokenisedDTD.insert (i, newToks[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
needToLoadDTD = false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < tokenisedDTD.size(); ++i)
|
||||
{
|
||||
if (tokenisedDTD[i] == entity)
|
||||
{
|
||||
if (tokenisedDTD[i - 1].equalsIgnoreCase ("<!entity"))
|
||||
{
|
||||
auto ent = tokenisedDTD [i + 1].trimCharactersAtEnd (">").trim().unquoted();
|
||||
|
||||
// check for sub-entities..
|
||||
auto ampersand = ent.indexOfChar ('&');
|
||||
|
||||
while (ampersand >= 0)
|
||||
{
|
||||
auto semiColon = ent.indexOf (i + 1, ";");
|
||||
|
||||
if (semiColon < 0)
|
||||
{
|
||||
setLastError ("entity without terminating semi-colon", false);
|
||||
break;
|
||||
}
|
||||
|
||||
auto resolved = expandEntity (ent.substring (i + 1, semiColon));
|
||||
|
||||
ent = ent.substring (0, ampersand)
|
||||
+ resolved
|
||||
+ ent.substring (semiColon + 1);
|
||||
|
||||
ampersand = ent.indexOfChar (semiColon + 1, '&');
|
||||
}
|
||||
|
||||
return ent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setLastError ("unknown entity", true);
|
||||
return entity;
|
||||
}
|
||||
|
||||
String XmlDocument::getParameterEntity (const String& entity)
|
||||
{
|
||||
for (int i = 0; i < tokenisedDTD.size(); ++i)
|
||||
{
|
||||
if (tokenisedDTD[i] == entity
|
||||
&& tokenisedDTD [i - 1] == "%"
|
||||
&& tokenisedDTD [i - 2].equalsIgnoreCase ("<!entity"))
|
||||
{
|
||||
auto ent = tokenisedDTD [i + 1].trimCharactersAtEnd (">");
|
||||
|
||||
if (ent.equalsIgnoreCase ("system"))
|
||||
return getFileContents (tokenisedDTD [i + 2].trimCharactersAtEnd (">"));
|
||||
|
||||
return ent.trim().unquoted();
|
||||
}
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user