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.

713 lines
23KB

  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. struct ClassDatabase
  18. {
  19. //==============================================================================
  20. struct MemberInfo
  21. {
  22. enum CodeLocationType
  23. {
  24. declaration = 0,
  25. addedToParent,
  26. setBoundsParamX,
  27. setBoundsParamY,
  28. setBoundsParamW,
  29. setBoundsParamH,
  30. // WARNING! When you change any of these, also update the copy that lives in the live editing code
  31. numCodeLocationTypes
  32. };
  33. MemberInfo() {}
  34. MemberInfo (const MemberInfo& other)
  35. : name (other.name), type (other.type)
  36. {
  37. for (int i = 0; i < numCodeLocationTypes; ++i)
  38. locations[i] = other.locations[i];
  39. }
  40. MemberInfo (const String& nm, const String& ty)
  41. : name (nm), type (ty)
  42. {
  43. }
  44. MemberInfo (const ValueTree& v)
  45. : name (v [Ids::name].toString()),
  46. type (v [Ids::class_].toString())
  47. {
  48. for (int i = 0; i < numCodeLocationTypes; ++i)
  49. locations[i] = v [getIdentifierForCodeLocationType (i)].toString();
  50. }
  51. const String& getName() const { return name; }
  52. const String& getType() const { return type; }
  53. const SourceCodeRange& getLocation (CodeLocationType t) const
  54. {
  55. return locations[t];
  56. }
  57. void setLocation (CodeLocationType t, const SourceCodeRange& range)
  58. {
  59. locations[t] = range;
  60. }
  61. void mergeWith (const MemberInfo& other)
  62. {
  63. jassert (name == other.name);
  64. if (other.type.isNotEmpty())
  65. type = other.type;
  66. for (int i = 0; i < numCodeLocationTypes; ++i)
  67. if (other.locations[i].isValid())
  68. locations[i] = other.locations[i];
  69. }
  70. void nudgeAllCodeRanges (const String& file, const int insertPoint, const int delta)
  71. {
  72. for (int i = 0; i < numCodeLocationTypes; ++i)
  73. locations[i].nudge (file, insertPoint, delta);
  74. }
  75. void fileContentChanged (const String& file)
  76. {
  77. for (int i = 0; i < numCodeLocationTypes; ++i)
  78. locations[i].fileContentChanged (file);
  79. }
  80. ValueTree toValueTree() const
  81. {
  82. ValueTree m (Ids::MEMBER);
  83. m.setProperty (Ids::name, name, nullptr);
  84. m.setProperty (Ids::class_, type, nullptr);
  85. for (int i = 0; i < numCodeLocationTypes; ++i)
  86. locations[i].writeToValueTree (m, getIdentifierForCodeLocationType (i));
  87. return m;
  88. }
  89. private:
  90. String name, type;
  91. SourceCodeRange locations [numCodeLocationTypes];
  92. static Identifier getIdentifierForCodeLocationType (int typeIndex)
  93. {
  94. // (These need to remain in order)
  95. static_assert (setBoundsParamX + 1 == setBoundsParamY && setBoundsParamY + 1 == setBoundsParamW
  96. && setBoundsParamW + 1 == setBoundsParamH, "");
  97. static const Identifier ids[] =
  98. {
  99. "declaration",
  100. "addedToParent",
  101. "setBoundsParamX",
  102. "setBoundsParamY",
  103. "setBoundsParamW",
  104. "setBoundsParamH"
  105. };
  106. return ids [typeIndex];
  107. }
  108. };
  109. //==============================================================================
  110. struct MethodInfo
  111. {
  112. MethodInfo() {}
  113. MethodInfo (const MethodInfo& other)
  114. : name (other.name), returnType (other.returnType),
  115. declaration (other.declaration), definition (other.definition),
  116. numArgs (other.numArgs), flags (other.flags)
  117. {
  118. }
  119. String name, returnType;
  120. SourceCodeRange declaration, definition;
  121. int numArgs, flags;
  122. enum
  123. {
  124. isConstructor = 1,
  125. isDefaultConstructor = 2,
  126. isTemplated = 4,
  127. isPublic = 8
  128. };
  129. MethodInfo (const ValueTree& v)
  130. : name (v[Ids::name].toString()),
  131. returnType (v[Ids::returnType].toString()),
  132. declaration (v[Ids::declaration].toString()),
  133. definition (v[Ids::definition].toString()),
  134. numArgs (v[Ids::numArgs]),
  135. flags (v[Ids::flags])
  136. {
  137. }
  138. ValueTree toValueTree() const
  139. {
  140. ValueTree m (Ids::METHOD);
  141. m.setProperty (Ids::name, name, nullptr);
  142. m.setProperty (Ids::returnType, returnType, nullptr);
  143. m.setProperty (Ids::numArgs, numArgs, nullptr);
  144. m.setProperty (Ids::flags, flags, nullptr);
  145. declaration.writeToValueTree (m, Ids::declaration);
  146. definition.writeToValueTree (m, Ids::definition);
  147. return m;
  148. }
  149. void nudgeAllCodeRanges (const String& file, const int insertPoint, const int delta)
  150. {
  151. declaration.nudge (file, insertPoint, delta);
  152. definition.nudge (file, insertPoint, delta);
  153. }
  154. void fileContentChanged (const String& file)
  155. {
  156. declaration.fileContentChanged (file);
  157. definition.fileContentChanged (file);
  158. }
  159. };
  160. //==============================================================================
  161. struct InstantiationFlags
  162. {
  163. InstantiationFlags()
  164. : isAbstract (false),
  165. inAnonymousNamespace (false),
  166. noDefaultConstructor (false)
  167. {}
  168. InstantiationFlags (const InstantiationFlags& other)
  169. : isAbstract (other.isAbstract),
  170. inAnonymousNamespace (other.inAnonymousNamespace),
  171. noDefaultConstructor (other.noDefaultConstructor)
  172. {}
  173. bool canBeInstantiated() const noexcept
  174. {
  175. return ! (isAbstract || inAnonymousNamespace || noDefaultConstructor);
  176. }
  177. String getReasonForUnavailability() const
  178. {
  179. if (isAbstract) return "This class is abstract";
  180. if (noDefaultConstructor) return "This class has no default constructor";
  181. if (inAnonymousNamespace) return "This class is declared inside an anonymous namespace";
  182. return String();
  183. }
  184. bool isDisallowed (const InstantiationFlags& disallowedFlags) const
  185. {
  186. return ! ((disallowedFlags.isAbstract && isAbstract)
  187. || (disallowedFlags.inAnonymousNamespace && inAnonymousNamespace)
  188. || (disallowedFlags.noDefaultConstructor && noDefaultConstructor));
  189. }
  190. bool isAbstract;
  191. bool inAnonymousNamespace;
  192. bool noDefaultConstructor;
  193. };
  194. //==============================================================================
  195. struct Class
  196. {
  197. Class() {}
  198. ~Class() {}
  199. Class (const Class& other)
  200. : className (other.className), members (other.members),
  201. methods (other.methods), classDeclaration (other.classDeclaration),
  202. instantiationFlags (other.instantiationFlags)
  203. {
  204. }
  205. Class (const String& name, const InstantiationFlags& flags,
  206. const Array<MemberInfo>& m,
  207. const Array<MethodInfo>& meth,
  208. const SourceCodeRange& classDeclarationRange)
  209. : className (name),
  210. members (m), methods (meth),
  211. classDeclaration (classDeclarationRange),
  212. instantiationFlags (flags)
  213. {
  214. }
  215. Class& operator= (const Class& other)
  216. {
  217. className = other.className;
  218. members = other.members;
  219. methods = other.methods;
  220. classDeclaration = other.classDeclaration;
  221. instantiationFlags = other.instantiationFlags;
  222. return *this;
  223. }
  224. const String& getName() const noexcept { return className; }
  225. const InstantiationFlags& getInstantiationFlags() const
  226. {
  227. return instantiationFlags;
  228. }
  229. void setInstantiationFlags (const InstantiationFlags& newFlags)
  230. {
  231. instantiationFlags = newFlags;
  232. }
  233. const SourceCodeRange& getClassDeclarationRange() const
  234. {
  235. return classDeclaration;
  236. }
  237. MemberInfo* findMember (const String& memberName) const
  238. {
  239. for (MemberInfo& m : members)
  240. if (m.getName() == memberName)
  241. return &m;
  242. return nullptr;
  243. }
  244. const MethodInfo* getDefaultConstructor() const
  245. {
  246. for (const MethodInfo& m : methods)
  247. if ((m.flags & MethodInfo::isDefaultConstructor) != 0)
  248. return &m;
  249. return nullptr;
  250. }
  251. const MethodInfo* getConstructor() const
  252. {
  253. if (const MethodInfo* m = getDefaultConstructor())
  254. return m;
  255. for (const MethodInfo& m : methods)
  256. if ((m.flags & MethodInfo::isConstructor) != 0)
  257. return &m;
  258. return nullptr;
  259. }
  260. const MethodInfo* getResizedMethod() const
  261. {
  262. for (const MethodInfo& m : methods)
  263. if (m.name == "resized" && m.numArgs == 0)
  264. return &m;
  265. return nullptr;
  266. }
  267. File getMainSourceFile() const
  268. {
  269. if (const MethodInfo* m = getResizedMethod())
  270. if (m->definition.isValid())
  271. return m->definition.file;
  272. if (const MethodInfo* m = getConstructor())
  273. if (m->definition.isValid())
  274. return m->definition.file;
  275. for (MethodInfo& m : methods)
  276. if (m.definition.isValid() && File (m.definition.file).hasFileExtension ("cpp;mm"))
  277. return m.definition.file;
  278. for (MethodInfo& m : methods)
  279. if ((m.flags & MethodInfo::isConstructor) != 0 && m.definition.isValid())
  280. return m.definition.file;
  281. for (MethodInfo& m : methods)
  282. if (m.definition.isValid() && File (m.definition.file).exists())
  283. return m.definition.file;
  284. return {};
  285. }
  286. Array<File> getAllSourceFiles() const
  287. {
  288. Array<File> files;
  289. for (const MethodInfo& m : methods)
  290. {
  291. files.addIfNotAlreadyThere (m.declaration.file);
  292. files.addIfNotAlreadyThere (m.definition.file);
  293. }
  294. return files;
  295. }
  296. bool isDeclaredInFile (const File& file) const
  297. {
  298. return file == classDeclaration.file;
  299. }
  300. void mergeWith (const Class& other)
  301. {
  302. jassert (*this == other);
  303. if (other.classDeclaration.isValid())
  304. classDeclaration = other.classDeclaration;
  305. for (const MemberInfo& m : other.members)
  306. {
  307. if (MemberInfo* existing = findMember (m.getName()))
  308. existing->mergeWith (m);
  309. else
  310. members.add (m);
  311. }
  312. }
  313. void nudgeAllCodeRanges (const String& file, int index, int delta)
  314. {
  315. for (MemberInfo& m : members) m.nudgeAllCodeRanges (file, index, delta);
  316. for (MethodInfo& m : methods) m.nudgeAllCodeRanges (file, index, delta);
  317. classDeclaration.nudge (file, index, delta);
  318. }
  319. void fileContentChanged (const String& file)
  320. {
  321. for (MemberInfo& m : members) m.fileContentChanged (file);
  322. for (MethodInfo& m : methods) m.fileContentChanged (file);
  323. classDeclaration.fileContentChanged (file);
  324. }
  325. Class (const ValueTree& v)
  326. {
  327. className = v[Ids::name];
  328. instantiationFlags.isAbstract = v[Ids::abstract];
  329. instantiationFlags.inAnonymousNamespace = v[Ids::anonymous];
  330. instantiationFlags.noDefaultConstructor = v[Ids::noDefConstructor];
  331. classDeclaration = v [Ids::classDecl].toString();
  332. for (int i = 0; i < v.getNumChildren(); ++i)
  333. members.add (MemberInfo (v.getChild(i)));
  334. }
  335. ValueTree toValueTree() const
  336. {
  337. ValueTree v (Ids::CLASS);
  338. v.setProperty (Ids::name, className, nullptr);
  339. v.setProperty (Ids::abstract, instantiationFlags.isAbstract, nullptr);
  340. v.setProperty (Ids::anonymous, instantiationFlags.inAnonymousNamespace, nullptr);
  341. v.setProperty (Ids::noDefConstructor, instantiationFlags.noDefaultConstructor, nullptr);
  342. classDeclaration.writeToValueTree (v, Ids::classDecl);
  343. for (const MemberInfo& m : members)
  344. v.addChild (m.toValueTree(), -1, nullptr);
  345. return v;
  346. }
  347. bool operator== (const Class& other) const noexcept { return className == other.className; }
  348. bool operator!= (const Class& other) const noexcept { return ! operator== (other); }
  349. bool operator< (const Class& other) const noexcept { return className < other.className; }
  350. const Array<MemberInfo>& getMembers() const { return members; }
  351. private:
  352. String className;
  353. Array<MemberInfo> members;
  354. Array<MethodInfo> methods;
  355. SourceCodeRange classDeclaration;
  356. InstantiationFlags instantiationFlags;
  357. JUCE_LEAK_DETECTOR (Class)
  358. };
  359. //==============================================================================
  360. struct Namespace
  361. {
  362. Namespace() : name ("Global Namespace") {}
  363. Namespace (const String& n, const String& full) : name (n), fullName (full) {}
  364. bool isEmpty() const noexcept
  365. {
  366. for (const auto& n : namespaces)
  367. if (! n.isEmpty())
  368. return false;
  369. return components.size() == 0;
  370. }
  371. int getTotalClassesAndNamespaces() const
  372. {
  373. int total = components.size();
  374. for (const auto& n : namespaces)
  375. total += n.getTotalClassesAndNamespaces();
  376. return total;
  377. }
  378. void add (const Class& c, const String::CharPointerType& localName)
  379. {
  380. const String::CharPointerType nextDoubleColon (CharacterFunctions::find (localName, CharPointer_ASCII ("::")));
  381. if (nextDoubleColon.isEmpty())
  382. merge (c);
  383. else
  384. getOrCreateNamespace (String (localName, nextDoubleColon))->add (c, nextDoubleColon + 2);
  385. }
  386. bool containsRecursively (const Class& c) const
  387. {
  388. if (components.contains (c))
  389. return true;
  390. for (const auto& n : namespaces)
  391. if (n.containsRecursively (c))
  392. return true;
  393. return false;
  394. }
  395. Class* findClass (const String& className) const
  396. {
  397. for (Class& c : components)
  398. if (c.getName() == className)
  399. return &c;
  400. for (const auto& n : namespaces)
  401. if (Class* c = n.findClass (className))
  402. return c;
  403. return nullptr;
  404. }
  405. const MemberInfo* findClassMemberInfo (const String& className, const String& memberName) const
  406. {
  407. if (const Class* classInfo = findClass (className))
  408. return classInfo->findMember (memberName);
  409. return nullptr;
  410. }
  411. void findClassesDeclaredInFile (Array<Class*>& results, const File& file) const
  412. {
  413. for (Class& c : components)
  414. if (c.isDeclaredInFile (file))
  415. results.add (&c);
  416. for (const auto& n : namespaces)
  417. n.findClassesDeclaredInFile (results, file);
  418. }
  419. void merge (const Namespace& other)
  420. {
  421. if (components.size() == 0)
  422. {
  423. components = other.components;
  424. }
  425. else
  426. {
  427. for (const auto& c : other.components)
  428. merge (c);
  429. }
  430. for (const auto& n : other.namespaces)
  431. getOrCreateNamespace (n.name)->merge (n);
  432. }
  433. void merge (const Class& c)
  434. {
  435. const int existing = components.indexOf (c);
  436. if (existing < 0)
  437. components.add (c);
  438. else
  439. components.getReference (existing).mergeWith (c);
  440. }
  441. Namespace* findNamespace (const String& targetName) const
  442. {
  443. for (auto& n : namespaces)
  444. if (n.name == targetName)
  445. return &n;
  446. return nullptr;
  447. }
  448. Namespace* createNamespace (const String& newName)
  449. {
  450. namespaces.add (Namespace (newName, fullName + "::" + newName));
  451. return findNamespace (newName);
  452. }
  453. Namespace* getOrCreateNamespace (const String& newName)
  454. {
  455. if (Namespace* existing = findNamespace (newName))
  456. return existing;
  457. return createNamespace (newName);
  458. }
  459. void addInstantiableClasses (SortedSet<Class>& classes) const
  460. {
  461. for (const auto& c : components)
  462. if (c.getInstantiationFlags().canBeInstantiated())
  463. classes.add (c);
  464. for (const auto& n : namespaces)
  465. n.addInstantiableClasses (classes);
  466. }
  467. void swapWith (Namespace& other) noexcept
  468. {
  469. name.swapWith (other.name);
  470. components.swapWith (other.components);
  471. namespaces.swapWith (other.namespaces);
  472. }
  473. void nudgeAllCodeRanges (const String& file, int index, int delta) const
  474. {
  475. for (auto& c : components) c.nudgeAllCodeRanges (file, index, delta);
  476. for (auto& n : namespaces) n.nudgeAllCodeRanges (file, index, delta);
  477. }
  478. void fileContentChanged (const String& file) const
  479. {
  480. for (auto& c : components) c.fileContentChanged (file);
  481. for (auto& n : namespaces) n.fileContentChanged (file);
  482. }
  483. bool matches (const Namespace& other) const
  484. {
  485. if (name == other.name
  486. && components == other.components
  487. && namespaces.size() == other.namespaces.size())
  488. {
  489. for (int i = namespaces.size(); --i >= 0;)
  490. if (! namespaces.getReference (i).matches (other.namespaces.getReference(i)))
  491. return false;
  492. return true;
  493. }
  494. return false;
  495. }
  496. void getAllClassNames (StringArray& results, const InstantiationFlags& disallowedFlags) const
  497. {
  498. for (const auto& c : components)
  499. if (c.getInstantiationFlags().isDisallowed (disallowedFlags))
  500. results.add (c.getName());
  501. for (const auto& n : namespaces)
  502. n.getAllClassNames (results, disallowedFlags);
  503. }
  504. ValueTree toValueTree() const
  505. {
  506. ValueTree v (Ids::CLASSLIST);
  507. v.setProperty (Ids::name, name, nullptr);
  508. for (const auto& c : components) v.addChild (c.toValueTree(), -1, nullptr);
  509. for (const auto& n : namespaces) v.addChild (n.toValueTree(), -1, nullptr);
  510. return v;
  511. }
  512. void loadFromValueTree (const ValueTree& v)
  513. {
  514. name = v[Ids::name];
  515. for (int i = 0; i < v.getNumChildren(); ++i)
  516. {
  517. const ValueTree c (v.getChild(i));
  518. if (c.hasType (Ids::CLASS))
  519. components.add (Class (c));
  520. else if (c.hasType (Ids::CLASSLIST))
  521. createNamespace (c[Ids::name])->loadFromValueTree (c);
  522. }
  523. }
  524. bool operator== (const Namespace& other) const noexcept { return name == other.name; }
  525. bool operator!= (const Namespace& other) const noexcept { return ! operator== (other); }
  526. bool operator< (const Namespace& other) const noexcept { return name < other.name; }
  527. String name, fullName;
  528. SortedSet<Class> components;
  529. SortedSet<Namespace> namespaces;
  530. JUCE_LEAK_DETECTOR (Namespace)
  531. };
  532. struct ClassList
  533. {
  534. ClassList() {}
  535. void clear()
  536. {
  537. Namespace newNamespace;
  538. globalNamespace.swapWith (newNamespace);
  539. }
  540. void registerComp (const Class& comp)
  541. {
  542. globalNamespace.add (comp, comp.getName().getCharPointer());
  543. }
  544. void merge (const ClassList& other)
  545. {
  546. globalNamespace.merge (other.globalNamespace);
  547. }
  548. void swapWith (ClassList& other) noexcept
  549. {
  550. globalNamespace.swapWith (other.globalNamespace);
  551. }
  552. //==============================================================================
  553. ValueTree toValueTree() const
  554. {
  555. return globalNamespace.toValueTree();
  556. }
  557. static ClassList fromValueTree (const ValueTree& v)
  558. {
  559. ClassList l;
  560. l.globalNamespace.loadFromValueTree (v);
  561. return l;
  562. }
  563. Namespace globalNamespace;
  564. bool operator== (const ClassList& other) const noexcept { return globalNamespace.matches (other.globalNamespace); }
  565. bool operator!= (const ClassList& other) const noexcept { return ! operator== (other); }
  566. private:
  567. JUCE_LEAK_DETECTOR (ClassList)
  568. };
  569. };