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.

731 lines
23KB

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