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.

541 lines
17KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-10 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "jucer_CodeGenerator.h"
  19. //==============================================================================
  20. CodeGenerator::CodeGenerator()
  21. {
  22. }
  23. CodeGenerator::~CodeGenerator()
  24. {
  25. }
  26. int CodeGenerator::getUniqueSuffix()
  27. {
  28. return ++suffix;
  29. }
  30. //==============================================================================
  31. String& CodeGenerator::getCallbackCode (const String& requiredParentClass,
  32. const String& returnType,
  33. const String& prototype,
  34. const bool hasPrePostUserSections)
  35. {
  36. String parentClass (requiredParentClass);
  37. if (parentClass.isNotEmpty()
  38. && ! (parentClass.startsWith ("public ")
  39. || parentClass.startsWith ("private ")
  40. || parentClass.startsWith ("protected ")))
  41. {
  42. parentClass = "public " + parentClass;
  43. }
  44. for (int i = callbacks.size(); --i >= 0;)
  45. {
  46. CallbackMethod* const cm = callbacks.getUnchecked(i);
  47. if (cm->requiredParentClass == parentClass
  48. && cm->returnType == returnType
  49. && cm->prototype == prototype)
  50. return cm->content;
  51. }
  52. CallbackMethod* const cm = new CallbackMethod();
  53. callbacks.add (cm);
  54. cm->requiredParentClass = parentClass;
  55. cm->returnType = returnType;
  56. cm->prototype = prototype;
  57. cm->hasPrePostUserSections = hasPrePostUserSections;
  58. return cm->content;
  59. }
  60. void CodeGenerator::removeCallback (const String& returnType, const String& prototype)
  61. {
  62. for (int i = callbacks.size(); --i >= 0;)
  63. {
  64. CallbackMethod* const cm = callbacks.getUnchecked(i);
  65. if (cm->returnType == returnType && cm->prototype == prototype)
  66. callbacks.remove (i);
  67. }
  68. }
  69. const StringArray CodeGenerator::getExtraParentClasses() const
  70. {
  71. StringArray s;
  72. for (int i = 0; i < callbacks.size(); ++i)
  73. {
  74. CallbackMethod* const cm = callbacks.getUnchecked(i);
  75. s.add (cm->requiredParentClass);
  76. }
  77. return s;
  78. }
  79. const String CodeGenerator::getCallbackDeclarations() const
  80. {
  81. String s;
  82. for (int i = 0; i < callbacks.size(); ++i)
  83. {
  84. CallbackMethod* const cm = callbacks.getUnchecked(i);
  85. s << cm->returnType << " " << cm->prototype << ";" << newLine;
  86. }
  87. return s;
  88. }
  89. const String CodeGenerator::getCallbackDefinitions() const
  90. {
  91. String s;
  92. for (int i = 0; i < callbacks.size(); ++i)
  93. {
  94. CallbackMethod* const cm = callbacks.getUnchecked(i);
  95. const String userCodeBlockName ("User" + makeValidCppIdentifier (cm->prototype.upToFirstOccurrenceOf ("(", false, false),
  96. true, true, false).trim());
  97. if (userCodeBlockName.isNotEmpty() && cm->hasPrePostUserSections)
  98. {
  99. s << cm->returnType << " " << className << "::" << cm->prototype
  100. << newLine
  101. << "{" << newLine
  102. << " //[" << userCodeBlockName << "_Pre]" << newLine
  103. << " //[/" << userCodeBlockName
  104. << "_Pre]" << newLine << newLine
  105. << " " << indentCode (cm->content.trim(), 4) << newLine
  106. << newLine
  107. << " //[" << userCodeBlockName << "_Post]" << newLine
  108. << " //[/" << userCodeBlockName << "_Post]" << newLine
  109. << "}" << newLine
  110. << newLine;
  111. }
  112. else
  113. {
  114. s << cm->returnType << " " << className << "::" << cm->prototype << newLine
  115. << "{" << newLine
  116. << " " << indentCode (cm->content.trim(), 4) << newLine
  117. << "}" << newLine
  118. << newLine;
  119. }
  120. }
  121. return s;
  122. }
  123. //==============================================================================
  124. const String CodeGenerator::getClassDeclaration() const
  125. {
  126. StringArray parentClassLines;
  127. parentClassLines.addTokens (parentClasses, ",", String::empty);
  128. parentClassLines.addArray (getExtraParentClasses());
  129. parentClassLines.trim();
  130. parentClassLines.removeEmptyStrings();
  131. parentClassLines.removeDuplicates (false);
  132. if (parentClassLines.contains ("public Button", false))
  133. parentClassLines.removeString ("public Component", false);
  134. String r;
  135. r << "class " << className << " : ";
  136. r << parentClassLines.joinIntoString ("," + String (newLine) + String::repeatedString (" ", r.length()));
  137. return r;
  138. }
  139. const String CodeGenerator::getInitialiserList() const
  140. {
  141. String s;
  142. StringArray inits (memberInitialisers);
  143. if (parentClassInitialiser.isNotEmpty())
  144. inits.insert (0, parentClassInitialiser);
  145. inits.trim();
  146. inits.removeEmptyStrings();
  147. inits.removeDuplicates (false);
  148. if (inits.size() > 0)
  149. {
  150. s << " : ";
  151. for (int i = 0; i < inits.size(); ++i)
  152. {
  153. String init (inits[i]);
  154. while (init.endsWithChar (','))
  155. init = init.dropLastCharacters (1);
  156. s << init;
  157. if (i < inits.size() - 1)
  158. s << "," << newLine << " ";
  159. else
  160. s << newLine;
  161. }
  162. }
  163. return s;
  164. }
  165. static const String getIncludeFileCode (StringArray files)
  166. {
  167. files.trim();
  168. files.removeEmptyStrings();
  169. files.removeDuplicates (false);
  170. String s;
  171. for (int i = 0; i < files.size(); ++i)
  172. s << "#include \"" << files[i] << "\"" << newLine;
  173. return s;
  174. }
  175. //==============================================================================
  176. static bool getUserSection (const StringArray& lines, const String& tag, StringArray& resultLines)
  177. {
  178. const int start = indexOfLineStartingWith (lines, "//[" + tag + "]", 0);
  179. if (start < 0)
  180. return false;
  181. const int end = indexOfLineStartingWith (lines, "//[/" + tag + "]", start + 1);
  182. for (int i = start + 1; i < end; ++i)
  183. resultLines.add (lines [i]);
  184. return true;
  185. }
  186. //==============================================================================
  187. static void replaceTemplate (String& text, const String& itemName, const String& value)
  188. {
  189. for (;;)
  190. {
  191. const int index = text.indexOf ("%%" + itemName + "%%");
  192. if (index < 0)
  193. break;
  194. int indentLevel = 0;
  195. for (int i = index; --i >= 0;)
  196. {
  197. if (text[i] == '\n')
  198. break;
  199. ++indentLevel;
  200. }
  201. text = text.replaceSection (index, itemName.length() + 4,
  202. indentCode (value, indentLevel));
  203. }
  204. }
  205. //==============================================================================
  206. void CodeGenerator::applyToCode (String& code, const File& targetFile,
  207. bool isForPreview, Project* project) const
  208. {
  209. replaceTemplate (code, "juceVersion", SystemStats::getJUCEVersion());
  210. replaceTemplate (code, "headerGuard", makeHeaderGuardName (targetFile));
  211. replaceTemplate (code, "className", className);
  212. replaceTemplate (code, "constructorParams", constructorParams);
  213. replaceTemplate (code, "initialisers", getInitialiserList());
  214. replaceTemplate (code, "classDeclaration", getClassDeclaration());
  215. replaceTemplate (code, "privateMemberDeclarations", privateMemberDeclarations);
  216. replaceTemplate (code, "publicMemberDeclarations", getCallbackDeclarations() + newLine + publicMemberDeclarations);
  217. replaceTemplate (code, "methodDefinitions", getCallbackDefinitions());
  218. if (project != 0)
  219. replaceTemplate (code, "defaultJuceInclude", createIncludeStatement (project->getAppIncludeFile(), targetFile));
  220. else
  221. replaceTemplate (code, "defaultJuceInclude", "#include \"juce_amalgamated.h\"");
  222. replaceTemplate (code, "includeFilesH", getIncludeFileCode (includeFilesH));
  223. replaceTemplate (code, "includeFilesCPP", getIncludeFileCode (includeFilesCPP));
  224. replaceTemplate (code, "constructor", constructorCode);
  225. replaceTemplate (code, "destructor", destructorCode);
  226. if (! isForPreview)
  227. {
  228. replaceTemplate (code, "metadata", jucerMetadata);
  229. replaceTemplate (code, "staticMemberDefinitions", staticMemberDefinitions);
  230. }
  231. else
  232. {
  233. replaceTemplate (code, "metadata", " << Metadata isn't shown in the code preview >>" + String (newLine));
  234. replaceTemplate (code, "staticMemberDefinitions", "// Static member declarations and resources would go here... (these aren't shown in the code preview)");
  235. }
  236. }
  237. //==============================================================================
  238. CodeGenerator::CustomCodeList::Iterator::Iterator (const String& documentText, CustomCodeList& customCode_)
  239. : customCode (customCode_), i (0), codeDocument (0)
  240. {
  241. lines.addLines (documentText);
  242. }
  243. CodeGenerator::CustomCodeList::Iterator::~Iterator()
  244. {
  245. }
  246. bool CodeGenerator::CustomCodeList::Iterator::next()
  247. {
  248. textBefore = String::empty;
  249. textAfter = String::empty;
  250. while (i < lines.size())
  251. {
  252. textBefore += lines[i] + "\n";
  253. if (lines[i].trimStart().startsWith ("//["))
  254. {
  255. String tag (lines[i].trimStart().substring (3));
  256. tag = tag.upToFirstOccurrenceOf ("]", false, false).trim();
  257. if (! (tag.isEmpty() || tag.startsWithChar ('/')))
  258. {
  259. const int endLine = indexOfLineStartingWith (lines, "//[/" + tag + "]", i + 1);
  260. if (endLine > i)
  261. {
  262. sectionName = tag;
  263. codeDocument = customCode.getDocumentFor (tag, true);
  264. i = endLine;
  265. bool isLastTag = true;
  266. for (int j = i + 1; j < lines.size(); ++j)
  267. {
  268. if (lines[j].trimStart().startsWith ("//["))
  269. {
  270. isLastTag = false;
  271. break;
  272. }
  273. }
  274. if (isLastTag)
  275. {
  276. textAfter = lines.joinIntoString (newLine, i, lines.size() - i);
  277. i = lines.size();
  278. }
  279. return true;
  280. }
  281. }
  282. }
  283. ++i;
  284. }
  285. return false;
  286. }
  287. //==============================================================================
  288. CodeGenerator::CustomCodeList::CustomCodeList()
  289. {
  290. }
  291. CodeGenerator::CustomCodeList::~CustomCodeList()
  292. {
  293. }
  294. void CodeGenerator::CustomCodeList::reloadFrom (const String& fileContent)
  295. {
  296. StringArray newNames;
  297. ReferenceCountedArray<CodeDocumentRef> newContent;
  298. StringArray lines;
  299. lines.addLines (fileContent);
  300. for (int i = 0; i < lines.size(); ++i)
  301. {
  302. if (lines[i].trimStart().startsWith ("//["))
  303. {
  304. String tag (lines[i].trimStart().substring (3));
  305. tag = tag.upToFirstOccurrenceOf ("]", false, false).trim();
  306. jassert (! (tag.isEmpty() || tag.startsWithChar ('/')));
  307. if (! (tag.isEmpty() || tag.startsWithChar ('/')))
  308. {
  309. const int endLine = indexOfLineStartingWith (lines, "//[/" + tag + "]", i + 1);
  310. if (endLine > i)
  311. {
  312. String content (lines.joinIntoString (newLine, i + 1, endLine - i - 1));
  313. newNames.add (tag);
  314. CodeDocumentRef::Ptr doc (getDocumentFor (tag, false));
  315. if (doc == 0)
  316. {
  317. CodeDocument* const codeDoc = new CodeDocument();
  318. doc = new CodeDocumentRef (codeDoc);
  319. codeDoc->replaceAllContent (content);
  320. codeDoc->clearUndoHistory();
  321. codeDoc->setSavePoint();
  322. }
  323. newContent.add (doc);
  324. i = endLine;
  325. }
  326. }
  327. }
  328. }
  329. sectionNames = newNames;
  330. sectionContent = newContent;
  331. sendSynchronousChangeMessage (this);
  332. }
  333. void CodeGenerator::CustomCodeList::applyTo (String& fileContent) const
  334. {
  335. StringArray lines;
  336. lines.addLines (fileContent);
  337. for (int i = 0; i < lines.size(); ++i)
  338. {
  339. if (lines[i].trimStart().startsWith ("//["))
  340. {
  341. String tag (lines[i].trimStart().substring (3));
  342. tag = tag.upToFirstOccurrenceOf ("]", false, false);
  343. jassert (! tag.startsWithChar ('/'));
  344. if (! tag.startsWithChar ('/'))
  345. {
  346. const int endLine = indexOfLineStartingWith (lines, "//[/" + tag + "]", i + 1);
  347. if (endLine > i)
  348. {
  349. StringArray sourceLines;
  350. sourceLines.addLines (getSectionContent (tag));
  351. if (sourceLines.size() > 0)
  352. {
  353. lines.removeRange (i + 1, endLine - i - 1);
  354. for (int j = 0; j < sourceLines.size(); ++j)
  355. lines.insert (++i, sourceLines [j].trimEnd());
  356. ++i;
  357. }
  358. else
  359. {
  360. i = endLine;
  361. }
  362. }
  363. }
  364. }
  365. lines.set (i, lines[i].trimEnd());
  366. }
  367. if (lines[lines.size() - 1].isNotEmpty())
  368. lines.add (String::empty);
  369. fileContent = lines.joinIntoString (newLine);
  370. }
  371. bool CodeGenerator::CustomCodeList::needsSaving() const
  372. {
  373. for (int i = sectionContent.size(); --i >= 0;)
  374. if (sectionContent.getUnchecked(i)->getDocument().hasChangedSinceSavePoint())
  375. return true;
  376. return false;
  377. }
  378. int CodeGenerator::CustomCodeList::getNumSections() const
  379. {
  380. return sectionNames.size();
  381. }
  382. const String CodeGenerator::CustomCodeList::getSectionName (int index) const
  383. {
  384. return sectionNames [index];
  385. }
  386. const CodeGenerator::CustomCodeList::CodeDocumentRef::Ptr CodeGenerator::CustomCodeList::getDocument (int index) const
  387. {
  388. return sectionContent [index];
  389. }
  390. const CodeGenerator::CustomCodeList::CodeDocumentRef::Ptr CodeGenerator::CustomCodeList::getDocumentFor (const String& sectionName, bool createIfNotFound)
  391. {
  392. const int index = sectionNames.indexOf (sectionName);
  393. if (index >= 0)
  394. return sectionContent [index];
  395. if (createIfNotFound)
  396. {
  397. sectionNames.add (sectionName);
  398. const CodeDocumentRef::Ptr doc (new CodeDocumentRef (new CodeDocument()));
  399. sectionContent.add (doc);
  400. return doc;
  401. }
  402. return 0;
  403. }
  404. const String CodeGenerator::CustomCodeList::getSectionContent (const String& sectionName) const
  405. {
  406. const int index = sectionNames.indexOf (sectionName);
  407. if (index >= 0)
  408. return sectionContent[index]->getDocument().getAllContent();
  409. return String::empty;
  410. }
  411. void CodeGenerator::CustomCodeList::removeSection (const String& sectionName)
  412. {
  413. const int index = sectionNames.indexOf (sectionName);
  414. if (index >= 0)
  415. {
  416. sectionNames.remove (index);
  417. sectionContent.remove (index);
  418. }
  419. }