The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

527 lines
18KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #include "../jucer_Headers.h"
  18. #include "jucer_CodeHelpers.h"
  19. //==============================================================================
  20. namespace CodeHelpers
  21. {
  22. String indent (const String& code, const int numSpaces, bool indentFirstLine)
  23. {
  24. if (numSpaces == 0)
  25. return code;
  26. const String space (String::repeatedString (" ", numSpaces));
  27. StringArray lines;
  28. lines.addLines (code);
  29. for (int i = (indentFirstLine ? 0 : 1); i < lines.size(); ++i)
  30. {
  31. String s (lines[i].trimEnd());
  32. if (s.isNotEmpty())
  33. s = space + s;
  34. lines.set (i, s);
  35. }
  36. return lines.joinIntoString (newLine);
  37. }
  38. String makeValidIdentifier (String s, bool capitalise, bool removeColons, bool allowTemplates)
  39. {
  40. if (s.isEmpty())
  41. return "unknown";
  42. if (removeColons)
  43. s = s.replaceCharacters (".,;:/@", "______");
  44. else
  45. s = s.replaceCharacters (".,;/@", "_____");
  46. for (int i = s.length(); --i > 0;)
  47. if (CharacterFunctions::isLetter (s[i])
  48. && CharacterFunctions::isLetter (s[i - 1])
  49. && CharacterFunctions::isUpperCase (s[i])
  50. && ! CharacterFunctions::isUpperCase (s[i - 1]))
  51. s = s.substring (0, i) + " " + s.substring (i);
  52. String allowedChars ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_ 0123456789");
  53. if (allowTemplates)
  54. allowedChars += "<>";
  55. if (! removeColons)
  56. allowedChars += ":";
  57. StringArray words;
  58. words.addTokens (s.retainCharacters (allowedChars), false);
  59. words.trim();
  60. String n (words[0]);
  61. if (capitalise)
  62. n = n.toLowerCase();
  63. for (int i = 1; i < words.size(); ++i)
  64. {
  65. if (capitalise && words[i].length() > 1)
  66. n << words[i].substring (0, 1).toUpperCase()
  67. << words[i].substring (1).toLowerCase();
  68. else
  69. n << words[i];
  70. }
  71. if (CharacterFunctions::isDigit (n[0]))
  72. n = "_" + n;
  73. if (CPlusPlusCodeTokeniser::isReservedKeyword (n))
  74. n << '_';
  75. return n;
  76. }
  77. static void writeEscapeChars (OutputStream& out, const char* utf8, const int numBytes,
  78. const int maxCharsOnLine, const bool breakAtNewLines,
  79. const bool replaceSingleQuotes, const bool allowStringBreaks)
  80. {
  81. int charsOnLine = 0;
  82. bool lastWasHexEscapeCode = false;
  83. for (int i = 0; i < numBytes || numBytes < 0; ++i)
  84. {
  85. const unsigned char c = (unsigned char) utf8[i];
  86. bool startNewLine = false;
  87. switch (c)
  88. {
  89. case '\t': out << "\\t"; lastWasHexEscapeCode = false; charsOnLine += 2; break;
  90. case '\r': out << "\\r"; lastWasHexEscapeCode = false; charsOnLine += 2; break;
  91. case '\n': out << "\\n"; lastWasHexEscapeCode = false; charsOnLine += 2; startNewLine = breakAtNewLines; break;
  92. case '\\': out << "\\\\"; lastWasHexEscapeCode = false; charsOnLine += 2; break;
  93. case '\"': out << "\\\""; lastWasHexEscapeCode = false; charsOnLine += 2; break;
  94. case 0:
  95. if (numBytes < 0)
  96. return;
  97. out << "\\0";
  98. lastWasHexEscapeCode = true;
  99. charsOnLine += 2;
  100. break;
  101. case '\'':
  102. if (replaceSingleQuotes)
  103. {
  104. out << "\\\'";
  105. lastWasHexEscapeCode = false;
  106. charsOnLine += 2;
  107. break;
  108. }
  109. // deliberate fall-through...
  110. default:
  111. if (c >= 32 && c < 127 && ! (lastWasHexEscapeCode // (have to avoid following a hex escape sequence with a valid hex digit)
  112. && CharacterFunctions::getHexDigitValue (c) >= 0))
  113. {
  114. out << (char) c;
  115. lastWasHexEscapeCode = false;
  116. ++charsOnLine;
  117. }
  118. else if (allowStringBreaks && lastWasHexEscapeCode && c >= 32 && c < 127)
  119. {
  120. out << "\"\"" << (char) c;
  121. lastWasHexEscapeCode = false;
  122. charsOnLine += 3;
  123. }
  124. else
  125. {
  126. out << (c < 16 ? "\\x0" : "\\x") << String::toHexString ((int) c);
  127. lastWasHexEscapeCode = true;
  128. charsOnLine += 4;
  129. }
  130. break;
  131. }
  132. if ((startNewLine || (maxCharsOnLine > 0 && charsOnLine >= maxCharsOnLine))
  133. && (numBytes < 0 || i < numBytes - 1))
  134. {
  135. charsOnLine = 0;
  136. out << "\"" << newLine << "\"";
  137. lastWasHexEscapeCode = false;
  138. }
  139. }
  140. }
  141. String addEscapeChars (const String& s)
  142. {
  143. MemoryOutputStream out;
  144. writeEscapeChars (out, s.toRawUTF8(), -1, -1, false, true, true);
  145. return out.toUTF8();
  146. }
  147. String createIncludeStatement (const File& includeFile, const File& targetFile)
  148. {
  149. return createIncludeStatement (FileHelpers::unixStylePath (FileHelpers::getRelativePathFrom (includeFile, targetFile.getParentDirectory())));
  150. }
  151. String createIncludeStatement (const String& includePath)
  152. {
  153. if (includePath.startsWithChar ('<') || includePath.startsWithChar ('"'))
  154. return "#include " + includePath;
  155. return "#include \"" + includePath + "\"";
  156. }
  157. String makeHeaderGuardName (const File& file)
  158. {
  159. return "__" + file.getFileName().toUpperCase()
  160. .replaceCharacters (" .", "__")
  161. .retainCharacters ("_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
  162. + "_" + String::toHexString (file.hashCode()).toUpperCase() + "__";
  163. }
  164. String makeBinaryDataIdentifierName (const File& file)
  165. {
  166. return makeValidIdentifier (file.getFileName()
  167. .replaceCharacters (" .", "__")
  168. .retainCharacters ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789"),
  169. false, true, false);
  170. }
  171. String stringLiteral (const String& text, int maxLineLength)
  172. {
  173. if (text.isEmpty())
  174. return "String::empty";
  175. StringArray lines;
  176. lines.add (text);
  177. if (maxLineLength > 0)
  178. {
  179. while (lines [lines.size() - 1].length() > maxLineLength)
  180. {
  181. String& lastLine = lines.getReference (lines.size() - 1);
  182. const String start (lastLine.substring (0, maxLineLength));
  183. const String end (lastLine.substring (maxLineLength));
  184. lastLine = start;
  185. lines.add (end);
  186. }
  187. }
  188. for (int i = 0; i < lines.size(); ++i)
  189. lines.getReference(i) = "\"" + addEscapeChars (lines.getReference(i)) + "\"";
  190. String result (lines.joinIntoString (newLine));
  191. if (! CharPointer_ASCII::isValidString (text.toUTF8(), std::numeric_limits<int>::max()))
  192. result = "CharPointer_UTF8 (" + result + ")";
  193. return result;
  194. }
  195. String alignFunctionCallParams (const String& call, const StringArray& parameters, const int maxLineLength)
  196. {
  197. String result, currentLine (call);
  198. for (int i = 0; i < parameters.size(); ++i)
  199. {
  200. if (currentLine.length() >= maxLineLength)
  201. {
  202. result += currentLine.trimEnd() + newLine;
  203. currentLine = String::repeatedString (" ", call.length()) + parameters[i];
  204. }
  205. else
  206. {
  207. currentLine += parameters[i];
  208. }
  209. if (i < parameters.size() - 1)
  210. currentLine << ", ";
  211. }
  212. return result + currentLine.trimEnd() + ")";
  213. }
  214. String floatLiteral (double value, int numDecPlaces)
  215. {
  216. String s (value, numDecPlaces);
  217. if (s.containsChar ('.'))
  218. s << 'f';
  219. else
  220. s << ".0f";
  221. return s;
  222. }
  223. String boolLiteral (bool value)
  224. {
  225. return value ? "true" : "false";
  226. }
  227. String colourToCode (Colour col)
  228. {
  229. const Colour colours[] =
  230. {
  231. #define COL(col) Colours::col,
  232. #include "jucer_Colours.h"
  233. #undef COL
  234. Colours::transparentBlack
  235. };
  236. static const char* colourNames[] =
  237. {
  238. #define COL(col) #col,
  239. #include "jucer_Colours.h"
  240. #undef COL
  241. 0
  242. };
  243. for (int i = 0; i < numElementsInArray (colourNames) - 1; ++i)
  244. if (col == colours[i])
  245. return "Colours::" + String (colourNames[i]);
  246. return "Colour (0x" + hexString8Digits ((int) col.getARGB()) + ')';
  247. }
  248. String justificationToCode (Justification justification)
  249. {
  250. switch (justification.getFlags())
  251. {
  252. case Justification::centred: return "Justification::centred";
  253. case Justification::centredLeft: return "Justification::centredLeft";
  254. case Justification::centredRight: return "Justification::centredRight";
  255. case Justification::centredTop: return "Justification::centredTop";
  256. case Justification::centredBottom: return "Justification::centredBottom";
  257. case Justification::topLeft: return "Justification::topLeft";
  258. case Justification::topRight: return "Justification::topRight";
  259. case Justification::bottomLeft: return "Justification::bottomLeft";
  260. case Justification::bottomRight: return "Justification::bottomRight";
  261. case Justification::left: return "Justification::left";
  262. case Justification::right: return "Justification::right";
  263. case Justification::horizontallyCentred: return "Justification::horizontallyCentred";
  264. case Justification::top: return "Justification::top";
  265. case Justification::bottom: return "Justification::bottom";
  266. case Justification::verticallyCentred: return "Justification::verticallyCentred";
  267. case Justification::horizontallyJustified: return "Justification::horizontallyJustified";
  268. default: break;
  269. }
  270. jassertfalse;
  271. return "Justification (" + String (justification.getFlags()) + ")";
  272. }
  273. void writeDataAsCppLiteral (const MemoryBlock& mb, OutputStream& out,
  274. bool breakAtNewLines, bool allowStringBreaks)
  275. {
  276. const int maxCharsOnLine = 250;
  277. const unsigned char* data = (const unsigned char*) mb.getData();
  278. int charsOnLine = 0;
  279. bool canUseStringLiteral = mb.getSize() < 32768; // MS compilers can't handle big string literals..
  280. if (canUseStringLiteral)
  281. {
  282. unsigned int numEscaped = 0;
  283. for (size_t i = 0; i < mb.getSize(); ++i)
  284. {
  285. const unsigned int num = (unsigned int) data[i];
  286. if (! ((num >= 32 && num < 127) || num == '\t' || num == '\r' || num == '\n'))
  287. {
  288. if (++numEscaped > mb.getSize() / 4)
  289. {
  290. canUseStringLiteral = false;
  291. break;
  292. }
  293. }
  294. }
  295. }
  296. if (! canUseStringLiteral)
  297. {
  298. out << "{ ";
  299. for (size_t i = 0; i < mb.getSize(); ++i)
  300. {
  301. const int num = (int) (unsigned int) data[i];
  302. out << num << ',';
  303. charsOnLine += 2;
  304. if (num >= 10)
  305. {
  306. ++charsOnLine;
  307. if (num >= 100)
  308. ++charsOnLine;
  309. }
  310. if (charsOnLine >= maxCharsOnLine)
  311. {
  312. charsOnLine = 0;
  313. out << newLine;
  314. }
  315. }
  316. out << "0,0 };";
  317. }
  318. else
  319. {
  320. out << "\"";
  321. writeEscapeChars (out, (const char*) data, (int) mb.getSize(),
  322. maxCharsOnLine, breakAtNewLines, false, allowStringBreaks);
  323. out << "\";";
  324. }
  325. }
  326. //==============================================================================
  327. static unsigned int calculateHash (const String& s, const int hashMultiplier)
  328. {
  329. const char* t = s.toUTF8();
  330. unsigned int hash = 0;
  331. while (*t != 0)
  332. hash = hashMultiplier * hash + (unsigned int) *t++;
  333. return hash;
  334. }
  335. static int findBestHashMultiplier (const StringArray& strings)
  336. {
  337. int v = 31;
  338. for (;;)
  339. {
  340. SortedSet <unsigned int> hashes;
  341. bool collision = false;
  342. for (int i = strings.size(); --i >= 0;)
  343. {
  344. const unsigned int hash = calculateHash (strings[i], v);
  345. if (hashes.contains (hash))
  346. {
  347. collision = true;
  348. break;
  349. }
  350. hashes.add (hash);
  351. }
  352. if (! collision)
  353. break;
  354. v += 2;
  355. }
  356. return v;
  357. }
  358. void createStringMatcher (OutputStream& out, const String& utf8PointerVariable,
  359. const StringArray& strings, const StringArray& codeToExecute, const int indentLevel)
  360. {
  361. jassert (strings.size() == codeToExecute.size());
  362. const String indent (String::repeatedString (" ", indentLevel));
  363. const int hashMultiplier = findBestHashMultiplier (strings);
  364. out << indent << "unsigned int hash = 0;" << newLine
  365. << indent << "if (" << utf8PointerVariable << " != 0)" << newLine
  366. << indent << " while (*" << utf8PointerVariable << " != 0)" << newLine
  367. << indent << " hash = " << hashMultiplier << " * hash + (unsigned int) *" << utf8PointerVariable << "++;" << newLine
  368. << newLine
  369. << indent << "switch (hash)" << newLine
  370. << indent << "{" << newLine;
  371. for (int i = 0; i < strings.size(); ++i)
  372. {
  373. out << indent << " case 0x" << hexString8Digits (calculateHash (strings[i], hashMultiplier))
  374. << ": " << codeToExecute[i] << newLine;
  375. }
  376. out << indent << " default: break;" << newLine
  377. << indent << "}" << newLine << newLine;
  378. }
  379. String getLeadingWhitespace (String line)
  380. {
  381. line = line.removeCharacters ("\r\n");
  382. const String::CharPointerType endOfLeadingWS (line.getCharPointer().findEndOfWhitespace());
  383. return String (line.getCharPointer(), endOfLeadingWS);
  384. }
  385. int getBraceCount (String::CharPointerType line)
  386. {
  387. int braces = 0;
  388. for (;;)
  389. {
  390. const juce_wchar c = line.getAndAdvance();
  391. if (c == 0) break;
  392. else if (c == '{') ++braces;
  393. else if (c == '}') --braces;
  394. else if (c == '/') { if (*line == '/') break; }
  395. else if (c == '"' || c == '\'') { while (! (line.isEmpty() || line.getAndAdvance() == c)) {} }
  396. }
  397. return braces;
  398. }
  399. bool getIndentForCurrentBlock (CodeDocument::Position pos, const String& tab,
  400. String& blockIndent, String& lastLineIndent)
  401. {
  402. int braceCount = 0;
  403. bool indentFound = false;
  404. while (pos.getLineNumber() > 0)
  405. {
  406. pos = pos.movedByLines (-1);
  407. const String line (pos.getLineText());
  408. const String trimmedLine (line.trimStart());
  409. braceCount += getBraceCount (trimmedLine.getCharPointer());
  410. if (braceCount > 0)
  411. {
  412. blockIndent = getLeadingWhitespace (line);
  413. if (! indentFound)
  414. lastLineIndent = blockIndent + tab;
  415. return true;
  416. }
  417. if ((! indentFound) && trimmedLine.isNotEmpty())
  418. {
  419. indentFound = true;
  420. lastLineIndent = getLeadingWhitespace (line);
  421. }
  422. }
  423. return false;
  424. }
  425. }