OpenShot Audio Library | OpenShotAudio  0.3.1
juce_XmlDocument.cpp
1 /*
2  ==============================================================================
3 
4  This file is part of the JUCE library.
5  Copyright (c) 2017 - ROLI Ltd.
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 
23 namespace juce
24 {
25 
26 XmlDocument::XmlDocument (const String& text) : originalText (text) {}
27 XmlDocument::XmlDocument (const File& file) : inputSource (new FileInputSource (file)) {}
28 
30 
31 std::unique_ptr<XmlElement> XmlDocument::parse (const File& file)
32 {
33  return XmlDocument (file).getDocumentElement();
34 }
35 
36 std::unique_ptr<XmlElement> XmlDocument::parse (const String& textToParse)
37 {
38  return XmlDocument (textToParse).getDocumentElement();
39 }
40 
41 std::unique_ptr<XmlElement> parseXML (const String& textToParse)
42 {
43  return XmlDocument (textToParse).getDocumentElement();
44 }
45 
46 std::unique_ptr<XmlElement> parseXML (const File& file)
47 {
48  return XmlDocument (file).getDocumentElement();
49 }
50 
51 std::unique_ptr<XmlElement> parseXMLIfTagMatches (const String& textToParse, StringRef requiredTag)
52 {
53  return XmlDocument (textToParse).getDocumentElementIfTagMatches (requiredTag);
54 }
55 
56 std::unique_ptr<XmlElement> parseXMLIfTagMatches (const File& file, StringRef requiredTag)
57 {
58  return XmlDocument (file).getDocumentElementIfTagMatches (requiredTag);
59 }
60 
61 void XmlDocument::setInputSource (InputSource* newSource) noexcept
62 {
63  inputSource.reset (newSource);
64 }
65 
66 void XmlDocument::setEmptyTextElementsIgnored (bool shouldBeIgnored) noexcept
67 {
68  ignoreEmptyTextElements = shouldBeIgnored;
69 }
70 
71 namespace 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 
110 std::unique_ptr<XmlElement> XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentElement)
111 {
112  if (originalText.isEmpty() && inputSource != nullptr)
113  {
114  std::unique_ptr<InputStream> in (inputSource->createInputStream());
115 
116  if (in != nullptr)
117  {
118  MemoryOutputStream data;
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 
150 std::unique_ptr<XmlElement> XmlDocument::getDocumentElementIfTagMatches (StringRef requiredTag)
151 {
152  if (auto xml = getDocumentElement (true))
153  if (xml->hasTagName (requiredTag))
154  return getDocumentElement (false);
155 
156  return {};
157 }
158 
159 const String& XmlDocument::getLastParseError() const noexcept
160 {
161  return lastError;
162 }
163 
164 void XmlDocument::setLastError (const String& desc, const bool carryOn)
165 {
166  lastError = desc;
167  errorOccurred = ! carryOn;
168 }
169 
170 String 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 
183 juce_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 
196 std::unique_ptr<XmlElement> XmlDocument::parseDocumentElement (String::CharPointerType textToParse,
197  bool onlyReadOuterDocumentElement)
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();
219  std::unique_ptr<XmlElement> result (readNextElement (! onlyReadOuterDocumentElement));
220 
221  if (! errorOccurred)
222  return result;
223  }
224 
225  return {};
226 }
227 
228 bool 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 
264 bool 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 
290 void XmlDocument::skipNextWhiteSpace()
291 {
292  for (;;)
293  {
294  input = input.findEndOfWhitespace();
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 
341 void 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 
392 XmlElement* 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 
440  if (alsoParseSubElements)
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 
491 void 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  {
615  textElementContent << entity;
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);
644  contentShouldBeUsed = contentShouldBeUsed || ! CharacterFunctions::isWhitespace (nextChar);
645  }
646  }
647  }
648 
649  if (contentShouldBeUsed)
650  childAppender.append (XmlElement::createTextElement (textElementContent.toUTF8()));
651  }
652  }
653 }
654 
655 void 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  int charCode = 0;
688  ++input;
689 
690  if (*input == 'x' || *input == 'X')
691  {
692  ++input;
693  int numChars = 0;
694 
695  while (input[0] != ';')
696  {
697  auto hexValue = CharacterFunctions::getHexDigitValue (input[0]);
698 
699  if (hexValue < 0 || ++numChars > 8)
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  while (input[0] != ';')
716  {
717  if (++numChars > 12)
718  {
719  setLastError ("illegal escape sequence", true);
720  break;
721  }
722 
723  charCode = charCode * 10 + ((int) input[0] - '0');
724  ++input;
725  }
726 
727  ++input;
728  }
729  else
730  {
731  setLastError ("illegal escape sequence", true);
732  result += '&';
733  return;
734  }
735 
736  result << (juce_wchar) charCode;
737  }
738  else
739  {
740  auto entityNameStart = input;
741  auto closingSemiColon = input.indexOf ((juce_wchar) ';');
742 
743  if (closingSemiColon < 0)
744  {
745  outOfData = true;
746  result += '&';
747  }
748  else
749  {
750  input += closingSemiColon + 1;
751  result += expandExternalEntity (String (entityNameStart, (size_t) closingSemiColon));
752  }
753  }
754 }
755 
756 String XmlDocument::expandEntity (const String& ent)
757 {
758  if (ent.equalsIgnoreCase ("amp")) return String::charToString ('&');
759  if (ent.equalsIgnoreCase ("quot")) return String::charToString ('"');
760  if (ent.equalsIgnoreCase ("apos")) return String::charToString ('\'');
761  if (ent.equalsIgnoreCase ("lt")) return String::charToString ('<');
762  if (ent.equalsIgnoreCase ("gt")) return String::charToString ('>');
763 
764  if (ent[0] == '#')
765  {
766  auto char1 = ent[1];
767 
768  if (char1 == 'x' || char1 == 'X')
769  return String::charToString (static_cast<juce_wchar> (ent.substring (2).getHexValue32()));
770 
771  if (char1 >= '0' && char1 <= '9')
772  return String::charToString (static_cast<juce_wchar> (ent.substring (1).getIntValue()));
773 
774  setLastError ("illegal escape sequence", false);
775  return String::charToString ('&');
776  }
777 
778  return expandExternalEntity (ent);
779 }
780 
781 String XmlDocument::expandExternalEntity (const String& entity)
782 {
783  if (needToLoadDTD)
784  {
785  if (dtdText.isNotEmpty())
786  {
787  dtdText = dtdText.trimCharactersAtEnd (">");
788  tokenisedDTD.addTokens (dtdText, true);
789 
790  if (tokenisedDTD[tokenisedDTD.size() - 2].equalsIgnoreCase ("system")
791  && tokenisedDTD[tokenisedDTD.size() - 1].isQuotedString())
792  {
793  auto fn = tokenisedDTD[tokenisedDTD.size() - 1];
794 
795  tokenisedDTD.clear();
796  tokenisedDTD.addTokens (getFileContents (fn), true);
797  }
798  else
799  {
800  tokenisedDTD.clear();
801  auto openBracket = dtdText.indexOfChar ('[');
802 
803  if (openBracket > 0)
804  {
805  auto closeBracket = dtdText.lastIndexOfChar (']');
806 
807  if (closeBracket > openBracket)
808  tokenisedDTD.addTokens (dtdText.substring (openBracket + 1,
809  closeBracket), true);
810  }
811  }
812 
813  for (int i = tokenisedDTD.size(); --i >= 0;)
814  {
815  if (tokenisedDTD[i].startsWithChar ('%')
816  && tokenisedDTD[i].endsWithChar (';'))
817  {
818  auto parsed = getParameterEntity (tokenisedDTD[i].substring (1, tokenisedDTD[i].length() - 1));
819  StringArray newToks;
820  newToks.addTokens (parsed, true);
821 
822  tokenisedDTD.remove (i);
823 
824  for (int j = newToks.size(); --j >= 0;)
825  tokenisedDTD.insert (i, newToks[j]);
826  }
827  }
828  }
829 
830  needToLoadDTD = false;
831  }
832 
833  for (int i = 0; i < tokenisedDTD.size(); ++i)
834  {
835  if (tokenisedDTD[i] == entity)
836  {
837  if (tokenisedDTD[i - 1].equalsIgnoreCase ("<!entity"))
838  {
839  auto ent = tokenisedDTD [i + 1].trimCharactersAtEnd (">").trim().unquoted();
840 
841  // check for sub-entities..
842  auto ampersand = ent.indexOfChar ('&');
843 
844  while (ampersand >= 0)
845  {
846  auto semiColon = ent.indexOf (i + 1, ";");
847 
848  if (semiColon < 0)
849  {
850  setLastError ("entity without terminating semi-colon", false);
851  break;
852  }
853 
854  auto resolved = expandEntity (ent.substring (i + 1, semiColon));
855 
856  ent = ent.substring (0, ampersand)
857  + resolved
858  + ent.substring (semiColon + 1);
859 
860  ampersand = ent.indexOfChar (semiColon + 1, '&');
861  }
862 
863  return ent;
864  }
865  }
866  }
867 
868  setLastError ("unknown entity", true);
869  return entity;
870 }
871 
872 String XmlDocument::getParameterEntity (const String& entity)
873 {
874  for (int i = 0; i < tokenisedDTD.size(); ++i)
875  {
876  if (tokenisedDTD[i] == entity
877  && tokenisedDTD [i - 1] == "%"
878  && tokenisedDTD [i - 2].equalsIgnoreCase ("<!entity"))
879  {
880  auto ent = tokenisedDTD [i + 1].trimCharactersAtEnd (">");
881 
882  if (ent.equalsIgnoreCase ("system"))
883  return getFileContents (tokenisedDTD [i + 2].trimCharactersAtEnd (">"));
884 
885  return ent.trim().unquoted();
886  }
887  }
888 
889  return entity;
890 }
891 
892 }
static bool isByteOrderMarkBigEndian(const void *possibleByteOrder) noexcept
static bool isByteOrderMarkLittleEndian(const void *possibleByteOrder) noexcept
static bool isByteOrderMark(const void *possibleByteOrder) noexcept
static int getHexDigitValue(juce_wchar digit) noexcept
static bool isWhitespace(char character) noexcept
static bool isLetterOrDigit(char character) noexcept
static CharPointerType1 find(CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept
static int compareUpTo(CharPointerType1 s1, CharPointerType2 s2, int maxChars) noexcept
const void * getData() const noexcept
size_t getDataSize() const noexcept
int64 writeFromInputStream(InputStream &, int64 maxNumBytesToWrite) override
virtual bool writeByte(char byte)
void insert(int index, String stringToAdd)
int size() const noexcept
void remove(int index)
int addTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
CharPointerType getCharPointer() const noexcept
Definition: juce_String.h:1198
int indexOfChar(juce_wchar characterToLookFor) const noexcept
String trim() const
bool isEmpty() const noexcept
Definition: juce_String.h:296
void clear() noexcept
int lastIndexOfChar(juce_wchar character) const noexcept
String trimCharactersAtEnd(StringRef charactersToTrim) const
static String charToString(juce_wchar character)
String substring(int startIndex, int endIndex) const
bool isNotEmpty() const noexcept
Definition: juce_String.h:302
const String & getLastParseError() const noexcept
std::unique_ptr< XmlElement > getDocumentElementIfTagMatches(StringRef requiredTag)
std::unique_ptr< XmlElement > getDocumentElement(bool onlyReadOuterDocumentElement=false)
XmlDocument(const String &documentText)
static std::unique_ptr< XmlElement > parse(const File &file)
void setInputSource(InputSource *newSource) noexcept
void setEmptyTextElementsIgnored(bool shouldBeIgnored) noexcept
static XmlElement * createTextElement(const String &text)