int StringArray::addLines (const String& sourceText) { int numLines = 0; String::CharPointerType text (sourceText.getCharPointer()); bool finished = text.isEmpty(); while (! finished) { for (String::CharPointerType startOfLine (text);;) { const String::CharPointerType endOfLine (text); switch (text.getAndAdvance()) { case 0: finished = true; break; case '\n': break; case '\r': if (*text == '\n') ++text; break; default: continue; } strings.add (String (startOfLine, endOfLine)); ++numLines; break; } } return numLines; }
int parseIdentifier (CodeDocument::Iterator& source) noexcept { int tokenLength = 0; String::CharPointerType::CharType possibleIdentifier [100]; String::CharPointerType possible (possibleIdentifier); while (isIdentifierBody (source.peekNextChar())) { const juce_wchar c = source.nextChar(); if (tokenLength < 20) possible.write (c); ++tokenLength; } if (tokenLength > 1 && tokenLength <= 16) { possible.writeNull(); if (isReservedKeyword (String::CharPointerType (possibleIdentifier), tokenLength)) return CPlusPlusCodeTokeniser::tokenType_builtInKeyword; } return CPlusPlusCodeTokeniser::tokenType_identifier; }
bool XmlDocument::parseHeader() { skipNextWhiteSpace(); if (CharacterFunctions::compareUpTo (input, CharPointer_ASCII ("<?xml"), 5) == 0) { const String::CharPointerType headerEnd (CharacterFunctions::find (input, CharPointer_ASCII ("?>"))); if (headerEnd.isEmpty()) return false; #if JUCE_DEBUG const String 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; }
static int parseIdentifier (Iterator& source) noexcept { int tokenLength = 0; String::CharPointerType::CharType possibleIdentifier [100]; String::CharPointerType possible (possibleIdentifier); while (CppTokeniserFunctions::isIdentifierBody (source.peekNextChar())) { const juce_wchar c = source.nextChar(); if (tokenLength < 20) possible.write (c); ++tokenLength; } if (tokenLength > 1 && tokenLength <= 16) { possible.writeNull(); if (isReservedKeyword (String::CharPointerType (possibleIdentifier), tokenLength)) return LuaTokeniser::tokenType_keyword; } return LuaTokeniser::tokenType_identifier; }
String stringLiteral (const String& text, int maxLineLength) { if (text.isEmpty()) return "String::empty"; StringArray lines; { String::CharPointerType t (text.getCharPointer()); bool finished = t.isEmpty(); while (! finished) { for (String::CharPointerType startOfLine (t);;) { switch (t.getAndAdvance()) { case 0: finished = true; break; case '\n': break; case '\r': if (*t == '\n') ++t; break; default: continue; } lines.add (String (startOfLine, t)); break; } } } if (maxLineLength > 0) { for (int i = 0; i < lines.size(); ++i) { String& line = lines.getReference (i); if (line.length() > maxLineLength) { const String start (line.substring (0, maxLineLength)); const String end (line.substring (maxLineLength)); line = start; lines.insert (i + 1, end); } } } for (int i = 0; i < lines.size(); ++i) lines.getReference(i) = CppTokeniserFunctions::addEscapeChars (lines.getReference(i)); lines.removeEmptyStrings(); for (int i = 0; i < lines.size(); ++i) lines.getReference(i) = "\"" + lines.getReference(i) + "\""; String result (lines.joinIntoString (newLine)); if (! CharPointer_ASCII::isValidString (text.toUTF8(), std::numeric_limits<int>::max())) result = "CharPointer_UTF8 (" + result + ")"; return result; }
void LivePropertyEditorBase::findOriginalValueInCode() { CodeDocument::Position pos (document, value.sourceLine, 0); String line (pos.getLineText()); String::CharPointerType p (line.getCharPointer()); p = CharacterFunctions::find (p, CharPointer_ASCII ("JUCE_LIVE_CONSTANT")); if (p.isEmpty()) { // Not sure how this would happen - some kind of mix-up between source code and line numbers.. jassertfalse; return; } p += (int) (sizeof ("JUCE_LIVE_CONSTANT") - 1); p = p.findEndOfWhitespace(); if (! CharacterFunctions::find (p, CharPointer_ASCII ("JUCE_LIVE_CONSTANT")).isEmpty()) { // Aargh! You've added two JUCE_LIVE_CONSTANT macros on the same line! // They're identified by their line number, so you must make sure each // one goes on a separate line! jassertfalse; } if (p.getAndAdvance() == '(') { String::CharPointerType start (p), end (p); int depth = 1; while (! end.isEmpty()) { const juce_wchar c = end.getAndAdvance(); if (c == '(') ++depth; if (c == ')') --depth; if (depth == 0) { --end; break; } } if (end > start) { valueStart = CodeDocument::Position (document, value.sourceLine, (int) (start - line.getCharPointer())); valueEnd = CodeDocument::Position (document, value.sourceLine, (int) (end - line.getCharPointer())); valueStart.setPositionMaintained (true); valueEnd.setPositionMaintained (true); wasHex = String (start, end).containsIgnoreCase ("0x"); } } }
void OutputStream::writeText (const String& text, const bool asUTF16, const bool writeUTF16ByteOrderMark) { if (asUTF16) { if (writeUTF16ByteOrderMark) write ("\x0ff\x0fe", 2); String::CharPointerType src (text.getCharPointer()); bool lastCharWasReturn = false; for (;;) { const juce_wchar c = src.getAndAdvance(); if (c == 0) break; if (c == '\n' && ! lastCharWasReturn) writeShort ((short) '\r'); lastCharWasReturn = (c == L'\r'); writeShort ((short) c); } } else { const char* src = text.toUTF8(); const char* t = src; for (;;) { if (*t == '\n') { if (t > src) write (src, (int) (t - src)); write ("\r\n", 2); src = t + 1; } else if (*t == '\r') { if (t[1] == '\n') ++t; } else if (*t == 0) { if (t > src) write (src, (int) (t - src)); break; } ++t; } } }
static Result parseString (const juce_wchar quoteChar, String::CharPointerType& t, var& result) { MemoryOutputStream buffer (256); for (;;) { juce_wchar c = t.getAndAdvance(); if (c == quoteChar) break; if (c == '\\') { c = t.getAndAdvance(); switch (c) { case '"': case '\'': case '\\': case '/': break; case 'a': c = '\a'; break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'u': { c = 0; for (int i = 4; --i >= 0;) { const int digitValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); if (digitValue < 0) return createFail ("Syntax error in unicode escape sequence"); c = (juce_wchar) ((c << 4) + digitValue); } break; } } } if (c == 0) return createFail ("Unexpected end-of-input in string constant"); buffer.appendUTF8Char (c); } result = buffer.toUTF8(); return Result::ok(); }
static void createLines (Array <CodeDocumentLine*>& newLines, const String& text) { String::CharPointerType t (text.getCharPointer()); int charNumInFile = 0; bool finished = false; while (! (finished || t.isEmpty())) { String::CharPointerType startOfLine (t); int startOfLineInFile = charNumInFile; int lineLength = 0; int numNewLineChars = 0; for (;;) { const juce_wchar c = t.getAndAdvance(); if (c == 0) { finished = true; break; } ++charNumInFile; ++lineLength; if (c == '\r') { ++numNewLineChars; if (*t == '\n') { ++t; ++charNumInFile; ++lineLength; ++numNewLineChars; } break; } if (c == '\n') { ++numNewLineChars; break; } } newLines.add (new CodeDocumentLine (startOfLine, lineLength, numNewLineChars, startOfLineInFile)); } jassert (charNumInFile == text.length()); }
void SimpleTypeLayout::appendText (const AttributedString& text, const Range<int>& stringRange, const Font& font, const Colour& colour) { String stringText = text.getText().substring(stringRange.getStart(), stringRange.getEnd()); String::CharPointerType t (stringText.getCharPointer()); String currentString; int lastCharType = 0; for (;;) { const juce_wchar c = t.getAndAdvance(); if (c == 0) break; int charType; if (c == '\r' || c == '\n') { charType = 0; } else if (CharacterFunctions::isWhitespace (c)) { charType = 2; } else { charType = 1; } if (charType == 0 || charType != lastCharType) { if (currentString.isNotEmpty()) { tokens.add (new Token (currentString, font, colour, lastCharType == 2 || lastCharType == 0)); } currentString = String::charToString (c); if (c == '\r' && *t == '\n') currentString += t.getAndAdvance(); } else { currentString += c; } lastCharType = charType; } if (currentString.isNotEmpty()) tokens.add (new Token (currentString, font, colour, lastCharType == 2)); }
static Result parseObjectOrArray (String::CharPointerType t, var& result) { t = t.findEndOfWhitespace(); switch (t.getAndAdvance()) { case 0: result = var::null; return Result::ok(); case '{': return parseObject (t, result); case '[': return parseArray (t, result); } return createFail ("Expected '{' or '['", &t); }
static String nextToken (String::CharPointerType& t) { t = t.findEndOfWhitespace(); String::CharPointerType start (t); size_t numChars = 0; while (! (t.isEmpty() || t.isWhitespace())) { ++t; ++numChars; } return String (start, numChars); }
int findFirstNonWhitespaceChar (const String& line) noexcept { String::CharPointerType t (line.getCharPointer()); int i = 0; while (! t.isEmpty()) { if (! t.isWhitespace()) return i; ++t; ++i; } return 0; }
int indexToColumn (int index, const String& line, int spacesPerTab) const noexcept { jassert (index <= line.length()); String::CharPointerType t (line.getCharPointer()); int col = 0; for (int i = 0; i < index; ++i) { if (t.getAndAdvance() != '\t') ++col; else col += spacesPerTab - (col % spacesPerTab); } return col; }
MD5::MD5 (const String& text) { ProcessContext context; String::CharPointerType t (text.getCharPointer()); while (! t.isEmpty()) { // force the string into integer-sized unicode characters, to try to make it // get the same results on all platforms + compilers. uint32 unicodeChar = ByteOrder::swapIfBigEndian ((uint32) t.getAndAdvance()); context.processBlock (&unicodeChar, sizeof (unicodeChar)); } context.finish (result); }
inline void skipComma (String::CharPointerType& s) { s = s.findEndOfWhitespace(); if (*s == ',') ++s; }
int getBraceCount (String::CharPointerType line) { int braces = 0; for (;;) { const juce_wchar c = line.getAndAdvance(); if (c == 0) break; else if (c == '{') ++braces; else if (c == '}') --braces; else if (c == '/') { if (*line == '/') break; } else if (c == '"' || c == '\'') { while (! (line.isEmpty() || line.getAndAdvance() == c)) {} } } return braces; }
static Result parseAny (String::CharPointerType& t, var& result) { t = t.findEndOfWhitespace(); auto t2 = t; switch (t2.getAndAdvance()) { case '{': t = t2; return parseObject (t, result); case '[': t = t2; return parseArray (t, result); case '"': t = t2; return parseString ('"', t, result); case '\'': t = t2; return parseString ('\'', t, result); case '-': t2 = t2.findEndOfWhitespace(); if (! CharacterFunctions::isDigit (*t2)) break; t = t2; return parseNumber (t, result, true); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return parseNumber (t, result, false); case 't': // "true" if (t2.getAndAdvance() == 'r' && t2.getAndAdvance() == 'u' && t2.getAndAdvance() == 'e') { t = t2; result = var (true); return Result::ok(); } break; case 'f': // "false" if (t2.getAndAdvance() == 'a' && t2.getAndAdvance() == 'l' && t2.getAndAdvance() == 's' && t2.getAndAdvance() == 'e') { t = t2; result = var (false); return Result::ok(); } break; case 'n': // "null" if (t2.getAndAdvance() == 'u' && t2.getAndAdvance() == 'l' && t2.getAndAdvance() == 'l') { t = t2; result = var(); return Result::ok(); } break; default: break; } return createFail ("Syntax error", &t); }
void updateLength() noexcept { lineLength = 0; lineLengthWithoutNewLines = 0; for (String::CharPointerType t (line.getCharPointer());;) { const juce_wchar c = t.getAndAdvance(); if (c == 0) break; ++lineLength; if (c != '\n' && c != '\r') lineLengthWithoutNewLines = lineLength; } }
bool isReservedKeyword (String::CharPointerType token, const int tokenLength) noexcept { static const char* const keywords2Char[] = { "if", "do", "or", "id", 0 }; static const char* const keywords3Char[] = { "for", "int", "new", "try", "xor", "and", "asm", "not", 0 }; static const char* const keywords4Char[] = { "bool", "void", "this", "true", "long", "else", "char", "enum", "case", "goto", "auto", 0 }; static const char* const keywords5Char[] = { "while", "bitor", "break", "catch", "class", "compl", "const", "false", "float", "short", "throw", "union", "using", "or_eq", 0 }; static const char* const keywords6Char[] = { "return", "struct", "and_eq", "bitand", "delete", "double", "extern", "friend", "inline", "not_eq", "public", "sizeof", "static", "signed", "switch", "typeid", "wchar_t", "xor_eq", 0}; static const char* const keywordsOther[] = { "const_cast", "continue", "default", "explicit", "mutable", "namespace", "operator", "private", "protected", "register", "reinterpret_cast", "static_cast", "template", "typedef", "typename", "unsigned", "virtual", "volatile", "@implementation", "@interface", "@end", "@synthesize", "@dynamic", "@public", "@private", "@property", "@protected", "@class", 0 }; const char* const* k; switch (tokenLength) { case 2: k = keywords2Char; break; case 3: k = keywords3Char; break; case 4: k = keywords4Char; break; case 5: k = keywords5Char; break; case 6: k = keywords6Char; break; default: if (tokenLength < 2 || tokenLength > 16) return false; k = keywordsOther; break; } int i = 0; while (k[i] != 0) { if (token.compare (CharPointer_ASCII (k[i])) == 0) return true; ++i; } return false; }
String StringPool::getPooledString (String::CharPointerType start, String::CharPointerType end) { if (start.isEmpty() || start == end) return String(); const ScopedLock sl (lock); garbageCollectIfNeeded(); return addPooledString (strings, StartEndString (start, end)); }
void GlyphArrangement::addCurtailedLineOfText (const Font& font, const String& text, const float xOffset, const float yOffset, const float maxWidthPixels, const bool useEllipsis) { if (text.isNotEmpty()) { Array <int> newGlyphs; Array <float> xOffsets; font.getGlyphPositions (text, newGlyphs, xOffsets); const int textLen = newGlyphs.size(); glyphs.ensureStorageAllocated (glyphs.size() + textLen); String::CharPointerType t (text.getCharPointer()); for (int i = 0; i < textLen; ++i) { const float thisX = xOffsets.getUnchecked (i); const float nextX = xOffsets.getUnchecked (i + 1); if (nextX > maxWidthPixels + 1.0f) { // curtail the string if it's too wide.. if (useEllipsis && textLen > 3 && glyphs.size() >= 3) insertEllipsis (font, xOffset + maxWidthPixels, 0, glyphs.size()); break; } else { const bool isWhitespace = t.isWhitespace(); glyphs.add (new PositionedGlyph (font, t.getAndAdvance(), newGlyphs.getUnchecked(i), xOffset + thisX, yOffset, nextX - thisX, isWhitespace)); } } } }
int StringArray::addLines (const String& sourceText) { int numLines = 0; String::CharPointerType text (sourceText.getCharPointer()); bool finished = text.isEmpty(); while (! finished) { String::CharPointerType startOfLine (text); size_t numChars = 0; for (;;) { const juce_wchar c = text.getAndAdvance(); if (c == 0) { finished = true; break; } if (c == '\n') break; if (c == '\r') { if (*text == '\n') ++text; break; } ++numChars; } add (String (startOfLine, numChars)); ++numLines; } return numLines; }
static int findLongestCommonSubstring (String::CharPointerType a, const int lenA, const String::CharPointerType b, const int lenB, int& indexInA, int& indexInB) { if (lenA == 0 || lenB == 0) return 0; HeapBlock<int> lines; lines.calloc (2 + 2 * (size_t) lenB); int* l0 = lines; int* l1 = l0 + lenB + 1; int loopsWithoutImprovement = 0; int bestLength = 0; indexInA = indexInB = 0; for (int i = 0; i < lenA; ++i) { const juce_wchar ca = a.getAndAdvance(); String::CharPointerType b2 (b); for (int j = 0; j < lenB; ++j) { if (ca != b2.getAndAdvance()) { l1[j + 1] = 0; } else { const int len = l0[j] + 1; l1[j + 1] = len; if (len > bestLength) { loopsWithoutImprovement = 0; bestLength = len; indexInA = i; indexInB = j; } } } if (++loopsWithoutImprovement > 100) break; std::swap (l0, l1); } indexInA -= bestLength - 1; indexInB -= bestLength - 1; return bestLength; }
int CodeEditorComponent::indexToColumn (int lineNum, int index) const noexcept { String::CharPointerType t (document.getLine (lineNum).getCharPointer()); int col = 0; for (int i = 0; i < index; ++i) { if (t.isEmpty()) { jassertfalse; break; } if (t.getAndAdvance() != '\t') ++col; else col += getTabSize() - (col % getTabSize()); } return col; }
int CodeEditorComponent::columnToIndex (int lineNum, int column) const noexcept { String::CharPointerType t (document.getLine (lineNum).getCharPointer()); int i = 0, col = 0; while (! t.isEmpty()) { if (t.getAndAdvance() != '\t') ++col; else col += getTabSize() - (col % getTabSize()); if (col > column) break; ++i; } return i; }
//============================================================================== String StringArray::joinIntoString (const String& separator, int start, int numberToJoin) const { const int last = (numberToJoin < 0) ? size() : jmin (size(), start + numberToJoin); if (start < 0) start = 0; if (start >= last) return String::empty; if (start == last - 1) return strings.getReference (start); const size_t separatorBytes = separator.getCharPointer().sizeInBytes() - sizeof (String::CharPointerType::CharType); size_t bytesNeeded = separatorBytes * (last - start - 1); for (int i = start; i < last; ++i) bytesNeeded += strings.getReference(i).getCharPointer().sizeInBytes() - sizeof (String::CharPointerType::CharType); String result; result.preallocateBytes (bytesNeeded); String::CharPointerType dest (result.getCharPointer()); while (start < last) { const String& s = strings.getReference (start); if (! s.isEmpty()) dest.writeAll (s.getCharPointer()); if (++start < last && separatorBytes > 0) dest.writeAll (separator.getCharPointer()); } dest.writeNull(); return result; }
int StringArray::addTokens (StringRef text, StringRef breakCharacters, StringRef quoteCharacters) { int num = 0; if (text.isNotEmpty()) { for (String::CharPointerType t (text.text);;) { String::CharPointerType tokenEnd (CharacterFunctions::findEndOfToken (t, breakCharacters.text, quoteCharacters.text)); strings.add (String (t, tokenEnd)); ++num; if (tokenEnd.isEmpty()) break; t = ++tokenEnd; } } return num; }
int StringArray::addTokens (const String& text, const String& breakCharacters, const String& quoteCharacters) { int num = 0; String::CharPointerType t (text.getCharPointer()); if (! t.isEmpty()) { for (;;) { String::CharPointerType tokenEnd (CharacterFunctions::findEndOfToken (t, breakCharacters.getCharPointer(), quoteCharacters.getCharPointer())); add (String (t, tokenEnd)); ++num; if (tokenEnd.isEmpty()) break; t = ++tokenEnd; } } return num; }
void getGlyphPositions (const String& text, Array<int>& glyphs, Array<float>& xOffsets) override { JNIEnv* env = getEnv(); const int numChars = text.length(); jfloatArray widths = env->NewFloatArray (numChars); const int numDone = paint.callIntMethod (Paint.getTextWidths, javaString (text).get(), widths); HeapBlock<jfloat> localWidths (numDone); env->GetFloatArrayRegion (widths, 0, numDone, localWidths); env->DeleteLocalRef (widths); String::CharPointerType s (text.getCharPointer()); xOffsets.add (0); float x = 0; for (int i = 0; i < numDone; ++i) { glyphs.add ((int) s.getAndAdvance()); x += localWidths[i]; xOffsets.add (x * referenceFontToUnits); } }