ts2famix 1.4.0 → 2.0.0

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 (119) hide show
  1. package/README.md +1 -1
  2. package/dist/analyze.js +6 -3
  3. package/dist/analyze_functions/process_functions.js +248 -100
  4. package/dist/famix2puml.js +1 -0
  5. package/dist/famix_functions/EntityDictionary.js +661 -155
  6. package/dist/famix_functions/helpers_creation.js +26 -6
  7. package/dist/fqn.js +156 -69
  8. package/dist/lib/famix/src/famix_JSON_exporter.js +1 -0
  9. package/dist/lib/famix/src/famix_base_element.js +1 -0
  10. package/dist/lib/famix/src/famix_repository.js +9 -8
  11. package/dist/lib/famix/src/index.js +1 -0
  12. package/dist/lib/famix/src/model/famix/access.js +3 -2
  13. package/dist/lib/famix/src/model/famix/accessor.js +1 -0
  14. package/dist/lib/famix/src/model/famix/alias.js +2 -1
  15. package/dist/lib/famix/src/model/famix/arrowFunction.js +17 -0
  16. package/dist/lib/famix/src/model/famix/behavioral_entity.js +13 -15
  17. package/dist/lib/famix/src/model/famix/class.js +1 -0
  18. package/dist/lib/famix/src/model/famix/comment.js +2 -1
  19. package/dist/lib/famix/src/model/famix/concretisation.js +31 -0
  20. package/dist/lib/famix/src/model/famix/container_entity.js +7 -6
  21. package/dist/lib/famix/src/model/famix/decorator.js +2 -1
  22. package/dist/lib/famix/src/model/famix/entity.js +1 -0
  23. package/dist/lib/famix/src/model/famix/enum.js +2 -1
  24. package/dist/lib/famix/src/model/famix/enum_value.js +2 -1
  25. package/dist/lib/famix/src/model/famix/function.js +1 -0
  26. package/dist/lib/famix/src/model/famix/implicit_variable.js +1 -0
  27. package/dist/lib/famix/src/model/famix/import_clause.js +5 -3
  28. package/dist/lib/famix/src/model/famix/index.js +18 -11
  29. package/dist/lib/famix/src/model/famix/indexed_file_anchor.js +3 -2
  30. package/dist/lib/famix/src/model/famix/inheritance.js +3 -2
  31. package/dist/lib/famix/src/model/famix/interface.js +2 -1
  32. package/dist/lib/famix/src/model/famix/invocation.js +3 -2
  33. package/dist/lib/famix/src/model/famix/method.js +2 -1
  34. package/dist/lib/famix/src/model/famix/module.js +53 -0
  35. package/dist/lib/famix/src/model/famix/named_entity.js +4 -3
  36. package/dist/lib/famix/src/model/famix/parameter.js +2 -1
  37. package/dist/lib/famix/src/model/famix/parameterConcretisation.js +44 -0
  38. package/dist/lib/famix/src/model/famix/parameter_type.js +22 -1
  39. package/dist/lib/famix/src/model/famix/parametric_arrow_function.js +31 -0
  40. package/dist/lib/famix/src/model/famix/parametric_class.js +44 -0
  41. package/dist/lib/famix/src/model/famix/parametric_function.js +31 -0
  42. package/dist/lib/famix/src/model/famix/parametric_interface.js +44 -0
  43. package/dist/lib/famix/src/model/famix/parametric_method.js +31 -0
  44. package/dist/lib/famix/src/model/famix/primitive_type.js +1 -0
  45. package/dist/lib/famix/src/model/famix/property.js +91 -9
  46. package/dist/lib/famix/src/model/famix/reference.js +3 -2
  47. package/dist/lib/famix/src/model/famix/scoping_entity.js +12 -10
  48. package/dist/lib/famix/src/model/famix/script_entity.js +1 -2
  49. package/dist/lib/famix/src/model/famix/source_anchor.js +1 -0
  50. package/dist/lib/famix/src/model/famix/source_language.js +1 -1
  51. package/dist/lib/famix/src/model/famix/sourced_entity.js +2 -1
  52. package/dist/lib/famix/src/model/famix/structural_entity.js +1 -0
  53. package/dist/lib/famix/src/model/famix/text_anchor.js +1 -0
  54. package/dist/lib/famix/src/model/famix/type.js +6 -4
  55. package/dist/lib/famix/src/model/famix/variable.js +1 -0
  56. package/dist/lib/ts-complex/cyclomatic-service.js +2 -2
  57. package/dist/ts2famix-cli-wrapper.js +16 -0
  58. package/dist/ts2famix-cli.js +8 -1
  59. package/dist/ts2famix-tsconfig.js +1 -0
  60. package/doc-uml/famix-typescript-model.puml +559 -0
  61. package/doc-uml/famix-typescript-model.svg +1 -0
  62. package/jest.config.json +2 -1
  63. package/package.json +10 -10
  64. package/src/analyze.ts +25 -22
  65. package/src/analyze_functions/process_functions.ts +272 -96
  66. package/src/famix_functions/EntityDictionary.ts +731 -182
  67. package/src/famix_functions/helpers_creation.ts +28 -2
  68. package/src/fqn.ts +132 -10
  69. package/src/lib/famix/src/famix_repository.ts +9 -9
  70. package/src/lib/famix/src/model/famix/access.ts +2 -2
  71. package/src/lib/famix/src/model/famix/alias.ts +1 -1
  72. package/src/lib/famix/src/model/famix/arrowFunction.ts +15 -0
  73. package/src/lib/famix/src/model/famix/behavioral_entity.ts +12 -19
  74. package/src/lib/famix/src/model/famix/comment.ts +1 -1
  75. package/src/lib/famix/src/model/famix/concretisation.ts +42 -0
  76. package/src/lib/famix/src/model/famix/container_entity.ts +6 -6
  77. package/src/lib/famix/src/model/famix/decorator.ts +1 -1
  78. package/src/lib/famix/src/model/famix/enum.ts +1 -1
  79. package/src/lib/famix/src/model/famix/enum_value.ts +1 -1
  80. package/src/lib/famix/src/model/famix/import_clause.ts +4 -3
  81. package/src/lib/famix/src/model/famix/index.ts +8 -5
  82. package/src/lib/famix/src/model/famix/indexed_file_anchor.ts +2 -2
  83. package/src/lib/famix/src/model/famix/inheritance.ts +3 -4
  84. package/src/lib/famix/src/model/famix/interface.ts +1 -1
  85. package/src/lib/famix/src/model/famix/invocation.ts +2 -2
  86. package/src/lib/famix/src/model/famix/method.ts +1 -1
  87. package/src/lib/famix/src/model/famix/module.ts +67 -1
  88. package/src/lib/famix/src/model/famix/named_entity.ts +3 -3
  89. package/src/lib/famix/src/model/famix/parameter.ts +1 -1
  90. package/src/lib/famix/src/model/famix/parameterConcretisation.ts +54 -0
  91. package/src/lib/famix/src/model/famix/parameter_type.ts +33 -6
  92. package/src/lib/famix/src/model/famix/parametric_arrow_function.ts +32 -0
  93. package/src/lib/famix/src/model/famix/parametric_class.ts +49 -0
  94. package/src/lib/famix/src/model/famix/parametric_function.ts +32 -0
  95. package/src/lib/famix/src/model/famix/parametric_interface.ts +49 -0
  96. package/src/lib/famix/src/model/famix/parametric_method.ts +32 -0
  97. package/src/lib/famix/src/model/famix/property.ts +109 -11
  98. package/src/lib/famix/src/model/famix/reference.ts +2 -2
  99. package/src/lib/famix/src/model/famix/scoping_entity.ts +12 -11
  100. package/src/lib/famix/src/model/famix/script_entity.ts +0 -2
  101. package/src/lib/famix/src/model/famix/source_language.ts +0 -1
  102. package/src/lib/famix/src/model/famix/sourced_entity.ts +1 -1
  103. package/src/lib/famix/src/model/famix/type.ts +5 -4
  104. package/src/ts2famix-cli-wrapper.ts +17 -0
  105. package/src/ts2famix-cli.ts +7 -1
  106. package/tsconfig.json +5 -5
  107. package/dist/lib/famix/src/model/famix/association.js +0 -36
  108. package/dist/lib/famix/src/model/famix/namespace.js +0 -24
  109. package/dist/lib/famix/src/model/famix/parameterizable_class.js +0 -30
  110. package/dist/lib/famix/src/model/famix/parameterizable_interface.js +0 -30
  111. package/dist/lib/famix/src/model/famix/parameterized_type.js +0 -36
  112. package/doc-uml/metamodel-full.svg +0 -1
  113. package/doc-uml/metamodel.svg +0 -1
  114. package/plantuml.jar +0 -0
  115. package/src/lib/famix/src/model/famix/association.ts +0 -44
  116. package/src/lib/famix/src/model/famix/namespace.ts +0 -28
  117. package/src/lib/famix/src/model/famix/parameterizable_class.ts +0 -31
  118. package/src/lib/famix/src/model/famix/parameterizable_interface.ts +0 -31
  119. package/src/lib/famix/src/model/famix/parameterized_type.ts +0 -40
@@ -1,4 +1,5 @@
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";
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";
2
3
  import * as Famix from "../lib/famix/src/model/famix";
3
4
  import { logger, config } from "../analyze";
4
5
  import GraphemeSplitter from "grapheme-splitter";
@@ -6,24 +7,27 @@ import * as Helpers from "./helpers_creation";
6
7
  import * as FQNFunctions from "../fqn";
7
8
  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;
15
+
12
16
  export class EntityDictionary {
13
17
 
14
18
  public famixRep = new FamixRepository();
15
19
  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
20
+ private fmxClassMap = new Map<string, Famix.Class | Famix.ParametricClass>(); // Maps the fully qualified class names to their Famix model
21
+ private fmxInterfaceMap = new Map<string, Famix.Interface | Famix.ParametricInterface>(); // Maps the interface names to their Famix model
22
+ private fmxModuleMap = new Map<string, Famix.Module>(); // Maps the namespace names to their Famix model
19
23
  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
24
+ private fmxTypeMap = new Map<string, Famix.Type | Famix.PrimitiveType | Famix.ParameterType>(); // Maps the type names to their Famix model
25
+ private fmxFunctionAndMethodMap = new Map<string, Famix.Function | Famix.ParametricFunction | Famix.Method | Famix.ParametricMethod> // Maps the function names to their Famix model
21
26
  private UNKNOWN_VALUE = '(unknown due to parsing error)'; // The value to use when a name is not usable
22
27
  public fmxElementObjectMap = new Map<Famix.Entity,TSMorphObjectType>();
23
28
 
24
29
  constructor() {
25
- this.famixRep.setFmxElementObjectMap(this.fmxElementObjectMap);
26
-
30
+ this.famixRep.setFmxElementObjectMap(this.fmxElementObjectMap);
27
31
  }
28
32
 
29
33
  /**
@@ -35,7 +39,7 @@ export class EntityDictionary {
35
39
  logger.debug("making index file anchor for '" + sourceElement?.getText() + "' with famixElement " + famixElement.getJSON());
36
40
  const fmxIndexFileAnchor = new Famix.IndexedFileAnchor();
37
41
  fmxIndexFileAnchor.setElement(famixElement);
38
- this.fmxElementObjectMap.set(famixElement,sourceElement);
42
+ this.fmxElementObjectMap.set(famixElement, sourceElement);
39
43
 
40
44
  if (sourceElement !== null) {
41
45
  const absolutePathProject = this.famixRep.getAbsolutePath();
@@ -102,8 +106,10 @@ export class EntityDictionary {
102
106
  fmxIndexFileAnchor.setEndLine(sourceLineEnd);
103
107
  }
104
108
 
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));
109
+ 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)) {
110
+ const fqn = FQNFunctions.getFQN(sourceElement);
111
+ logger.debug("Setting fully qualified name for " + famixElement.getJSON() + " to " + fqn);
112
+ (famixElement as Famix.NamedEntity).setFullyQualifiedName(fqn);
107
113
  }
108
114
  } else {
109
115
  // sourceElement is null
@@ -123,13 +129,13 @@ export class EntityDictionary {
123
129
  * @returns The Famix model of the source file
124
130
  */
125
131
  public createOrGetFamixFile(f: SourceFile, isModule: boolean): Famix.ScriptEntity | Famix.Module {
126
- let fmxFile: Famix.ScriptEntity | Famix.Module;
132
+ let fmxFile: Famix.ScriptEntity; // | Famix.Module;
127
133
 
128
134
  const fileName = f.getBaseName();
129
135
  const fullyQualifiedFilename = f.getFilePath();
130
136
  if (!this.fmxFileMap.has(fullyQualifiedFilename)) {
131
137
  if (isModule) {
132
- fmxFile = new Famix.Module();
138
+ fmxFile = new Famix.Module();
133
139
  }
134
140
  else {
135
141
  fmxFile = new Famix.ScriptEntity();
@@ -152,29 +158,32 @@ export class EntityDictionary {
152
158
  }
153
159
 
154
160
  /**
155
- * Creates or gets a Famix namespace
156
- * @param m A namespace
157
- * @returns The Famix model of the namespace
161
+ * Creates or gets a Famix Module
162
+ * @param m A module
163
+ * @returns The Famix model of the module
158
164
  */
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
+ public createOrGetFamixModule(m: ModuleDeclaration): Famix.Module {
166
+ let fmxModule: Famix.Module;
167
+ const moduleName = m.getName();
168
+ if (!this.fmxModuleMap.has(moduleName)) {
169
+ fmxModule = new Famix.Module();
170
+ fmxModule.setName(moduleName);
171
+ fmxModule.$isAmbient = isAmbient(m);
172
+ fmxModule.$isNamespace = isNamespace(m);
173
+ fmxModule.$isModule = !fmxModule.$isNamespace && !fmxModule.$isAmbient;
165
174
 
166
- this.makeFamixIndexFileAnchor(m, fmxNamespace);
175
+ this.makeFamixIndexFileAnchor(m, fmxModule);
167
176
 
168
- this.fmxNamespaceMap.set(namespaceName, fmxNamespace);
177
+ this.fmxModuleMap.set(moduleName, fmxModule);
169
178
 
170
- this.famixRep.addElement(fmxNamespace);
179
+ this.famixRep.addElement(fmxModule);
171
180
  }
172
181
  else {
173
- fmxNamespace = this.fmxNamespaceMap.get(namespaceName);
182
+ fmxModule = this.fmxModuleMap.get(moduleName);
174
183
  }
175
184
 
176
- this.fmxElementObjectMap.set(fmxNamespace,m);
177
- return fmxNamespace;
185
+ this.fmxElementObjectMap.set(fmxModule,m);
186
+ return fmxModule;
178
187
  }
179
188
 
180
189
  /**
@@ -214,15 +223,15 @@ export class EntityDictionary {
214
223
  * @param cls A class
215
224
  * @returns The Famix model of the class
216
225
  */
217
- public createOrGetFamixClass(cls: ClassDeclaration): Famix.Class | Famix.ParameterizableClass {
218
- let fmxClass: Famix.Class | Famix.ParameterizableClass;
226
+ public createOrGetFamixClass(cls: ClassDeclaration): Famix.Class | Famix.ParametricClass {
227
+ let fmxClass: Famix.Class | Famix.ParametricClass;
219
228
  const isAbstract = cls.isAbstract();
220
229
  const classFullyQualifiedName = FQNFunctions.getFQN(cls);
221
230
  const clsName = cls.getName();
231
+ const isGeneric = cls.getTypeParameters().length;
222
232
  if (!this.fmxClassMap.has(classFullyQualifiedName)) {
223
- const isGeneric = cls.getTypeParameters().length;
224
233
  if (isGeneric) {
225
- fmxClass = new Famix.ParameterizableClass();
234
+ fmxClass = new Famix.ParametricClass();
226
235
  }
227
236
  else {
228
237
  fmxClass = new Famix.Class();
@@ -238,13 +247,12 @@ export class EntityDictionary {
238
247
 
239
248
  this.famixRep.addElement(fmxClass);
240
249
 
250
+ this.fmxElementObjectMap.set(fmxClass,cls);
241
251
  }
242
252
  else {
243
- fmxClass = this.fmxClassMap.get(classFullyQualifiedName) as (Famix.Class | Famix.ParameterizableClass);
253
+ fmxClass = this.fmxClassMap.get(classFullyQualifiedName) as (Famix.Class | Famix.ParametricClass);
244
254
  }
245
255
 
246
- this.fmxElementObjectMap.set(fmxClass,cls);
247
-
248
256
  return fmxClass;
249
257
  }
250
258
 
@@ -253,14 +261,14 @@ export class EntityDictionary {
253
261
  * @param inter An interface
254
262
  * @returns The Famix model of the interface
255
263
  */
256
- public createOrGetFamixInterface(inter: InterfaceDeclaration): Famix.Interface | Famix.ParameterizableInterface {
257
- let fmxInterface: Famix.Interface | Famix.ParameterizableInterface;
264
+ public createOrGetFamixInterface(inter: InterfaceDeclaration): Famix.Interface | Famix.ParametricInterface {
265
+ let fmxInterface: Famix.Interface | Famix.ParametricInterface;
258
266
  const interName = inter.getName();
259
267
  const interFullyQualifiedName = FQNFunctions.getFQN(inter);
260
- if (!this.fmxInterfaceMap.has(interName)) {
268
+ if (!this.fmxInterfaceMap.has(interFullyQualifiedName)) {
261
269
  const isGeneric = inter.getTypeParameters().length;
262
270
  if (isGeneric) {
263
- fmxInterface = new Famix.ParameterizableInterface();
271
+ fmxInterface = new Famix.ParametricInterface();
264
272
  }
265
273
  else {
266
274
  fmxInterface = new Famix.Interface();
@@ -273,15 +281,71 @@ export class EntityDictionary {
273
281
  this.fmxInterfaceMap.set(interFullyQualifiedName, fmxInterface);
274
282
 
275
283
  this.famixRep.addElement(fmxInterface);
284
+
285
+ this.fmxElementObjectMap.set(fmxInterface,inter);
276
286
  }
277
287
  else {
278
- fmxInterface = this.fmxInterfaceMap.get(interName) as (Famix.Interface | Famix.ParameterizableInterface);
288
+ fmxInterface = this.fmxInterfaceMap.get(interFullyQualifiedName) as (Famix.Interface | Famix.ParametricInterface);
279
289
  }
280
-
281
- this.fmxElementObjectMap.set(fmxInterface,inter);
282
290
  return fmxInterface;
283
291
  }
284
292
 
293
+ /**
294
+ * Creates or gets a Famix concrete element
295
+ * @param el A parametric Element
296
+ * @param elDeclaration the element declaration
297
+ * @param concreteArguments concrete arguments
298
+ * @returns A parametric Element
299
+ */
300
+ public createOrGetFamixConcreteElement(el : Famix.ParametricClass | Famix.ParametricInterface | Famix.ParametricFunction | Famix.ParametricMethod, elDeclaration : ClassDeclaration | InterfaceDeclaration | FunctionDeclaration | MethodDeclaration, concreteArguments : any): Famix.ParametricClass | Famix.ParametricInterface | Famix.ParametricFunction | Famix.ParametricMethod {
301
+
302
+ let fullyQualifiedFilename = el.getFullyQualifiedName();
303
+ let params = "";
304
+
305
+ concreteArguments.map((param) => {
306
+ params = params+param.getText()+','
307
+ })
308
+
309
+ params = params.substring(0, params.length - 1)
310
+
311
+ fullyQualifiedFilename = Helpers.replaceLastBetweenTags(fullyQualifiedFilename,params);
312
+
313
+ let concElement;
314
+
315
+ if (!this.fmxInterfaceMap.has(fullyQualifiedFilename) && !this.fmxClassMap.has(fullyQualifiedFilename) && !this.fmxFunctionAndMethodMap.has(fullyQualifiedFilename)){
316
+ concElement = _.cloneDeep(el);
317
+ concElement.setFullyQualifiedName(fullyQualifiedFilename);
318
+ concElement.clearGenericParameters();
319
+ concreteArguments.map((param) => {
320
+ const parameter = this.createOrGetFamixConcreteType(param);
321
+ concElement.addConcreteParameter(parameter);
322
+ })
323
+
324
+ if (el instanceof Famix.ParametricClass) {
325
+ this.fmxClassMap.set(fullyQualifiedFilename, concElement as Famix.ParametricClass);
326
+ } else if (el instanceof Famix.ParametricInterface) {
327
+ this.fmxInterfaceMap.set(fullyQualifiedFilename, concElement as Famix.ParametricInterface);
328
+ } else if (el instanceof Famix.ParametricFunction) {
329
+ this.fmxFunctionAndMethodMap.set(fullyQualifiedFilename, concElement as Famix.ParametricFunction);
330
+ } else if (el instanceof Famix.ParametricMethod) {
331
+ this.fmxFunctionAndMethodMap.set(fullyQualifiedFilename, concElement as Famix.ParametricMethod);
332
+ }
333
+ this.famixRep.addElement(concElement);
334
+ this.fmxElementObjectMap.set(concElement,elDeclaration);
335
+ } else {
336
+ if (el instanceof Famix.ParametricClass) {
337
+ concElement = this.fmxClassMap.get(fullyQualifiedFilename);
338
+ } else if (el instanceof Famix.ParametricInterface) {
339
+ concElement = this.fmxInterfaceMap.get(fullyQualifiedFilename);
340
+ } else if (el instanceof Famix.ParametricFunction) {
341
+ concElement = this.fmxFunctionAndMethodMap.get(fullyQualifiedFilename);
342
+ } else if (el instanceof Famix.ParametricMethod) {
343
+ concElement = this.fmxFunctionAndMethodMap.get(fullyQualifiedFilename);
344
+ }
345
+ }
346
+ return concElement;
347
+ }
348
+
285
349
  /**
286
350
  * Creates a Famix property
287
351
  * @param property A property
@@ -302,22 +366,39 @@ export class EntityDictionary {
302
366
  const fmxType = this.createOrGetFamixType(propTypeName, property);
303
367
  fmxProperty.setDeclaredType(fmxType);
304
368
 
305
- property.getModifiers().forEach(m => fmxProperty.addModifier(m.getText()));
369
+ // add the visibility (public, private, etc.) to the fmxProperty
370
+ fmxProperty.visibility = "";
371
+
372
+ property.getModifiers().forEach(m => {
373
+ switch (m.getText()) {
374
+ case Scope.Public:
375
+ fmxProperty.visibility = "public";
376
+ break;
377
+ case Scope.Protected:
378
+ fmxProperty.visibility = "protected";
379
+ break;
380
+ case Scope.Private:
381
+ fmxProperty.visibility = "private";
382
+ break;
383
+ case "static":
384
+ fmxProperty.setIsClassSide(true);
385
+ break;
386
+ case "readonly":
387
+ fmxProperty.readOnly = true;
388
+ break;
389
+ default:
390
+ break;
391
+ }
392
+ });
393
+
306
394
  if (!isSignature && property.getExclamationTokenNode()) {
307
- fmxProperty.addModifier("!");
395
+ fmxProperty.isDefinitelyAssigned = true;
308
396
  }
309
397
  if (property.getQuestionTokenNode()) {
310
- fmxProperty.addModifier("?");
398
+ fmxProperty.isOptional = true;
311
399
  }
312
400
  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);
401
+ fmxProperty.isJavaScriptPrivate = true;
321
402
  }
322
403
 
323
404
  this.makeFamixIndexFileAnchor(property, fmxProperty);
@@ -335,89 +416,102 @@ export class EntityDictionary {
335
416
  * @param currentCC The cyclomatic complexity metrics of the current source file
336
417
  * @returns The Famix model of the method or the accessor
337
418
  */
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;
419
+ public createOrGetFamixMethod(method: MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration, currentCC: unknown): Famix.Method | Famix.Accessor | Famix.ParametricMethod {
420
+ let fmxMethod: Famix.Method | Famix.Accessor | Famix.ParametricMethod;
354
421
  const isGeneric = method.getTypeParameters().length > 0;
355
- fmxMethod.setIsGeneric(isGeneric);
422
+ const functionFullyQualifiedName = FQNFunctions.getFQN(method);
423
+ if (!this.fmxFunctionAndMethodMap.has(functionFullyQualifiedName)) {
424
+
425
+ if (method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration) {
426
+ fmxMethod = new Famix.Accessor();
427
+ const isGetter = method instanceof GetAccessorDeclaration;
428
+ const isSetter = method instanceof SetAccessorDeclaration;
429
+ if (isGetter) {(fmxMethod as Famix.Accessor).setKind("getter");}
430
+ if (isSetter) {(fmxMethod as Famix.Accessor).setKind("setter");}
431
+ this.famixRep.addElement(fmxMethod);
432
+ }
433
+ else {
434
+ if (isGeneric) {
435
+ fmxMethod = new Famix.ParametricMethod();
436
+ }
437
+ else {
438
+ fmxMethod = new Famix.Method();
439
+ }
440
+ this.famixRep.addElement(fmxMethod);
441
+ }
442
+ const isConstructor = method instanceof ConstructorDeclaration;
443
+ const isSignature = method instanceof MethodSignature;
444
+
445
+ let isAbstract = false;
446
+ let isStatic = false;
447
+ if (method instanceof MethodDeclaration || method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration) {
448
+ isAbstract = method.isAbstract();
449
+ isStatic = method.isStatic();
450
+ }
356
451
 
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
- }
452
+ if (isConstructor) {(fmxMethod as Famix.Accessor).setKind("constructor");}
453
+ fmxMethod.setIsAbstract(isAbstract);
454
+ fmxMethod.setIsClassSide(isStatic);
455
+ fmxMethod.setIsPrivate((method instanceof MethodDeclaration || method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration) ? (method.getModifiers().find(x => x.getText() === 'private')) !== undefined : false);
456
+ fmxMethod.setIsProtected((method instanceof MethodDeclaration || method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration) ? (method.getModifiers().find(x => x.getText() === 'protected')) !== undefined : false);
457
+ fmxMethod.setSignature(Helpers.computeSignature(method.getText()));
363
458
 
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()));
459
+ let methodName: string;
460
+ if (isConstructor) {
461
+ methodName = "constructor";
462
+ }
463
+ else {
464
+ methodName = (method as MethodDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration).getName();
465
+ }
466
+ fmxMethod.setName(methodName);
370
467
 
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);
468
+ if (!isConstructor) {
469
+ if (method.getName().substring(0, 1) === "#") {
470
+ fmxMethod.setIsPrivate(true);
471
+ }
472
+ }
379
473
 
380
- if (!isConstructor) {
381
- if (method.getName().substring(0, 1) === "#") {
382
- fmxMethod.setIsPrivate(true);
474
+ if (!fmxMethod.getIsPrivate() && !fmxMethod.getIsProtected()) {
475
+ fmxMethod.setIsPublic(true);
476
+ }
477
+ else {
478
+ fmxMethod.setIsPublic(false);
383
479
  }
384
- }
385
480
 
386
- if (!fmxMethod.getIsPrivate() && !fmxMethod.getIsProtected()) {
387
- fmxMethod.setIsPublic(true);
388
- }
389
- else {
390
- fmxMethod.setIsPublic(false);
391
- }
481
+ if (!isSignature) {
482
+ fmxMethod.setCyclomaticComplexity(currentCC[fmxMethod.getName()]);
483
+ }
484
+ else {
485
+ fmxMethod.setCyclomaticComplexity(0);
486
+ }
392
487
 
393
- if (!isSignature) {
394
- fmxMethod.setCyclomaticComplexity(currentCC[fmxMethod.getName()]);
395
- }
396
- else {
397
- fmxMethod.setCyclomaticComplexity(0);
398
- }
488
+ let methodTypeName = this.UNKNOWN_VALUE;
489
+ try {
490
+ methodTypeName = method.getReturnType().getText().trim();
491
+ } catch (error) {
492
+ logger.error(`> WARNING: got exception ${error}. Failed to get usable name for return type of method: ${fmxMethod.getName()}. Continuing...`);
493
+ }
399
494
 
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
- }
495
+ const fmxType = this.createOrGetFamixType(methodTypeName, method);
496
+ fmxMethod.setDeclaredType(fmxType);
497
+ fmxMethod.setNumberOfLinesOfCode(method.getEndLineNumber() - method.getStartLineNumber());
498
+ const parameters = method.getParameters();
499
+ fmxMethod.setNumberOfParameters(parameters.length);
406
500
 
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);
501
+ if (!isSignature) {
502
+ fmxMethod.setNumberOfStatements(method.getStatements().length);
503
+ }
504
+ else {
505
+ fmxMethod.setNumberOfStatements(0);
506
+ }
507
+
508
+ this.makeFamixIndexFileAnchor(method, fmxMethod);
412
509
 
413
- if (!isSignature) {
414
- fmxMethod.setNumberOfStatements(method.getStatements().length);
510
+ this.fmxFunctionAndMethodMap.set(functionFullyQualifiedName, fmxMethod);
415
511
  }
416
512
  else {
417
- fmxMethod.setNumberOfStatements(0);
513
+ fmxMethod = this.fmxFunctionAndMethodMap.get(functionFullyQualifiedName) as (Famix.Method | Famix.Accessor | Famix.ParametricMethod);
418
514
  }
419
-
420
- this.makeFamixIndexFileAnchor(method, fmxMethod);
421
515
 
422
516
  this.fmxElementObjectMap.set(fmxMethod,method);
423
517
 
@@ -430,38 +524,54 @@ export class EntityDictionary {
430
524
  * @param currentCC The cyclomatic complexity metrics of the current source file
431
525
  * @returns The Famix model of the function
432
526
  */
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());
527
+ public createOrGetFamixFunction(func: FunctionDeclaration | FunctionExpression, currentCC: unknown): Famix.Function | Famix.ParametricFunction {
528
+ let fmxFunction: Famix.Function | Famix.ParametricFunction;
529
+ const isGeneric = func.getTypeParameters().length > 0;
530
+ const functionFullyQualifiedName = FQNFunctions.getFQN(func);
531
+ if (!this.fmxFunctionAndMethodMap.has(functionFullyQualifiedName)) {
532
+ if (isGeneric) {
533
+ fmxFunction = new Famix.ParametricFunction();
534
+ }
535
+ else {
536
+ fmxFunction = new Famix.Function();
537
+ }
538
+
539
+ if (func.getName()) {
540
+ fmxFunction.setName(func.getName());
541
+ }
542
+ else {
543
+ fmxFunction.setName("anonymous");
544
+ }
545
+
546
+ fmxFunction.setSignature(Helpers.computeSignature(func.getText()));
547
+ fmxFunction.setCyclomaticComplexity(currentCC[fmxFunction.getName()]);
548
+ fmxFunction.setFullyQualifiedName(functionFullyQualifiedName);
549
+
550
+ let functionTypeName = this.UNKNOWN_VALUE;
551
+ try {
552
+ functionTypeName = func.getReturnType().getText().trim();
553
+ } catch (error) {
554
+ logger.error(`> WARNING: got exception ${error}. Failed to get usable name for return type of function: ${func.getName()}. Continuing...`);
555
+ }
556
+
557
+ const fmxType = this.createOrGetFamixType(functionTypeName, func);
558
+ fmxFunction.setDeclaredType(fmxType);
559
+ fmxFunction.setNumberOfLinesOfCode(func.getEndLineNumber() - func.getStartLineNumber());
560
+ const parameters = func.getParameters();
561
+ fmxFunction.setNumberOfParameters(parameters.length);
562
+ fmxFunction.setNumberOfStatements(func.getStatements().length);
563
+
564
+ this.makeFamixIndexFileAnchor(func, fmxFunction);
565
+
566
+ this.famixRep.addElement(fmxFunction);
567
+
568
+ this.fmxElementObjectMap.set(fmxFunction,func);
569
+
570
+ this.fmxFunctionAndMethodMap.set(functionFullyQualifiedName, fmxFunction);
437
571
  }
438
572
  else {
439
- fmxFunction.setName("anonymous");
573
+ fmxFunction = this.fmxFunctionAndMethodMap.get(functionFullyQualifiedName) as (Famix.Function | Famix.ParametricFunction);
440
574
  }
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
575
 
466
576
  return fmxFunction;
467
577
  }
@@ -500,8 +610,10 @@ export class EntityDictionary {
500
610
  * @returns The Famix model of the type parameter
501
611
  */
502
612
  public createFamixParameterType(tp: TypeParameterDeclaration): Famix.ParameterType {
613
+
503
614
  const fmxParameterType = new Famix.ParameterType();
504
- fmxParameterType.setName(tp.getName());
615
+
616
+ fmxParameterType.setName(tp.getName());
505
617
 
506
618
  this.makeFamixIndexFileAnchor(tp, fmxParameterType);
507
619
 
@@ -512,6 +624,71 @@ export class EntityDictionary {
512
624
  return fmxParameterType;
513
625
  }
514
626
 
627
+ /**
628
+ * Creates a Famix type parameter
629
+ * @param tp A type parameter
630
+ * @returns The Famix model of the type parameter
631
+ */
632
+ public createOrGetFamixConcreteType(param: TypeNode): Famix.ParameterType | Famix.PrimitiveType | Famix.Class | Famix.Interface {
633
+ const typeParameterDeclaration = param.getSymbol()?.getDeclarations()[0] as TypeParameterDeclaration;
634
+ const parameterTypeName : string = param.getText();
635
+ let fmxParameterType: Famix.Type | Famix.Class | Famix.Interface;
636
+
637
+ let isClassOrInterface = false;
638
+ if (this.fmxClassMap.has(parameterTypeName)){
639
+ this.fmxClassMap.forEach((obj, name) => {
640
+ if(obj instanceof Famix.ParametricClass){
641
+ if (name === param.getText() && obj.getGenericParameters().size>0) {
642
+ fmxParameterType = obj;
643
+ isClassOrInterface = true;
644
+ }
645
+ } else {
646
+ if (name === param.getText()) {
647
+ fmxParameterType = obj;
648
+ isClassOrInterface = true;
649
+ }
650
+ }
651
+ })
652
+ }
653
+
654
+ if (this.fmxInterfaceMap.has(parameterTypeName)){
655
+ this.fmxInterfaceMap.forEach((obj, name) => {
656
+ if(obj instanceof Famix.ParametricInterface){
657
+ if (name === param.getText() && obj.getGenericParameters().size>0) {
658
+ fmxParameterType = obj;
659
+ isClassOrInterface = true;
660
+ }
661
+ } else {
662
+ if (name === param.getText()) {
663
+ fmxParameterType = obj;
664
+ isClassOrInterface = true;
665
+ }
666
+ }
667
+ })
668
+ }
669
+
670
+ if(!isClassOrInterface){
671
+ if (!this.fmxTypeMap.has(parameterTypeName)) {
672
+ if (parameterTypeName === "number" || parameterTypeName === "string" || parameterTypeName === "boolean" || parameterTypeName === "bigint" || parameterTypeName === "symbol" || parameterTypeName === "undefined" || parameterTypeName === "null" || parameterTypeName === "any" || parameterTypeName === "unknown" || parameterTypeName === "never" || parameterTypeName === "void") {
673
+ fmxParameterType = new Famix.PrimitiveType();
674
+ fmxParameterType.setIsStub(true);
675
+ } else {
676
+ fmxParameterType = new Famix.ParameterType();
677
+ }
678
+
679
+ fmxParameterType.setName(parameterTypeName);
680
+ this.famixRep.addElement(fmxParameterType);
681
+ this.fmxTypeMap.set(parameterTypeName, fmxParameterType);
682
+ this.fmxElementObjectMap.set(fmxParameterType,typeParameterDeclaration);
683
+ }
684
+ else {
685
+ fmxParameterType = this.fmxTypeMap.get(parameterTypeName);
686
+ }
687
+ }
688
+
689
+ return fmxParameterType;
690
+ }
691
+
515
692
  /**
516
693
  * Creates a Famix variable
517
694
  * @param variable A variable
@@ -519,24 +696,24 @@ export class EntityDictionary {
519
696
  */
520
697
  public createFamixVariable(variable: VariableDeclaration): Famix.Variable {
521
698
  const fmxVariable = new Famix.Variable();
522
-
699
+
523
700
  let variableTypeName = this.UNKNOWN_VALUE;
524
701
  try {
525
702
  variableTypeName = variable.getType().getText().trim();
526
703
  } catch (error) {
527
704
  logger.error(`> WARNING: got exception ${error}. Failed to get usable name for variable: ${variable.getName()}. Continuing...`);
528
705
  }
529
-
706
+
530
707
  const fmxType = this.createOrGetFamixType(variableTypeName, variable);
531
708
  fmxVariable.setDeclaredType(fmxType);
532
709
  fmxVariable.setName(variable.getName());
533
-
710
+
534
711
  this.makeFamixIndexFileAnchor(variable, fmxVariable);
535
-
712
+
536
713
  this.famixRep.addElement(fmxVariable);
537
-
714
+
538
715
  this.fmxElementObjectMap.set(fmxVariable,variable);
539
-
716
+
540
717
  return fmxVariable;
541
718
  }
542
719
 
@@ -640,19 +817,23 @@ export class EntityDictionary {
640
817
  * @param element A ts-morph element
641
818
  * @returns The Famix model of the type
642
819
  */
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;
820
+ public createOrGetFamixType(typeName: string, element: TypeDeclaration): Famix.Type | Famix.PrimitiveType | Famix.ParameterType {
821
+ let fmxType: Famix.Type | Famix.PrimitiveType | Famix.ParameterType;
645
822
  let isPrimitiveType = false;
646
- let isParameterizedType = false;
823
+ let isParameterType = false;
647
824
 
648
825
  logger.debug("Creating (or getting) type: '" + typeName + "' of element: " + element?.getText() + " of kind: " + element?.getKindName());
649
826
  let ancestor: Famix.ContainerEntity;
650
827
  if (element !== undefined) {
651
828
  const typeAncestor = Helpers.findTypeAncestor(element);
829
+ if (!typeAncestor) {
830
+ throw new Error(`Ancestor not found for element ${element.getText()}.`);
831
+ }
652
832
  const ancestorFullyQualifiedName = FQNFunctions.getFQN(typeAncestor);
653
833
  ancestor = this.famixRep.getFamixEntityByFullyQualifiedName(ancestorFullyQualifiedName) as Famix.ContainerEntity;
654
834
  if (!ancestor) {
655
- throw new Error(`Ancestor ${ancestorFullyQualifiedName} not found.`);
835
+ logger.debug(`Ancestor ${FQNFunctions.getFQN(typeAncestor)} not found. Adding the new type.`);
836
+ ancestor = this.createOrGetFamixType(typeAncestor.getText(), typeAncestor as TypeDeclaration);
656
837
  }
657
838
  }
658
839
 
@@ -661,7 +842,7 @@ export class EntityDictionary {
661
842
  }
662
843
 
663
844
  if(!isPrimitiveType && typeName.includes("<") && typeName.includes(">") && !(typeName.includes("=>"))) {
664
- isParameterizedType = true;
845
+ isParameterType = true;
665
846
  }
666
847
 
667
848
  if (!this.fmxTypeMap.has(typeName)) {
@@ -669,16 +850,16 @@ export class EntityDictionary {
669
850
  fmxType = new Famix.PrimitiveType();
670
851
  fmxType.setIsStub(true);
671
852
  }
672
- else if (isParameterizedType) {
673
- fmxType = new Famix.ParameterizedType();
853
+ else if (isParameterType) {
854
+ fmxType = new Famix.ParameterType();
674
855
  const parameterTypeNames = typeName.substring(typeName.indexOf("<") + 1, typeName.indexOf(">")).split(",").map(s => s.trim());
675
856
  const baseTypeName = typeName.substring(0, typeName.indexOf("<")).trim();
676
857
  parameterTypeNames.forEach(parameterTypeName => {
677
858
  const fmxParameterType = this.createOrGetFamixType(parameterTypeName, element);
678
- (fmxType as Famix.ParameterizedType).addArgument(fmxParameterType);
859
+ (fmxType as Famix.ParameterType).addArgument(fmxParameterType);
679
860
  });
680
861
  const fmxBaseType = this.createOrGetFamixType(baseTypeName, element);
681
- (fmxType as Famix.ParameterizedType).setBaseType(fmxBaseType);
862
+ (fmxType as Famix.ParameterType).setBaseType(fmxBaseType);
682
863
  }
683
864
  else {
684
865
  fmxType = new Famix.Type();
@@ -709,16 +890,24 @@ export class EntityDictionary {
709
890
  */
710
891
  public createFamixAccess(node: Identifier, id: number): void {
711
892
  const fmxVar = this.famixRep.getFamixEntityById(id) as Famix.StructuralEntity;
893
+ if (!fmxVar) {
894
+ throw new Error(`Famix entity with id ${id} not found, for node ${node.getText()} in ${node.getSourceFile().getBaseName()} at line ${node.getStartLineNumber()}.`);
895
+ }
896
+
897
+ logger.debug(`Creating FamixAccess. Node: [${node.getKindName()}] '${node.getText()}' at line ${node.getStartLineNumber()} in ${node.getSourceFile().getBaseName()}, id: ${id} refers to fmxVar '${fmxVar.getFullyQualifiedName()}'.`);
898
+
712
899
  const nodeReferenceAncestor = Helpers.findAncestor(node);
713
900
  const ancestorFullyQualifiedName = FQNFunctions.getFQN(nodeReferenceAncestor);
714
- const accessor = this.famixRep.getFamixEntityByFullyQualifiedName(ancestorFullyQualifiedName) as Famix.ContainerEntity;
901
+ let accessor = this.famixRep.getFamixEntityByFullyQualifiedName(ancestorFullyQualifiedName) as Famix.ContainerEntity;
902
+ if (!accessor) {
903
+ logger.error(`Ancestor ${ancestorFullyQualifiedName} of kind ${nodeReferenceAncestor.getKindName()} not found.`);
904
+ // accessor = this.createOrGetFamixType(ancestorFullyQualifiedName, nodeReferenceAncestor as TypeDeclaration);
905
+ }
715
906
 
716
907
  const fmxAccess = new Famix.Access();
717
908
  fmxAccess.setAccessor(accessor);
718
909
  fmxAccess.setVariable(fmxVar);
719
910
 
720
- this.makeFamixIndexFileAnchor(node, fmxAccess);
721
-
722
911
  this.famixRep.addElement(fmxAccess);
723
912
 
724
913
  this.fmxElementObjectMap.set(fmxAccess,node);
@@ -744,8 +933,6 @@ export class EntityDictionary {
744
933
  fmxInvocation.addCandidate(fmxMethodOrFunction);
745
934
  fmxInvocation.setSignature(fmxMethodOrFunction.getSignature());
746
935
 
747
- this.makeFamixIndexFileAnchor(node, fmxInvocation);
748
-
749
936
  this.famixRep.addElement(fmxInvocation);
750
937
 
751
938
  this.fmxElementObjectMap.set(fmxInvocation,node);
@@ -813,14 +1000,20 @@ export class EntityDictionary {
813
1000
  fmxInheritance.setSubclass(subClass);
814
1001
  fmxInheritance.setSuperclass(superClass);
815
1002
 
816
- this.makeFamixIndexFileAnchor(null, fmxInheritance);
817
-
818
1003
  this.famixRep.addElement(fmxInheritance);
819
1004
 
820
1005
  this.fmxElementObjectMap.set(fmxInheritance,null);
821
1006
 
822
1007
  }
823
1008
 
1009
+ public createFamixImportClause(importedEntity: Famix.NamedEntity, importingEntity: Famix.Module) {
1010
+ const fmxImportClause = new Famix.ImportClause();
1011
+ fmxImportClause.setImportedEntity(importedEntity);
1012
+ fmxImportClause.setImportingEntity(importingEntity);
1013
+ importingEntity.addOutgoingImport(fmxImportClause);
1014
+ this.famixRep.addElement(fmxImportClause);
1015
+ }
1016
+
824
1017
  /**
825
1018
  * Creates a Famix import clause
826
1019
  * @param importClauseInfo The information needed to create a Famix import clause
@@ -831,8 +1024,8 @@ export class EntityDictionary {
831
1024
  * @param isInExports A boolean indicating if the imported entity is in the exports
832
1025
  * @param isDefaultExport A boolean indicating if the imported entity is a default export
833
1026
  */
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;
1027
+ public oldCreateFamixImportClause(importClauseInfo: {importDeclaration?: ImportDeclaration | ImportEqualsDeclaration, importerSourceFile: SourceFile, moduleSpecifierFilePath: string, importElement: ImportSpecifier | Identifier, isInExports: boolean, isDefaultExport: boolean}): void {
1028
+ const {importDeclaration, importerSourceFile: importer, moduleSpecifierFilePath, importElement, isInExports, isDefaultExport} = importClauseInfo;
836
1029
  logger.debug(`createFamixImportClause: Creating import clause:`);
837
1030
  const fmxImportClause = new Famix.ImportClause();
838
1031
 
@@ -844,10 +1037,12 @@ export class EntityDictionary {
844
1037
  const absolutePath = path.normalize(moduleSpecifierFilePath);
845
1038
  // convert the path and remove any windows backslashes introduced by path.normalize
846
1039
  const pathInProject: string = this.convertToRelativePath(absolutePath, absolutePathProject).replace(/\\/g, "/");
847
-
848
1040
  let pathName = "{" + pathInProject + "}.";
849
1041
 
850
1042
  // Named imports, e.g. import { ClassW } from "./complexExportModule";
1043
+
1044
+ // Start with simple import clause (without referring to the actual variable)
1045
+
851
1046
  if (importDeclaration instanceof ImportDeclaration
852
1047
  && importElement instanceof ImportSpecifier) {
853
1048
  importedEntityName = importElement.getName();
@@ -863,6 +1058,8 @@ export class EntityDictionary {
863
1058
  }
864
1059
  this.makeFamixIndexFileAnchor(importElement, importedEntity);
865
1060
  importedEntity.setFullyQualifiedName(pathName);
1061
+ // must add entity to repository
1062
+ this.famixRep.addElement(importedEntity);
866
1063
  }
867
1064
  }
868
1065
  // handle import equals declarations, e.g. import myModule = require("./complexExportModule");
@@ -884,8 +1081,8 @@ export class EntityDictionary {
884
1081
  this.makeFamixIndexFileAnchor(importElement, importedEntity);
885
1082
  importedEntity.setFullyQualifiedName(pathName);
886
1083
  }
887
-
888
- this.famixRep.addElement(importedEntity);
1084
+ // I don't think it should be added to the repository if it exists already
1085
+ if (!isInExports) this.famixRep.addElement(importedEntity);
889
1086
  const importerFullyQualifiedName = FQNFunctions.getFQN(importer);
890
1087
  const fmxImporter = this.famixRep.getFamixEntityByFullyQualifiedName(importerFullyQualifiedName) as Famix.Module;
891
1088
  fmxImportClause.setImportingEntity(fmxImporter);
@@ -899,9 +1096,6 @@ export class EntityDictionary {
899
1096
  logger.debug(`createFamixImportClause: ${fmxImportClause.getImportedEntity()?.getName()} (of type ${
900
1097
  Helpers.getSubTypeName(fmxImportClause.getImportedEntity())}) is imported by ${fmxImportClause.getImportingEntity()?.getName()}`);
901
1098
 
902
- // make an index file anchor for the import clause
903
- this.makeFamixIndexFileAnchor(importDeclaration, fmxImportClause);
904
-
905
1099
  fmxImporter.addOutgoingImport(fmxImportClause);
906
1100
 
907
1101
  this.famixRep.addElement(fmxImportClause);
@@ -909,7 +1103,362 @@ export class EntityDictionary {
909
1103
  this.fmxElementObjectMap.set(fmxImportClause,importDeclaration);
910
1104
  }
911
1105
 
1106
+ /**
1107
+ * Creates a Famix Arrow Function
1108
+ * @param arrowExpression An Expression
1109
+ * @returns The Famix model of the variable
1110
+ */
1111
+ public createFamixArrowFunction(arrowExpression: Expression ,currentCC: unknown): Famix.ArrowFunction | Famix.ParametricArrowFunction {
1112
+
1113
+ let fmxArrowFunction: Famix.ArrowFunction | Famix.ParametricArrowFunction;
1114
+
1115
+ const arrowFunction = arrowExpression.asKindOrThrow(SyntaxKind.ArrowFunction);
1116
+
1117
+ const isGeneric = arrowFunction.getTypeParameters().length > 0;
1118
+
1119
+ if (isGeneric) {
1120
+ fmxArrowFunction = new Famix.ParametricArrowFunction();
1121
+ }
1122
+ else {
1123
+ fmxArrowFunction = new Famix.ArrowFunction();
1124
+ }
1125
+
1126
+ // Get the parent of the arrow function (the variable declaration)
1127
+ const parent = arrowFunction.getParentIfKind(SyntaxKind.VariableDeclaration);
1128
+ let functionName = '(NO_NAME)';
1129
+
1130
+ if (parent && parent instanceof VariableDeclaration) {
1131
+ // Get the name of the variable
1132
+ functionName = parent.getName();
1133
+ }
1134
+
1135
+ if (functionName) {
1136
+ fmxArrowFunction.setName(functionName);
1137
+ }
1138
+ else {
1139
+ fmxArrowFunction.setName("anonymous");
1140
+ }
1141
+
1142
+ // Signature of an arrow function is (parameters) => return_type
1143
+ const parametersSignature = arrowFunction.getParameters().map(p => p.getText()).join(", ");
1144
+ const returnTypeSignature = arrowFunction.getReturnType().getText();
1145
+ fmxArrowFunction.setSignature(`(${parametersSignature}) => ${returnTypeSignature}`);
1146
+ fmxArrowFunction.setCyclomaticComplexity(currentCC[fmxArrowFunction.getName()]);
1147
+
1148
+ let functionTypeName = this.UNKNOWN_VALUE;
1149
+ try {
1150
+ functionTypeName = arrowFunction.getReturnType().getText().trim();
1151
+ } catch (error) {
1152
+ logger.error(`> WARNING: got exception ${error}. Failed to get usable name for return type of function: ${functionName}. Continuing...`);
1153
+ }
1154
+
1155
+ const fmxType = this.createOrGetFamixType(functionTypeName, arrowFunction as unknown as FunctionDeclaration);
1156
+ fmxArrowFunction.setDeclaredType(fmxType);
1157
+ fmxArrowFunction.setNumberOfLinesOfCode(arrowFunction.getEndLineNumber() - arrowFunction.getStartLineNumber());
1158
+ const parameters = arrowFunction.getParameters();
1159
+ fmxArrowFunction.setNumberOfParameters(parameters.length);
1160
+ fmxArrowFunction.setNumberOfStatements(arrowFunction.getStatements().length);
1161
+
1162
+ this.makeFamixIndexFileAnchor(arrowExpression as unknown as TSMorphObjectType, fmxArrowFunction);
1163
+ this.famixRep.addElement(fmxArrowFunction);
1164
+ this.fmxElementObjectMap.set(fmxArrowFunction,arrowFunction as unknown as TSMorphObjectType);
1165
+
1166
+ return fmxArrowFunction;
1167
+ }
1168
+
1169
+ /**
1170
+ * Creates a Famix concretisation
1171
+ * @param cls A class
1172
+ * @returns The Famix model of the concretisation
1173
+ */
1174
+ public createFamixConcretisation(conEntity : Famix.ParametricClass | Famix.ParametricInterface | Famix.ParametricFunction | Famix.ParametricMethod ,genEntity : Famix.ParametricClass | Famix.ParametricInterface | Famix.ParametricFunction | Famix.ParametricMethod): Famix.Concretisation {
1175
+
1176
+ const fmxConcretisation : Famix.Concretisation = new Famix.Concretisation();
1177
+
1178
+ fmxConcretisation.setConcreteEntity(conEntity);
1179
+ fmxConcretisation.setGenericEntity(genEntity);
1180
+ this.fmxElementObjectMap.set(fmxConcretisation,null);
1181
+ this.famixRep.addElement(fmxConcretisation);
1182
+ const parameterConcretisation = this.createFamixParameterConcrestisation(fmxConcretisation);
1183
+
1184
+ return fmxConcretisation;
1185
+ }
1186
+
1187
+ /**
1188
+ * Creates a Famix concretisation
1189
+ * @param concretisation A FamixConcretisation
1190
+ * @returns The Famix model of the ParameterConcrestisation
1191
+ */
1192
+ public createFamixParameterConcrestisation(concretisation: Famix.Concretisation): Famix.ParameterConcretisation {
1193
+ const conClass = concretisation.getConcreteEntity();
1194
+ const genClass = concretisation.getGenericEntity();
1195
+ const parameterConcretisations = this.famixRep._getAllEntitiesWithType("ParameterConcretisation");
1196
+ const concreteParameters = conClass.getConcreteParameters();
1197
+ const genericParameters = genClass.getGenericParameters();
1198
+
1199
+ let conClassTypeParametersIterator = concreteParameters.values();
1200
+ let genClassTypeParametersIterator = genericParameters.values();
1201
+ let fmxParameterConcretisation : Famix.ParameterConcretisation;
1202
+
1203
+ for (let i = 0; i < genericParameters.size; i++) {
1204
+ const conClassTypeParameter = conClassTypeParametersIterator.next().value;
1205
+ const genClassTypeParameter = genClassTypeParametersIterator.next().value;
1206
+ let createParameterConcretisation : boolean = true;
1207
+ if(conClassTypeParameter && genClassTypeParameter && conClassTypeParameter.getName() != genClassTypeParameter.getName()){
1208
+ parameterConcretisations.forEach((param : Famix.ParameterConcretisation) => {
1209
+ if (conClassTypeParameter.getName() == param.getConcreteParameter().getName() && genClassTypeParameter.getName() == param.getGenericParameter().getName()) {
1210
+ createParameterConcretisation = false;
1211
+ fmxParameterConcretisation = param;
1212
+ }
1213
+ })
1214
+ if (createParameterConcretisation) {
1215
+ fmxParameterConcretisation = new Famix.ParameterConcretisation();
1216
+ fmxParameterConcretisation.setGenericParameter(genClassTypeParameter);
1217
+ fmxParameterConcretisation.setConcreteParameter(conClassTypeParameter);
1218
+ fmxParameterConcretisation.addConcretisation(concretisation);
1219
+ this.fmxElementObjectMap.set(fmxParameterConcretisation,null);
1220
+ } else {
1221
+ fmxParameterConcretisation.addConcretisation(concretisation);
1222
+ }
1223
+ this.famixRep.addElement(fmxParameterConcretisation);
1224
+ }
1225
+ }
1226
+
1227
+ return fmxParameterConcretisation;
1228
+
1229
+ }
1230
+
1231
+ /**
1232
+ * Creates a Famix concretisation between two classes or two interfaces
1233
+ * @param element A class or an Interface
1234
+ */
1235
+ public createFamixConcretisationClassOrInterfaceSpecialisation(element: ClassDeclaration | InterfaceDeclaration){
1236
+
1237
+ const superEntity = element.getExtends();
1238
+ let superEntityArray;
1239
+ if (superEntity){
1240
+ superEntityArray = Array.isArray(superEntity) ? superEntity : [superEntity];
1241
+ }
1242
+ if (superEntityArray && superEntityArray.length > 0) {
1243
+ superEntityArray.forEach(entity => {
1244
+ let entityIsGeneric;
1245
+ const superEntitySymbol = entity.getExpression().getSymbolOrThrow();
1246
+ let superEntityDeclaration;
1247
+ if (superEntity instanceof ExpressionWithTypeArguments) {
1248
+ superEntityDeclaration = superEntitySymbol.getDeclarations()[0].asKind(ts.SyntaxKind.ClassDeclaration);
1249
+ } else {
1250
+ superEntityDeclaration = superEntitySymbol.getDeclarations()[0].asKind(ts.SyntaxKind.InterfaceDeclaration);
1251
+ }
1252
+ if (superEntityDeclaration) {
1253
+ entityIsGeneric = superEntityDeclaration.getTypeParameters().length > 0;
1254
+ }
1255
+ if (entityIsGeneric) {
1256
+ let EntityDeclaration;
1257
+ let genEntity;
1258
+ if (superEntity instanceof ExpressionWithTypeArguments) {
1259
+ EntityDeclaration = entity.getExpression().getSymbol().getDeclarations()[0] as ClassDeclaration;
1260
+ genEntity = this.createOrGetFamixClass(EntityDeclaration) as Famix.ParametricClass;
1261
+ } else {
1262
+ EntityDeclaration = entity.getExpression().getSymbol().getDeclarations()[0] as InterfaceDeclaration;
1263
+ genEntity = this.createOrGetFamixInterface(EntityDeclaration) as Famix.ParametricInterface;
1264
+ }
1265
+ const genParams = EntityDeclaration.getTypeParameters().map((param) => param.getText());
1266
+ const args = element.getHeritageClauses()[0].getTypeNodes()[0].getTypeArguments()
1267
+ const conParams = element.getHeritageClauses()[0].getTypeNodes()[0].getTypeArguments().map((param) => param.getText());
1268
+ if (!Helpers.arraysAreEqual(conParams,genParams)) {
1269
+ let conEntity;
1270
+ conEntity = this.createOrGetFamixConcreteElement(genEntity,EntityDeclaration,args);
1271
+ const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation");
1272
+ let createConcretisation : boolean = true;
1273
+ concretisations.forEach((conc : Famix.Concretisation) => {
1274
+ if (genEntity.getFullyQualifiedName() == conc.getGenericEntity().getFullyQualifiedName() && conc.getConcreteEntity().getFullyQualifiedName() == conEntity.getFullyQualifiedName()){
1275
+ createConcretisation = false;
1276
+ }
1277
+ });
1278
+
1279
+ if (createConcretisation) {
1280
+ const fmxConcretisation : Famix.Concretisation = this.createFamixConcretisation(conEntity,genEntity);
1281
+ }
1282
+ }
1283
+ }
1284
+ });
1285
+ }
1286
+ }
1287
+
1288
+
1289
+ /**
1290
+ * Creates a Famix concretisation between a class and its instanciations
1291
+ * @param cls A class
1292
+ */
1293
+ public createFamixConcretisationGenericInstantiation(cls: ClassDeclaration){
1294
+
1295
+ const isGeneric = cls.getTypeParameters().length > 0;
1296
+ if (isGeneric) {
1297
+ const instances = cls.getSourceFile().getDescendantsOfKind(ts.SyntaxKind.NewExpression)
1298
+ .filter(newExpr => {
1299
+ const expression = newExpr.getExpression();
1300
+ return expression.getText() === cls.getName();
1301
+ });
1302
+
1303
+ instances.forEach(instance => {
1304
+ const instanceIsGeneric = instance.getTypeArguments().length > 0;
1305
+ if (instanceIsGeneric) {
1306
+ const conParams = instance.getTypeArguments().map((param) => param.getText());
1307
+ const genEntity = this.createOrGetFamixClass(cls) as Famix.ParametricClass;
1308
+ const genParams = cls.getTypeParameters().map((param) => param.getText());
1309
+ if (!Helpers.arraysAreEqual(conParams,genParams)) {
1310
+ let conEntity;
1311
+ conEntity = this.createOrGetFamixConcreteElement(genEntity,cls,instance.getTypeArguments());
1312
+ const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation");
1313
+ let createConcretisation : boolean = true;
1314
+ concretisations.forEach((conc : Famix.Concretisation) => {
1315
+ if (genEntity.getFullyQualifiedName() == conc.getGenericEntity().getFullyQualifiedName() && conc.getConcreteEntity().getFullyQualifiedName() == conEntity.getFullyQualifiedName()){
1316
+ createConcretisation = false;
1317
+ }
1318
+ });
1319
+
1320
+ if (createConcretisation) {
1321
+ const fmxConcretisation : Famix.Concretisation = this.createFamixConcretisation(conEntity,genEntity);
1322
+ }
1323
+ }
1324
+ }
1325
+ })
1326
+ }
1327
+ }
1328
+
1329
+ /**
1330
+ * Creates a Famix concretisation between a class and its instanciations
1331
+ * @param func A function
1332
+ */
1333
+ public createFamixConcretisationFunctionInstantiation(element: FunctionDeclaration | MethodDeclaration){
1334
+ const isGeneric = element.getTypeParameters().length > 0;
1335
+ if (isGeneric) {
1336
+ const genParams = element.getTypeParameters().map(param => param.getText());
1337
+ const uses = element.findReferencesAsNodes();
1338
+ uses.forEach(usage => {
1339
+ let currentNode = usage;
1340
+
1341
+ while (currentNode) {
1342
+ if (currentNode.getKind() === SyntaxKind.CallExpression) {
1343
+ const callExpression = currentNode.asKind(SyntaxKind.CallExpression);
1344
+ const instanceIsGeneric = callExpression.getTypeArguments().length > 0;
1345
+ if (instanceIsGeneric) {
1346
+ const args = callExpression.getTypeArguments();
1347
+ const conParams = callExpression.getTypeArguments().map(param => param.getText());
1348
+ if (!Helpers.arraysAreEqual(conParams,genParams)) {
1349
+ let genElement;
1350
+ if(element instanceof FunctionDeclaration){
1351
+ genElement = this.createOrGetFamixFunction(element,0) as Famix.ParametricFunction;
1352
+ } else {
1353
+ genElement = this.createOrGetFamixMethod(element,0) as Famix.ParametricMethod;
1354
+ }
1355
+ let concElement;
1356
+ concElement = this.createOrGetFamixConcreteElement(genElement,element,args);
1357
+ const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation");
1358
+ let createConcretisation : boolean = true;
1359
+ concretisations.forEach((conc : Famix.Concretisation) => {
1360
+ if (genElement.getFullyQualifiedName() == conc.getGenericEntity().getFullyQualifiedName() && conc.getConcreteEntity().getFullyQualifiedName() == concElement.getFullyQualifiedName()){
1361
+ createConcretisation = false;
1362
+ }
1363
+ });
1364
+
1365
+ if (createConcretisation) {
1366
+ const fmxConcretisation : Famix.Concretisation = this.createFamixConcretisation(concElement,genElement);
1367
+ }
1368
+ }
1369
+ }
1370
+ break;
1371
+ }
1372
+ // Remonter à l'élément parent (utile si le nœud de référence est un enfant)
1373
+ currentNode = currentNode.getParent();
1374
+ }
1375
+ });
1376
+ }
1377
+ }
1378
+
1379
+ /**
1380
+ * Creates a Famix concretisation between a class and an interface
1381
+ * @param cls A class
1382
+ */
1383
+ public createFamixConcretisationInterfaceClass(cls: ClassDeclaration){
1384
+
1385
+ const superInterfaces = cls.getImplements();
1386
+ superInterfaces.forEach(interfaceType => {
1387
+ const interfaceIsGeneric = interfaceType.getTypeArguments().length>0;
1388
+ if (interfaceIsGeneric) {
1389
+ const interfaceDeclaration = interfaceType.getExpression().getSymbol().getDeclarations()[0] as InterfaceDeclaration;
1390
+ const genParams = interfaceDeclaration.getTypeParameters().map((param) => param.getText());
1391
+ const conParams = cls.getHeritageClauses()[0].getTypeNodes()[0].getTypeArguments().map((param) => param.getText());
1392
+ const args = cls.getHeritageClauses()[0].getTypeNodes()[0].getTypeArguments();
1393
+ if (!Helpers.arraysAreEqual(conParams,genParams)) {
1394
+ const genInterface = this.createOrGetFamixInterface(interfaceDeclaration) as Famix.ParametricInterface;
1395
+ const conInterface = this.createOrGetFamixConcreteElement(genInterface,interfaceDeclaration,args);
1396
+ const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation");
1397
+ let createConcretisation : boolean = true;
1398
+ concretisations.forEach((conc : Famix.Concretisation) => {
1399
+ if (genInterface.getFullyQualifiedName() == conc.getGenericEntity().getFullyQualifiedName() && conc.getConcreteEntity().getFullyQualifiedName() == conInterface.getFullyQualifiedName()){
1400
+ createConcretisation = false;
1401
+ }
1402
+ });
1403
+
1404
+ if (createConcretisation) {
1405
+ const fmxConcretisation : Famix.Concretisation = this.createFamixConcretisation(conInterface,genInterface);
1406
+ }
1407
+ }
1408
+ }
1409
+ });
1410
+ }
1411
+
1412
+ /**
1413
+ * Creates a Famix concretisation between an interface and a Type
1414
+ * @param element A variable or a function
1415
+ * @param inter An interface
1416
+ */
1417
+ public createFamixConcretisationTypeInstanciation(element: InterfaceDeclaration | ClassDeclaration){
1418
+
1419
+ const isGeneric = element.getTypeParameters().length > 0;
1420
+ if (isGeneric) {
1421
+ const genParams = element.getTypeParameters().map(param => param.getText());
1422
+ const uses = element.findReferencesAsNodes();
1423
+ uses.forEach(use => {
1424
+ let parentNode = use.getParent();
1425
+ while (parentNode) {
1426
+ if (parentNode.getKind() === SyntaxKind.TypeReference) {
1427
+ const typeReferenceNode = parentNode.asKind(SyntaxKind.TypeReference);
1428
+ const typeReferenceNodeIsGeneric = typeReferenceNode.getTypeArguments().length > 0;
1429
+ if (typeReferenceNodeIsGeneric) {}
1430
+ const args = typeReferenceNode.getTypeArguments();
1431
+ const conParams = typeReferenceNode.getTypeArguments().map(param => param.getText());
1432
+ if (!Helpers.arraysAreEqual(conParams,genParams)) {
1433
+ let genElement;
1434
+ if(element instanceof ClassDeclaration){
1435
+ genElement = this.createOrGetFamixClass(element) as Famix.ParametricClass;
1436
+ } else {
1437
+ genElement = this.createOrGetFamixInterface(element) as Famix.ParametricInterface;
1438
+ }
1439
+ let concElement;
1440
+ concElement = this.createOrGetFamixConcreteElement(genElement,element,args);
1441
+ const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation");
1442
+ let createConcretisation : boolean = true;
1443
+ concretisations.forEach((conc : Famix.Concretisation) => {
1444
+ if (genElement.getFullyQualifiedName() == conc.getGenericEntity().getFullyQualifiedName() && conc.getConcreteEntity().getFullyQualifiedName() == concElement.getFullyQualifiedName()){
1445
+ createConcretisation = false;
1446
+ }
1447
+ });
1448
+
1449
+ if (createConcretisation) {
1450
+ const fmxConcretisation : Famix.Concretisation = this.createFamixConcretisation(concElement,genElement);
1451
+ }
1452
+ }
1453
+ break;
1454
+ }
1455
+ parentNode = parentNode.getParent();
1456
+ }
1457
+ });
1458
+ }
1459
+ }
1460
+
912
1461
  public convertToRelativePath(absolutePath: string, absolutePathProject: string) {
913
1462
  return absolutePath.replace(absolutePathProject, "").slice(1);
914
1463
  }
915
- }
1464
+ }