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.

386 lines
12KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 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. void CodeGenerator::addImageResourceLoader (const String& imageMemberName, const String& resourceName)
  70. {
  71. const String initialiser (imageMemberName + " (0)");
  72. if (! memberInitialisers.contains (initialiser, false))
  73. {
  74. memberInitialisers.add (initialiser);
  75. privateMemberDeclarations
  76. << "Image* " << imageMemberName << ";" << newLine;
  77. if (resourceName.isNotEmpty())
  78. {
  79. constructorCode
  80. << imageMemberName << " = ImageCache::getFromMemory ("
  81. << resourceName << ", " << resourceName << "Size);" << newLine;
  82. destructorCode
  83. << "ImageCache::release (" << imageMemberName << ");" << newLine;
  84. }
  85. }
  86. }
  87. const StringArray CodeGenerator::getExtraParentClasses() const
  88. {
  89. StringArray s;
  90. for (int i = 0; i < callbacks.size(); ++i)
  91. {
  92. CallbackMethod* const cm = callbacks.getUnchecked(i);
  93. s.add (cm->requiredParentClass);
  94. }
  95. return s;
  96. }
  97. const String CodeGenerator::getCallbackDeclarations() const
  98. {
  99. String s;
  100. for (int i = 0; i < callbacks.size(); ++i)
  101. {
  102. CallbackMethod* const cm = callbacks.getUnchecked(i);
  103. s << cm->returnType << " " << cm->prototype << ";" << newLine;
  104. }
  105. return s;
  106. }
  107. const String CodeGenerator::getCallbackDefinitions() const
  108. {
  109. String s;
  110. for (int i = 0; i < callbacks.size(); ++i)
  111. {
  112. CallbackMethod* const cm = callbacks.getUnchecked(i);
  113. const String userCodeBlockName ("User" + makeValidCppIdentifier (cm->prototype.upToFirstOccurrenceOf ("(", false, false),
  114. true, true, false).trim());
  115. if (userCodeBlockName.isNotEmpty() && cm->hasPrePostUserSections)
  116. {
  117. s << cm->returnType << " " << className << "::" << cm->prototype
  118. << newLine
  119. << "{" << newLine
  120. << " //[" << userCodeBlockName << "_Pre]" << newLine
  121. << " //[/" << userCodeBlockName
  122. << "_Pre]" << newLine << newLine
  123. << " " << indentCode (cm->content.trim(), 4) << newLine
  124. << newLine
  125. << " //[" << userCodeBlockName << "_Post]" << newLine
  126. << " //[/" << userCodeBlockName << "_Post]" << newLine
  127. << "}" << newLine
  128. << newLine;
  129. }
  130. else
  131. {
  132. s << cm->returnType << " " << className << "::" << cm->prototype << newLine
  133. << "{" << newLine
  134. << " " << indentCode (cm->content.trim(), 4) << newLine
  135. << "}" << newLine
  136. << newLine;
  137. }
  138. }
  139. return s;
  140. }
  141. //==============================================================================
  142. const String CodeGenerator::getClassDeclaration() const
  143. {
  144. StringArray parentClassLines;
  145. parentClassLines.addTokens (parentClasses, ",", String::empty);
  146. parentClassLines.addArray (getExtraParentClasses());
  147. parentClassLines.trim();
  148. parentClassLines.removeEmptyStrings();
  149. parentClassLines.removeDuplicates (false);
  150. if (parentClassLines.contains ("public Button", false))
  151. parentClassLines.removeString ("public Component", false);
  152. String r ("class ");
  153. r << className << " : ";
  154. r += parentClassLines.joinIntoString ("," + String (newLine) + String::repeatedString (" ", r.length()));
  155. return r;
  156. }
  157. const String CodeGenerator::getInitialiserList() const
  158. {
  159. StringArray inits (memberInitialisers);
  160. if (parentClassInitialiser.isNotEmpty())
  161. inits.insert (0, parentClassInitialiser);
  162. inits.trim();
  163. inits.removeEmptyStrings();
  164. inits.removeDuplicates (false);
  165. String s;
  166. if (inits.size() == 0)
  167. return s;
  168. s << " : ";
  169. for (int i = 0; i < inits.size(); ++i)
  170. {
  171. String init (inits[i]);
  172. while (init.endsWithChar (','))
  173. init = init.dropLastCharacters (1);
  174. s << init;
  175. if (i < inits.size() - 1)
  176. s << "," << newLine << " ";
  177. else
  178. s << newLine;
  179. }
  180. return s;
  181. }
  182. static const String getIncludeFileCode (StringArray files)
  183. {
  184. files.trim();
  185. files.removeEmptyStrings();
  186. files.removeDuplicates (false);
  187. String s;
  188. for (int i = 0; i < files.size(); ++i)
  189. s << "#include \"" << files[i] << "\"" << newLine;
  190. return s;
  191. }
  192. //==============================================================================
  193. static bool getUserSection (const StringArray& lines, const String& tag, StringArray& resultLines)
  194. {
  195. const int start = indexOfLineStartingWith (lines, "//[" + tag + "]", 0);
  196. if (start < 0)
  197. return false;
  198. const int end = indexOfLineStartingWith (lines, "//[/" + tag + "]", start + 1);
  199. for (int i = start + 1; i < end; ++i)
  200. resultLines.add (lines [i]);
  201. return true;
  202. }
  203. static void copyAcrossUserSections (String& dest, const String& src)
  204. {
  205. StringArray srcLines, dstLines;
  206. srcLines.addLines (src);
  207. dstLines.addLines (dest);
  208. for (int i = 0; i < dstLines.size(); ++i)
  209. {
  210. if (dstLines[i].trimStart().startsWith ("//["))
  211. {
  212. String tag (dstLines[i].trimStart().substring (3));
  213. tag = tag.upToFirstOccurrenceOf ("]", false, false);
  214. jassert (! tag.startsWithChar ('/'));
  215. if (! tag.startsWithChar ('/'))
  216. {
  217. const int endLine = indexOfLineStartingWith (dstLines,
  218. "//[/" + tag + "]",
  219. i + 1);
  220. if (endLine > i)
  221. {
  222. StringArray sourceLines;
  223. if (getUserSection (srcLines, tag, sourceLines))
  224. {
  225. int j;
  226. for (j = endLine - i; --j > 0;)
  227. dstLines.remove (i + 1);
  228. for (j = 0; j < sourceLines.size(); ++j)
  229. dstLines.insert (++i, sourceLines [j].trimEnd());
  230. ++i;
  231. }
  232. else
  233. {
  234. i = endLine;
  235. }
  236. }
  237. }
  238. }
  239. dstLines.set (i, dstLines[i].trimEnd());
  240. }
  241. dest = dstLines.joinIntoString (newLine) + newLine;
  242. }
  243. //==============================================================================
  244. static void replaceTemplate (String& text, const String& itemName, const String& value)
  245. {
  246. for (;;)
  247. {
  248. const int index = text.indexOf ("%%" + itemName + "%%");
  249. if (index < 0)
  250. break;
  251. int indentLevel = 0;
  252. for (int i = index; --i >= 0;)
  253. {
  254. if (text[i] == '\n')
  255. break;
  256. ++indentLevel;
  257. }
  258. text = text.replaceSection (index, itemName.length() + 4,
  259. indentCode (value, indentLevel));
  260. }
  261. }
  262. //==============================================================================
  263. void CodeGenerator::applyToCode (String& code,
  264. const String& fileNameRoot,
  265. const bool isForPreview,
  266. const String& oldFileWithUserData) const
  267. {
  268. // header guard..
  269. String headerGuard ("__JUCER_HEADER_");
  270. headerGuard << className.toUpperCase().retainCharacters ("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  271. << "_" << fileNameRoot.toUpperCase().retainCharacters ("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  272. << "_" << String::toHexString (Random::getSystemRandom().nextInt()).toUpperCase() << "__";
  273. replaceTemplate (code, "headerGuard", headerGuard);
  274. replaceTemplate (code, "creationTime", Time::getCurrentTime().toString (true, true, true));
  275. replaceTemplate (code, "className", className);
  276. replaceTemplate (code, "constructorParams", constructorParams);
  277. replaceTemplate (code, "initialisers", getInitialiserList());
  278. replaceTemplate (code, "classDeclaration", getClassDeclaration());
  279. replaceTemplate (code, "privateMemberDeclarations", privateMemberDeclarations);
  280. replaceTemplate (code, "publicMemberDeclarations", getCallbackDeclarations() + newLine + publicMemberDeclarations);
  281. replaceTemplate (code, "methodDefinitions", getCallbackDefinitions());
  282. replaceTemplate (code, "includeFilesH", getIncludeFileCode (includeFilesH));
  283. replaceTemplate (code, "includeFilesCPP", getIncludeFileCode (includeFilesCPP));
  284. replaceTemplate (code, "constructor", constructorCode);
  285. replaceTemplate (code, "destructor", destructorCode);
  286. if (! isForPreview)
  287. {
  288. replaceTemplate (code, "metadata", jucerMetadata);
  289. replaceTemplate (code, "staticMemberDefinitions", staticMemberDefinitions);
  290. }
  291. else
  292. {
  293. replaceTemplate (code, "metadata", " << Metadata isn't shown in the code preview >>" + String (newLine));
  294. replaceTemplate (code, "staticMemberDefinitions", "// Static member declarations and resources would go here... (these aren't shown in the code preview)");
  295. }
  296. copyAcrossUserSections (code, oldFileWithUserData);
  297. }