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.

738 lines
24KB

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