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.

739 lines
24KB

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