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.

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