JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_XmlDocument.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21*/
22
23namespace juce
24{
25
26XmlDocument::XmlDocument (const String& text) : originalText (text) {}
27XmlDocument::XmlDocument (const File& file) : inputSource (new FileInputSource (file)) {}
28
30
35
40
45
50
55
60
62{
63 inputSource.reset (newSource);
64}
65
67{
68 ignoreEmptyTextElements = shouldBeIgnored;
69}
70
71namespace XmlIdentifierChars
72{
73 static bool isIdentifierCharSlow (juce_wchar c) noexcept
74 {
76 || c == '_' || c == '-' || c == ':' || c == '.';
77 }
78
79 static bool isIdentifierChar (juce_wchar c) noexcept
80 {
81 static const uint32 legalChars[] = { 0, 0x7ff6000, 0x87fffffe, 0x7fffffe, 0 };
82
83 return ((int) c < (int) numElementsInArray (legalChars) * 32) ? ((legalChars [c >> 5] & (uint32) (1 << (c & 31))) != 0)
84 : isIdentifierCharSlow (c);
85 }
86
87 /*static void generateIdentifierCharConstants()
88 {
89 uint32 n[8] = { 0 };
90 for (int i = 0; i < 256; ++i)
91 if (isIdentifierCharSlow (i))
92 n[i >> 5] |= (1 << (i & 31));
93
94 String s;
95 for (int i = 0; i < 8; ++i)
96 s << "0x" << String::toHexString ((int) n[i]) << ", ";
97
98 DBG (s);
99 }*/
100
101 static String::CharPointerType findEndOfToken (String::CharPointerType p) noexcept
102 {
103 while (isIdentifierChar (*p))
104 ++p;
105
106 return p;
107 }
108}
109
111{
112 if (originalText.isEmpty() && inputSource != nullptr)
113 {
114 std::unique_ptr<InputStream> in (inputSource->createInputStream());
115
116 if (in != nullptr)
117 {
119 data.writeFromInputStream (*in, onlyReadOuterDocumentElement ? 8192 : -1);
120
121 #if JUCE_STRING_UTF_TYPE == 8
122 if (data.getDataSize() > 2)
123 {
124 data.writeByte (0);
125 auto* text = static_cast<const char*> (data.getData());
126
129 {
130 originalText = data.toString();
131 }
132 else
133 {
135 text += 3;
136
137 // parse the input buffer directly to avoid copying it all to a string..
138 return parseDocumentElement (String::CharPointerType (text), onlyReadOuterDocumentElement);
139 }
140 }
141 #else
142 originalText = data.toString();
143 #endif
144 }
145 }
146
147 return parseDocumentElement (originalText.getCharPointer(), onlyReadOuterDocumentElement);
148}
149
151{
152 if (auto xml = getDocumentElement (true))
153 if (xml->hasTagName (requiredTag))
154 return getDocumentElement (false);
155
156 return {};
157}
158
160{
161 return lastError;
162}
163
164void XmlDocument::setLastError (const String& desc, const bool carryOn)
165{
166 lastError = desc;
167 errorOccurred = ! carryOn;
168}
169
170String XmlDocument::getFileContents (const String& filename) const
171{
172 if (inputSource != nullptr)
173 {
174 std::unique_ptr<InputStream> in (inputSource->createInputStreamFor (filename.trim().unquoted()));
175
176 if (in != nullptr)
177 return in->readEntireStreamAsString();
178 }
179
180 return {};
181}
182
183juce_wchar XmlDocument::readNextChar() noexcept
184{
185 auto c = input.getAndAdvance();
186
187 if (c == 0)
188 {
189 outOfData = true;
190 --input;
191 }
192
193 return c;
194}
195
196std::unique_ptr<XmlElement> XmlDocument::parseDocumentElement (String::CharPointerType textToParse,
198{
199 input = textToParse;
200 errorOccurred = false;
201 outOfData = false;
202 needToLoadDTD = true;
203
204 if (textToParse.isEmpty())
205 {
206 lastError = "not enough input";
207 }
208 else if (! parseHeader())
209 {
210 lastError = "malformed header";
211 }
212 else if (! parseDTD())
213 {
214 lastError = "malformed DTD";
215 }
216 else
217 {
218 lastError.clear();
220
221 if (! errorOccurred)
222 return result;
223 }
224
225 return {};
226}
227
228bool XmlDocument::parseHeader()
229{
230 skipNextWhiteSpace();
231
232 if (CharacterFunctions::compareUpTo (input, CharPointer_ASCII ("<?xml"), 5) == 0)
233 {
234 auto headerEnd = CharacterFunctions::find (input, CharPointer_ASCII ("?>"));
235
236 if (headerEnd.isEmpty())
237 return false;
238
239 #if JUCE_DEBUG
240 auto encoding = String (input, headerEnd)
241 .fromFirstOccurrenceOf ("encoding", false, true)
242 .fromFirstOccurrenceOf ("=", false, false)
243 .fromFirstOccurrenceOf ("\"", false, false)
244 .upToFirstOccurrenceOf ("\"", false, false)
245 .trim();
246
247 /* If you load an XML document with a non-UTF encoding type, it may have been
248 loaded wrongly.. Since all the files are read via the normal juce file streams,
249 they're treated as UTF-8, so by the time it gets to the parser, the encoding will
250 have been lost. Best plan is to stick to utf-8 or if you have specific files to
251 read, use your own code to convert them to a unicode String, and pass that to the
252 XML parser.
253 */
254 jassert (encoding.isEmpty() || encoding.startsWithIgnoreCase ("utf-"));
255 #endif
256
257 input = headerEnd + 2;
258 skipNextWhiteSpace();
259 }
260
261 return true;
262}
263
264bool XmlDocument::parseDTD()
265{
266 if (CharacterFunctions::compareUpTo (input, CharPointer_ASCII ("<!DOCTYPE"), 9) == 0)
267 {
268 input += 9;
269 auto dtdStart = input;
270
271 for (int n = 1; n > 0;)
272 {
273 auto c = readNextChar();
274
275 if (outOfData)
276 return false;
277
278 if (c == '<')
279 ++n;
280 else if (c == '>')
281 --n;
282 }
283
284 dtdText = String (dtdStart, input - 1).trim();
285 }
286
287 return true;
288}
289
290void XmlDocument::skipNextWhiteSpace()
291{
292 for (;;)
293 {
295
296 if (input.isEmpty())
297 {
298 outOfData = true;
299 break;
300 }
301
302 if (*input == '<')
303 {
304 if (input[1] == '!'
305 && input[2] == '-'
306 && input[3] == '-')
307 {
308 input += 4;
309 auto closeComment = input.indexOf (CharPointer_ASCII ("-->"));
310
311 if (closeComment < 0)
312 {
313 outOfData = true;
314 break;
315 }
316
317 input += closeComment + 3;
318 continue;
319 }
320
321 if (input[1] == '?')
322 {
323 input += 2;
324 auto closeBracket = input.indexOf (CharPointer_ASCII ("?>"));
325
326 if (closeBracket < 0)
327 {
328 outOfData = true;
329 break;
330 }
331
332 input += closeBracket + 2;
333 continue;
334 }
335 }
336
337 break;
338 }
339}
340
341void XmlDocument::readQuotedString (String& result)
342{
343 auto quote = readNextChar();
344
345 while (! outOfData)
346 {
347 auto c = readNextChar();
348
349 if (c == quote)
350 break;
351
352 --input;
353
354 if (c == '&')
355 {
356 readEntity (result);
357 }
358 else
359 {
360 auto start = input;
361
362 for (;;)
363 {
364 auto character = *input;
365
366 if (character == quote)
367 {
368 result.appendCharPointer (start, input);
369 ++input;
370 return;
371 }
372
373 if (character == '&')
374 {
375 result.appendCharPointer (start, input);
376 break;
377 }
378
379 if (character == 0)
380 {
381 setLastError ("unmatched quotes", false);
382 outOfData = true;
383 break;
384 }
385
386 ++input;
387 }
388 }
389 }
390}
391
392XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements)
393{
394 XmlElement* node = nullptr;
395 skipNextWhiteSpace();
396
397 if (outOfData)
398 return nullptr;
399
400 if (*input == '<')
401 {
402 ++input;
403 auto endOfToken = XmlIdentifierChars::findEndOfToken (input);
404
405 if (endOfToken == input)
406 {
407 // no tag name - but allow for a gap after the '<' before giving an error
408 skipNextWhiteSpace();
409 endOfToken = XmlIdentifierChars::findEndOfToken (input);
410
411 if (endOfToken == input)
412 {
413 setLastError ("tag name missing", false);
414 return node;
415 }
416 }
417
418 node = new XmlElement (input, endOfToken);
419 input = endOfToken;
420 LinkedListPointer<XmlElement::XmlAttributeNode>::Appender attributeAppender (node->attributes);
421
422 // look for attributes
423 for (;;)
424 {
425 skipNextWhiteSpace();
426 auto c = *input;
427
428 // empty tag..
429 if (c == '/' && input[1] == '>')
430 {
431 input += 2;
432 break;
433 }
434
435 // parse the guts of the element..
436 if (c == '>')
437 {
438 ++input;
439
441 readChildElements (*node);
442
443 break;
444 }
445
446 // get an attribute..
447 if (XmlIdentifierChars::isIdentifierChar (c))
448 {
449 auto attNameEnd = XmlIdentifierChars::findEndOfToken (input);
450
451 if (attNameEnd != input)
452 {
453 auto attNameStart = input;
454 input = attNameEnd;
455 skipNextWhiteSpace();
456
457 if (readNextChar() == '=')
458 {
459 skipNextWhiteSpace();
460 auto nextChar = *input;
461
462 if (nextChar == '"' || nextChar == '\'')
463 {
464 auto* newAtt = new XmlElement::XmlAttributeNode (attNameStart, attNameEnd);
465 readQuotedString (newAtt->value);
466 attributeAppender.append (newAtt);
467 continue;
468 }
469 }
470 else
471 {
472 setLastError ("expected '=' after attribute '"
473 + String (attNameStart, attNameEnd) + "'", false);
474 return node;
475 }
476 }
477 }
478 else
479 {
480 if (! outOfData)
481 setLastError ("illegal character found in " + node->getTagName() + ": '" + c + "'", false);
482 }
483
484 break;
485 }
486 }
487
488 return node;
489}
490
491void XmlDocument::readChildElements (XmlElement& parent)
492{
493 LinkedListPointer<XmlElement>::Appender childAppender (parent.firstChildElement);
494
495 for (;;)
496 {
497 auto preWhitespaceInput = input;
498 skipNextWhiteSpace();
499
500 if (outOfData)
501 {
502 setLastError ("unmatched tags", false);
503 break;
504 }
505
506 if (*input == '<')
507 {
508 auto c1 = input[1];
509
510 if (c1 == '/')
511 {
512 // our close tag..
513 auto closeTag = input.indexOf ((juce_wchar) '>');
514
515 if (closeTag >= 0)
516 input += closeTag + 1;
517
518 break;
519 }
520
521 if (c1 == '!' && CharacterFunctions::compareUpTo (input + 2, CharPointer_ASCII ("[CDATA["), 7) == 0)
522 {
523 input += 9;
524 auto inputStart = input;
525
526 for (;;)
527 {
528 auto c0 = *input;
529
530 if (c0 == 0)
531 {
532 setLastError ("unterminated CDATA section", false);
533 outOfData = true;
534 break;
535 }
536
537 if (c0 == ']' && input[1] == ']' && input[2] == '>')
538 {
539 childAppender.append (XmlElement::createTextElement (String (inputStart, input)));
540 input += 3;
541 break;
542 }
543
544 ++input;
545 }
546 }
547 else
548 {
549 // this is some other element, so parse and add it..
550 if (auto* n = readNextElement (true))
551 childAppender.append (n);
552 else
553 break;
554 }
555 }
556 else // must be a character block
557 {
558 input = preWhitespaceInput; // roll back to include the leading whitespace
559 MemoryOutputStream textElementContent;
560 bool contentShouldBeUsed = ! ignoreEmptyTextElements;
561
562 for (;;)
563 {
564 auto c = *input;
565
566 if (c == '<')
567 {
568 if (input[1] == '!' && input[2] == '-' && input[3] == '-')
569 {
570 input += 4;
571 auto closeComment = input.indexOf (CharPointer_ASCII ("-->"));
572
573 if (closeComment < 0)
574 {
575 setLastError ("unterminated comment", false);
576 outOfData = true;
577 return;
578 }
579
580 input += closeComment + 3;
581 continue;
582 }
583
584 break;
585 }
586
587 if (c == 0)
588 {
589 setLastError ("unmatched tags", false);
590 outOfData = true;
591 return;
592 }
593
594 if (c == '&')
595 {
596 String entity;
597 readEntity (entity);
598
599 if (entity.startsWithChar ('<') && entity [1] != 0)
600 {
601 auto oldInput = input;
602 auto oldOutOfData = outOfData;
603
604 input = entity.getCharPointer();
605 outOfData = false;
606
607 while (auto* n = readNextElement (true))
608 childAppender.append (n);
609
610 input = oldInput;
611 outOfData = oldOutOfData;
612 }
613 else
614 {
616 contentShouldBeUsed = contentShouldBeUsed || entity.containsNonWhitespaceChars();
617 }
618 }
619 else
620 {
621 for (;; ++input)
622 {
623 auto nextChar = *input;
624
625 if (nextChar == '\r')
626 {
627 nextChar = '\n';
628
629 if (input[1] == '\n')
630 continue;
631 }
632
633 if (nextChar == '<' || nextChar == '&')
634 break;
635
636 if (nextChar == 0)
637 {
638 setLastError ("unmatched tags", false);
639 outOfData = true;
640 return;
641 }
642
643 textElementContent.appendUTF8Char (nextChar);
645 }
646 }
647 }
648
651 }
652 }
653}
654
655void XmlDocument::readEntity (String& result)
656{
657 // skip over the ampersand
658 ++input;
659
660 if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("amp;"), 4) == 0)
661 {
662 input += 4;
663 result += '&';
664 }
665 else if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("quot;"), 5) == 0)
666 {
667 input += 5;
668 result += '"';
669 }
670 else if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("apos;"), 5) == 0)
671 {
672 input += 5;
673 result += '\'';
674 }
675 else if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("lt;"), 3) == 0)
676 {
677 input += 3;
678 result += '<';
679 }
680 else if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("gt;"), 3) == 0)
681 {
682 input += 3;
683 result += '>';
684 }
685 else if (*input == '#')
686 {
687 int64_t charCode = 0;
688 ++input;
689
690 if (*input == 'x' || *input == 'X')
691 {
692 ++input;
693 int numChars = 0;
694
695 while (input[0] != ';')
696 {
698
700 {
701 setLastError ("illegal escape sequence", true);
702 break;
703 }
704
705 charCode = (charCode << 4) | hexValue;
706 ++input;
707 }
708
709 ++input;
710 }
711 else if (input[0] >= '0' && input[0] <= '9')
712 {
713 int numChars = 0;
714
715 for (;;)
716 {
717 const auto firstChar = input[0];
718
719 if (firstChar == 0)
720 {
721 setLastError ("unexpected end of input", true);
722 return;
723 }
724
725 if (firstChar == ';')
726 break;
727
728 if (++numChars > 12)
729 {
730 setLastError ("illegal escape sequence", true);
731 break;
732 }
733
734 charCode = charCode * 10 + ((int) firstChar - '0');
735 ++input;
736 }
737
738 ++input;
739 }
740 else
741 {
742 setLastError ("illegal escape sequence", true);
743 result += '&';
744 return;
745 }
746
747 result << (juce_wchar) charCode;
748 }
749 else
750 {
751 auto entityNameStart = input;
752 auto closingSemiColon = input.indexOf ((juce_wchar) ';');
753
754 if (closingSemiColon < 0)
755 {
756 outOfData = true;
757 result += '&';
758 }
759 else
760 {
761 input += closingSemiColon + 1;
762 result += expandExternalEntity (String (entityNameStart, (size_t) closingSemiColon));
763 }
764 }
765}
766
767String XmlDocument::expandEntity (const String& ent)
768{
769 if (ent.equalsIgnoreCase ("amp")) return String::charToString ('&');
770 if (ent.equalsIgnoreCase ("quot")) return String::charToString ('"');
771 if (ent.equalsIgnoreCase ("apos")) return String::charToString ('\'');
772 if (ent.equalsIgnoreCase ("lt")) return String::charToString ('<');
773 if (ent.equalsIgnoreCase ("gt")) return String::charToString ('>');
774
775 if (ent[0] == '#')
776 {
777 auto char1 = ent[1];
778
779 if (char1 == 'x' || char1 == 'X')
780 return String::charToString (static_cast<juce_wchar> (ent.substring (2).getHexValue32()));
781
782 if (char1 >= '0' && char1 <= '9')
783 return String::charToString (static_cast<juce_wchar> (ent.substring (1).getIntValue()));
784
785 setLastError ("illegal escape sequence", false);
786 return String::charToString ('&');
787 }
788
789 return expandExternalEntity (ent);
790}
791
792String XmlDocument::expandExternalEntity (const String& entity)
793{
794 if (needToLoadDTD)
795 {
796 if (dtdText.isNotEmpty())
797 {
798 dtdText = dtdText.trimCharactersAtEnd (">");
799 tokenisedDTD.addTokens (dtdText, true);
800
801 if (tokenisedDTD[tokenisedDTD.size() - 2].equalsIgnoreCase ("system")
802 && tokenisedDTD[tokenisedDTD.size() - 1].isQuotedString())
803 {
804 auto fn = tokenisedDTD[tokenisedDTD.size() - 1];
805
806 tokenisedDTD.clear();
807 tokenisedDTD.addTokens (getFileContents (fn), true);
808 }
809 else
810 {
811 tokenisedDTD.clear();
812 auto openBracket = dtdText.indexOfChar ('[');
813
814 if (openBracket > 0)
815 {
816 auto closeBracket = dtdText.lastIndexOfChar (']');
817
818 if (closeBracket > openBracket)
819 tokenisedDTD.addTokens (dtdText.substring (openBracket + 1,
820 closeBracket), true);
821 }
822 }
823
824 for (int i = tokenisedDTD.size(); --i >= 0;)
825 {
826 if (tokenisedDTD[i].startsWithChar ('%')
827 && tokenisedDTD[i].endsWithChar (';'))
828 {
829 auto parsed = getParameterEntity (tokenisedDTD[i].substring (1, tokenisedDTD[i].length() - 1));
830 StringArray newToks;
831 newToks.addTokens (parsed, true);
832
833 tokenisedDTD.remove (i);
834
835 for (int j = newToks.size(); --j >= 0;)
836 tokenisedDTD.insert (i, newToks[j]);
837 }
838 }
839 }
840
841 needToLoadDTD = false;
842 }
843
844 for (int i = 0; i < tokenisedDTD.size(); ++i)
845 {
846 if (tokenisedDTD[i] == entity)
847 {
848 if (tokenisedDTD[i - 1].equalsIgnoreCase ("<!entity"))
849 {
850 auto ent = tokenisedDTD [i + 1].trimCharactersAtEnd (">").trim().unquoted();
851
852 // check for sub-entities..
853 auto ampersand = ent.indexOfChar ('&');
854
855 while (ampersand >= 0)
856 {
857 auto semiColon = ent.indexOf (i + 1, ";");
858
859 if (semiColon < 0)
860 {
861 setLastError ("entity without terminating semi-colon", false);
862 break;
863 }
864
865 auto resolved = expandEntity (ent.substring (i + 1, semiColon));
866
867 ent = ent.substring (0, ampersand)
868 + resolved
869 + ent.substring (semiColon + 1);
870
871 ampersand = ent.indexOfChar (semiColon + 1, '&');
872 }
873
874 return ent;
875 }
876 }
877 }
878
879 setLastError ("unknown entity", true);
880 return entity;
881}
882
883String XmlDocument::getParameterEntity (const String& entity)
884{
885 for (int i = 0; i < tokenisedDTD.size(); ++i)
886 {
887 if (tokenisedDTD[i] == entity
888 && tokenisedDTD [i - 1] == "%"
889 && tokenisedDTD [i - 2].equalsIgnoreCase ("<!entity"))
890 {
891 auto ent = tokenisedDTD [i + 1].trimCharactersAtEnd (">");
892
893 if (ent.equalsIgnoreCase ("system"))
894 return getFileContents (tokenisedDTD [i + 2].trimCharactersAtEnd (">"));
895
896 return ent.trim().unquoted();
897 }
898 }
899
900 return entity;
901}
902
903}
static bool isByteOrderMarkBigEndian(const void *possibleByteOrder) noexcept
Returns true if the first pair of bytes in this pointer are the UTF16 byte-order mark (big endian).
static bool isByteOrderMarkLittleEndian(const void *possibleByteOrder) noexcept
Returns true if the first pair of bytes in this pointer are the UTF16 byte-order mark (little endian)...
Wraps a pointer to a null-terminated UTF-8 character string, and provides various methods to operate ...
int compareIgnoreCaseUpTo(const CharPointer other, const int maxChars) const noexcept
Compares this string with another one, up to a specified number of characters.
void incrementToEndOfWhitespace() noexcept
Move this pointer to the first non-whitespace character in the string.
juce_wchar getAndAdvance() noexcept
Returns the character that this pointer is currently pointing to, and then advances the pointer to po...
bool isEmpty() const noexcept
Returns true if this pointer is pointing to a null character.
int indexOf(const CharPointer stringToFind) const noexcept
Returns the character index of a substring, or -1 if it isn't found.
static bool isByteOrderMark(const void *possibleByteOrder) noexcept
Returns true if the first three bytes in this pointer are the UTF8 byte-order mark (BOM).
static bool isWhitespace(char character) noexcept
Checks whether a character is whitespace.
static int getHexDigitValue(juce_wchar digit) noexcept
Returns 0 to 16 for '0' to 'F", or -1 for characters that aren't a legal hex digit.
static bool isLetterOrDigit(char character) noexcept
Checks whether a character is alphabetic or numeric.
static CharPointerType1 find(CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept
Returns a pointer to the first occurrence of a substring in a string.
static int compareUpTo(CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept
Compares two null-terminated character strings, up to a given number of characters.
A type of InputSource that represents a normal file.
Represents a local file or directory.
Definition juce_File.h:45
A lightweight object that can create a stream to read some kind of resource.
Writes data to an internal memory buffer, which grows as required.
void insert(int index, String stringToAdd)
Inserts a string into the array.
void clear()
Removes all elements from the array.
int size() const noexcept
Returns the number of strings in the array.
void trim()
Deletes any whitespace characters from the starts and ends of all the strings.
void remove(int index)
Removes a string from the array.
int addTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
Breaks up a string into tokens and adds them to this array.
A simple class for holding temporary references to a string literal or String.
The JUCE String class!
Definition juce_String.h:53
CharPointerType getCharPointer() const noexcept
Returns the character pointer currently being used to store this string.
int indexOfChar(juce_wchar characterToLookFor) const noexcept
Searches for a character inside this string.
bool isEmpty() const noexcept
Returns true if the string contains no characters.
void clear() noexcept
Resets this string to be empty.
int lastIndexOfChar(juce_wchar character) const noexcept
Searches for a character inside this string (working backwards from the end of the string).
String trimCharactersAtEnd(StringRef charactersToTrim) const
Returns a copy of this string, having removed a specified set of characters from its end.
static String charToString(juce_wchar character)
Creates a string from a single character.
String substring(int startIndex, int endIndex) const
Returns a subsection of the string.
CharPointer_UTF8 CharPointerType
This is the character encoding type used internally to store the string.
bool isNotEmpty() const noexcept
Returns true if the string contains at least one character.
Parses a text-based XML document and creates an XmlElement object from it.
const String & getLastParseError() const noexcept
Returns the parsing error that occurred the last time getDocumentElement was called.
std::unique_ptr< XmlElement > getDocumentElementIfTagMatches(StringRef requiredTag)
Does an inexpensive check to see whether the outer element has the given tag name,...
std::unique_ptr< XmlElement > getDocumentElement(bool onlyReadOuterDocumentElement=false)
Creates an XmlElement object to represent the main document node.
XmlDocument(const String &documentText)
Creates an XmlDocument from the xml text.
static std::unique_ptr< XmlElement > parse(const File &file)
A handy static method that parses a file.
void setInputSource(InputSource *newSource) noexcept
Sets an input source object to use for parsing documents that reference external entities.
~XmlDocument()
Destructor.
void setEmptyTextElementsIgnored(bool shouldBeIgnored) noexcept
Sets a flag to change the treatment of empty text elements.
static XmlElement * createTextElement(const String &text)
Creates a text element that can be added to a parent element.
#define jassert(expression)
Platform-independent assertion macro.
typedef int
JUCE Namespace.
wchar_t juce_wchar
A platform-independent 32-bit unicode character type.
std::unique_ptr< XmlElement > parseXMLIfTagMatches(const String &textToParse, StringRef requiredTag)
Does an inexpensive check to see whether the top-level element has the given tag name,...
std::unique_ptr< XmlElement > parseXML(const String &textToParse)
Attempts to parse some XML text, returning a new XmlElement if it was valid.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
unsigned int uint32
A platform-independent 32-bit unsigned integer type.
constexpr int numElementsInArray(Type(&)[N]) noexcept
Handy function for getting the number of elements in a simple const C array.
typedef int64_t