• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

moosetechnology / Famix-Value / 12865043431

20 Jan 2025 09:33AM UTC coverage: 22.913% (+0.6%) from 22.326%
12865043431

push

github

Gabriel-Darbord
Merge b121a334f

64 of 194 new or added lines in 6 files covered. (32.99%)

3 existing lines in 3 files now uncovered.

796 of 3474 relevant lines covered (22.91%)

0.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

35.71
/src/Famix-Value-Exporter/FamixValue2FASTJavaVisitor.class.st
1
"
2
A visitor to export a FamixValue model to FASTJava.
3
The generated code is a block statement with a variable declared for each value.
4

5
Object attributes are initialized by finding the corresponding setters.
6
Collections and Dictionaries are constructed with the methods `add(element)` and `put(key, value)` respectively.
7

8
When exporting a `FamixValueOfObjectAttribute`, if the setter for its `FamixTAttribute` cannot be found, it is set using the Java Reflection API.
9
All attributes that fall into this category are added as `markedForReflection`.
10
"
11
Class {
12
        #name : 'FamixValue2FASTJavaVisitor',
13
        #superclass : 'FamixValue2ASTVisitor',
14
        #instVars : [
15
                'builder',
16
                'constructorCache',
17
                'staticAttributesCache',
18
                'objectExportStrategy',
19
                'reflections',
20
                'defaultHelperPackage'
21
        ],
22
        #category : 'Famix-Value-Exporter-Visitors',
23
        #package : 'Famix-Value-Exporter',
24
        #tag : 'Visitors'
25
}
26

27
{ #category : 'private' }
28
FamixValue2FASTJavaVisitor >> addAttributesFrom: object asArgumentsTo: invocation usingConstructor: constructor [
×
29

×
30
        (constructorCache
×
31
                 at: constructor
×
32
                 ifAbsentPut: [ constructor mapConstructorParametersToAttributes ])
×
33
                withIndexDo: [ :paramAttribute :index |
×
34
                        invocation addArgument: (paramAttribute
×
NEW
35
                                         ifNil: [ "constructor parameter is not mapped to an attribute
×
NEW
36
                                                => give it a default value"
×
37
                                                 (constructor parameters at: index) declaredType
×
NEW
38
                                                         ifNil: [ "TODO fix Java parser
×
NEW
39
                                                                if it's unknown then it must be a class, otherwise the parser would have found it
×
NEW
40
                                                                => the default for class instances is `null`"
×
NEW
41
                                                                 self model newNullLiteral ]
×
42
                                                         ifNotNil: [ :declaredType |
×
43
                                                         declaredType asFASTJavaDefaultValueOn: self model ] ]
×
NEW
44
                                         ifNotNil: [ "parameter is mapped to attribute"
×
45
                                                 object value
×
46
                                                         detect: [ :objAttribute | "find the matching value attribute"
×
47
                                                                 objAttribute attribute == paramAttribute ]
×
48
                                                         ifFound: [ :objAttribute | "dispatch for var naming context"
×
49
                                                                 objectExportStrategy
×
50
                                                                         makeVariableExpression: objAttribute value
×
51
                                                                         on: self ]
×
52
                                                         ifNone: [ "the object does not have the attribute set"
×
53
                                                                 paramAttribute declaredType asFASTJavaDefaultValueOn:
×
54
                                                                         self model ] ]) ]
×
55
]
×
56

57
{ #category : 'accessing' }
58
FamixValue2FASTJavaVisitor >> builder [
1✔
59

1✔
60
        ^ builder ifNil: [ builder := FASTJavaBuilder new model: self model ]
1✔
61
]
1✔
62

63
{ #category : 'private' }
64
FamixValue2FASTJavaVisitor >> constructObject: object [
1✔
65
        "Build code to instantiate the object and store it in a variable"
1✔
66

1✔
67
        | constructor invocation |
1✔
68
        constructor := self findConstructorFor: object.
1✔
69

1✔
70
        "make the constructor invocation"
1✔
71
        invocation := self reflections
1✔
72
                              at: constructor
1✔
73
                              ifPresent: [ :varName | "use reflection: var contains the Constructor object"
1✔
74
                                      self
1✔
75
                                              makeReflectionConstructorInvocation: varName
1✔
76
                                              for: object ]
1✔
77
                              ifAbsent: [ "regular use of the constructor"
1✔
78
                                      self
1✔
79
                                              makeConstructorInvocation: constructor
1✔
80
                                              for: object ].
1✔
81

1✔
82
        "add arguments to match constructor parameters"
1✔
83
        constructor parameters ifNotEmpty: [
1✔
84
                self
1✔
85
                        addAttributesFrom: object
1✔
86
                        asArgumentsTo: invocation
1✔
87
                        usingConstructor: constructor ]
1✔
88
]
1✔
89

90
{ #category : 'exporting' }
91
FamixValue2FASTJavaVisitor >> defaultHelperPackage [
×
92

×
93
        ^ defaultHelperPackage ifNil: [ defaultHelperPackage := 'org.modest' ]
×
94
]
×
95

96
{ #category : 'exporting' }
97
FamixValue2FASTJavaVisitor >> defaultHelperPackage: aString [
×
98

×
99
        defaultHelperPackage := aString
×
100
]
×
101

102
{ #category : 'private' }
103
FamixValue2FASTJavaVisitor >> ensureReflection [
×
104
        "Ensure that the infrastructure needed to use reflection is generated.
×
105
        A static method called `initializeReflection` is created and will be populated by calls to `ensureReflectionField:` and `ensureReflectionConstructor:`.
×
106
        To handle exceptions that can be thrown by these operations, a static initializer calls this method with a trycatch.
×
107
        Finally, the convenience methods `setField` and `instantiate` are created to handle exceptions during reflexive operations: .
×
108
        Return the statement block of `initializeReflection` for callers to populate."
×
109

×
110
        | declarations declaration initStatementBlock |
×
111
        declarations := self objectExportStrategy currentDeclarations. "does not work for inline strategy..."
×
112

×
113
        "check if already defined and return the body of initializeReflection()"
×
114
        declarations at: -3 ifPresent: [ :decl | ^ decl statementBlock ].
×
115

×
116
        "static {
×
117
                try {
×
118
                        initializeReflection();
×
119
                } catch (Exception e) {
×
120
                        new RuntimeException(e);
×
121
                }
×
122
        }"
×
123
        declaration := self model newInitializer isStatic: true.
×
124
        declaration cacheAt: #order put: -4.
×
125
        declarations at: -4 put: declaration.
×
126
        declaration statementBlock:
×
127
                (model newStatementBlock statements: { (model newTryCatchStatement
×
128
                                  try: (model newStatementBlock statements:
×
129
                                                           { (model newExpressionStatement expression:
×
130
                                                                            (model newMethodInvocation name: 'initializeReflection')) });
×
131
                                  catches: { (model newCatchPartStatement
×
132
                                                   catchedTypes: { (model newClassTypeExpression typeName:
×
133
                                                                            (model newTypeName name: 'Exception')) };
×
134
                                                   body: (model newStatementBlock statements:
×
135
                                                                            { (model newThrowStatement expression:
×
136
                                                                                             (model newNewExpression
×
137
                                                                                                      type: (model newClassTypeExpression typeName:
×
138
                                                                                                                               (model newTypeName name: 'RuntimeException'));
×
139
                                                                                                      arguments:
×
140
                                                                                                              { (model newVariableExpression name: 'e') })) });
×
141
                                                   parameter: (model newVariableExpression name: 'e')) }) }).
×
142

×
143
        "private static void initializeReflection() throws NoSuchFieldException, NoSuchMethodException, SecurityException {
×
144
                // fields and constructors will be obtained and set as accessible here
×
145
        }"
×
146
        declaration := model newMethodEntity name: 'initializeReflection'.
×
147
        declaration cacheAt: #order put: -3.
×
148
        declarations at: -3 put: declaration.
×
149
        declaration
×
150
                type: (model newVoidTypeExpression name: 'void');
×
151
                modifiers: {
×
152
                                (model newModifier token: 'private').
×
153
                                (model newModifier token: 'static') };
×
154
                throws: {
×
155
                                (model newClassTypeExpression typeName:
×
156
                                                 (model newTypeName name: 'NoSuchFieldException')).
×
157
                                (model newClassTypeExpression typeName:
×
158
                                                 (model newTypeName name: 'NoSuchMethodException')).
×
159
                                (model newClassTypeExpression typeName:
×
160
                                                 (model newTypeName name: 'SecurityException')) };
×
161
                statementBlock: (initStatementBlock := model newStatementBlock).
×
162

×
163
        "public static void setField(Object object, Field field, Object value) {
×
164
                try {
×
165
                        field.set(object, value);
×
166
                } catch (IllegalAccessException e) {
×
167
                        throw new RuntimeException(e);
×
168
                }
×
169
        }"
×
170
        declaration := model newMethodEntity name: 'setField'.
×
171
        declaration cacheAt: #order put: -2.
×
172
        declarations at: -2 put: declaration.
×
173
        declaration
×
174
                type: (model newVoidTypeExpression name: 'void');
×
175
                parameters: {
×
176
                                (model newParameter
×
177
                                         variable: (model newVariableExpression name: 'object');
×
178
                                         type: (model newClassTypeExpression typeName:
×
179
                                                                  (model newTypeName name: 'Object'))).
×
180
                                (model newParameter
×
181
                                         variable: (model newVariableExpression name: 'field');
×
182
                                         type: (model newClassTypeExpression typeName:
×
183
                                                                  (model newTypeName name: 'Field'))).
×
184
                                (model newParameter
×
185
                                         variable: (model newVariableExpression name: 'value');
×
186
                                         type: (model newClassTypeExpression typeName:
×
187
                                                                  (model newTypeName name: 'Object'))) };
×
188
                modifiers: {
×
189
                                (model newModifier token: 'public').
×
190
                                (model newModifier token: 'static') };
×
191
                statementBlock:
×
192
                        (model newStatementBlock statements: { (model newTryCatchStatement
×
193
                                          try: (model newStatementBlock statements:
×
194
                                                                   { (model newExpressionStatement expression:
×
195
                                                                                    (model newMethodInvocation
×
196
                                                                                             receiver: (model newVariableExpression name: 'field');
×
197
                                                                                             name: 'set';
×
198
                                                                                             arguments: {
×
199
                                                                                                             (model newVariableExpression name: 'object').
×
200
                                                                                                             (model newVariableExpression name: 'value') })) });
×
201
                                          catches: { (model newCatchPartStatement
×
202
                                                           catchedTypes: { (model newClassTypeExpression typeName:
×
203
                                                                                    (model newTypeName name: 'IllegalAccessException')) };
×
204
                                                           body: (model newStatementBlock statements:
×
205
                                                                                    { (model newThrowStatement expression:
×
206
                                                                                                     (model newNewExpression
×
207
                                                                                                              type: (model newClassTypeExpression typeName:
×
208
                                                                                                                                       (model newTypeName name: 'RuntimeException'));
×
209
                                                                                                              arguments:
×
210
                                                                                                                      { (model newVariableExpression name: 'e') })) });
×
211
                                                           parameter: (model newVariableExpression name: 'e')) }) }).
×
212

×
213
        "private static <T> T instantiate(Constructor<T> constructor, Object... arguments) {
×
214
                T instance;
×
215
                try {
×
216
                        instance = constructor.newInstance(arguments);
×
217
                } catch (Exception e) {
×
218
                        throw new RuntimeException(e);
×
219
                }
×
220
                return instance;
×
221
        }"
×
222
        declaration := model newMethodEntity name: 'instantiate'.
×
223
        declaration cacheAt: #order put: -1.
×
224
        declarations at: -1 put: declaration.
×
225
        declaration
×
226
                typeParameters: { (model newTypeParameterExpression name: 'T') };
×
227
                type:
×
228
                        (model newClassTypeExpression typeName:
×
229
                                         (model newTypeName name: 'T'));
×
230
                parameters: {
×
231
                                (model newParameter
×
232
                                         variable: (model newVariableExpression name: 'constructor');
×
233
                                         type: (model newClassTypeExpression
×
234
                                                          arguments: { (model newClassTypeExpression typeName:
×
235
                                                                                   (model newTypeName name: 'T')) };
×
236
                                                          typeName: (model newTypeName name: 'Constructor'))).
×
237
                                (model newParameter
×
238
                                         variable: (model newVariableExpression name: 'arguments');
×
239
                                         type: (model newClassTypeExpression typeName:
×
240
                                                                  (model newTypeName name: 'Object'));
×
241
                                         hasVariableArguments: true) };
×
242
                modifiers: {
×
243
                                (model newModifier token: 'private').
×
244
                                (model newModifier token: 'static') };
×
245
                statementBlock: (model newStatementBlock statements: {
×
246
                                         (model newVarDeclStatement
×
247
                                                  type:
×
248
                                                          (model newClassTypeExpression typeName:
×
249
                                                                           (model newTypeName name: 'T'));
×
250
                                                  declarators: { (model newVariableDeclarator variable:
×
251
                                                                           (model newVariableExpression name: 'instance')) }).
×
252
                                         (model newTryCatchStatement
×
253
                                                  try: (model newStatementBlock statements:
×
254
                                                                           { (model newExpressionStatement expression:
×
255
                                                                                            (model newAssignmentExpression
×
256
                                                                                                     variable:
×
257
                                                                                                             (model newVariableExpression name: 'instance');
×
258
                                                                                                     expression: (model newMethodInvocation
×
259
                                                                                                                      receiver:
×
260
                                                                                                                              (model newIdentifier name: 'constructor');
×
261
                                                                                                                      name: 'newInstance';
×
262
                                                                                                                      arguments:
×
263
                                                                                                                              { (model newVariableExpression name: 'arguments') });
×
264
                                                                                                     operator: '=')) });
×
265
                                                  catches: { (model newCatchPartStatement
×
266
                                                                   catchedTypes: { (model newClassTypeExpression typeName:
×
267
                                                                                            (model newTypeName name: 'Exception')) };
×
268
                                                                   body: (model newStatementBlock statements:
×
269
                                                                                            { (model newThrowStatement expression:
×
270
                                                                                                             (model newNewExpression
×
271
                                                                                                                      type: (model newClassTypeExpression typeName:
×
272
                                                                                                                                               (model newTypeName name: 'RuntimeException'));
×
273
                                                                                                                      arguments:
×
274
                                                                                                                              { (model newVariableExpression name: 'e') })) });
×
275
                                                                   parameter: (model newVariableExpression name: 'e')) }).
×
276
                                         (model newReturnStatement expression:
×
277
                                                  (model newVariableExpression name: 'instance')) }).
×
278

×
279
        ^ initStatementBlock
×
280
]
×
281

282
{ #category : 'private' }
283
FamixValue2FASTJavaVisitor >> ensureReflectionConstructor: aFamixJavaMethod [
×
284

×
285
        | declarations initStatementBlock varName |
×
286
        declarations := self objectExportStrategy currentDeclarations.
×
287
        initStatementBlock := self ensureReflection.
×
288
        varName := aFamixJavaMethod name asUppercase.
×
289
        self reflections at: aFamixJavaMethod put: varName.
×
290

×
291
        declarations
×
292
                at: varName
×
293
                ifPresent: [ "handle naming collisions, some ideas:
×
294
                                - use fully qualified type name (only when necessary?)
×
295
                                - use a HashMap to store the fields based on class and attribute name"
×
296
                        self shouldBeImplemented ]
×
297
                ifAbsentPut: [
×
298
                        model newVarDeclStatement
×
299
                                type: (model newClassTypeExpression
×
300
                                                 typeName: (model newTypeName name: 'Constructor');
×
301
                                                 arguments:
×
302
                                                         { (self builder referType: aFamixJavaMethod parentType) });
×
303
                                modifiers: {
×
304
                                                (model newModifier token: 'private').
×
305
                                                (model newModifier token: 'static') };
×
306
                                declarators: { (model newVariableDeclarator variable:
×
307
                                                 (model newVariableExpression name: varName)) } ].
×
308

×
309
        "add the code in `initializeReflection` to get the Field and set it as accessible:
×
310
        CLASSNAME = CLASS.class.getDeclaredConstructor(PARAMETER_CLASSES);
×
311
        CLASSNAME.setAccessible(true);"
×
312
        initStatementBlock addStatement:
×
313
                (model newExpressionStatement expression:
×
314
                         (model newAssignmentExpression
×
315
                                  variable: (model newVariableExpression name: varName);
×
316
                                  expression: (model newMethodInvocation
×
317
                                                   receiver: (model newClassProperty
×
318
                                                                    type:
×
319
                                                                            (self builder referType: aFamixJavaMethod parentType);
×
320
                                                                    fieldName: 'class');
×
321
                                                   name: 'getDeclaredConstructor';
×
322
                                                   arguments:
×
323
                                                           (aFamixJavaMethod sortedParameters collect: [ :parameter |
×
324
                                                                    model newClassProperty
×
325
                                                                            type: (self builder referType: parameter declaredType);
×
326
                                                                            fieldName: 'class' ]));
×
327
                                  operator: '=')).
×
328
        initStatementBlock addStatement:
×
329
                (model newExpressionStatement expression:
×
330
                         (model newMethodInvocation
×
331
                                  receiver: (model newVariableExpression name: varName);
×
332
                                  name: 'setAccessible';
×
333
                                  arguments: { (model newBooleanLiteral primitiveValue: 'true') }))
×
334
]
×
335

336
{ #category : 'private' }
337
FamixValue2FASTJavaVisitor >> ensureReflectionField: aFamixJavaAttribute [
×
338
        "Ensure an attribute exists to hold the java.lang.reflect.Field for the given attribute."
×
339

×
340
        ^ self reflections at: aFamixJavaAttribute ifAbsentPut: [
×
341
                  | declarations initStatementBlock varName |
×
342
                  declarations := self objectExportStrategy currentDeclarations.
×
343
                  initStatementBlock := self ensureReflection.
×
344
                  varName := aFamixJavaAttribute parentType name asUppercase , '_'
×
345
                             , aFamixJavaAttribute name asUppercase.
×
346

×
347
                  declarations
×
348
                          at: varName
×
349
                          ifPresent: [ "handle naming collisions, some ideas:
×
350
                                - use fully qualified type name (only when necessary?)
×
351
                                - use a HashMap to store the fields based on class and attribute name"
×
352
                                  self shouldBeImplemented ]
×
353
                          ifAbsentPut: [
×
354
                                  model newVarDeclStatement
×
355
                                          type: (model newClassTypeExpression typeName:
×
356
                                                                   (model newTypeName name: 'Field'));
×
357
                                          modifiers: {
×
358
                                                          (model newModifier token: 'private').
×
359
                                                          (model newModifier token: 'static') };
×
360
                                          declarators: { (model newVariableDeclarator variable:
×
361
                                                                   (model newVariableExpression name: varName)) } ].
×
362

×
363
                  "add the code in `initializeReflection` to get the Field and set it as accessible:
×
364
                  CLASSNAME_FIELDNAME = CLASS.class.getDeclaredField(FIELDNAME);
×
365
                  CLASSNAME.setAccessible(true);"
×
366
                  initStatementBlock addStatement:
×
367
                          (model newExpressionStatement expression:
×
368
                                   (model newAssignmentExpression
×
369
                                            variable: (model newVariableExpression name: varName);
×
370
                                            expression: (model newMethodInvocation
×
371
                                                             receiver: (model newClassProperty
×
372
                                                                              type:
×
373
                                                                                      (self builder referType:
×
374
                                                                                                       aFamixJavaAttribute parentType);
×
375
                                                                              fieldName: 'class');
×
376
                                                             name: 'getDeclaredField';
×
377
                                                             arguments:
×
378
                                                             { (model newStringLiteral primitiveValue:
×
379
                                                                      aFamixJavaAttribute name) });
×
380
                                            operator: '=')).
×
381
                  initStatementBlock addStatement:
×
382
                          (model newExpressionStatement expression:
×
383
                                   (model newMethodInvocation
×
384
                                            receiver: (model newVariableExpression name: varName);
×
385
                                            name: 'setAccessible';
×
386
                                            arguments:
×
387
                                                    { (model newBooleanLiteral primitiveValue: 'true') })).
×
388
                  varName ]
×
389
]
×
390

391
{ #category : 'private' }
392
FamixValue2FASTJavaVisitor >> filterAttributesToSet: attributes for: type [
1✔
393
        "No need to set attributes that are set in the constructor."
1✔
394

1✔
395
        | attributesToIgnore |
1✔
396
        "there can be more attributes to ignore in the future"
1✔
397
        attributesToIgnore := type entityCache
1✔
398
                                      at: #syntheticOuterInstance
1✔
399
                                      ifPresent: [ :field | { field } ]
1✔
400
                                      ifAbsent: [ #(  ) ].
1✔
401

1✔
402
        ^ constructorCache
1✔
403
                  at: type
1✔
404
                  ifPresent: [ :constructor |
1✔
405
                          constructorCache
1✔
406
                                  at: constructor
1✔
407
                                  ifPresent: [ :constructorAttributes |
1✔
408
                                          attributes reject: [ :attribute |
1✔
409
                                                  | codeAttribute |
1✔
410
                                                  codeAttribute := attribute attribute.
1✔
411
                                                  (constructorAttributes includes: codeAttribute) or: [
1✔
412
                                                          attributesToIgnore includes: codeAttribute ] ] ]
1✔
413
                                  ifAbsent: [ attributes ] ]
1✔
414
                  ifAbsent: [ attributes ]
1✔
415
]
1✔
416

417
{ #category : 'ast' }
418
FamixValue2FASTJavaVisitor >> finalizeHelpers [
×
419
        "This method must be called after all of the other value exports are done."
×
420

×
421
        self objectExportStrategy makeHelpersOn: self
×
422
]
×
423

424
{ #category : 'private' }
425
FamixValue2FASTJavaVisitor >> findConstructorFor: object [
1✔
426
        "Order all constructors according to attributes set and number of parameters, then choose best public one.
1✔
427
        If no constructor is explicitly declared, use the default constructor. Otherwise, rely on reflection."
1✔
428

1✔
429
        | type |
1✔
430
        type := object type.
1✔
431
        ^ constructorCache at: type ifAbsentPut: [
1✔
432
                  type constructorsOrderedByScore
1✔
433
                          ifEmpty: [ "use default constructor, this acts as a null object"
1✔
434
                                  FamixJavaMethod new ]
1✔
435
                          ifNotEmpty: [ :constructors |
1✔
436
                                  constructors
1✔
437
                                          detect: [ :constructor | constructor isPublic ]
1✔
438
                                          ifNone: [ "fallback to using reflection with constructor with best score"
1✔
439
                                                  | constructor |
1✔
440
                                                  constructor := constructors first.
1✔
441
                                                  self ensureReflectionConstructor: constructor.
1✔
442
                                                  constructor ] ] ]
1✔
443
]
1✔
444

445
{ #category : 'private' }
446
FamixValue2FASTJavaVisitor >> findStaticAttributeMatching: object [
1✔
447
        "Only try to find static attributes for object composed of only primitive values, for now."
1✔
448

1✔
449
        | type staticAttributes objAttributes |
1✔
450
        type := object type.
1✔
451
        ((objAttributes := object relevantAttributes) allSatisfy: [
1✔
452
                 :attribute | attribute value isOfPrimitiveType ]) ifFalse: [ ^ nil ].
1✔
453
        "eligible attributes are public, static, of the same type, and have an initializer expression; the conditions are ordered for time efficiency"
1✔
454
        staticAttributes := staticAttributesCache at: type ifAbsentPut: [
1✔
455
                                    type attributes select: [ :attribute |
1✔
456
                                            attribute declaredType == object type and: [
1✔
457
                                                    attribute isStaticConstant ] ] ].
1✔
458
        staticAttributes ifEmpty: [ ^ nil ].
1✔
459
        "get a list of the object's attributes represented as source code literals"
1✔
460
        objAttributes := objAttributes collect: [ :objAttribute |
1✔
461
                                 objAttribute value type name = 'String'
1✔
462
                                         ifTrue: [ '"' , objAttribute value value , '"' ]
1✔
463
                                         ifFalse: [ objAttribute value value asString ] ].
1✔
464
        ^ staticAttributes
1✔
465
                  detect: [ :attribute |
1✔
466
                  attribute initializerMatchesValues: objAttributes ]
1✔
467
                  ifNone: nil
1✔
468
]
1✔
469

470
{ #category : 'exporting' }
471
FamixValue2FASTJavaVisitor >> helperPackageFor: type [
×
472
        "Most helper methods will be put in a common helper class in a default location.
×
473
        However, types with protected or package-private visibility need to be created in the same package they are declared in.
×
474
        This means a helper class should be created in the same package."
×
475

×
476
        | container |
×
477
        type isPublic ifTrue: [ ^ self defaultHelperPackage ].
×
478

×
479
        (type isProtected or: [ type isPackageVisibility ]) ifFalse: [
×
480
                FamixValueExporterError signal:
×
481
                        'Cannot recreate object of private class: '
×
482
                        , type mooseNameWithDots ].
×
483

×
484
        "requires the helper to be in the same package"
×
485
        container := type.
×
486
        [ (container := container typeContainer) isPackage ] whileFalse.
×
487
        ^ container mooseNameWithDots
×
488
]
×
489

490
{ #category : 'initialization' }
491
FamixValue2FASTJavaVisitor >> initialize [
1✔
492

1✔
493
        constructorCache := IdentityDictionary new.
1✔
494
        staticAttributesCache := IdentityDictionary new
1✔
495
]
1✔
496

497
{ #category : 'ast' }
498
FamixValue2FASTJavaVisitor >> makeClassTypeExpression: typeName [
1✔
499

1✔
500
        ^ self model newClassTypeExpression typeName:
1✔
501
                  (model newTypeName name: typeName)
1✔
502
]
1✔
503

504
{ #category : 'ast' }
505
FamixValue2FASTJavaVisitor >> makeConstructorInvocation: constructor for: object [
1✔
506
        "Make the statement calling the constructor and return the invocation expression."
1✔
507

1✔
508
        | invocation statement |
1✔
509
        statement := self makeVarDeclStatement: object.
1✔
510
        invocation := statement declarators first expression.
1✔
511

1✔
512
        "If the object is a non-static inner class, it has a reference to its enclosing instance.
1✔
513
        It must be exported beforehand, and it must be the receiver of the invocation."
1✔
514
        object type cacheAt: #syntheticOuterInstance ifPresent: [ :field |
1✔
515
                | outerInstance |
1✔
516
                outerInstance := object value detect: [ :attribute |
1✔
517
                                         attribute attribute == field ].
1✔
518
                self ensureVisited: outerInstance value.
1✔
519
                invocation receiver: (self objectExportStrategy
1✔
520
                                 makeVariableExpression: outerInstance
1✔
521
                                 on: self) ].
1✔
522

1✔
523
        "handle exceptions thrown by the constructor and rethrow as an unchecked RuntimeException"
1✔
524
        constructor thrownExceptions ifNotEmpty: [ :exceptions |
1✔
525
                statement := self makeTry: statement catch: exceptions ].
1✔
526

1✔
527
        self statementBlock addStatement: statement.
1✔
528
        ^ invocation
1✔
529
]
1✔
530

531
{ #category : 'helpers' }
532
FamixValue2FASTJavaVisitor >> makeDefaultHelper [
×
533
        "The default helper should always be generated, regardless of strategy."
×
534

×
535
        self makeHelperInPackage: self defaultHelperPackage
×
536
]
×
537

538
{ #category : 'ast' }
539
FamixValue2FASTJavaVisitor >> makeHelperClass [
×
540
        "The helper class should not be instantiated so it has a private constructor."
×
541

×
542
        ^ self model newClassDeclaration
×
543
                  name: 'ModestHelper';
×
544
                  addModifier: (model newModifier token: 'public');
×
545
                  addDeclaration: (model newMethodEntity
×
546
                                   name: 'ModestHelper';
×
547
                                   addModifier: (model newModifier token: 'private');
×
548
                                   statementBlock: model newStatementBlock);
×
549
                  yourself
×
550
]
×
551

552
{ #category : 'ast' }
553
FamixValue2FASTJavaVisitor >> makeHelperInPackage: aPackageName [
×
554

×
555
        | helperClass compilationUnit |
×
556
        helperClass := self makeHelperClass.
×
557
        (compilationUnit := self model newCompilationUnit)
×
558
                packageDeclaration: (model newPackageDeclaration qualifiedName:
×
559
                                         (model newQualifiedName name: aPackageName));
×
560
                importDeclarations: self builder makeImportDeclarations;
×
561
                addClassDeclaration: helperClass.
×
562

×
563
        reflections ifNotNil: [ "TODO reflections per package"
×
564
                compilationUnit
×
565
                        addImportDeclaration: (model newImportDeclaration qualifiedName:
×
566
                                                 (model newQualifiedName name: 'java.lang.reflect.Field'));
×
567
                        addImportDeclaration: (model newImportDeclaration qualifiedName:
×
568
                                                 (model newQualifiedName name: 'java.lang.reflect.Constructor'));
×
569
                        addImportDeclaration: (model newImportDeclaration qualifiedName:
×
570
                                                 (model newQualifiedName name:
×
571
                                                                  'java.lang.reflect.InvocationTargetException')) ].
×
572

×
573
        ^ helperClass
×
574
]
×
575

576
{ #category : 'ast' }
577
FamixValue2FASTJavaVisitor >> makeNewExpression: object [
1✔
578

1✔
579
        ^ self model newNewExpression type:
1✔
580
                  (object asFASTJavaTypeExpressionOn: self)
1✔
581
        "(self makeClassTypeExpression: object type concreteTypeName)"
1✔
582
]
1✔
583

584
{ #category : 'ast' }
NEW
585
FamixValue2FASTJavaVisitor >> makeReflectionConstructorInvocation: constructorVarName for: object [
×
NEW
586
        "Use `T instantiate(Constructor<T> constructor, Object... arguments)` from the UT helper.
×
NEW
587
        constructorVarName is the name of the variable that contains the Constructor object."
×
NEW
588

×
NEW
589
        | invocation |
×
NEW
590
        (invocation := model newMethodInvocation name: 'instantiate')
×
NEW
591
                addArgument: (model newVariableExpression name: constructorVarName).
×
NEW
592

×
NEW
593
        "If the object is a non-static inner class, it has a reference to its enclosing instance.
×
NEW
594
        It must be exported beforehand, and it must be the first argument of the invocation."
×
NEW
595
        object type cacheAt: #syntheticOuterInstance ifPresent: [ :field |
×
NEW
596
                | outerInstance |
×
NEW
597
                outerInstance := object value detect: [ :attribute |
×
NEW
598
                                         attribute attribute == field ].
×
NEW
599
                self ensureVisited: outerInstance value.
×
NEW
600
                invocation addArgument: (self objectExportStrategy
×
NEW
601
                                 makeVariableExpression: outerInstance
×
NEW
602
                                 on: self) ].
×
NEW
603

×
NEW
604
        "invoke the method and store the result in a variable"
×
NEW
605
        (self statementBlock addStatement: self model newVarDeclStatement)
×
NEW
606
                type: (self builder referType: object type);
×
NEW
607
                addDeclarator: (model newVariableDeclarator
×
NEW
608
                                 variable: (self makeVariableExpression: object);
×
NEW
609
                                 expression: invocation).
×
NEW
610
        ^ invocation
×
NEW
611
]
×
612

613
{ #category : 'ast' }
614
FamixValue2FASTJavaVisitor >> makeReflectionFieldGetter: attribute [
×
615

×
616
        ^ self model newMethodInvocation
×
617
                  receiver: (model newClassProperty
×
618
                                   type:
×
619
                                           (self makeClassTypeExpression: attribute object type typeName);
×
620
                                   fieldName: 'class');
×
621
                  name: 'getDeclaredField';
×
622
                  addArgument:
×
623
                  (model newStringLiteral primitiveValue: attribute attribute name);
×
624
                  yourself
×
625
]
×
626

627
{ #category : 'ast' }
628
FamixValue2FASTJavaVisitor >> makeReflectionSetterInvocation: attribute [
×
629
        "Use reflection to set an attribute on an object."
×
630

×
631
        | fieldName |
×
632
        fieldName := self ensureReflectionField: attribute attribute.
×
633
        self statementBlock addStatement:
×
634
                (self model newExpressionStatement expression:
×
635
                         (model newMethodInvocation
×
636
                                  name: 'setField';
×
637
                                  arguments: {
×
638
                                                  (self makeVariableExpression: attribute object).
×
639
                                                  (model newVariableExpression name: fieldName).
×
640
                                                  (model newVariableExpression name: attribute value varName) }))
×
641
]
×
642

643
{ #category : 'ast' }
644
FamixValue2FASTJavaVisitor >> makeSetterInvocation: setter for: attribute [
1✔
645

1✔
646
        self
1✔
647
                makeSetterInvocation: setter
1✔
648
                for: attribute
1✔
649
                named: (self varNameFor: attribute value)
1✔
650
]
1✔
651

652
{ #category : 'ast' }
653
FamixValue2FASTJavaVisitor >> makeSetterInvocation: setter for: attribute named: aString [
1✔
654

1✔
655
        | invocation |
1✔
656
        (invocation := self model newExpressionStatement) expression:
1✔
657
                (model newMethodInvocation
1✔
658
                         receiver: (self makeVariableExpression: attribute object);
1✔
659
                         name: setter name;
1✔
660
                         addArgument: (model newVariableExpression name: aString);
1✔
661
                         famixMethod: setter).
1✔
662

1✔
663
        self statementBlock addStatement: (setter thrownExceptions
1✔
664
                         ifEmpty: [ invocation ]
1✔
665
                         ifNotEmpty: [ :exceptions | "handle setters that can throw exceptions to rethrow an unchecked exception"
1✔
666
                                 self makeTry: invocation catch: exceptions ])
1✔
667
]
1✔
668

669
{ #category : 'ast' }
670
FamixValue2FASTJavaVisitor >> makeTry: statement catch: exceptions [
×
671
        "Wrap the statement in a try-catch for the given exceptions, and rethrow as an unchecked RuntimeException."
×
672

×
673
        ^ self model newTryCatchStatement
×
NEW
674
                  try: (model newStatementBlock
×
NEW
675
                                   addStatement: statement;
×
NEW
676
                                   yourself);
×
NEW
677
                  addCatch: (model newCatchPartStatement
×
678
                                   catchedTypes: (exceptions collect: [ :exception |
×
679
                                                            exception asFASTJavaTypeExpressionOn: self ]);
×
680
                                   parameter: (model newVariableExpression name: 'e');
×
NEW
681
                                   body: (model newStatementBlock
×
NEW
682
                                                    addStatement:
×
NEW
683
                                                            (model newThrowStatement expression:
×
684
                                                                             (model newNewExpression
×
685
                                                                                      type: (model newClassTypeExpression typeName:
×
686
                                                                                                               (model newTypeName name: 'RuntimeException'));
×
NEW
687
                                                                                      addArgument: (model newVariableExpression name: 'e');
×
NEW
688
                                                                                      yourself));
×
NEW
689
                                                    yourself));
×
NEW
690
                  yourself
×
UNCOV
691
]
×
692

693
{ #category : 'ast' }
694
FamixValue2FASTJavaVisitor >> makeVarDeclStatement: value [
1✔
695

1✔
696
        ^ self model newVarDeclStatement
1✔
697
                  type: (value asFASTJavaTypeExpressionOn: self);
1✔
698
                  addDeclarator: (self makeVariableDeclarator: value);
1✔
699
                  yourself
1✔
700
]
1✔
701

702
{ #category : 'ast' }
703
FamixValue2FASTJavaVisitor >> makeVarDeclStatement: object usingStaticAttribute: attribute [
×
704
        "Declare a variable for object, initialized with the value of the given static attribute.
×
705
        For example: MyClass myClass = MyClass.MY_STATIC_ATTRIBUTE;"
×
706

×
707
        self statementBlock addStatement: (model newVarDeclStatement
×
708
                         type: (object asFASTJavaTypeExpressionOn: self);
×
709
                         addDeclarator: (model newVariableDeclarator
×
710
                                          variable: (self makeVariableExpression: object);
×
711
                                          expression: (model newClassProperty
×
712
                                                           type: (self makeClassTypeExpression: object type name);
×
713
                                                           fieldName: attribute name));
×
714
                         yourself)
×
715
]
×
716

717
{ #category : 'ast' }
718
FamixValue2FASTJavaVisitor >> makeVariableDeclarator: value [
1✔
719

1✔
720
        ^ self model newVariableDeclarator
1✔
721
                  variable: (self makeVariableExpression: value);
1✔
722
                  expression: (value asFASTJavaExpressionOn: self)
1✔
723
]
1✔
724

725
{ #category : 'ast' }
726
FamixValue2FASTJavaVisitor >> makeVariableExpression: value [
1✔
727

1✔
728
        ^ self model newVariableExpression name: (self varNameFor: value)
1✔
729
]
1✔
730

731
{ #category : 'accessing' }
732
FamixValue2FASTJavaVisitor >> model [
1✔
733

1✔
734
        ^ model ifNil: [ model := FASTJavaModel new ]
1✔
735
]
1✔
736

737
{ #category : 'accessing' }
738
FamixValue2FASTJavaVisitor >> objectExportStrategy [
1✔
739

1✔
740
        ^ objectExportStrategy ifNil: [
1✔
741
                  objectExportStrategy := FamixValueInlineObjectExportStrategy new ]
1✔
742
]
1✔
743

744
{ #category : 'accessing' }
745
FamixValue2FASTJavaVisitor >> objectExportStrategy: anObjectExportStrategy [
×
746

×
747
        objectExportStrategy := anObjectExportStrategy
×
748
]
×
749

750
{ #category : 'accessing' }
751
FamixValue2FASTJavaVisitor >> reflections [
1✔
752

1✔
753
        ^ reflections ifNil: [ reflections := IdentityDictionary new ]
1✔
754
]
1✔
755

756
{ #category : 'accessing' }
757
FamixValue2FASTJavaVisitor >> statementBlock [
1✔
758

1✔
759
        ^ statementBlock ifNil: [
1✔
760
                  statementBlock := self model newStatementBlock ]
1✔
761
]
1✔
762

763
{ #category : 'visiting' }
764
FamixValue2FASTJavaVisitor >> visitCollection: collection [
1✔
765
        "Dispatch to type to handle special cases"
1✔
766

1✔
767
        ^ collection type acceptValueVisitor: self forCollection: collection
1✔
768
]
1✔
769

770
{ #category : 'visiting' }
771
FamixValue2FASTJavaVisitor >> visitCollectionOfRegularType: collection [
1✔
772

1✔
773
        | varName |
1✔
774
        self statementBlock addStatement:
1✔
775
                (self makeVarDeclStatement: collection).
1✔
776
        varName := self varNameDict at: collection.
1✔
777
        collection value do: [ :element |
1✔
778
                self ensureVisited: element value.
1✔
779
                statementBlock addStatement:
1✔
780
                        (self model newExpressionStatement expression:
1✔
781
                                 (model newMethodInvocation
1✔
782
                                          receiver: (model newIdentifier name: varName);
1✔
783
                                          name: 'add';
1✔
784
                                          addArgument: (self makeVariableExpression: element value);
1✔
785
                                          yourself)) ].
1✔
786
        ^ statementBlock
1✔
787
]
1✔
788

789
{ #category : 'visiting' }
790
FamixValue2FASTJavaVisitor >> visitDictionary: dictionary [
1✔
791

1✔
792
        | varName |
1✔
793
        self statementBlock addStatement:
1✔
794
                (self makeVarDeclStatement: dictionary).
1✔
795
        varName := self varNameDict at: dictionary.
1✔
796
        dictionary value do: [ :association |
1✔
797
                self ensureVisited: association key.
1✔
798
                self ensureVisited: association value.
1✔
799
                statementBlock addStatement:
1✔
800
                        (self model newExpressionStatement expression:
1✔
801
                                 (model newMethodInvocation
1✔
802
                                          receiver: (model newIdentifier name: varName);
1✔
803
                                          name: 'put';
1✔
804
                                          addArgument: (self makeVariableExpression: association key);
1✔
805
                                          addArgument: (self makeVariableExpression: association value);
1✔
806
                                          yourself)) ].
1✔
807
        ^ statementBlock
1✔
808
]
1✔
809

810
{ #category : 'visiting' }
811
FamixValue2FASTJavaVisitor >> visitEnumValue: enumValue [
1✔
812

1✔
813
        ^ self statementBlock
1✔
814
                  addStatement: (self makeVarDeclStatement: enumValue);
1✔
815
                  yourself
1✔
816
]
1✔
817

818
{ #category : 'visiting' }
819
FamixValue2FASTJavaVisitor >> visitObject: object [
1✔
820
        "Dispatch to type to handle special cases"
1✔
821

1✔
822
        ^ object type acceptValueVisitor: self forObject: object
1✔
823
]
1✔
824

825
{ #category : 'visiting' }
826
FamixValue2FASTJavaVisitor >> visitObjectAttribute: attribute [
1✔
827

1✔
828
        attribute attribute ifNil: [ "ignore unknown attributes" ^ self ].
1✔
829
        self ensureVisited: attribute value.
1✔
830
        (attribute object type findSetterOf: attribute attribute)
1✔
831
                ifNotNil: [ :setter | self makeSetterInvocation: setter for: attribute ]
1✔
832
                ifNil: [ self makeReflectionSetterInvocation: attribute ]
1✔
833
]
1✔
834

835
{ #category : 'visiting' }
836
FamixValue2FASTJavaVisitor >> visitObjectOfRegularType: object [
1✔
837
        "Dispatched here from #visitObject: if the object has a regular creation schema:
1✔
838
        use constructor, then setup each attribute and use setter or reflection."
1✔
839

1✔
840
        (self findStaticAttributeMatching: object)
1✔
841
                ifNotNil: [ :attribute |
1✔
842
                        self makeVarDeclStatement: object usingStaticAttribute: attribute ]
1✔
843
                ifNil: [ self objectExportStrategy export: object on: self ].
1✔
844
        ^ statementBlock
1✔
845
]
1✔
846

847
{ #category : 'visiting' }
848
FamixValue2FASTJavaVisitor >> visitObjectStub: object [
×
849
        "Previously tried to recreate stubs by deserializing their JSON representation, but getting a correct JSON string is a challenge."
×
850

×
851
        FamixUTStubError signal: object type
×
852
]
×
853

854
{ #category : 'visiting' }
855
FamixValue2FASTJavaVisitor >> visitPrimitive: primitive [
1✔
856

1✔
857
        ^ self statementBlock
1✔
858
                  addStatement: (self makeVarDeclStatement: primitive);
1✔
859
                  yourself
1✔
860
]
1✔
861

862
{ #category : 'visiting' }
863
FamixValue2FASTJavaVisitor >> visitTypeReference: aFamixValueOfTypeReference [
×
864
        "Class xxx = MyClass.class;"
×
865

×
866
        ^ self statementBlock
×
867
                  addStatement: (model newVarDeclStatement
×
868
                                   type: (self makeClassTypeExpression: 'Class');
×
869
                                   addDeclarator: (model newVariableDeclarator
×
870
                                                    variable:
×
871
                                                            (self makeVariableExpression: aFamixValueOfTypeReference);
×
872
                                                    expression: (model newClassProperty
×
873
                                                                     type:
×
874
                                                                             (self makeClassTypeExpression:
×
875
                                                                                              aFamixValueOfTypeReference value name);
×
876
                                                                     fieldName: 'class'));
×
877
                                   yourself);
×
878
                  yourself
×
879
]
×
880

881
{ #category : 'visiting' }
882
FamixValue2FASTJavaVisitor >> visitValueOfUnknownType: value [
1✔
883

1✔
884
        ^ self statementBlock
1✔
885
                  addStatement: (self makeVarDeclStatement: value);
1✔
886
                  yourself
1✔
887
]
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc