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.

476 lines
16KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI 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. String createIncludeStatement (const File& includeFile, const File& targetFile)
  78. {
  79. return createIncludeStatement (FileHelpers::unixStylePath (FileHelpers::getRelativePathFrom (includeFile, targetFile.getParentDirectory())));
  80. }
  81. String createIncludeStatement (const String& includePath)
  82. {
  83. if (includePath.startsWithChar ('<') || includePath.startsWithChar ('"'))
  84. return "#include " + includePath;
  85. return "#include \"" + includePath + "\"";
  86. }
  87. String makeHeaderGuardName (const File& file)
  88. {
  89. return file.getFileName().toUpperCase()
  90. .replaceCharacters (" .", "__")
  91. .retainCharacters ("_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
  92. + "_INCLUDED";
  93. }
  94. String makeBinaryDataIdentifierName (const File& file)
  95. {
  96. return makeValidIdentifier (file.getFileName()
  97. .replaceCharacters (" .", "__")
  98. .retainCharacters ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789"),
  99. false, true, false);
  100. }
  101. String stringLiteral (const String& text, int maxLineLength)
  102. {
  103. if (text.isEmpty())
  104. return "String()";
  105. StringArray lines;
  106. {
  107. String::CharPointerType t (text.getCharPointer());
  108. bool finished = t.isEmpty();
  109. while (! finished)
  110. {
  111. for (String::CharPointerType startOfLine (t);;)
  112. {
  113. switch (t.getAndAdvance())
  114. {
  115. case 0: finished = true; break;
  116. case '\n': break;
  117. case '\r': if (*t == '\n') ++t; break;
  118. default: continue;
  119. }
  120. lines.add (String (startOfLine, t));
  121. break;
  122. }
  123. }
  124. }
  125. if (maxLineLength > 0)
  126. {
  127. for (int i = 0; i < lines.size(); ++i)
  128. {
  129. String& line = lines.getReference (i);
  130. if (line.length() > maxLineLength)
  131. {
  132. const String start (line.substring (0, maxLineLength));
  133. const String end (line.substring (maxLineLength));
  134. line = start;
  135. lines.insert (i + 1, end);
  136. }
  137. }
  138. }
  139. for (int i = 0; i < lines.size(); ++i)
  140. lines.getReference(i) = CppTokeniserFunctions::addEscapeChars (lines.getReference(i));
  141. lines.removeEmptyStrings();
  142. for (int i = 0; i < lines.size(); ++i)
  143. lines.getReference(i) = "\"" + lines.getReference(i) + "\"";
  144. String result (lines.joinIntoString (newLine));
  145. if (! CharPointer_ASCII::isValidString (text.toUTF8(), std::numeric_limits<int>::max()))
  146. result = "CharPointer_UTF8 (" + result + ")";
  147. return result;
  148. }
  149. String alignFunctionCallParams (const String& call, const StringArray& parameters, const int maxLineLength)
  150. {
  151. String result, currentLine (call);
  152. for (int i = 0; i < parameters.size(); ++i)
  153. {
  154. if (currentLine.length() >= maxLineLength)
  155. {
  156. result += currentLine.trimEnd() + newLine;
  157. currentLine = String::repeatedString (" ", call.length()) + parameters[i];
  158. }
  159. else
  160. {
  161. currentLine += parameters[i];
  162. }
  163. if (i < parameters.size() - 1)
  164. currentLine << ", ";
  165. }
  166. return result + currentLine.trimEnd() + ")";
  167. }
  168. String floatLiteral (double value, int numDecPlaces)
  169. {
  170. String s (value, numDecPlaces);
  171. if (s.containsChar ('.'))
  172. s << 'f';
  173. else
  174. s << ".0f";
  175. return s;
  176. }
  177. String boolLiteral (bool value)
  178. {
  179. return value ? "true" : "false";
  180. }
  181. String colourToCode (Colour col)
  182. {
  183. const Colour colours[] =
  184. {
  185. #define COL(col) Colours::col,
  186. #include "jucer_Colours.h"
  187. #undef COL
  188. Colours::transparentBlack
  189. };
  190. static const char* colourNames[] =
  191. {
  192. #define COL(col) #col,
  193. #include "jucer_Colours.h"
  194. #undef COL
  195. 0
  196. };
  197. for (int i = 0; i < numElementsInArray (colourNames) - 1; ++i)
  198. if (col == colours[i])
  199. return "Colours::" + String (colourNames[i]);
  200. return "Colour (0x" + hexString8Digits ((int) col.getARGB()) + ')';
  201. }
  202. String justificationToCode (Justification justification)
  203. {
  204. switch (justification.getFlags())
  205. {
  206. case Justification::centred: return "Justification::centred";
  207. case Justification::centredLeft: return "Justification::centredLeft";
  208. case Justification::centredRight: return "Justification::centredRight";
  209. case Justification::centredTop: return "Justification::centredTop";
  210. case Justification::centredBottom: return "Justification::centredBottom";
  211. case Justification::topLeft: return "Justification::topLeft";
  212. case Justification::topRight: return "Justification::topRight";
  213. case Justification::bottomLeft: return "Justification::bottomLeft";
  214. case Justification::bottomRight: return "Justification::bottomRight";
  215. case Justification::left: return "Justification::left";
  216. case Justification::right: return "Justification::right";
  217. case Justification::horizontallyCentred: return "Justification::horizontallyCentred";
  218. case Justification::top: return "Justification::top";
  219. case Justification::bottom: return "Justification::bottom";
  220. case Justification::verticallyCentred: return "Justification::verticallyCentred";
  221. case Justification::horizontallyJustified: return "Justification::horizontallyJustified";
  222. default: break;
  223. }
  224. jassertfalse;
  225. return "Justification (" + String (justification.getFlags()) + ")";
  226. }
  227. void writeDataAsCppLiteral (const MemoryBlock& mb, OutputStream& out,
  228. bool breakAtNewLines, bool allowStringBreaks)
  229. {
  230. const int maxCharsOnLine = 250;
  231. const unsigned char* data = (const unsigned char*) mb.getData();
  232. int charsOnLine = 0;
  233. bool canUseStringLiteral = mb.getSize() < 32768; // MS compilers can't handle big string literals..
  234. if (canUseStringLiteral)
  235. {
  236. unsigned int numEscaped = 0;
  237. for (size_t i = 0; i < mb.getSize(); ++i)
  238. {
  239. const unsigned int num = (unsigned int) data[i];
  240. if (! ((num >= 32 && num < 127) || num == '\t' || num == '\r' || num == '\n'))
  241. {
  242. if (++numEscaped > mb.getSize() / 4)
  243. {
  244. canUseStringLiteral = false;
  245. break;
  246. }
  247. }
  248. }
  249. }
  250. if (! canUseStringLiteral)
  251. {
  252. out << "{ ";
  253. for (size_t i = 0; i < mb.getSize(); ++i)
  254. {
  255. const int num = (int) (unsigned int) data[i];
  256. out << num << ',';
  257. charsOnLine += 2;
  258. if (num >= 10)
  259. {
  260. ++charsOnLine;
  261. if (num >= 100)
  262. ++charsOnLine;
  263. }
  264. if (charsOnLine >= maxCharsOnLine)
  265. {
  266. charsOnLine = 0;
  267. out << newLine;
  268. }
  269. }
  270. out << "0,0 };";
  271. }
  272. else
  273. {
  274. out << "\"";
  275. CppTokeniserFunctions::writeEscapeChars (out, (const char*) data, (int) mb.getSize(),
  276. maxCharsOnLine, breakAtNewLines, false, allowStringBreaks);
  277. out << "\";";
  278. }
  279. }
  280. //==============================================================================
  281. static unsigned int calculateHash (const String& s, const unsigned int hashMultiplier)
  282. {
  283. const char* t = s.toUTF8();
  284. unsigned int hash = 0;
  285. while (*t != 0)
  286. hash = hashMultiplier * hash + (unsigned int) *t++;
  287. return hash;
  288. }
  289. static unsigned int findBestHashMultiplier (const StringArray& strings)
  290. {
  291. unsigned int v = 31;
  292. for (;;)
  293. {
  294. SortedSet <unsigned int> hashes;
  295. bool collision = false;
  296. for (int i = strings.size(); --i >= 0;)
  297. {
  298. const unsigned int hash = calculateHash (strings[i], v);
  299. if (hashes.contains (hash))
  300. {
  301. collision = true;
  302. break;
  303. }
  304. hashes.add (hash);
  305. }
  306. if (! collision)
  307. break;
  308. v += 2;
  309. }
  310. return v;
  311. }
  312. void createStringMatcher (OutputStream& out, const String& utf8PointerVariable,
  313. const StringArray& strings, const StringArray& codeToExecute, const int indentLevel)
  314. {
  315. jassert (strings.size() == codeToExecute.size());
  316. const String indent (String::repeatedString (" ", indentLevel));
  317. const unsigned int hashMultiplier = findBestHashMultiplier (strings);
  318. out << indent << "unsigned int hash = 0;" << newLine
  319. << indent << "if (" << utf8PointerVariable << " != 0)" << newLine
  320. << indent << " while (*" << utf8PointerVariable << " != 0)" << newLine
  321. << indent << " hash = " << (int) hashMultiplier << " * hash + (unsigned int) *" << utf8PointerVariable << "++;" << newLine
  322. << newLine
  323. << indent << "switch (hash)" << newLine
  324. << indent << "{" << newLine;
  325. for (int i = 0; i < strings.size(); ++i)
  326. {
  327. out << indent << " case 0x" << hexString8Digits ((int) calculateHash (strings[i], hashMultiplier))
  328. << ": " << codeToExecute[i] << newLine;
  329. }
  330. out << indent << " default: break;" << newLine
  331. << indent << "}" << newLine << newLine;
  332. }
  333. String getLeadingWhitespace (String line)
  334. {
  335. line = line.removeCharacters ("\r\n");
  336. const String::CharPointerType endOfLeadingWS (line.getCharPointer().findEndOfWhitespace());
  337. return String (line.getCharPointer(), endOfLeadingWS);
  338. }
  339. int getBraceCount (String::CharPointerType line)
  340. {
  341. int braces = 0;
  342. for (;;)
  343. {
  344. const juce_wchar c = line.getAndAdvance();
  345. if (c == 0) break;
  346. else if (c == '{') ++braces;
  347. else if (c == '}') --braces;
  348. else if (c == '/') { if (*line == '/') break; }
  349. else if (c == '"' || c == '\'') { while (! (line.isEmpty() || line.getAndAdvance() == c)) {} }
  350. }
  351. return braces;
  352. }
  353. bool getIndentForCurrentBlock (CodeDocument::Position pos, const String& tab,
  354. String& blockIndent, String& lastLineIndent)
  355. {
  356. int braceCount = 0;
  357. bool indentFound = false;
  358. while (pos.getLineNumber() > 0)
  359. {
  360. pos = pos.movedByLines (-1);
  361. const String line (pos.getLineText());
  362. const String trimmedLine (line.trimStart());
  363. braceCount += getBraceCount (trimmedLine.getCharPointer());
  364. if (braceCount > 0)
  365. {
  366. blockIndent = getLeadingWhitespace (line);
  367. if (! indentFound)
  368. lastLineIndent = blockIndent + tab;
  369. return true;
  370. }
  371. if ((! indentFound) && trimmedLine.isNotEmpty())
  372. {
  373. indentFound = true;
  374. lastLineIndent = getLeadingWhitespace (line);
  375. }
  376. }
  377. return false;
  378. }
  379. }