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.

715 lines
23KB

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