ts2famix 1.4.1 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +30 -61
  3. package/dist/analyze.js +4 -2
  4. package/dist/analyze_functions/process_functions.js +285 -131
  5. package/dist/famix_functions/EntityDictionary.js +864 -231
  6. package/dist/famix_functions/helpers_creation.js +61 -10
  7. package/dist/fqn.js +160 -111
  8. package/dist/lib/famix/famix_JSON_exporter.js +55 -0
  9. package/dist/lib/famix/famix_base_element.js +18 -0
  10. package/dist/lib/famix/famix_repository.js +224 -0
  11. package/dist/lib/famix/{src/index.js → index.js} +1 -0
  12. package/dist/lib/famix/model/famix/access.js +40 -0
  13. package/dist/lib/famix/model/famix/accessor.js +17 -0
  14. package/dist/lib/famix/model/famix/alias.js +33 -0
  15. package/dist/lib/famix/model/famix/arrow_function.js +17 -0
  16. package/dist/lib/famix/model/famix/behavioral_entity.js +79 -0
  17. package/dist/lib/famix/model/famix/class.js +71 -0
  18. package/dist/lib/famix/model/famix/comment.js +39 -0
  19. package/dist/lib/famix/model/famix/concretisation.js +31 -0
  20. package/dist/lib/famix/model/famix/container_entity.js +126 -0
  21. package/dist/lib/famix/model/famix/decorator.js +32 -0
  22. package/dist/lib/famix/model/famix/entity.js +17 -0
  23. package/dist/lib/famix/model/famix/enum.js +31 -0
  24. package/dist/lib/famix/model/famix/enum_value.js +25 -0
  25. package/dist/lib/famix/model/famix/function.js +17 -0
  26. package/dist/lib/famix/model/famix/import_clause.js +41 -0
  27. package/dist/lib/famix/model/famix/index.js +86 -0
  28. package/dist/lib/famix/model/famix/indexed_file_anchor.js +38 -0
  29. package/dist/lib/famix/model/famix/inheritance.js +33 -0
  30. package/dist/lib/famix/model/famix/interface.js +64 -0
  31. package/dist/lib/famix/model/famix/invocation.js +54 -0
  32. package/dist/lib/famix/model/famix/method.js +67 -0
  33. package/dist/lib/famix/model/famix/module.js +60 -0
  34. package/dist/lib/famix/model/famix/named_entity.js +78 -0
  35. package/dist/lib/famix/model/famix/parameter.js +25 -0
  36. package/dist/lib/famix/model/famix/parameter_concretisation.js +44 -0
  37. package/dist/lib/famix/model/famix/parameter_type.js +45 -0
  38. package/dist/lib/famix/model/famix/parametric_arrow_function.js +31 -0
  39. package/dist/lib/famix/model/famix/parametric_class.js +44 -0
  40. package/dist/lib/famix/model/famix/parametric_function.js +31 -0
  41. package/dist/lib/famix/model/famix/parametric_interface.js +44 -0
  42. package/dist/lib/famix/model/famix/parametric_method.js +31 -0
  43. package/dist/lib/famix/model/famix/primitive_type.js +17 -0
  44. package/dist/lib/famix/model/famix/property.js +73 -0
  45. package/dist/lib/famix/model/famix/reference.js +33 -0
  46. package/dist/lib/famix/model/famix/scoping_entity.js +36 -0
  47. package/dist/lib/famix/model/famix/script_entity.js +29 -0
  48. package/dist/lib/famix/model/famix/source_anchor.js +27 -0
  49. package/dist/lib/famix/model/famix/source_language.js +35 -0
  50. package/dist/lib/famix/model/famix/sourced_entity.js +60 -0
  51. package/dist/lib/famix/model/famix/structural_entity.js +39 -0
  52. package/dist/lib/famix/model/famix/type.js +73 -0
  53. package/dist/lib/famix/model/famix/variable.js +24 -0
  54. package/dist/lib/ts-complex/cyclomatic-service.js +3 -3
  55. package/dist/refactorer/refactor-getter-setter.js +142 -0
  56. package/dist/ts2famix-cli-wrapper.js +42 -0
  57. package/dist/ts2famix-cli.js +8 -1
  58. package/dist/ts2famix-tsconfig.js +1 -0
  59. package/doc-uml/famix-typescript-model.puml +608 -0
  60. package/doc-uml/famix-typescript-model.svg +1 -0
  61. package/jest.config.json +2 -1
  62. package/package.json +13 -12
  63. package/src/analyze.ts +24 -23
  64. package/src/analyze_functions/process_functions.ts +310 -129
  65. package/src/famix_functions/EntityDictionary.ts +949 -271
  66. package/src/famix_functions/helpers_creation.ts +64 -6
  67. package/src/fqn.ts +169 -96
  68. package/{dist/lib/famix/src/famix_JSON_exporter.js → src/lib/famix/famix_JSON_exporter.ts} +16 -14
  69. package/src/lib/famix/famix_base_element.ts +22 -0
  70. package/{dist/lib/famix/src/famix_repository.js → src/lib/famix/famix_repository.ts} +96 -75
  71. package/src/lib/famix/model/famix/access.ts +50 -0
  72. package/src/lib/famix/model/famix/alias.ts +39 -0
  73. package/src/lib/famix/{src/model/famix/implicit_variable.ts → model/famix/arrow_function.ts} +3 -3
  74. package/src/lib/famix/model/famix/behavioral_entity.ts +97 -0
  75. package/src/lib/famix/model/famix/class.ts +85 -0
  76. package/src/lib/famix/model/famix/comment.ts +47 -0
  77. package/src/lib/famix/model/famix/concretisation.ts +40 -0
  78. package/src/lib/famix/model/famix/container_entity.ts +160 -0
  79. package/src/lib/famix/model/famix/decorator.ts +37 -0
  80. package/src/lib/famix/model/famix/enum.ts +30 -0
  81. package/src/lib/famix/model/famix/enum_value.ts +28 -0
  82. package/src/lib/famix/model/famix/import_clause.ts +51 -0
  83. package/src/lib/famix/{src/model → model}/famix/index.ts +8 -7
  84. package/src/lib/famix/model/famix/indexed_file_anchor.ts +46 -0
  85. package/src/lib/famix/model/famix/inheritance.ts +40 -0
  86. package/src/lib/famix/model/famix/interface.ts +75 -0
  87. package/src/lib/famix/model/famix/invocation.ts +65 -0
  88. package/src/lib/famix/model/famix/method.ts +89 -0
  89. package/src/lib/famix/model/famix/module.ts +71 -0
  90. package/src/lib/famix/model/famix/named_entity.ts +95 -0
  91. package/src/lib/famix/{src/model → model}/famix/parameter.ts +11 -12
  92. package/src/lib/famix/model/famix/parameter_concretisation.ts +51 -0
  93. package/src/lib/famix/model/famix/parameter_type.ts +58 -0
  94. package/src/lib/famix/model/famix/parametric_arrow_function.ts +32 -0
  95. package/src/lib/famix/model/famix/parametric_class.ts +49 -0
  96. package/src/lib/famix/model/famix/parametric_function.ts +32 -0
  97. package/src/lib/famix/model/famix/parametric_interface.ts +49 -0
  98. package/src/lib/famix/model/famix/parametric_method.ts +32 -0
  99. package/src/lib/famix/model/famix/primitive_type.ts +15 -0
  100. package/src/lib/famix/model/famix/property.ts +94 -0
  101. package/src/lib/famix/model/famix/reference.ts +40 -0
  102. package/src/lib/famix/model/famix/scoping_entity.ts +35 -0
  103. package/src/lib/famix/model/famix/script_entity.ts +34 -0
  104. package/src/lib/famix/model/famix/source_anchor.ts +30 -0
  105. package/src/lib/famix/model/famix/source_language.ts +35 -0
  106. package/src/lib/famix/model/famix/sourced_entity.ts +70 -0
  107. package/src/lib/famix/model/famix/structural_entity.ts +43 -0
  108. package/src/lib/famix/model/famix/type.ts +87 -0
  109. package/src/lib/famix/model/famix/variable.ts +27 -0
  110. package/src/lib/famix/package.json +1 -1
  111. package/src/lib/ts-complex/cyclomatic-service.ts +10 -10
  112. package/src/refactorer/refactor-getter-setter.ts +140 -0
  113. package/src/ts2famix-cli-wrapper.ts +21 -0
  114. package/src/ts2famix-cli.ts +8 -2
  115. package/tsconfig.check-tests.json +14 -0
  116. package/tsconfig.json +71 -69
  117. package/dist/famix2puml.js +0 -125
  118. package/dist/lib/famix/src/famix_base_element.js +0 -17
  119. package/dist/lib/famix/src/model/famix/access.js +0 -39
  120. package/dist/lib/famix/src/model/famix/accessor.js +0 -16
  121. package/dist/lib/famix/src/model/famix/alias.js +0 -32
  122. package/dist/lib/famix/src/model/famix/association.js +0 -36
  123. package/dist/lib/famix/src/model/famix/behavioral_entity.js +0 -81
  124. package/dist/lib/famix/src/model/famix/class.js +0 -70
  125. package/dist/lib/famix/src/model/famix/comment.js +0 -38
  126. package/dist/lib/famix/src/model/famix/container_entity.js +0 -125
  127. package/dist/lib/famix/src/model/famix/decorator.js +0 -31
  128. package/dist/lib/famix/src/model/famix/entity.js +0 -16
  129. package/dist/lib/famix/src/model/famix/enum.js +0 -30
  130. package/dist/lib/famix/src/model/famix/enum_value.js +0 -24
  131. package/dist/lib/famix/src/model/famix/function.js +0 -16
  132. package/dist/lib/famix/src/model/famix/implicit_variable.js +0 -16
  133. package/dist/lib/famix/src/model/famix/import_clause.js +0 -39
  134. package/dist/lib/famix/src/model/famix/index.js +0 -83
  135. package/dist/lib/famix/src/model/famix/indexed_file_anchor.js +0 -51
  136. package/dist/lib/famix/src/model/famix/inheritance.js +0 -32
  137. package/dist/lib/famix/src/model/famix/interface.js +0 -63
  138. package/dist/lib/famix/src/model/famix/invocation.js +0 -53
  139. package/dist/lib/famix/src/model/famix/method.js +0 -66
  140. package/dist/lib/famix/src/model/famix/module.js +0 -31
  141. package/dist/lib/famix/src/model/famix/named_entity.js +0 -77
  142. package/dist/lib/famix/src/model/famix/namespace.js +0 -24
  143. package/dist/lib/famix/src/model/famix/parameter.js +0 -24
  144. package/dist/lib/famix/src/model/famix/parameter_type.js +0 -24
  145. package/dist/lib/famix/src/model/famix/parameterizable_class.js +0 -30
  146. package/dist/lib/famix/src/model/famix/parameterizable_interface.js +0 -30
  147. package/dist/lib/famix/src/model/famix/parameterized_type.js +0 -36
  148. package/dist/lib/famix/src/model/famix/primitive_type.js +0 -16
  149. package/dist/lib/famix/src/model/famix/property.js +0 -44
  150. package/dist/lib/famix/src/model/famix/reference.js +0 -32
  151. package/dist/lib/famix/src/model/famix/scoping_entity.js +0 -35
  152. package/dist/lib/famix/src/model/famix/script_entity.js +0 -30
  153. package/dist/lib/famix/src/model/famix/source_anchor.js +0 -26
  154. package/dist/lib/famix/src/model/famix/source_language.js +0 -35
  155. package/dist/lib/famix/src/model/famix/sourced_entity.js +0 -59
  156. package/dist/lib/famix/src/model/famix/structural_entity.js +0 -38
  157. package/dist/lib/famix/src/model/famix/text_anchor.js +0 -37
  158. package/dist/lib/famix/src/model/famix/type.js +0 -71
  159. package/dist/lib/famix/src/model/famix/variable.js +0 -23
  160. package/doc-uml/metamodel-full.svg +0 -1
  161. package/doc-uml/metamodel.svg +0 -1
  162. package/jest.config-old.ts +0 -199
  163. package/plantuml.jar +0 -0
  164. package/src/famix2puml.ts +0 -119
  165. package/src/lib/famix/package-lock.json +0 -301
  166. package/src/lib/famix/readme.md +0 -5
  167. package/src/lib/famix/src/famix_JSON_exporter.ts +0 -56
  168. package/src/lib/famix/src/famix_base_element.ts +0 -22
  169. package/src/lib/famix/src/famix_repository.ts +0 -243
  170. package/src/lib/famix/src/model/famix/access.ts +0 -53
  171. package/src/lib/famix/src/model/famix/alias.ts +0 -41
  172. package/src/lib/famix/src/model/famix/association.ts +0 -44
  173. package/src/lib/famix/src/model/famix/behavioral_entity.ts +0 -107
  174. package/src/lib/famix/src/model/famix/class.ts +0 -86
  175. package/src/lib/famix/src/model/famix/comment.ts +0 -50
  176. package/src/lib/famix/src/model/famix/container_entity.ts +0 -165
  177. package/src/lib/famix/src/model/famix/decorator.ts +0 -39
  178. package/src/lib/famix/src/model/famix/enum.ts +0 -31
  179. package/src/lib/famix/src/model/famix/enum_value.ts +0 -29
  180. package/src/lib/famix/src/model/famix/import_clause.ts +0 -53
  181. package/src/lib/famix/src/model/famix/indexed_file_anchor.ts +0 -71
  182. package/src/lib/famix/src/model/famix/inheritance.ts +0 -42
  183. package/src/lib/famix/src/model/famix/interface.ts +0 -75
  184. package/src/lib/famix/src/model/famix/invocation.ts +0 -68
  185. package/src/lib/famix/src/model/famix/method.ts +0 -96
  186. package/src/lib/famix/src/model/famix/module.ts +0 -31
  187. package/src/lib/famix/src/model/famix/named_entity.ts +0 -98
  188. package/src/lib/famix/src/model/famix/namespace.ts +0 -28
  189. package/src/lib/famix/src/model/famix/parameter_type.ts +0 -33
  190. package/src/lib/famix/src/model/famix/parameterizable_class.ts +0 -31
  191. package/src/lib/famix/src/model/famix/parameterizable_interface.ts +0 -31
  192. package/src/lib/famix/src/model/famix/parameterized_type.ts +0 -40
  193. package/src/lib/famix/src/model/famix/primitive_type.ts +0 -15
  194. package/src/lib/famix/src/model/famix/property.ts +0 -54
  195. package/src/lib/famix/src/model/famix/reference.ts +0 -42
  196. package/src/lib/famix/src/model/famix/scoping_entity.ts +0 -35
  197. package/src/lib/famix/src/model/famix/script_entity.ts +0 -38
  198. package/src/lib/famix/src/model/famix/source_anchor.ts +0 -31
  199. package/src/lib/famix/src/model/famix/source_language.ts +0 -37
  200. package/src/lib/famix/src/model/famix/sourced_entity.ts +0 -73
  201. package/src/lib/famix/src/model/famix/structural_entity.ts +0 -44
  202. package/src/lib/famix/src/model/famix/text_anchor.ts +0 -49
  203. package/src/lib/famix/src/model/famix/type.ts +0 -88
  204. package/src/lib/famix/src/model/famix/variable.ts +0 -28
  205. package/src/lib/famix/tsconfig.json +0 -27
  206. package/src/lib/famix/tslint.json +0 -15
  207. /package/src/lib/famix/{src/index.ts → index.ts} +0 -0
  208. /package/src/lib/famix/{src/model → model}/famix/accessor.ts +0 -0
  209. /package/src/lib/famix/{src/model → model}/famix/entity.ts +0 -0
  210. /package/src/lib/famix/{src/model → model}/famix/function.ts +0 -0
@@ -1,29 +1,94 @@
1
- import { ClassDeclaration, ConstructorDeclaration, FunctionDeclaration, Identifier, InterfaceDeclaration, MethodDeclaration, MethodSignature, ModuleDeclaration, PropertyDeclaration, PropertySignature, SourceFile, TypeParameterDeclaration, VariableDeclaration, ParameterDeclaration, Decorator, GetAccessorDeclaration, SetAccessorDeclaration, ImportSpecifier, CommentRange, EnumDeclaration, EnumMember, TypeAliasDeclaration, FunctionExpression, ExpressionWithTypeArguments, ImportDeclaration, ImportEqualsDeclaration } from "ts-morph";
2
- import * as Famix from "../lib/famix/src/model/famix";
1
+ import { ClassDeclaration, ConstructorDeclaration, FunctionDeclaration, Identifier, InterfaceDeclaration, MethodDeclaration, MethodSignature, ModuleDeclaration, PropertyDeclaration, PropertySignature, SourceFile, TypeParameterDeclaration, VariableDeclaration, ParameterDeclaration, Decorator, GetAccessorDeclaration, SetAccessorDeclaration, ImportSpecifier, CommentRange, EnumDeclaration, EnumMember, TypeAliasDeclaration, FunctionExpression, ExpressionWithTypeArguments, ImportDeclaration, ImportEqualsDeclaration, SyntaxKind, Expression, TypeNode, Node, ts, Scope, Type, ArrowFunction } from "ts-morph";
2
+ import { isAmbient, isNamespace } from "../analyze_functions/process_functions";
3
+ import * as Famix from "../lib/famix/model/famix";
4
+ import { FamixRepository } from "../lib/famix/famix_repository";
3
5
  import { logger, config } from "../analyze";
4
6
  import GraphemeSplitter from "grapheme-splitter";
5
7
  import * as Helpers from "./helpers_creation";
6
8
  import * as FQNFunctions from "../fqn";
7
- import { FamixRepository } from "../lib/famix/src/famix_repository";
8
9
  import path from "path";
10
+ import _ from 'lodash';
9
11
 
10
12
  export type TSMorphObjectType = ImportDeclaration | ImportEqualsDeclaration | SourceFile | ModuleDeclaration | ClassDeclaration | InterfaceDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature | FunctionDeclaration | FunctionExpression | ParameterDeclaration | VariableDeclaration | PropertyDeclaration | PropertySignature | TypeParameterDeclaration | Identifier | Decorator | GetAccessorDeclaration | SetAccessorDeclaration | ImportSpecifier | CommentRange | EnumDeclaration | EnumMember | TypeAliasDeclaration | ExpressionWithTypeArguments;
11
13
 
14
+ export type TypeDeclaration = TypeAliasDeclaration | PropertyDeclaration | PropertySignature | MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration | FunctionDeclaration | FunctionExpression | ParameterDeclaration | VariableDeclaration | EnumMember | ImportEqualsDeclaration;
15
+
16
+ type ParametricVariantType = Famix.ParametricClass | Famix.ParametricInterface | Famix.ParametricFunction | Famix.ParametricMethod;
17
+
18
+ type ConcreteElementTSMorphType = ClassDeclaration | InterfaceDeclaration | FunctionDeclaration | MethodDeclaration;
19
+
12
20
  export class EntityDictionary {
13
21
 
14
22
  public famixRep = new FamixRepository();
15
23
  private fmxAliasMap = new Map<string, Famix.Alias>(); // Maps the alias names to their Famix model
16
- private fmxClassMap = new Map<string, Famix.Class | Famix.ParameterizableClass>(); // Maps the fully qualified class names to their Famix model
17
- private fmxInterfaceMap = new Map<string, Famix.Interface | Famix.ParameterizableInterface>(); // Maps the interface names to their Famix model
18
- private fmxNamespaceMap = new Map<string, Famix.Namespace>(); // Maps the namespace names to their Famix model
24
+ private fmxClassMap = new Map<string, Famix.Class | Famix.ParametricClass>(); // Maps the fully qualified class names to their Famix model
25
+ private fmxInterfaceMap = new Map<string, Famix.Interface | Famix.ParametricInterface>(); // Maps the interface names to their Famix model
26
+ private fmxModuleMap = new Map<string, Famix.Module>(); // Maps the namespace names to their Famix model
19
27
  private fmxFileMap = new Map<string, Famix.ScriptEntity | Famix.Module>(); // Maps the source file names to their Famix model
20
- private fmxTypeMap = new Map<string, Famix.Type | Famix.PrimitiveType | Famix.ParameterizedType>(); // Maps the type names to their Famix model
28
+ private fmxTypeMap = new Map<string, Famix.Type | Famix.PrimitiveType | Famix.ParameterType>(); // Maps the type names to their Famix model
29
+ private fmxFunctionAndMethodMap = new Map<string, Famix.Function | Famix.ParametricFunction | Famix.Method | Famix.ParametricMethod> // Maps the function names to their Famix model
21
30
  private UNKNOWN_VALUE = '(unknown due to parsing error)'; // The value to use when a name is not usable
22
31
  public fmxElementObjectMap = new Map<Famix.Entity,TSMorphObjectType>();
32
+ public tsMorphElementObjectMap = new Map<TSMorphObjectType,Famix.Entity>();
23
33
 
24
34
  constructor() {
25
- this.famixRep.setFmxElementObjectMap(this.fmxElementObjectMap);
35
+ this.famixRep.setFmxElementObjectMap(this.fmxElementObjectMap);
36
+ }
37
+
38
+ public addSourceAnchor(fmx: Famix.SourcedEntity, node: TSMorphObjectType): Famix.IndexedFileAnchor {
39
+ const sourceAnchor: Famix.IndexedFileAnchor = new Famix.IndexedFileAnchor();
40
+ let sourceStart, sourceEnd: number;
41
+ if (fmx && node) {
42
+ // find the start and end positions of the source element
43
+ if (!(node instanceof CommentRange)) {
44
+ sourceStart = node.getStart();
45
+ sourceEnd = node.getEnd();
46
+ } else {
47
+ sourceStart = node.getPos();
48
+ sourceEnd = node.getEnd();
49
+ }
50
+
51
+ if (config.expectGraphemes) {
52
+ /**
53
+ * The following logic handles the case of multi-code point characters (e.g. emoji) in the source text.
54
+ * This is needed because Pharo/Smalltalk treats multi-code point characters as a single character,
55
+ * but JavaScript treats them as multiple characters. This means that the start and end positions
56
+ * of a source element in Pharo/Smalltalk will be different than the start and end positions of the
57
+ * same source element in JavaScript. This logic finds the start and end positions of the source
58
+ * element in JavaScript and then uses those positions to set the start and end positions of the
59
+ * Famix index file anchor.
60
+ * It depends on code in the 'grapheme-splitter' package in npm.
61
+ */
62
+ const splitter = new GraphemeSplitter();
63
+ const sourceFileText = node.getSourceFile().getFullText();
64
+ const hasGraphemeClusters = splitter.countGraphemes(sourceFileText) > 1;
65
+ if (hasGraphemeClusters) {
66
+ const sourceElementText = sourceFileText.substring(sourceStart, sourceEnd);
67
+ const sourceElementTextGraphemes = splitter.splitGraphemes(sourceElementText);
68
+ const sourceFileTextGraphemes = splitter.splitGraphemes(sourceFileText);
69
+ const numberOfGraphemeClustersBeforeStart = splitter.countGraphemes(sourceFileText.substring(0, sourceStart));
26
70
 
71
+ // find the start of the sourceElementTextGraphemes array in the sourceFileTextGraphemes array
72
+ sourceStart = Helpers.indexOfSplitArray({searchArray: sourceFileTextGraphemes,
73
+ targetArray: sourceElementTextGraphemes,
74
+ start: sourceStart - numberOfGraphemeClustersBeforeStart});
75
+ sourceEnd = sourceStart + sourceElementTextGraphemes.length;
76
+ }
77
+ }
78
+
79
+ // The +1 is because the source anchor (Pharo) is 1-based, but ts-morph is 0-based
80
+ sourceAnchor.startPos = sourceStart + 1;
81
+ sourceAnchor.endPos = sourceEnd + 1;
82
+
83
+ const fileName = node.getSourceFile().getFilePath();
84
+
85
+ sourceAnchor.element = fmx;
86
+ sourceAnchor.fileName = fileName;
87
+ fmx.sourceAnchor = sourceAnchor;
88
+ this.famixRep.addElement(sourceAnchor);
89
+
90
+ }
91
+ return sourceAnchor;
27
92
  }
28
93
 
29
94
  /**
@@ -32,10 +97,19 @@ export class EntityDictionary {
32
97
  * @param famixElement The Famix model of the source element
33
98
  */
34
99
  public makeFamixIndexFileAnchor(sourceElement: TSMorphObjectType, famixElement: Famix.SourcedEntity): void {
100
+ // check if famixElement doesn't have a valid fullyQualifiedName
101
+ if (typeof (famixElement as any).getFullyQualifiedName === 'function') {
102
+ // The method exists
103
+ const fullyQualifiedName = (famixElement as any).fullyQualifiedName;
104
+ if (!fullyQualifiedName || fullyQualifiedName === this.UNKNOWN_VALUE) {
105
+ throw new Error(`Famix element ${famixElement.constructor.name} has no valid fullyQualifiedName.`);
106
+ }
107
+ }
108
+
35
109
  logger.debug("making index file anchor for '" + sourceElement?.getText() + "' with famixElement " + famixElement.getJSON());
36
110
  const fmxIndexFileAnchor = new Famix.IndexedFileAnchor();
37
- fmxIndexFileAnchor.setElement(famixElement);
38
- this.fmxElementObjectMap.set(famixElement,sourceElement);
111
+ fmxIndexFileAnchor.element = famixElement;
112
+ this.fmxElementObjectMap.set(famixElement, sourceElement);
39
113
 
40
114
  if (sourceElement !== null) {
41
115
  const absolutePathProject = this.famixRep.getAbsolutePath();
@@ -56,7 +130,7 @@ export class EntityDictionary {
56
130
  // revert any backslashes to forward slashes (path.normalize on windows introduces them)
57
131
  pathInProject = pathInProject.replace(/\\/g, "/");
58
132
 
59
- fmxIndexFileAnchor.setFileName(pathInProject);
133
+ fmxIndexFileAnchor.fileName = pathInProject;
60
134
  let sourceStart, sourceEnd, sourceLineStart, sourceLineEnd: number;
61
135
  if (!(sourceElement instanceof CommentRange)) {
62
136
  sourceStart = sourceElement.getStart();
@@ -95,22 +169,18 @@ export class EntityDictionary {
95
169
  }
96
170
  }
97
171
  // note: the +1 is because the source anchor is 1-based, but ts-morph is 0-based
98
- fmxIndexFileAnchor.setStartPos(sourceStart + 1);
99
- fmxIndexFileAnchor.setEndPos(sourceEnd + 1);
100
- if (!(sourceElement instanceof CommentRange)) {
101
- fmxIndexFileAnchor.setStartLine(sourceLineStart);
102
- fmxIndexFileAnchor.setEndLine(sourceLineEnd);
103
- }
172
+ fmxIndexFileAnchor.startPos = sourceStart + 1;
173
+ fmxIndexFileAnchor.endPos = sourceEnd + 1;
104
174
 
105
- if (!(famixElement instanceof Famix.Association) && !(famixElement instanceof Famix.Comment) && !(sourceElement instanceof CommentRange) && !(sourceElement instanceof Identifier) && !(sourceElement instanceof ImportSpecifier) && !(sourceElement instanceof ExpressionWithTypeArguments)) {
106
- (famixElement as Famix.NamedEntity).setFullyQualifiedName(FQNFunctions.getFQN(sourceElement));
107
- }
175
+ // if (!(famixElement instanceof Famix.ImportClause || famixElement instanceof Famix.Access || famixElement instanceof Famix.Reference || famixElement instanceof Famix.Invocation || famixElement instanceof Famix.Inheritance) && !(famixElement instanceof Famix.Comment) && !(sourceElement instanceof CommentRange) && !(sourceElement instanceof Identifier) && !(sourceElement instanceof ImportSpecifier) && !(sourceElement instanceof ExpressionWithTypeArguments)) {
176
+ // initFQN(sourceElement, famixElement);
177
+ // }
108
178
  } else {
109
179
  // sourceElement is null
110
180
  logger.warn("sourceElement is null for famixElement " + famixElement.getJSON());
111
- fmxIndexFileAnchor.setFileName("unknown");
112
- fmxIndexFileAnchor.setStartPos(0);
113
- fmxIndexFileAnchor.setEndPos(0);
181
+ fmxIndexFileAnchor.fileName = "unknown";
182
+ fmxIndexFileAnchor.startPos = 0;
183
+ fmxIndexFileAnchor.endPos = 0;
114
184
  }
115
185
 
116
186
  this.famixRep.addElement(fmxIndexFileAnchor);
@@ -123,20 +193,23 @@ export class EntityDictionary {
123
193
  * @returns The Famix model of the source file
124
194
  */
125
195
  public createOrGetFamixFile(f: SourceFile, isModule: boolean): Famix.ScriptEntity | Famix.Module {
126
- let fmxFile: Famix.ScriptEntity | Famix.Module;
196
+ let fmxFile: Famix.ScriptEntity; // | Famix.Module;
127
197
 
128
198
  const fileName = f.getBaseName();
129
199
  const fullyQualifiedFilename = f.getFilePath();
130
- if (!this.fmxFileMap.has(fullyQualifiedFilename)) {
200
+ const foundFileName = this.fmxFileMap.get(fullyQualifiedFilename);
201
+ if (!foundFileName) {
131
202
  if (isModule) {
132
- fmxFile = new Famix.Module();
203
+ fmxFile = new Famix.Module();
133
204
  }
134
205
  else {
135
206
  fmxFile = new Famix.ScriptEntity();
136
207
  }
137
- fmxFile.setName(fileName);
138
- fmxFile.setNumberOfLinesOfText(f.getEndLineNumber() - f.getStartLineNumber());
139
- fmxFile.setNumberOfCharacters(f.getFullText().length);
208
+ fmxFile.name = fileName;
209
+ fmxFile.numberOfLinesOfText = f.getEndLineNumber() - f.getStartLineNumber();
210
+ fmxFile.numberOfCharacters = f.getFullText().length;
211
+
212
+ initFQN(f, fmxFile);
140
213
 
141
214
  this.makeFamixIndexFileAnchor(f, fmxFile);
142
215
 
@@ -144,7 +217,7 @@ export class EntityDictionary {
144
217
  this.famixRep.addElement(fmxFile);
145
218
  }
146
219
  else {
147
- fmxFile = this.fmxFileMap.get(fullyQualifiedFilename);
220
+ fmxFile = foundFileName;
148
221
  }
149
222
 
150
223
  this.fmxElementObjectMap.set(fmxFile,f);
@@ -152,29 +225,34 @@ export class EntityDictionary {
152
225
  }
153
226
 
154
227
  /**
155
- * Creates or gets a Famix namespace
156
- * @param m A namespace
157
- * @returns The Famix model of the namespace
228
+ * Creates or gets a Famix Module
229
+ * @param m A module
230
+ * @returns The Famix model of the module
158
231
  */
159
- public createOrGetFamixNamespace(m: ModuleDeclaration): Famix.Namespace {
160
- let fmxNamespace: Famix.Namespace;
161
- const namespaceName = m.getName();
162
- if (!this.fmxNamespaceMap.has(namespaceName)) {
163
- fmxNamespace = new Famix.Namespace();
164
- fmxNamespace.setName(namespaceName);
165
-
166
- this.makeFamixIndexFileAnchor(m, fmxNamespace);
167
-
168
- this.fmxNamespaceMap.set(namespaceName, fmxNamespace);
169
-
170
- this.famixRep.addElement(fmxNamespace);
232
+ public createOrGetFamixModule(m: ModuleDeclaration): Famix.Module {
233
+ let fmxModule: Famix.Module;
234
+ const moduleName = m.getName();
235
+ const foundModuleName = this.fmxModuleMap.get(moduleName);
236
+ if (!foundModuleName) {
237
+ fmxModule = new Famix.Module();
238
+ fmxModule.name = moduleName;
239
+ fmxModule.isAmbient = isAmbient(m);
240
+ fmxModule.isNamespace = isNamespace(m);
241
+ fmxModule.isModule = !fmxModule.isNamespace && !fmxModule.isAmbient;
242
+
243
+ initFQN(m, fmxModule);
244
+ this.makeFamixIndexFileAnchor(m, fmxModule);
245
+
246
+ this.fmxModuleMap.set(moduleName, fmxModule);
247
+
248
+ this.famixRep.addElement(fmxModule);
171
249
  }
172
250
  else {
173
- fmxNamespace = this.fmxNamespaceMap.get(namespaceName);
251
+ fmxModule = foundModuleName;
174
252
  }
175
253
 
176
- this.fmxElementObjectMap.set(fmxNamespace,m);
177
- return fmxNamespace;
254
+ this.fmxElementObjectMap.set(fmxModule,m);
255
+ return fmxModule;
178
256
  }
179
257
 
180
258
  /**
@@ -186,15 +264,16 @@ export class EntityDictionary {
186
264
  let fmxAlias: Famix.Alias;
187
265
  const aliasName = a.getName();
188
266
  const aliasFullyQualifiedName = a.getType().getText(); // FQNFunctions.getFQN(a);
189
- if (!this.fmxAliasMap.has(aliasFullyQualifiedName)) {
267
+ const foundAlias = this.fmxAliasMap.get(aliasFullyQualifiedName);
268
+ if (!foundAlias) {
190
269
  fmxAlias = new Famix.Alias();
191
- fmxAlias.setName(a.getName());
270
+ fmxAlias.name = a.getName();
192
271
  const aliasNameWithGenerics = aliasName + (a.getTypeParameters().length ? ("<" + a.getTypeParameters().map(tp => tp.getName()).join(", ") + ">") : "");
193
272
  logger.debug(`> NOTE: alias ${aliasName} has fully qualified name ${aliasFullyQualifiedName} and name with generics ${aliasNameWithGenerics}.`);
194
273
 
195
274
  const fmxType = this.createOrGetFamixType(aliasNameWithGenerics, a);
196
- fmxAlias.setAliasedEntity(fmxType);
197
-
275
+ fmxAlias.aliasedEntity = fmxType;
276
+ initFQN(a, fmxAlias);
198
277
  this.makeFamixIndexFileAnchor(a, fmxAlias);
199
278
 
200
279
  this.fmxAliasMap.set(aliasFullyQualifiedName, fmxAlias);
@@ -202,7 +281,7 @@ export class EntityDictionary {
202
281
  this.famixRep.addElement(fmxAlias);
203
282
  }
204
283
  else {
205
- fmxAlias = this.fmxAliasMap.get(aliasFullyQualifiedName);
284
+ fmxAlias = foundAlias;
206
285
  }
207
286
  this.fmxElementObjectMap.set(fmxAlias,a);
208
287
 
@@ -214,23 +293,24 @@ export class EntityDictionary {
214
293
  * @param cls A class
215
294
  * @returns The Famix model of the class
216
295
  */
217
- public createOrGetFamixClass(cls: ClassDeclaration): Famix.Class | Famix.ParameterizableClass {
218
- let fmxClass: Famix.Class | Famix.ParameterizableClass;
296
+ public createOrGetFamixClass(cls: ClassDeclaration): Famix.Class | Famix.ParametricClass {
297
+ let fmxClass: Famix.Class | Famix.ParametricClass;
219
298
  const isAbstract = cls.isAbstract();
220
299
  const classFullyQualifiedName = FQNFunctions.getFQN(cls);
221
- const clsName = cls.getName();
222
- if (!this.fmxClassMap.has(classFullyQualifiedName)) {
223
- const isGeneric = cls.getTypeParameters().length;
300
+ const clsName = cls.getName() || this.UNKNOWN_VALUE;
301
+ const isGeneric = cls.getTypeParameters().length;
302
+ const foundClass = this.fmxClassMap.get(classFullyQualifiedName);
303
+ if (!foundClass) {
224
304
  if (isGeneric) {
225
- fmxClass = new Famix.ParameterizableClass();
305
+ fmxClass = new Famix.ParametricClass();
226
306
  }
227
307
  else {
228
308
  fmxClass = new Famix.Class();
229
309
  }
230
310
 
231
- fmxClass.setName(clsName);
232
- fmxClass.setFullyQualifiedName(classFullyQualifiedName);
233
- fmxClass.setIsAbstract(isAbstract);
311
+ fmxClass.name = clsName;
312
+ fmxClass.fullyQualifiedName = classFullyQualifiedName;
313
+ fmxClass.isAbstract = isAbstract;
234
314
 
235
315
  this.makeFamixIndexFileAnchor(cls, fmxClass);
236
316
 
@@ -238,13 +318,12 @@ export class EntityDictionary {
238
318
 
239
319
  this.famixRep.addElement(fmxClass);
240
320
 
321
+ this.fmxElementObjectMap.set(fmxClass,cls);
241
322
  }
242
323
  else {
243
- fmxClass = this.fmxClassMap.get(classFullyQualifiedName) as (Famix.Class | Famix.ParameterizableClass);
324
+ fmxClass = foundClass;
244
325
  }
245
326
 
246
- this.fmxElementObjectMap.set(fmxClass,cls);
247
-
248
327
  return fmxClass;
249
328
  }
250
329
 
@@ -253,35 +332,98 @@ export class EntityDictionary {
253
332
  * @param inter An interface
254
333
  * @returns The Famix model of the interface
255
334
  */
256
- public createOrGetFamixInterface(inter: InterfaceDeclaration): Famix.Interface | Famix.ParameterizableInterface {
257
- let fmxInterface: Famix.Interface | Famix.ParameterizableInterface;
335
+ public createOrGetFamixInterface(inter: InterfaceDeclaration): Famix.Interface | Famix.ParametricInterface {
336
+
337
+ let fmxInterface: Famix.Interface | Famix.ParametricInterface;
258
338
  const interName = inter.getName();
259
339
  const interFullyQualifiedName = FQNFunctions.getFQN(inter);
260
- if (!this.fmxInterfaceMap.has(interName)) {
340
+ const foundInterface = this.fmxInterfaceMap.get(interFullyQualifiedName);
341
+ if (!foundInterface) {
261
342
  const isGeneric = inter.getTypeParameters().length;
262
343
  if (isGeneric) {
263
- fmxInterface = new Famix.ParameterizableInterface();
344
+ fmxInterface = new Famix.ParametricInterface();
264
345
  }
265
346
  else {
266
347
  fmxInterface = new Famix.Interface();
267
348
  }
268
349
 
269
- fmxInterface.setName(interName);
270
-
350
+ fmxInterface.name = interName;
351
+ initFQN(inter, fmxInterface);
271
352
  this.makeFamixIndexFileAnchor(inter, fmxInterface);
272
353
 
273
354
  this.fmxInterfaceMap.set(interFullyQualifiedName, fmxInterface);
274
355
 
275
356
  this.famixRep.addElement(fmxInterface);
357
+
358
+ this.fmxElementObjectMap.set(fmxInterface,inter);
276
359
  }
277
360
  else {
278
- fmxInterface = this.fmxInterfaceMap.get(interName) as (Famix.Interface | Famix.ParameterizableInterface);
361
+ fmxInterface = foundInterface;
279
362
  }
280
-
281
- this.fmxElementObjectMap.set(fmxInterface,inter);
282
363
  return fmxInterface;
283
364
  }
284
365
 
366
+
367
+ /**
368
+ * Creates or gets a Famix concrete element
369
+ * @param concreteElement A parametric Element
370
+ * @param concreteElementDeclaration the element declaration
371
+ * @param concreteArguments concrete arguments
372
+ * @returns A parametric Element
373
+ */
374
+ public createOrGetFamixConcreteElement(concreteElement : ParametricVariantType,
375
+ concreteElementDeclaration : ConcreteElementTSMorphType,
376
+ concreteArguments: TypeNode[]): ParametricVariantType {
377
+
378
+ let fullyQualifiedFilename = concreteElement.fullyQualifiedName;
379
+ let params = "";
380
+
381
+ concreteArguments.map((param) => {
382
+ params = params+param.getText()+','
383
+ })
384
+
385
+ params = params.substring(0, params.length - 1)
386
+
387
+ fullyQualifiedFilename = Helpers.replaceLastBetweenTags(fullyQualifiedFilename,params);
388
+
389
+ let concElement: ParametricVariantType;
390
+
391
+ if (!this.fmxInterfaceMap.has(fullyQualifiedFilename) &&
392
+ !this.fmxClassMap.has(fullyQualifiedFilename) &&
393
+ !this.fmxFunctionAndMethodMap.has(fullyQualifiedFilename)){
394
+ concElement = _.cloneDeep(concreteElement);
395
+ concElement.fullyQualifiedName = fullyQualifiedFilename;
396
+ concElement.clearGenericParameters();
397
+ concreteArguments.map((param) => {
398
+ const parameter = this.createOrGetFamixConcreteType(param);
399
+ concElement.addConcreteParameter(parameter);
400
+ })
401
+
402
+ if (concreteElement instanceof Famix.ParametricClass) {
403
+ this.fmxClassMap.set(fullyQualifiedFilename, concElement as Famix.ParametricClass);
404
+ } else if (concreteElement instanceof Famix.ParametricInterface) {
405
+ this.fmxInterfaceMap.set(fullyQualifiedFilename, concElement as Famix.ParametricInterface);
406
+ } else if (concreteElement instanceof Famix.ParametricFunction) {
407
+ this.fmxFunctionAndMethodMap.set(fullyQualifiedFilename, concElement as Famix.ParametricFunction);
408
+ } else { // if (concreteElement instanceof Famix.ParametricMethod) {
409
+ this.fmxFunctionAndMethodMap.set(fullyQualifiedFilename, concElement as Famix.ParametricMethod);
410
+ }
411
+ this.famixRep.addElement(concElement);
412
+ this.fmxElementObjectMap.set(concElement,concreteElementDeclaration);
413
+ } else {
414
+ if (concreteElement instanceof Famix.ParametricClass) {
415
+ concElement = this.fmxClassMap.get(fullyQualifiedFilename) as Famix.ParametricClass;
416
+ } else if (concreteElement instanceof Famix.ParametricInterface) {
417
+ concElement = this.fmxInterfaceMap.get(fullyQualifiedFilename) as Famix.ParametricInterface;
418
+ } else if (concreteElement instanceof Famix.ParametricFunction) {
419
+ concElement = this.fmxFunctionAndMethodMap.get(fullyQualifiedFilename) as Famix.ParametricFunction;
420
+ } else { // if (concreteElement instanceof Famix.ParametricMethod) {
421
+ concElement = this.fmxFunctionAndMethodMap.get(fullyQualifiedFilename) as Famix.ParametricMethod;
422
+ }
423
+ }
424
+ return concElement;
425
+ }
426
+
285
427
  /**
286
428
  * Creates a Famix property
287
429
  * @param property A property
@@ -290,7 +432,7 @@ export class EntityDictionary {
290
432
  public createFamixProperty(property: PropertyDeclaration | PropertySignature): Famix.Property {
291
433
  const fmxProperty = new Famix.Property();
292
434
  const isSignature = property instanceof PropertySignature;
293
- fmxProperty.setName(property.getName());
435
+ fmxProperty.name = property.getName();
294
436
 
295
437
  let propTypeName = this.UNKNOWN_VALUE;
296
438
  try {
@@ -300,26 +442,44 @@ export class EntityDictionary {
300
442
  }
301
443
 
302
444
  const fmxType = this.createOrGetFamixType(propTypeName, property);
303
- fmxProperty.setDeclaredType(fmxType);
445
+ fmxProperty.declaredType = fmxType;
446
+
447
+ // add the visibility (public, private, etc.) to the fmxProperty
448
+ fmxProperty.visibility = "";
449
+
450
+ property.getModifiers().forEach(m => {
451
+ switch (m.getText()) {
452
+ case Scope.Public:
453
+ fmxProperty.visibility = "public";
454
+ break;
455
+ case Scope.Protected:
456
+ fmxProperty.visibility = "protected";
457
+ break;
458
+ case Scope.Private:
459
+ fmxProperty.visibility = "private";
460
+ break;
461
+ case "static":
462
+ fmxProperty.isClassSide = true;
463
+ break;
464
+ case "readonly":
465
+ fmxProperty.readOnly = true;
466
+ break;
467
+ default:
468
+ break;
469
+ }
470
+ });
304
471
 
305
- property.getModifiers().forEach(m => fmxProperty.addModifier(m.getText()));
306
472
  if (!isSignature && property.getExclamationTokenNode()) {
307
- fmxProperty.addModifier("!");
473
+ fmxProperty.isDefinitelyAssigned = true;
308
474
  }
309
475
  if (property.getQuestionTokenNode()) {
310
- fmxProperty.addModifier("?");
476
+ fmxProperty.isOptional = true;
311
477
  }
312
478
  if (property.getName().substring(0, 1) === "#") {
313
- fmxProperty.addModifier("#");
314
- }
315
-
316
- if (fmxProperty.getModifiers().has("static")) {
317
- fmxProperty.setIsClassSide(true);
318
- }
319
- else {
320
- fmxProperty.setIsClassSide(false);
479
+ fmxProperty.isJavaScriptPrivate = true;
321
480
  }
322
481
 
482
+ initFQN(property, fmxProperty);
323
483
  this.makeFamixIndexFileAnchor(property, fmxProperty);
324
484
 
325
485
  this.famixRep.addElement(fmxProperty);
@@ -335,89 +495,103 @@ export class EntityDictionary {
335
495
  * @param currentCC The cyclomatic complexity metrics of the current source file
336
496
  * @returns The Famix model of the method or the accessor
337
497
  */
338
- public createFamixMethod(method: MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration, currentCC: unknown): Famix.Method | Famix.Accessor {
339
- let fmxMethod: Famix.Method | Famix.Accessor;
340
- if (method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration) {
341
- fmxMethod = new Famix.Accessor();
342
- const isGetter = method instanceof GetAccessorDeclaration;
343
- const isSetter = method instanceof SetAccessorDeclaration;
344
- if (isGetter) {(fmxMethod as Famix.Accessor).setKind("getter");}
345
- if (isSetter) {(fmxMethod as Famix.Accessor).setKind("setter");}
346
- this.famixRep.addElement(fmxMethod);
347
- }
348
- else {
349
- fmxMethod = new Famix.Method();
350
- this.famixRep.addElement(fmxMethod);
351
- }
352
- const isConstructor = method instanceof ConstructorDeclaration;
353
- const isSignature = method instanceof MethodSignature;
498
+ public createOrGetFamixMethod(method: MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration, currentCC: { [key: string]: number }): Famix.Method | Famix.Accessor | Famix.ParametricMethod {
499
+ let fmxMethod: Famix.Method | Famix.Accessor | Famix.ParametricMethod;
354
500
  const isGeneric = method.getTypeParameters().length > 0;
355
- fmxMethod.setIsGeneric(isGeneric);
501
+ const functionFullyQualifiedName = FQNFunctions.getFQN(method);
502
+ if (!this.fmxFunctionAndMethodMap.has(functionFullyQualifiedName)) {
503
+
504
+ if (method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration) {
505
+ fmxMethod = new Famix.Accessor();
506
+ const isGetter = method instanceof GetAccessorDeclaration;
507
+ const isSetter = method instanceof SetAccessorDeclaration;
508
+ if (isGetter) {(fmxMethod as Famix.Accessor).kind = "getter";}
509
+ if (isSetter) {(fmxMethod as Famix.Accessor).kind = "setter";}
510
+ this.famixRep.addElement(fmxMethod);
511
+ }
512
+ else {
513
+ if (isGeneric) {
514
+ fmxMethod = new Famix.ParametricMethod();
515
+ }
516
+ else {
517
+ fmxMethod = new Famix.Method();
518
+ }
519
+ this.famixRep.addElement(fmxMethod);
520
+ }
521
+ const isConstructor = method instanceof ConstructorDeclaration;
522
+ const isSignature = method instanceof MethodSignature;
523
+
524
+ let isAbstract = false;
525
+ let isStatic = false;
526
+ if (method instanceof MethodDeclaration || method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration) {
527
+ isAbstract = method.isAbstract();
528
+ isStatic = method.isStatic();
529
+ }
356
530
 
357
- let isAbstract = false;
358
- let isStatic = false;
359
- if (method instanceof MethodDeclaration || method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration) {
360
- isAbstract = method.isAbstract();
361
- isStatic = method.isStatic();
362
- }
531
+ if (isConstructor) {(fmxMethod as Famix.Accessor).kind = "constructor";}
532
+ fmxMethod.isAbstract = isAbstract;
533
+ fmxMethod.isClassSide = isStatic;
534
+ fmxMethod.isPrivate = (method instanceof MethodDeclaration || method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration) ? (method.getModifiers().find(x => x.getText() === 'private')) !== undefined : false;
535
+ fmxMethod.isProtected = (method instanceof MethodDeclaration || method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration) ? (method.getModifiers().find(x => x.getText() === 'protected')) !== undefined : false;
536
+ fmxMethod.signature = Helpers.computeSignature(method.getText());
363
537
 
364
- if (isConstructor) {(fmxMethod as Famix.Accessor).setKind("constructor");}
365
- fmxMethod.setIsAbstract(isAbstract);
366
- fmxMethod.setIsClassSide(isStatic);
367
- fmxMethod.setIsPrivate((method instanceof MethodDeclaration || method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration) ? (method.getModifiers().find(x => x.getText() === 'private')) !== undefined : false);
368
- fmxMethod.setIsProtected((method instanceof MethodDeclaration || method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration) ? (method.getModifiers().find(x => x.getText() === 'protected')) !== undefined : false);
369
- fmxMethod.setSignature(Helpers.computeSignature(method.getText()));
538
+ let methodName: string;
539
+ if (isConstructor) {
540
+ methodName = "constructor";
541
+ }
542
+ else {
543
+ methodName = (method as MethodDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration).getName();
544
+ }
545
+ fmxMethod.name = methodName;
370
546
 
371
- let methodName: string;
372
- if (isConstructor) {
373
- methodName = "constructor";
374
- }
375
- else {
376
- methodName = (method as MethodDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration).getName();
377
- }
378
- fmxMethod.setName(methodName);
547
+ if (!isConstructor) {
548
+ if (method.getName().substring(0, 1) === "#") {
549
+ fmxMethod.isPrivate = true;
550
+ }
551
+ }
379
552
 
380
- if (!isConstructor) {
381
- if (method.getName().substring(0, 1) === "#") {
382
- fmxMethod.setIsPrivate(true);
553
+ if (!fmxMethod.isPrivate && !fmxMethod.isProtected) {
554
+ fmxMethod.isPublic = true;
555
+ }
556
+ else {
557
+ fmxMethod.isPublic = false;
383
558
  }
384
- }
385
559
 
386
- if (!fmxMethod.getIsPrivate() && !fmxMethod.getIsProtected()) {
387
- fmxMethod.setIsPublic(true);
388
- }
389
- else {
390
- fmxMethod.setIsPublic(false);
391
- }
560
+ if (!isSignature) {
561
+ fmxMethod.cyclomaticComplexity = currentCC[fmxMethod.name];
562
+ }
563
+ else {
564
+ fmxMethod.cyclomaticComplexity = 0;
565
+ }
392
566
 
393
- if (!isSignature) {
394
- fmxMethod.setCyclomaticComplexity(currentCC[fmxMethod.getName()]);
395
- }
396
- else {
397
- fmxMethod.setCyclomaticComplexity(0);
398
- }
567
+ let methodTypeName = this.UNKNOWN_VALUE;
568
+ try {
569
+ methodTypeName = method.getReturnType().getText().trim();
570
+ } catch (error) {
571
+ logger.error(`> WARNING: got exception ${error}. Failed to get usable name for return type of method: ${fmxMethod.name}. Continuing...`);
572
+ }
399
573
 
400
- let methodTypeName = this.UNKNOWN_VALUE;
401
- try {
402
- methodTypeName = method.getReturnType().getText().trim();
403
- } catch (error) {
404
- logger.error(`> WARNING: got exception ${error}. Failed to get usable name for return type of method: ${fmxMethod.getName()}. Continuing...`);
405
- }
574
+ const fmxType = this.createOrGetFamixType(methodTypeName, method);
575
+ fmxMethod.declaredType = fmxType;
576
+ fmxMethod.numberOfLinesOfCode = method.getEndLineNumber() - method.getStartLineNumber();
577
+ const parameters = method.getParameters();
578
+ fmxMethod.numberOfParameters = parameters.length;
406
579
 
407
- const fmxType = this.createOrGetFamixType(methodTypeName, method);
408
- fmxMethod.setDeclaredType(fmxType);
409
- fmxMethod.setNumberOfLinesOfCode(method.getEndLineNumber() - method.getStartLineNumber());
410
- const parameters = method.getParameters();
411
- fmxMethod.setNumberOfParameters(parameters.length);
580
+ if (!isSignature) {
581
+ fmxMethod.numberOfStatements = method.getStatements().length;
582
+ }
583
+ else {
584
+ fmxMethod.numberOfStatements = 0;
585
+ }
586
+
587
+ initFQN(method, fmxMethod);
588
+ this.makeFamixIndexFileAnchor(method, fmxMethod);
412
589
 
413
- if (!isSignature) {
414
- fmxMethod.setNumberOfStatements(method.getStatements().length);
590
+ this.fmxFunctionAndMethodMap.set(functionFullyQualifiedName, fmxMethod);
415
591
  }
416
592
  else {
417
- fmxMethod.setNumberOfStatements(0);
593
+ fmxMethod = this.fmxFunctionAndMethodMap.get(functionFullyQualifiedName) as (Famix.Method | Famix.Accessor | Famix.ParametricMethod);
418
594
  }
419
-
420
- this.makeFamixIndexFileAnchor(method, fmxMethod);
421
595
 
422
596
  this.fmxElementObjectMap.set(fmxMethod,method);
423
597
 
@@ -430,38 +604,54 @@ export class EntityDictionary {
430
604
  * @param currentCC The cyclomatic complexity metrics of the current source file
431
605
  * @returns The Famix model of the function
432
606
  */
433
- public createFamixFunction(func: FunctionDeclaration | FunctionExpression, currentCC: unknown): Famix.Function {
434
- const fmxFunction = new Famix.Function();
435
- if (func.getName()) {
436
- fmxFunction.setName(func.getName());
607
+ public createOrGetFamixFunction(func: FunctionDeclaration | FunctionExpression, currentCC: { [key: string]: number }): Famix.Function | Famix.ParametricFunction {
608
+ let fmxFunction: Famix.Function | Famix.ParametricFunction;
609
+ const isGeneric = func.getTypeParameters().length > 0;
610
+ const functionFullyQualifiedName = FQNFunctions.getFQN(func);
611
+ if (!this.fmxFunctionAndMethodMap.has(functionFullyQualifiedName)) {
612
+ if (isGeneric) {
613
+ fmxFunction = new Famix.ParametricFunction();
614
+ }
615
+ else {
616
+ fmxFunction = new Famix.Function();
617
+ }
618
+
619
+ const name = func.getName();
620
+ if (name) {
621
+ fmxFunction.name = name;
622
+ }
623
+ else {
624
+ fmxFunction.name = "anonymous";
625
+ }
626
+
627
+ fmxFunction.signature = Helpers.computeSignature(func.getText());
628
+ fmxFunction.cyclomaticComplexity = currentCC[fmxFunction.name];
629
+ fmxFunction.fullyQualifiedName = functionFullyQualifiedName;
630
+
631
+ let functionTypeName = this.UNKNOWN_VALUE;
632
+ try {
633
+ functionTypeName = func.getReturnType().getText().trim();
634
+ } catch (error) {
635
+ logger.error(`> WARNING: got exception ${error}. Failed to get usable name for return type of function: ${func.getName()}. Continuing...`);
636
+ }
637
+
638
+ const fmxType = this.createOrGetFamixType(functionTypeName, func);
639
+ fmxFunction.declaredType = fmxType;
640
+ fmxFunction.numberOfLinesOfCode = func.getEndLineNumber() - func.getStartLineNumber();
641
+ const parameters = func.getParameters();
642
+ fmxFunction.numberOfParameters = parameters.length;
643
+ fmxFunction.numberOfStatements = func.getStatements().length;
644
+ this.makeFamixIndexFileAnchor(func, fmxFunction);
645
+
646
+ this.famixRep.addElement(fmxFunction);
647
+
648
+ this.fmxElementObjectMap.set(fmxFunction,func);
649
+
650
+ this.fmxFunctionAndMethodMap.set(functionFullyQualifiedName, fmxFunction);
437
651
  }
438
652
  else {
439
- fmxFunction.setName("anonymous");
653
+ fmxFunction = this.fmxFunctionAndMethodMap.get(functionFullyQualifiedName) as (Famix.Function | Famix.ParametricFunction);
440
654
  }
441
- fmxFunction.setSignature(Helpers.computeSignature(func.getText()));
442
- fmxFunction.setCyclomaticComplexity(currentCC[fmxFunction.getName()]);
443
- const isGeneric = func.getTypeParameters().length > 0;
444
- fmxFunction.setIsGeneric(isGeneric);
445
-
446
- let functionTypeName = this.UNKNOWN_VALUE;
447
- try {
448
- functionTypeName = func.getReturnType().getText().trim();
449
- } catch (error) {
450
- logger.error(`> WARNING: got exception ${error}. Failed to get usable name for return type of function: ${func.getName()}. Continuing...`);
451
- }
452
-
453
- const fmxType = this.createOrGetFamixType(functionTypeName, func);
454
- fmxFunction.setDeclaredType(fmxType);
455
- fmxFunction.setNumberOfLinesOfCode(func.getEndLineNumber() - func.getStartLineNumber());
456
- const parameters = func.getParameters();
457
- fmxFunction.setNumberOfParameters(parameters.length);
458
- fmxFunction.setNumberOfStatements(func.getStatements().length);
459
-
460
- this.makeFamixIndexFileAnchor(func, fmxFunction);
461
-
462
- this.famixRep.addElement(fmxFunction);
463
-
464
- this.fmxElementObjectMap.set(fmxFunction,func);
465
655
 
466
656
  return fmxFunction;
467
657
  }
@@ -482,9 +672,10 @@ export class EntityDictionary {
482
672
  }
483
673
 
484
674
  const fmxType = this.createOrGetFamixType(paramTypeName, param);
485
- fmxParam.setDeclaredType(fmxType);
486
- fmxParam.setName(param.getName());
675
+ fmxParam.declaredType = fmxType;
676
+ fmxParam.name = param.getName();
487
677
 
678
+ initFQN(param, fmxParam);
488
679
  this.makeFamixIndexFileAnchor(param, fmxParam);
489
680
 
490
681
  this.famixRep.addElement(fmxParam);
@@ -500,9 +691,11 @@ export class EntityDictionary {
500
691
  * @returns The Famix model of the type parameter
501
692
  */
502
693
  public createFamixParameterType(tp: TypeParameterDeclaration): Famix.ParameterType {
694
+
503
695
  const fmxParameterType = new Famix.ParameterType();
504
- fmxParameterType.setName(tp.getName());
505
-
696
+
697
+ fmxParameterType.name = tp.getName();
698
+ initFQN(tp, fmxParameterType);
506
699
  this.makeFamixIndexFileAnchor(tp, fmxParameterType);
507
700
 
508
701
  this.famixRep.addElement(fmxParameterType);
@@ -512,6 +705,78 @@ export class EntityDictionary {
512
705
  return fmxParameterType;
513
706
  }
514
707
 
708
+ /**
709
+ * Creates a Famix type parameter
710
+ * @param tp A type parameter
711
+ * @returns The Famix model of the type parameter
712
+ */
713
+ public createOrGetFamixConcreteType(param: TypeNode): Famix.ParameterType | Famix.PrimitiveType | Famix.Class | Famix.Interface {
714
+ const typeParameterDeclaration = param.getSymbol()?.getDeclarations()[0] as TypeParameterDeclaration;
715
+ const parameterTypeName : string = param.getText();
716
+ let fmxParameterType: Famix.Type | Famix.Class | Famix.Interface | undefined = undefined;
717
+
718
+ let isClassOrInterface = false;
719
+ if (this.fmxClassMap.has(parameterTypeName)){
720
+ this.fmxClassMap.forEach((obj, name) => {
721
+ if(obj instanceof Famix.ParametricClass){
722
+ if (name === param.getText() && obj.genericParameters.size>0) {
723
+ fmxParameterType = obj;
724
+ isClassOrInterface = true;
725
+ }
726
+ } else {
727
+ if (name === param.getText()) {
728
+ fmxParameterType = obj;
729
+ isClassOrInterface = true;
730
+ }
731
+ }
732
+ })
733
+ }
734
+
735
+ if (this.fmxInterfaceMap.has(parameterTypeName)){
736
+ this.fmxInterfaceMap.forEach((obj, name) => {
737
+ if(obj instanceof Famix.ParametricInterface){
738
+ if (name === param.getText() && obj.genericParameters.size>0) {
739
+ fmxParameterType = obj;
740
+ isClassOrInterface = true;
741
+ }
742
+ } else {
743
+ if (name === param.getText()) {
744
+ fmxParameterType = obj;
745
+ isClassOrInterface = true;
746
+ }
747
+ }
748
+ })
749
+ }
750
+
751
+ if(!isClassOrInterface){
752
+ if (!this.fmxTypeMap.has(parameterTypeName)) {
753
+ if (parameterTypeName === "number" || parameterTypeName === "string" || parameterTypeName === "boolean" || parameterTypeName === "bigint" || parameterTypeName === "symbol" || parameterTypeName === "undefined" || parameterTypeName === "null" || parameterTypeName === "any" || parameterTypeName === "unknown" || parameterTypeName === "never" || parameterTypeName === "void") {
754
+ fmxParameterType = new Famix.PrimitiveType();
755
+ fmxParameterType.isStub = true;
756
+ } else {
757
+ fmxParameterType = new Famix.ParameterType();
758
+ }
759
+
760
+ fmxParameterType.name = parameterTypeName;
761
+ this.famixRep.addElement(fmxParameterType);
762
+ this.fmxTypeMap.set(parameterTypeName, fmxParameterType);
763
+ this.fmxElementObjectMap.set(fmxParameterType,typeParameterDeclaration);
764
+ }
765
+ else {
766
+ const result = this.fmxTypeMap.get(parameterTypeName);
767
+ if (result) {
768
+ fmxParameterType = result;
769
+ } else {
770
+ throw new Error(`Famix type ${parameterTypeName} is not found in the Type map.`);
771
+ }
772
+ }
773
+ }
774
+ if (!fmxParameterType) {
775
+ throw new Error(`fmxParameterType was undefined for parameterTypeName ${parameterTypeName}`);
776
+ }
777
+ return fmxParameterType;
778
+ }
779
+
515
780
  /**
516
781
  * Creates a Famix variable
517
782
  * @param variable A variable
@@ -519,24 +784,24 @@ export class EntityDictionary {
519
784
  */
520
785
  public createFamixVariable(variable: VariableDeclaration): Famix.Variable {
521
786
  const fmxVariable = new Famix.Variable();
522
-
787
+
523
788
  let variableTypeName = this.UNKNOWN_VALUE;
524
789
  try {
525
790
  variableTypeName = variable.getType().getText().trim();
526
791
  } catch (error) {
527
792
  logger.error(`> WARNING: got exception ${error}. Failed to get usable name for variable: ${variable.getName()}. Continuing...`);
528
793
  }
529
-
794
+
530
795
  const fmxType = this.createOrGetFamixType(variableTypeName, variable);
531
- fmxVariable.setDeclaredType(fmxType);
532
- fmxVariable.setName(variable.getName());
533
-
796
+ fmxVariable.declaredType = fmxType;
797
+ fmxVariable.name = variable.getName();
798
+ initFQN(variable, fmxVariable);
534
799
  this.makeFamixIndexFileAnchor(variable, fmxVariable);
535
-
800
+
536
801
  this.famixRep.addElement(fmxVariable);
537
-
802
+
538
803
  this.fmxElementObjectMap.set(fmxVariable,variable);
539
-
804
+
540
805
  return fmxVariable;
541
806
  }
542
807
 
@@ -547,8 +812,8 @@ export class EntityDictionary {
547
812
  */
548
813
  public createFamixEnum(enumEntity: EnumDeclaration): Famix.Enum {
549
814
  const fmxEnum = new Famix.Enum();
550
- fmxEnum.setName(enumEntity.getName());
551
-
815
+ fmxEnum.name = enumEntity.getName();
816
+ initFQN(enumEntity, fmxEnum);
552
817
  this.makeFamixIndexFileAnchor(enumEntity, fmxEnum);
553
818
 
554
819
  this.famixRep.addElement(fmxEnum);
@@ -574,9 +839,9 @@ export class EntityDictionary {
574
839
  }
575
840
 
576
841
  const fmxType = this.createOrGetFamixType(enumValueTypeName, enumMember);
577
- fmxEnumValue.setDeclaredType(fmxType);
578
- fmxEnumValue.setName(enumMember.getName());
579
-
842
+ fmxEnumValue.declaredType = fmxType;
843
+ fmxEnumValue.name = enumMember.getName();
844
+ initFQN(enumMember, fmxEnumValue);
580
845
  this.makeFamixIndexFileAnchor(enumMember, fmxEnumValue);
581
846
 
582
847
  this.famixRep.addElement(fmxEnumValue);
@@ -597,12 +862,12 @@ export class EntityDictionary {
597
862
  const decoratorName = "@" + decorator.getName();
598
863
  const decoratorExpression = decorator.getText().substring(1);
599
864
 
600
- fmxDecorator.setName(decoratorName);
601
- fmxDecorator.setDecoratorExpression(decoratorExpression);
865
+ fmxDecorator.name = decoratorName;
866
+ fmxDecorator.decoratorExpression = decoratorExpression;
602
867
  const decoratedEntityFullyQualifiedName = FQNFunctions.getFQN(decoratedEntity);
603
868
  const fmxDecoratedEntity = this.famixRep.getFamixEntityByFullyQualifiedName(decoratedEntityFullyQualifiedName) as Famix.NamedEntity;
604
- fmxDecorator.setDecoratedEntity(fmxDecoratedEntity);
605
-
869
+ fmxDecorator.decoratedEntity = fmxDecoratedEntity;
870
+ initFQN(decorator, fmxDecorator);
606
871
  this.makeFamixIndexFileAnchor(decorator, fmxDecorator);
607
872
 
608
873
  this.famixRep.addElement(fmxDecorator);
@@ -620,10 +885,10 @@ export class EntityDictionary {
620
885
  * @returns The Famix model of the comment
621
886
  */
622
887
  public createFamixComment(comment: CommentRange, fmxScope: Famix.NamedEntity, isJSDoc: boolean): Famix.Comment {
623
- logger.debug(`> NOTE: creating comment ${comment.getText()} in scope ${fmxScope.getName()}.`);
888
+ logger.debug(`> NOTE: creating comment ${comment.getText()} in scope ${fmxScope.name}.`);
624
889
  const fmxComment = new Famix.Comment();
625
- fmxComment.setContainer(fmxScope); // adds comment to the container's comments collection
626
- fmxComment.setIsJSDoc(isJSDoc);
890
+ fmxComment.container = fmxScope; // adds comment to the container's comments collection
891
+ fmxComment.isJSDoc = isJSDoc;
627
892
 
628
893
  this.makeFamixIndexFileAnchor(comment, fmxComment);
629
894
 
@@ -640,19 +905,23 @@ export class EntityDictionary {
640
905
  * @param element A ts-morph element
641
906
  * @returns The Famix model of the type
642
907
  */
643
- public createOrGetFamixType(typeName: string, element: TypeAliasDeclaration | PropertyDeclaration | PropertySignature | MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration | FunctionDeclaration | FunctionExpression | ParameterDeclaration | VariableDeclaration | EnumMember): Famix.Type | Famix.PrimitiveType | Famix.ParameterizedType {
644
- let fmxType: Famix.Type | Famix.PrimitiveType | Famix.ParameterizedType;
908
+ public createOrGetFamixType(typeName: string, element: TypeDeclaration): Famix.Type | Famix.PrimitiveType | Famix.ParameterType {
909
+ let fmxType: Famix.Type | Famix.PrimitiveType | Famix.ParameterType;
645
910
  let isPrimitiveType = false;
646
- let isParameterizedType = false;
911
+ let isParameterType = false;
647
912
 
648
913
  logger.debug("Creating (or getting) type: '" + typeName + "' of element: " + element?.getText() + " of kind: " + element?.getKindName());
649
- let ancestor: Famix.ContainerEntity;
914
+ let ancestor: Famix.ContainerEntity | undefined = undefined;
650
915
  if (element !== undefined) {
651
916
  const typeAncestor = Helpers.findTypeAncestor(element);
917
+ if (!typeAncestor) {
918
+ throw new Error(`Ancestor not found for element ${element.getText()}.`);
919
+ }
652
920
  const ancestorFullyQualifiedName = FQNFunctions.getFQN(typeAncestor);
653
921
  ancestor = this.famixRep.getFamixEntityByFullyQualifiedName(ancestorFullyQualifiedName) as Famix.ContainerEntity;
654
922
  if (!ancestor) {
655
- throw new Error(`Ancestor ${ancestorFullyQualifiedName} not found.`);
923
+ logger.debug(`Ancestor ${FQNFunctions.getFQN(typeAncestor)} not found. Adding the new type.`);
924
+ ancestor = this.createOrGetFamixType(typeAncestor.getText(), typeAncestor as TypeDeclaration);
656
925
  }
657
926
  }
658
927
 
@@ -661,32 +930,35 @@ export class EntityDictionary {
661
930
  }
662
931
 
663
932
  if(!isPrimitiveType && typeName.includes("<") && typeName.includes(">") && !(typeName.includes("=>"))) {
664
- isParameterizedType = true;
933
+ isParameterType = true;
665
934
  }
666
935
 
667
936
  if (!this.fmxTypeMap.has(typeName)) {
668
937
  if (isPrimitiveType) {
669
938
  fmxType = new Famix.PrimitiveType();
670
- fmxType.setIsStub(true);
939
+ fmxType.isStub = true;
671
940
  }
672
- else if (isParameterizedType) {
673
- fmxType = new Famix.ParameterizedType();
941
+ else if (isParameterType) {
942
+ fmxType = new Famix.ParameterType();
674
943
  const parameterTypeNames = typeName.substring(typeName.indexOf("<") + 1, typeName.indexOf(">")).split(",").map(s => s.trim());
675
944
  const baseTypeName = typeName.substring(0, typeName.indexOf("<")).trim();
676
945
  parameterTypeNames.forEach(parameterTypeName => {
677
946
  const fmxParameterType = this.createOrGetFamixType(parameterTypeName, element);
678
- (fmxType as Famix.ParameterizedType).addArgument(fmxParameterType);
947
+ (fmxType as Famix.ParameterType).addArgument(fmxParameterType);
679
948
  });
680
949
  const fmxBaseType = this.createOrGetFamixType(baseTypeName, element);
681
- (fmxType as Famix.ParameterizedType).setBaseType(fmxBaseType);
950
+ (fmxType as Famix.ParameterType).baseType = fmxBaseType;
682
951
  }
683
952
  else {
684
953
  fmxType = new Famix.Type();
685
954
  }
686
955
 
687
- fmxType.setName(typeName);
688
- fmxType.setContainer(ancestor);
689
-
956
+ fmxType.name = typeName;
957
+ if (!ancestor) {
958
+ throw new Error(`Ancestor not found for type ${typeName}.`);
959
+ }
960
+ fmxType.container = ancestor;
961
+ initFQN(element, fmxType);
690
962
  this.makeFamixIndexFileAnchor(element, fmxType);
691
963
 
692
964
  this.famixRep.addElement(fmxType);
@@ -694,7 +966,12 @@ export class EntityDictionary {
694
966
  this.fmxTypeMap.set(typeName, fmxType);
695
967
  }
696
968
  else {
697
- fmxType = this.fmxTypeMap.get(typeName);
969
+ const result = this.fmxTypeMap.get(typeName);
970
+ if (result) {
971
+ fmxType = result;
972
+ } else {
973
+ throw new Error(`Famix type ${typeName} is not found in the Type map.`);
974
+ }
698
975
  }
699
976
 
700
977
  this.fmxElementObjectMap.set(fmxType,element);
@@ -709,15 +986,23 @@ export class EntityDictionary {
709
986
  */
710
987
  public createFamixAccess(node: Identifier, id: number): void {
711
988
  const fmxVar = this.famixRep.getFamixEntityById(id) as Famix.StructuralEntity;
989
+ if (!fmxVar) {
990
+ throw new Error(`Famix entity with id ${id} not found, for node ${node.getText()} in ${node.getSourceFile().getBaseName()} at line ${node.getStartLineNumber()}.`);
991
+ }
992
+
993
+ logger.debug(`Creating FamixAccess. Node: [${node.getKindName()}] '${node.getText()}' at line ${node.getStartLineNumber()} in ${node.getSourceFile().getBaseName()}, id: ${id} refers to fmxVar '${fmxVar.fullyQualifiedName}'.`);
994
+
712
995
  const nodeReferenceAncestor = Helpers.findAncestor(node);
713
996
  const ancestorFullyQualifiedName = FQNFunctions.getFQN(nodeReferenceAncestor);
714
- const accessor = this.famixRep.getFamixEntityByFullyQualifiedName(ancestorFullyQualifiedName) as Famix.ContainerEntity;
997
+ let accessor = this.famixRep.getFamixEntityByFullyQualifiedName(ancestorFullyQualifiedName) as Famix.ContainerEntity;
998
+ if (!accessor) {
999
+ logger.error(`Ancestor ${ancestorFullyQualifiedName} of kind ${nodeReferenceAncestor.getKindName()} not found.`);
1000
+ // accessor = this.createOrGetFamixType(ancestorFullyQualifiedName, nodeReferenceAncestor as TypeDeclaration);
1001
+ }
715
1002
 
716
1003
  const fmxAccess = new Famix.Access();
717
- fmxAccess.setAccessor(accessor);
718
- fmxAccess.setVariable(fmxVar);
719
-
720
- this.makeFamixIndexFileAnchor(node, fmxAccess);
1004
+ fmxAccess.accessor = accessor;
1005
+ fmxAccess.variable = fmxVar;
721
1006
 
722
1007
  this.famixRep.addElement(fmxAccess);
723
1008
 
@@ -739,12 +1024,10 @@ export class EntityDictionary {
739
1024
  const receiver = this.famixRep.getFamixEntityByFullyQualifiedName(receiverFullyQualifiedName) as Famix.NamedEntity;
740
1025
 
741
1026
  const fmxInvocation = new Famix.Invocation();
742
- fmxInvocation.setSender(sender);
743
- fmxInvocation.setReceiver(receiver);
1027
+ fmxInvocation.sender = sender;
1028
+ fmxInvocation.receiver = receiver;
744
1029
  fmxInvocation.addCandidate(fmxMethodOrFunction);
745
- fmxInvocation.setSignature(fmxMethodOrFunction.getSignature());
746
-
747
- this.makeFamixIndexFileAnchor(node, fmxInvocation);
1030
+ fmxInvocation.signature = fmxMethodOrFunction.signature;
748
1031
 
749
1032
  this.famixRep.addElement(fmxInvocation);
750
1033
 
@@ -761,19 +1044,25 @@ export class EntityDictionary {
761
1044
  // const clsName = cls.getName();
762
1045
  const classFullyQualifiedName = FQNFunctions.getFQN(cls);
763
1046
  logger.debug(`createFamixInheritance: classFullyQualifiedName: class fqn = ${classFullyQualifiedName}`);
764
- let subClass: Famix.Class | Famix.Interface;
1047
+ let subClass: Famix.Class | Famix.Interface | undefined;
765
1048
  if (cls instanceof ClassDeclaration) {
766
1049
  subClass = this.fmxClassMap.get(classFullyQualifiedName);
767
1050
  }
768
1051
  else {
769
1052
  subClass = this.fmxInterfaceMap.get(classFullyQualifiedName);
770
1053
  }
1054
+ if (!subClass) {
1055
+ throw new Error(`Subclass ${classFullyQualifiedName} not found in Class or Interface maps.`);
1056
+ }
771
1057
 
772
- let inhClassName: string;
1058
+ let inhClassName: string | undefined;
773
1059
  let inhClassFullyQualifiedName: string;
774
- let superClass: Famix.Class | Famix.Interface;
1060
+ let superClass: Famix.Class | Famix.Interface | undefined;
775
1061
  if (inhClass instanceof ClassDeclaration || inhClass instanceof InterfaceDeclaration) {
776
1062
  inhClassName = inhClass.getName();
1063
+ if (!inhClassName) {
1064
+ throw new Error(`Inherited class or interface name not found for ${inhClass.getText()}.`);
1065
+ }
777
1066
  inhClassFullyQualifiedName = FQNFunctions.getFQN(inhClass);
778
1067
  if (inhClass instanceof ClassDeclaration) {
779
1068
  superClass = this.fmxClassMap.get(inhClassFullyQualifiedName);
@@ -781,6 +1070,9 @@ export class EntityDictionary {
781
1070
  else {
782
1071
  superClass = this.fmxInterfaceMap.get(inhClassFullyQualifiedName);
783
1072
  }
1073
+ if (!superClass) {
1074
+ throw new Error(`Superclass ${classFullyQualifiedName} not found in Class or Interface maps.`);
1075
+ }
784
1076
  }
785
1077
  else {
786
1078
  // inhClass is an ExpressionWithTypeArguments
@@ -801,24 +1093,31 @@ export class EntityDictionary {
801
1093
 
802
1094
  this.fmxElementObjectMap.set(superClass,inhClass);
803
1095
 
804
- superClass.setName(inhClassName);
805
- superClass.setFullyQualifiedName(inhClassFullyQualifiedName);
806
- superClass.setIsStub(true);
1096
+ superClass.name = inhClassName;
1097
+ superClass.fullyQualifiedName = inhClassFullyQualifiedName;
1098
+ superClass.isStub = true;
807
1099
 
808
1100
  this.makeFamixIndexFileAnchor(inhClass, superClass);
809
1101
 
810
1102
  this.famixRep.addElement(superClass);
811
1103
  }
812
1104
 
813
- fmxInheritance.setSubclass(subClass);
814
- fmxInheritance.setSuperclass(superClass);
815
-
816
- this.makeFamixIndexFileAnchor(null, fmxInheritance);
1105
+ fmxInheritance.subclass = subClass;
1106
+ fmxInheritance.superclass = superClass;
817
1107
 
818
1108
  this.famixRep.addElement(fmxInheritance);
819
1109
 
820
- this.fmxElementObjectMap.set(fmxInheritance,null);
1110
+ // We don't map inheritance to the source code element because there are two elements (super, sub)
1111
+ // this.fmxElementObjectMap.set(fmxInheritance, null);
1112
+
1113
+ }
821
1114
 
1115
+ public createFamixImportClause(importedEntity: Famix.NamedEntity, importingEntity: Famix.Module) {
1116
+ const fmxImportClause = new Famix.ImportClause();
1117
+ fmxImportClause.importedEntity = importedEntity;
1118
+ fmxImportClause.importingEntity = importingEntity;
1119
+ importingEntity.addOutgoingImport(fmxImportClause);
1120
+ this.famixRep.addElement(fmxImportClause);
822
1121
  }
823
1122
 
824
1123
  /**
@@ -831,12 +1130,12 @@ export class EntityDictionary {
831
1130
  * @param isInExports A boolean indicating if the imported entity is in the exports
832
1131
  * @param isDefaultExport A boolean indicating if the imported entity is a default export
833
1132
  */
834
- public createFamixImportClause(importClauseInfo: {importDeclaration?: ImportDeclaration | ImportEqualsDeclaration, importer: SourceFile, moduleSpecifierFilePath: string, importElement: ImportSpecifier | Identifier, isInExports: boolean, isDefaultExport: boolean}): void {
835
- const {importDeclaration, importer, moduleSpecifierFilePath, importElement, isInExports, isDefaultExport} = importClauseInfo;
1133
+ public oldCreateFamixImportClause(importClauseInfo: {importDeclaration?: ImportDeclaration | ImportEqualsDeclaration, importerSourceFile: SourceFile, moduleSpecifierFilePath: string, importElement: ImportSpecifier | Identifier, isInExports: boolean, isDefaultExport: boolean}): void {
1134
+ const {importDeclaration, importerSourceFile: importer, moduleSpecifierFilePath, importElement, isInExports, isDefaultExport} = importClauseInfo;
836
1135
  logger.debug(`createFamixImportClause: Creating import clause:`);
837
1136
  const fmxImportClause = new Famix.ImportClause();
838
1137
 
839
- let importedEntity: Famix.NamedEntity | Famix.StructuralEntity;
1138
+ let importedEntity: Famix.NamedEntity | Famix.StructuralEntity | undefined = undefined;
840
1139
  let importedEntityName: string;
841
1140
 
842
1141
  const absolutePathProject = this.famixRep.getAbsolutePath();
@@ -844,10 +1143,12 @@ export class EntityDictionary {
844
1143
  const absolutePath = path.normalize(moduleSpecifierFilePath);
845
1144
  // convert the path and remove any windows backslashes introduced by path.normalize
846
1145
  const pathInProject: string = this.convertToRelativePath(absolutePath, absolutePathProject).replace(/\\/g, "/");
847
-
848
1146
  let pathName = "{" + pathInProject + "}.";
849
1147
 
850
1148
  // Named imports, e.g. import { ClassW } from "./complexExportModule";
1149
+
1150
+ // Start with simple import clause (without referring to the actual variable)
1151
+
851
1152
  if (importDeclaration instanceof ImportDeclaration
852
1153
  && importElement instanceof ImportSpecifier) {
853
1154
  importedEntityName = importElement.getName();
@@ -857,12 +1158,14 @@ export class EntityDictionary {
857
1158
  }
858
1159
  if (importedEntity === undefined) {
859
1160
  importedEntity = new Famix.NamedEntity();
860
- importedEntity.setName(importedEntityName);
1161
+ importedEntity.name = importedEntityName;
861
1162
  if (!isInExports) {
862
- importedEntity.setIsStub(true);
1163
+ importedEntity.isStub = true;
863
1164
  }
1165
+ importedEntity.fullyQualifiedName = pathName;
864
1166
  this.makeFamixIndexFileAnchor(importElement, importedEntity);
865
- importedEntity.setFullyQualifiedName(pathName);
1167
+ // must add entity to repository
1168
+ this.famixRep.addElement(importedEntity);
866
1169
  }
867
1170
  }
868
1171
  // handle import equals declarations, e.g. import myModule = require("./complexExportModule");
@@ -871,45 +1174,420 @@ export class EntityDictionary {
871
1174
  importedEntityName = importDeclaration?.getName();
872
1175
  pathName = pathName + importedEntityName;
873
1176
  importedEntity = new Famix.StructuralEntity();
874
- importedEntity.setName(importedEntityName);
1177
+ importedEntity.name = importedEntityName;
1178
+ initFQN(importDeclaration, importedEntity);
875
1179
  this.makeFamixIndexFileAnchor(importElement, importedEntity);
876
- importedEntity.setFullyQualifiedName(pathName);
877
- const anyType = this.createOrGetFamixType('any', undefined);
878
- (importedEntity as Famix.StructuralEntity).setDeclaredType(anyType);
1180
+ importedEntity.fullyQualifiedName = pathName;
1181
+ const anyType = this.createOrGetFamixType('any', importDeclaration);
1182
+ (importedEntity as Famix.StructuralEntity).declaredType = anyType;
879
1183
  } else { // default imports, e.g. import ClassW from "./complexExportModule";
880
1184
  importedEntityName = importElement.getText();
881
1185
  pathName = pathName + (isDefaultExport ? "defaultExport" : "namespaceExport");
882
1186
  importedEntity = new Famix.NamedEntity();
883
- importedEntity.setName(importedEntityName);
1187
+ importedEntity.name = importedEntityName;
1188
+ importedEntity.fullyQualifiedName = pathName;
884
1189
  this.makeFamixIndexFileAnchor(importElement, importedEntity);
885
- importedEntity.setFullyQualifiedName(pathName);
886
1190
  }
887
-
888
- this.famixRep.addElement(importedEntity);
1191
+ // I don't think it should be added to the repository if it exists already
1192
+ if (!isInExports) this.famixRep.addElement(importedEntity);
889
1193
  const importerFullyQualifiedName = FQNFunctions.getFQN(importer);
890
1194
  const fmxImporter = this.famixRep.getFamixEntityByFullyQualifiedName(importerFullyQualifiedName) as Famix.Module;
891
- fmxImportClause.setImportingEntity(fmxImporter);
892
- fmxImportClause.setImportedEntity(importedEntity);
1195
+ fmxImportClause.importingEntity = fmxImporter;
1196
+ fmxImportClause.importedEntity = importedEntity;
893
1197
  if (importDeclaration instanceof ImportEqualsDeclaration) {
894
- fmxImportClause.setModuleSpecifier(importDeclaration?.getModuleReference().getText() as string);
1198
+ fmxImportClause.moduleSpecifier = importDeclaration?.getModuleReference().getText() as string;
895
1199
  } else {
896
- fmxImportClause.setModuleSpecifier(importDeclaration?.getModuleSpecifierValue() as string);
1200
+ fmxImportClause.moduleSpecifier = importDeclaration?.getModuleSpecifierValue() as string;
897
1201
  }
898
1202
 
899
- logger.debug(`createFamixImportClause: ${fmxImportClause.getImportedEntity()?.getName()} (of type ${
900
- Helpers.getSubTypeName(fmxImportClause.getImportedEntity())}) is imported by ${fmxImportClause.getImportingEntity()?.getName()}`);
901
-
902
- // make an index file anchor for the import clause
903
- this.makeFamixIndexFileAnchor(importDeclaration, fmxImportClause);
1203
+ logger.debug(`createFamixImportClause: ${fmxImportClause.importedEntity?.name} (of type ${
1204
+ Helpers.getSubTypeName(fmxImportClause.importedEntity)}) is imported by ${fmxImportClause.importingEntity?.name}`);
904
1205
 
905
1206
  fmxImporter.addOutgoingImport(fmxImportClause);
906
1207
 
907
1208
  this.famixRep.addElement(fmxImportClause);
908
1209
 
909
- this.fmxElementObjectMap.set(fmxImportClause,importDeclaration);
1210
+ if (importDeclaration) this.fmxElementObjectMap.set(fmxImportClause, importDeclaration);
1211
+ }
1212
+
1213
+ /**
1214
+ * Creates a Famix Arrow Function
1215
+ * @param arrowExpression An Expression
1216
+ * @returns The Famix model of the variable
1217
+ */
1218
+ public createFamixArrowFunction(arrowExpression: Expression, currentCC: { [key: string]: number } ): Famix.ArrowFunction | Famix.ParametricArrowFunction {
1219
+
1220
+ let fmxArrowFunction: Famix.ArrowFunction | Famix.ParametricArrowFunction;
1221
+
1222
+ const arrowFunction = arrowExpression.asKindOrThrow(SyntaxKind.ArrowFunction);
1223
+
1224
+ const isGeneric = arrowFunction.getTypeParameters().length > 0;
1225
+
1226
+ if (isGeneric) {
1227
+ fmxArrowFunction = new Famix.ParametricArrowFunction();
1228
+ }
1229
+ else {
1230
+ fmxArrowFunction = new Famix.ArrowFunction();
1231
+ }
1232
+
1233
+ // Get the parent of the arrow function (the variable declaration)
1234
+ const parent = arrowFunction.getParentIfKind(SyntaxKind.VariableDeclaration);
1235
+ let functionName = '(NO_NAME)';
1236
+
1237
+ if (parent && parent instanceof VariableDeclaration) {
1238
+ // Get the name of the variable
1239
+ functionName = parent.getName();
1240
+ }
1241
+
1242
+ if (functionName) {
1243
+ fmxArrowFunction.name = functionName;
1244
+ }
1245
+ else {
1246
+ fmxArrowFunction.name = "anonymous";
1247
+ }
1248
+
1249
+ // Signature of an arrow function is (parameters) => return_type
1250
+ const parametersSignature = arrowFunction.getParameters().map(p => p.getText()).join(", ");
1251
+ const returnTypeSignature = arrowFunction.getReturnType().getText();
1252
+ fmxArrowFunction.signature = `(${parametersSignature}) => ${returnTypeSignature}`;
1253
+ fmxArrowFunction.cyclomaticComplexity = currentCC[fmxArrowFunction.name];
1254
+
1255
+ let functionTypeName = this.UNKNOWN_VALUE;
1256
+ try {
1257
+ functionTypeName = arrowFunction.getReturnType().getText().trim();
1258
+ } catch (error) {
1259
+ logger.error(`> WARNING: got exception ${error}. Failed to get usable name for return type of function: ${functionName}. Continuing...`);
1260
+ }
1261
+
1262
+ const fmxType = this.createOrGetFamixType(functionTypeName, arrowFunction as unknown as FunctionDeclaration);
1263
+ fmxArrowFunction.declaredType = fmxType;
1264
+ fmxArrowFunction.numberOfLinesOfCode = arrowFunction.getEndLineNumber() - arrowFunction.getStartLineNumber();
1265
+ const parameters = arrowFunction.getParameters();
1266
+ fmxArrowFunction.numberOfParameters = parameters.length;
1267
+ fmxArrowFunction.numberOfStatements = arrowFunction.getStatements().length;
1268
+ initFQN(arrowExpression as unknown as TSMorphObjectType, fmxArrowFunction);
1269
+ this.makeFamixIndexFileAnchor(arrowExpression as unknown as TSMorphObjectType, fmxArrowFunction);
1270
+ this.famixRep.addElement(fmxArrowFunction);
1271
+ this.fmxElementObjectMap.set(fmxArrowFunction,arrowFunction as unknown as TSMorphObjectType);
1272
+
1273
+ return fmxArrowFunction;
1274
+ }
1275
+
1276
+ /**
1277
+ * Creates a Famix concretisation
1278
+ * @param cls A class
1279
+ * @returns The Famix model of the concretisation
1280
+ */
1281
+ public createFamixConcretisation(conEntity : Famix.ParametricClass | Famix.ParametricInterface | Famix.ParametricFunction | Famix.ParametricMethod ,genEntity : Famix.ParametricClass | Famix.ParametricInterface | Famix.ParametricFunction | Famix.ParametricMethod): Famix.Concretisation {
1282
+
1283
+ const fmxConcretisation : Famix.Concretisation = new Famix.Concretisation();
1284
+
1285
+ fmxConcretisation.concreteEntity = conEntity;
1286
+ fmxConcretisation.genericEntity = genEntity;
1287
+ // this.fmxElementObjectMap.set(fmxConcretisation,null);
1288
+ this.famixRep.addElement(fmxConcretisation);
1289
+ const parameterConcretisation = this.createFamixParameterConcretisation(fmxConcretisation);
1290
+
1291
+ return fmxConcretisation;
1292
+ }
1293
+
1294
+ /**
1295
+ * Creates a Famix concretisation
1296
+ * @param concretisation A FamixConcretisation
1297
+ * @returns The Famix model of the ParameterConcrestisation
1298
+ */
1299
+ public createFamixParameterConcretisation(concretisation: Famix.Concretisation): Famix.ParameterConcretisation | undefined{
1300
+ const conClass = concretisation.concreteEntity;
1301
+ const genClass = concretisation.genericEntity;
1302
+ logger.debug(`Creating parameter concretisation between ${conClass.fullyQualifiedName} and ${genClass.fullyQualifiedName}`);
1303
+ const parameterConcretisations = this.famixRep._getAllEntitiesWithType("ParameterConcretisation") as Set<Famix.ParameterConcretisation>;
1304
+ const concreteParameters = conClass.concreteParameters;
1305
+ const genericParameters = genClass.genericParameters;
1306
+
1307
+ let conClassTypeParametersIterator = concreteParameters.values();
1308
+ let genClassTypeParametersIterator = genericParameters.values();
1309
+ let fmxParameterConcretisation : Famix.ParameterConcretisation | undefined = undefined;
1310
+
1311
+ for (let i = 0; i < genericParameters.size; i++) {
1312
+ const conClassTypeParameter = conClassTypeParametersIterator.next().value as Famix.ParameterType;
1313
+ const genClassTypeParameter = genClassTypeParametersIterator.next().value as Famix.ParameterType;
1314
+ let createParameterConcretisation : boolean = true;
1315
+ if(conClassTypeParameter && genClassTypeParameter && conClassTypeParameter.name != genClassTypeParameter.name){
1316
+ parameterConcretisations.forEach((param : Famix.ParameterConcretisation) => {
1317
+ if (conClassTypeParameter.name == param.concreteParameter.name && genClassTypeParameter.name == param.genericParameter.name) {
1318
+ createParameterConcretisation = false;
1319
+ fmxParameterConcretisation = param;
1320
+ }
1321
+ })
1322
+ if (createParameterConcretisation) {
1323
+ fmxParameterConcretisation = new Famix.ParameterConcretisation();
1324
+ fmxParameterConcretisation.genericParameter = genClassTypeParameter;
1325
+ fmxParameterConcretisation.concreteParameter = conClassTypeParameter;
1326
+ fmxParameterConcretisation.addConcretisation(concretisation);
1327
+ // this.fmxElementObjectMap.set(fmxParameterConcretisation,null);
1328
+ } else {
1329
+ if (!fmxParameterConcretisation) {
1330
+ throw new Error(`fmxParameterConcretisation was undefined for concretisation with generic parameter ${genClassTypeParameter.name} and concrete parameter ${conClassTypeParameter.name}`);
1331
+ }
1332
+ fmxParameterConcretisation.addConcretisation(concretisation);
1333
+ }
1334
+ this.famixRep.addElement(fmxParameterConcretisation);
1335
+ }
1336
+ }
1337
+ if (!fmxParameterConcretisation) {
1338
+ logger.error(`fmxParameterConcretisation was undefined for concretisation with concrete entity ${conClass.fullyQualifiedName} and generic entity ${genClass.fullyQualifiedName}`);
1339
+ }
1340
+ return fmxParameterConcretisation;
1341
+
1342
+ }
1343
+
1344
+ /**
1345
+ * Creates a Famix concretisation between two classes or two interfaces
1346
+ * @param element A class or an Interface
1347
+ */
1348
+ public createFamixConcretisationClassOrInterfaceSpecialisation(element: ClassDeclaration | InterfaceDeclaration){
1349
+
1350
+ const superEntity = element.getExtends();
1351
+ let superEntityArray;
1352
+ if (superEntity){
1353
+ superEntityArray = Array.isArray(superEntity) ? superEntity : [superEntity];
1354
+ }
1355
+ if (superEntityArray && superEntityArray.length > 0) {
1356
+ superEntityArray.forEach(entity => {
1357
+ let entityIsGeneric;
1358
+ const superEntitySymbol = entity.getExpression().getSymbolOrThrow();
1359
+ let superEntityDeclaration;
1360
+ if (superEntity instanceof ExpressionWithTypeArguments) {
1361
+ superEntityDeclaration = superEntitySymbol.getDeclarations()[0].asKind(ts.SyntaxKind.ClassDeclaration);
1362
+ } else {
1363
+ superEntityDeclaration = superEntitySymbol.getDeclarations()[0].asKind(ts.SyntaxKind.InterfaceDeclaration);
1364
+ }
1365
+ if (superEntityDeclaration) {
1366
+ entityIsGeneric = superEntityDeclaration.getTypeParameters().length > 0;
1367
+ }
1368
+ if (entityIsGeneric) {
1369
+ let EntityDeclaration;
1370
+ let genEntity;
1371
+ if (superEntity instanceof ExpressionWithTypeArguments) {
1372
+ EntityDeclaration = entity.getExpression().getSymbol()?.getDeclarations()[0] as ClassDeclaration;
1373
+ genEntity = this.createOrGetFamixClass(EntityDeclaration) as Famix.ParametricClass;
1374
+ } else {
1375
+ EntityDeclaration = entity.getExpression().getSymbol()?.getDeclarations()[0] as InterfaceDeclaration;
1376
+ genEntity = this.createOrGetFamixInterface(EntityDeclaration) as Famix.ParametricInterface;
1377
+ }
1378
+ const genParams = EntityDeclaration.getTypeParameters().map((param) => param.getText());
1379
+ const args = element.getHeritageClauses()[0].getTypeNodes()[0].getTypeArguments()
1380
+ const conParams = element.getHeritageClauses()[0].getTypeNodes()[0].getTypeArguments().map((param) => param.getText());
1381
+ if (!Helpers.arraysAreEqual(conParams,genParams)) {
1382
+ let conEntity;
1383
+ conEntity = this.createOrGetFamixConcreteElement(genEntity,EntityDeclaration,args);
1384
+ const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation") as Set<Famix.Concretisation>;
1385
+ let createConcretisation : boolean = true;
1386
+ concretisations.forEach((conc : Famix.Concretisation) => {
1387
+ if (genEntity.fullyQualifiedName == conc.genericEntity.fullyQualifiedName && conc.concreteEntity.fullyQualifiedName == conEntity.fullyQualifiedName){
1388
+ createConcretisation = false;
1389
+ }
1390
+ });
1391
+
1392
+ if (createConcretisation) {
1393
+ const fmxConcretisation : Famix.Concretisation = this.createFamixConcretisation(conEntity,genEntity);
1394
+ }
1395
+ }
1396
+ }
1397
+ });
1398
+ }
1399
+ // TODO: This function seems unfinished
1400
+ }
1401
+
1402
+
1403
+ /**
1404
+ * Creates a Famix concretisation between a class and its instanciations
1405
+ * @param cls A class
1406
+ */
1407
+ public createFamixConcretisationGenericInstantiation(cls: ClassDeclaration){
1408
+
1409
+ const isGeneric = cls.getTypeParameters().length > 0;
1410
+ if (isGeneric) {
1411
+ const instances = cls.getSourceFile().getDescendantsOfKind(ts.SyntaxKind.NewExpression)
1412
+ .filter(newExpr => {
1413
+ const expression = newExpr.getExpression();
1414
+ return expression.getText() === cls.getName();
1415
+ });
1416
+
1417
+ instances.forEach(instance => {
1418
+ const instanceIsGeneric = instance.getTypeArguments().length > 0;
1419
+ if (instanceIsGeneric) {
1420
+ const conParams = instance.getTypeArguments().map((param) => param.getText());
1421
+ const genEntity = this.createOrGetFamixClass(cls) as Famix.ParametricClass;
1422
+ const genParams = cls.getTypeParameters().map((param) => param.getText());
1423
+ if (!Helpers.arraysAreEqual(conParams,genParams)) {
1424
+ let conEntity;
1425
+ conEntity = this.createOrGetFamixConcreteElement(genEntity,cls,instance.getTypeArguments());
1426
+ const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation") as Set<Famix.Concretisation>;
1427
+ let createConcretisation : boolean = true;
1428
+ concretisations.forEach((conc : Famix.Concretisation) => {
1429
+ if (genEntity.fullyQualifiedName == conc.genericEntity.fullyQualifiedName && conc.concreteEntity.fullyQualifiedName == conEntity.fullyQualifiedName){
1430
+ createConcretisation = false;
1431
+ }
1432
+ });
1433
+
1434
+ if (createConcretisation) {
1435
+ const fmxConcretisation : Famix.Concretisation = this.createFamixConcretisation(conEntity,genEntity);
1436
+ }
1437
+ }
1438
+ }
1439
+ })
1440
+ }
1441
+ // TODO: This function seems unfinished
1442
+ }
1443
+
1444
+ /**
1445
+ * Creates a Famix concretisation between a class and its instanciations
1446
+ * @param func A function
1447
+ */
1448
+ public createFamixConcretisationFunctionInstantiation(element: FunctionDeclaration | MethodDeclaration){
1449
+ const isGeneric = element.getTypeParameters().length > 0;
1450
+ if (isGeneric) {
1451
+ const genParams = element.getTypeParameters().map(param => param.getText());
1452
+ const uses = element.findReferencesAsNodes();
1453
+ uses.forEach(usage => {
1454
+ let currentNode: Node | undefined = usage;
1455
+
1456
+ while (currentNode) {
1457
+ if (currentNode.getKind() === SyntaxKind.CallExpression) {
1458
+ const callExpression = currentNode.asKind(SyntaxKind.CallExpression);
1459
+ if (!callExpression) {
1460
+ throw new Error(`CallExpression not found for ${currentNode.getText()}`);
1461
+ }
1462
+ const instanceIsGeneric = callExpression.getTypeArguments().length > 0;
1463
+ if (instanceIsGeneric) {
1464
+ const args = callExpression.getTypeArguments();
1465
+ const conParams = callExpression.getTypeArguments().map(param => param.getText());
1466
+ if (!Helpers.arraysAreEqual(conParams,genParams)) {
1467
+ let genElement;
1468
+ if(element instanceof FunctionDeclaration){
1469
+ genElement = this.createOrGetFamixFunction(element, {}) as Famix.ParametricFunction;
1470
+ } else {
1471
+ genElement = this.createOrGetFamixMethod(element, {}) as Famix.ParametricMethod;
1472
+ }
1473
+ let concElement;
1474
+ concElement = this.createOrGetFamixConcreteElement(genElement,element,args);
1475
+ const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation") as Set<Famix.Concretisation>;
1476
+ let createConcretisation : boolean = true;
1477
+ concretisations.forEach((conc : Famix.Concretisation) => {
1478
+ if (genElement.fullyQualifiedName == conc.genericEntity.fullyQualifiedName && conc.concreteEntity.fullyQualifiedName == concElement.fullyQualifiedName){
1479
+ createConcretisation = false;
1480
+ }
1481
+ });
1482
+
1483
+ if (createConcretisation) {
1484
+ const fmxConcretisation : Famix.Concretisation = this.createFamixConcretisation(concElement,genElement);
1485
+ }
1486
+ }
1487
+ }
1488
+ break;
1489
+ }
1490
+ // Remonter à l'élément parent (utile si le nœud de référence est un enfant)
1491
+ currentNode = currentNode.getParent();
1492
+ }
1493
+ });
1494
+ }
1495
+ }
1496
+
1497
+ /**
1498
+ * Creates a Famix concretisation between a class and an interface
1499
+ * @param cls A class
1500
+ */
1501
+ public createFamixConcretisationInterfaceClass(cls: ClassDeclaration){
1502
+
1503
+ const superInterfaces = cls.getImplements();
1504
+ superInterfaces.forEach(interfaceType => {
1505
+ const interfaceIsGeneric = interfaceType.getTypeArguments().length>0;
1506
+ if (interfaceIsGeneric) {
1507
+ const interfaceDeclaration = interfaceType.getExpression().getSymbol()?.getDeclarations()[0] as InterfaceDeclaration;
1508
+ const genParams = interfaceDeclaration.getTypeParameters().map((param) => param.getText());
1509
+ const conParams = cls.getHeritageClauses()[0].getTypeNodes()[0].getTypeArguments().map((param) => param.getText());
1510
+ const args = cls.getHeritageClauses()[0].getTypeNodes()[0].getTypeArguments();
1511
+ if (!Helpers.arraysAreEqual(conParams,genParams)) {
1512
+ const genInterface = this.createOrGetFamixInterface(interfaceDeclaration) as Famix.ParametricInterface;
1513
+ const conInterface = this.createOrGetFamixConcreteElement(genInterface,interfaceDeclaration,args);
1514
+ const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation") as Set<Famix.Concretisation>;
1515
+ let createConcretisation : boolean = true;
1516
+ concretisations.forEach((conc : Famix.Concretisation) => {
1517
+ if (genInterface.fullyQualifiedName == conc.genericEntity.fullyQualifiedName && conc.concreteEntity.fullyQualifiedName == conInterface.fullyQualifiedName){
1518
+ createConcretisation = false;
1519
+ }
1520
+ });
1521
+
1522
+ if (createConcretisation) {
1523
+ const fmxConcretisation : Famix.Concretisation = this.createFamixConcretisation(conInterface,genInterface);
1524
+ }
1525
+ }
1526
+ }
1527
+ });
1528
+ }
1529
+
1530
+ /**
1531
+ * Creates a Famix concretisation between an interface and a Type
1532
+ * @param element A variable or a function
1533
+ * @param inter An interface
1534
+ */
1535
+ public createFamixConcretisationTypeInstanciation(element: InterfaceDeclaration | ClassDeclaration){
1536
+
1537
+ const isGeneric = element.getTypeParameters().length > 0;
1538
+ if (isGeneric) {
1539
+ const genParams = element.getTypeParameters().map(param => param.getText());
1540
+ const uses = element.findReferencesAsNodes();
1541
+ uses.forEach(use => {
1542
+ let parentNode = use.getParent();
1543
+ while (parentNode) {
1544
+ if (parentNode.getKind() === SyntaxKind.TypeReference) {
1545
+ const typeReferenceNode = parentNode.asKind(SyntaxKind.TypeReference);
1546
+ if (!typeReferenceNode) {
1547
+ throw new Error(`TypeReferenceNode not found for ${parentNode.getText()}`);
1548
+ }
1549
+ const typeReferenceNodeIsGeneric = typeReferenceNode.getTypeArguments().length > 0;
1550
+ if (typeReferenceNodeIsGeneric) {}
1551
+ const args = typeReferenceNode.getTypeArguments();
1552
+ const conParams = typeReferenceNode.getTypeArguments().map(param => param.getText());
1553
+ if (!Helpers.arraysAreEqual(conParams,genParams)) {
1554
+ let genElement;
1555
+ if(element instanceof ClassDeclaration){
1556
+ genElement = this.createOrGetFamixClass(element) as Famix.ParametricClass;
1557
+ } else {
1558
+ genElement = this.createOrGetFamixInterface(element) as Famix.ParametricInterface;
1559
+ }
1560
+ let concElement;
1561
+ concElement = this.createOrGetFamixConcreteElement(genElement,element,args);
1562
+ const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation") as Set<Famix.Concretisation>;
1563
+ let createConcretisation : boolean = true;
1564
+ concretisations.forEach((conc : Famix.Concretisation) => {
1565
+ if (genElement.fullyQualifiedName == conc.genericEntity.fullyQualifiedName && conc.concreteEntity.fullyQualifiedName == concElement.fullyQualifiedName){
1566
+ createConcretisation = false;
1567
+ }
1568
+ });
1569
+
1570
+ if (createConcretisation) {
1571
+ const fmxConcretisation : Famix.Concretisation = this.createFamixConcretisation(concElement,genElement);
1572
+ }
1573
+ }
1574
+ break;
1575
+ }
1576
+ parentNode = parentNode.getParent();
1577
+ }
1578
+ });
1579
+ }
910
1580
  }
911
1581
 
912
1582
  public convertToRelativePath(absolutePath: string, absolutePathProject: string) {
913
1583
  return absolutePath.replace(absolutePathProject, "").slice(1);
914
1584
  }
915
- }
1585
+ }
1586
+ function initFQN(sourceElement: TSMorphObjectType, famixElement: Famix.SourcedEntity) {
1587
+ if (!(sourceElement instanceof CommentRange)) {
1588
+ const fqn = FQNFunctions.getFQN(sourceElement);
1589
+ logger.debug("Setting fully qualified name for " + famixElement.getJSON() + " to " + fqn);
1590
+ (famixElement as Famix.NamedEntity).fullyQualifiedName = fqn;
1591
+ }
1592
+ }
1593
+