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

moosetechnology / VerveineJ / 28749328979

05 Jul 2026 05:41PM UTC coverage: 52.626% (+0.2%) from 52.433%
28749328979

Pull #264

github

web-flow
Merge 4354e4c15 into 83f899a8c
Pull Request #264: Dealing with record declaration

2023 of 4018 branches covered (50.35%)

Branch coverage included in aggregate %.

44 of 58 new or added lines in 5 files covered. (75.86%)

1 existing line in 1 file now uncovered.

4469 of 8318 relevant lines covered (53.73%)

2.22 hits per line

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

74.36
/app/src/main/java/fr/inria/verveine/extractor/java/EntityDictionary.java
1
package fr.inria.verveine.extractor.java;
2

3
import java.util.*;
4

5
import org.eclipse.jdt.core.dom.ASTNode;
6
import org.eclipse.jdt.core.dom.ArrayType;
7
import org.eclipse.jdt.core.dom.IBinding;
8
import org.eclipse.jdt.core.dom.IMethodBinding;
9
import org.eclipse.jdt.core.dom.IPackageBinding;
10
import org.eclipse.jdt.core.dom.ITypeBinding;
11
import org.eclipse.jdt.core.dom.IVariableBinding;
12
import org.eclipse.jdt.core.dom.MethodDeclaration;
13
import org.eclipse.jdt.core.dom.Modifier;
14
import org.eclipse.jdt.core.dom.QualifiedType;
15
import org.eclipse.jdt.core.dom.SimpleType;
16
import org.moosetechnology.model.famix.famixjavaentities.*;
17
import org.moosetechnology.model.famix.famixjavaentities.Class;
18
import org.moosetechnology.model.famix.famixjavaentities.Enum;
19
import org.moosetechnology.model.famix.famixjavaentities.Exception;
20
import org.moosetechnology.model.famix.famixjavaentities.Package;
21
import org.moosetechnology.model.famix.famixtraits.TAccessible;
22
import org.moosetechnology.model.famix.famixtraits.TAssociation;
23
import org.moosetechnology.model.famix.famixtraits.TCanBeClassSide;
24
import org.moosetechnology.model.famix.famixtraits.TCanBeFinal;
25
import org.moosetechnology.model.famix.famixtraits.TCanBeStub;
26
import org.moosetechnology.model.famix.famixtraits.TCanImplement;
27
import org.moosetechnology.model.famix.famixtraits.THasVisibility;
28
import org.moosetechnology.model.famix.famixtraits.TImplementable;
29
import org.moosetechnology.model.famix.famixtraits.TImplementation;
30
import org.moosetechnology.model.famix.famixtraits.TInheritance;
31
import org.moosetechnology.model.famix.famixtraits.TInvocationsReceiver;
32
import org.moosetechnology.model.famix.famixtraits.TMethod;
33
import org.moosetechnology.model.famix.famixtraits.TNamedEntity;
34
import org.moosetechnology.model.famix.famixtraits.TParametricEntity;
35
import org.moosetechnology.model.famix.famixtraits.TParametricAssociation;
36
import org.moosetechnology.model.famix.famixtraits.TReference;
37
import org.moosetechnology.model.famix.famixtraits.TSourceEntity;
38
import org.moosetechnology.model.famix.famixtraits.TStructuralEntity;
39
import org.moosetechnology.model.famix.famixtraits.TThrowable;
40
import org.moosetechnology.model.famix.famixtraits.TType;
41
import org.moosetechnology.model.famix.famixtraits.TTypeArgument;
42
import org.moosetechnology.model.famix.famixtraits.TTypedEntity;
43
import org.moosetechnology.model.famix.famixtraits.TWithAccesses;
44
import org.moosetechnology.model.famix.famixtraits.TWithAnnotationInstances;
45
import org.moosetechnology.model.famix.famixtraits.TWithAttributes;
46
import org.moosetechnology.model.famix.famixtraits.TWithComments;
47
import org.moosetechnology.model.famix.famixtraits.TWithInheritances;
48
import org.moosetechnology.model.famix.famixtraits.TWithLocalVariables;
49
import org.moosetechnology.model.famix.famixtraits.TWithMethods;
50
import org.moosetechnology.model.famix.famixtraits.TWithTypes;
51

52
import ch.akuhn.fame.Repository;
53
import fr.inria.verveine.extractor.java.utils.ImplicitVarBinding;
54
import fr.inria.verveine.extractor.java.utils.Util;
55

56
/**
57
 * A dictionary of FamixJava entities to help create them and find them back
58
 * Entities are mapped to keys which are the "binding" provided by the JDT pars
59
 * 
60
 * @author anquetil
61
 */
62
public class EntityDictionary {
63

64
        /**
65
         * A property added to CompilationUnits to record the name of the source file they belong to.
66
         * Used to create FileAnchors
67
         */
68
        public static final String SOURCE_FILENAME_PROPERTY = "verveine-source-filename";
69

70
        public static final String DEFAULT_PCKG_NAME = "<Default Package>";
71
        public static final String STUB_METHOD_CONTAINER_NAME = "<StubMethodContainer>";
72
        public static final String THIS_NAME = "this";
73
        public static final String SUPER_NAME = "super";
74
        
75
        public static final String OBJECT_NAME = "Object";
76
        public static final String METACLASS_NAME = "Class";
77
        public static final String OBJECT_PACKAGE_NAME = "java.lang";
78
        public static final String ARRAYS_NAME = "default[]";
79
        public static final String INIT_BLOCK_NAME = "<Initializer>";
80
        public static final String ANONYMOUS_NAME_PREFIX = "_Anonymous";
81

82
        public static final int UNKNOWN_MODIFIERS = 0;
83
        public static final String MODIFIER_PUBLIC   = "public";
84
        public static final String MODIFIER_PRIVATE  = "private";
85
        public static final String MODIFIER_PROTECTED= "protected";
86
        public static final String MODIFIER_PACKAGE = "package";
87

88
    /**
89
     * The symbol kind to use to define that a method is a default implementation in an interface
90
     */
91
    public static final String DEFAULT_IMPLEMENTATION_KIND_MARKER = "default";
92

93
        /** name of the entity representing the "unknown" type 'var'
94
         * The entity is intended to be unique, see {@link #ensureFamixUniqEntity(java.lang.Class, IBinding , String )}
95
         */
96
        public static final String IMPLICIT_VAR_TYPE_NAME = "<ImplicitVarType>";
97

98
        /**
99
         * The FAMIX repository where all FAMIX entities are created and stored
100
         */
101
        protected Repository famixRepo;
102

103
        /**
104
         * A dictionary to map a key (provided by the user) to FAMIX Entity
105
         */
106
        protected Map<IBinding,TNamedEntity> keyToEntity;
107
        /**
108
         * A reverse dictionary (see {@link #keyToEntity}) to find the key of an entity.
109
         */
110
        protected Map<TNamedEntity,IBinding> entityToKey;
111

112
        /**
113
         * Another dictionary to map a name to FAMIX Entities with this name
114
         */
115
        protected Map<String,Collection<TNamedEntity>> nameToEntity;
116

117
        /**
118
         * Yet another dictionary for implicit variables ('self' and 'super')
119
         * Because they are implicit, they may not have a binding provided by the parser,
120
         * or may have the same binding as their associated type so they can't be kept easily in {@link #keyToEntity}
121
         */
122
        @Deprecated
123
        protected Map<Type,ImplicitVars> typeToImpVar;
124

125
        /**
126
         * Used to keep the two possible ImplicitVariable for a given Class binding
127
         * @author anquetil
128
         */
129
        @Deprecated
130
        protected class ImplicitVars {
×
131
                public ImplicitVariable self_iv;
132
                public ImplicitVariable super_iv;
133
        }
134
        
135
        /**
136
         * Result of utility methods for checking matching between two entities
137
         */
138
        private enum CheckResult {
3✔
139
                MATCH, UNDECIDED, FAIL;
18✔
140
        }
141

142

143
        /** Constructor taking a FAMIX repository
144
         * @param famixRepo
145
         */
146
        public EntityDictionary(Repository famixRepo) {
2✔
147
                        this.famixRepo = famixRepo;
3✔
148
                        
149
                        this.keyToEntity = new Hashtable<IBinding,TNamedEntity>();
5✔
150
                        this.entityToKey = new Hashtable<TNamedEntity,IBinding>();
5✔
151
                        this.nameToEntity = new Hashtable<String,Collection<TNamedEntity>>();
5✔
152
                        this.typeToImpVar = new Hashtable<Type,ImplicitVars>();
5✔
153
                        
154
                        if (! this.famixRepo.isEmpty()) {
4✔
155
                                recoverExistingRepository();
2✔
156
                        }
157
                }
1✔
158

159
    /**
160
         * Resets the dictionnary in a proper state after loading entities from an existing MSE file:
161
         * <UL>
162
         * <li>map all named entities to their names in <b>mapName</b></li>
163
         * <li>reset some boolean properties (e.g. <b>isStub</b>) that are false (they are not saved in the mse file and therefore not initialized)</li>
164
         * </ul>
165
         */
166
        protected void recoverExistingRepository() {
167
                for (NamedEntity ent : famixRepo.all(NamedEntity.class)) {
13✔
168
                        try {
169
                                mapEntityToName(ent.getName(), ent);
5✔
170
                        } catch (java.lang.Exception e) {
×
171
                                System.err.println("Error recovering entity " + ent.getName() + " from repository " + famixRepo);
×
172
                        };
1✔
173
                        // for the Exception to be raised, the return value must be tested
174
                        try { if (((TCanBeStub) ent).getIsStub()) {} }
5✔
175
                        catch (NullPointerException e) { ((TCanBeStub)ent).setIsStub(Boolean.FALSE); }
6✔
176
                }
1✔
177

178
                for (Access acc : famixRepo.all(Access.class)) {
13✔
179
                        // for the Exception to be raised, the return value must be tested
180
                        try { if (acc.getIsWrite()) {}        }
4✔
181
                        catch (NullPointerException e) { acc.setIsWrite(Boolean.FALSE); }
5✔
182
                }
1✔
183
        }
1✔
184

185
        protected void mapEntityToName(String name, TNamedEntity ent) {
186
                
187
                Collection<TNamedEntity> l_ent = nameToEntity.get(name);
6✔
188
                if (l_ent == null) {
2✔
189
                        l_ent = new LinkedList<>();
4✔
190
                }
191
                l_ent.add(ent);
4✔
192
                nameToEntity.put(name, l_ent);
6✔
193
        }
1✔
194

195
        public void removeEntity( NamedEntity ent) {
196
                IBinding key;
197
                key = entityToKey.get(ent);
6✔
198
                if (key != null) {
2✔
199
                        entityToKey.remove(ent);
5✔
200
                        keyToEntity.remove(key);
5✔
201
                }
202

203
                Collection<TNamedEntity> l_ent = nameToEntity.get(ent.getName());
7✔
204
                if (l_ent != null) {
2✔
205
                        l_ent.remove(ent);
4✔
206
                }
207

208
                famixRepo.getElements().remove(ent);
6✔
209
        }
1✔
210
        
211
        protected void mapEntityToKey(IBinding key, TNamedEntity ent) {
212
                TNamedEntity old = keyToEntity.get(key);
6✔
213
                if (old != null) {
2✔
214
                        entityToKey.remove(old);
5✔
215
                }
216
                keyToEntity.put(key, ent);
6✔
217
                entityToKey.put(ent, key);
6✔
218
        }
1✔
219
        
220
        /**
221
         * Returns all the Famix Entity with the given name and class 
222
         * @param fmxClass -- the subtype of Famix Entity we are looking for
223
         * @param name -- the name of the entity
224
         * @return the Collection of Famix Entities with the given name and class (possibly empty)
225
         */
226
        @SuppressWarnings("unchecked")
227
        public <T extends TNamedEntity> Collection<T> getEntityByName(java.lang.Class<T> fmxClass, String name) {
228
                Collection<T> ret = new LinkedList<T>();
4✔
229
                Collection<TNamedEntity> l_name = nameToEntity.get(name);
6✔
230
                
231
                if (l_name != null ) {
2✔
232
                        for (TNamedEntity obj : l_name) {
10✔
233
                                if (fmxClass.isInstance(obj)) {
4✔
234
                                        ret.add((T) obj);
4✔
235
                                }
236
                        }
1✔
237
                }
238

239
                return ret;
2✔
240
        }
241

242
        /**
243
         * Returns the Famix Entity associated to the given key.
244
         * <b>Note</b>: Be careful that ImplicitVariables share the same binding as their associated Class and cannot be retrieved with this method.
245
         * In such a case, this method will always retrieve the Class associated to the key.
246
         * To get an ImplicitVariable from the key, use {@link #getImplicitVariableByBinding(IBinding, String)}
247
         * @param key -- the key
248
         * @return the Famix Entity associated to the binding or null if not found
249
         */
250
        public TNamedEntity getEntityByKey(IBinding key) {
251
                if (key == null) {
2✔
252
                        return null;
2✔
253
                }
254
                else {
255
                        return keyToEntity.get(key);
6✔
256
                }
257
        }
258

259
        /**
260
         * Returns the Famix Entity associated to the given key if it is an instance of the given Famix class.
261
         * This avoids recovering a binding as another kind of entity than the one requested.
262
         * @param key -- the key
263
         * @param fmxClass -- the expected Famix class
264
         * @return the Famix Entity associated to the binding or null if not found or not an instance of <b>fmxClass</b>
265
         */
266
        protected <T extends TNamedEntity> T getEntityByKey(IBinding key, java.lang.Class<T> fmxClass) {
267
                TNamedEntity fmx = getEntityByKey(key);
4✔
268
                if (fmxClass.isInstance(fmx)) {
4✔
269
                        return fmxClass.cast(fmx);
5✔
270
                }
271
                return null;
2✔
272
        }
273

274
        /**
275
         * Returns the key associated to a Famix Entity.
276
         * @param e -- the Named entity
277
         * @return the key associated to this entity or null if none
278
         */
279
        public IBinding getEntityKey(TNamedEntity e) {
280
                return entityToKey.get(e);
6✔
281
        }
282

283
        /**
284
         * Creates and returns a FAMIX Entity of the type <b>fmxjava.lang.Class</b>.
285
         * The Entity is always created.
286
         * @param fmxClass -- the FAMIX class of the instance to create
287
         * @param name -- the name of the new instance must not be null (and this is not tested)
288
         * @return the FAMIX Entity or null in case of a FAMIX error
289
         */
290
        protected <T extends TNamedEntity & TSourceEntity> T createFamixEntity(java.lang.Class<T> fmxClass, String name) {
291
                T fmx = null;
2✔
292

293
                if (name == null) {
2!
294
                        return null;
×
295
                }
296
                
297
                try {
298
                        fmx = fmxClass.getDeclaredConstructor().newInstance();
9✔
299
                } catch (java.lang.Exception e) {
×
300
                        System.err.println("Unexpected error, could not create a FAMIX entity: "+e.getMessage());
×
301
                        e.printStackTrace();
×
302
                }
1✔
303
                
304
                if (fmx != null) {
2!
305
                        fmx.setName(name);
3✔
306
                        if (fmx instanceof TCanBeStub) {
3!
307
                                ((TCanBeStub)fmx).setIsStub(Boolean.TRUE);
4✔
308
                        }
309

310
                        mapEntityToName(name, fmx);
4✔
311
                        
312
                        // put new entity in Famix repository
313
                        famixRepoAdd((Entity) fmx);
4✔
314
                }
315

316
                return fmx;
2✔
317
        }
318
        
319
        /**
320
         * Returns a Famix Entity of the type <b>fmxjava.lang.Class</b> and maps it to its binding <b>bnd</b> (if not null).
321
         * The Entity is created if it did not exist.
322
         * @param fmxClass -- the Famix class of the instance to create
323
         * @param bnd -- the binding to map to the new instance
324
         * @param name -- the name of the new instance (used if <pre>{@code bnd == null}</pre>)
325
         * @return the Famix Entity or null if <b>bnd</b> was null or in case of a Famix error
326
         */
327
        @SuppressWarnings("unchecked")
328
        protected <T extends TNamedEntity & TSourceEntity> T ensureFamixEntity(java.lang.Class<T> fmxClass, IBinding bnd, String name) {
329
                T fmx = null;
2✔
330
                
331
                /* 
332
                 * Unfortunately different entities with the same name and same type may exist
333
                 * e.g. 2 parameters of 2 different methods but having the same name
334
                 * so we cannot recover just from the name
335
                 */
336

337
                if (bnd != null) {
2✔
338
                        fmx = getEntityByKey(bnd, fmxClass);
5✔
339
                        if (fmx != null) {
2✔
340
                                return fmx;
2✔
341
                        }
342
                }
343

344
                // else
345
                fmx = createFamixEntity(fmxClass, name);
5✔
346
                if ( (bnd != null) && (fmx != null) ) {
4!
347
                        mapEntityToKey(bnd, fmx);
4✔
348
                }
349
                
350
                return fmx;
2✔
351
        }
352

353
        /**
354
         * Adds an already created Entity to the Famix repository
355
         * Used mainly for non-NamedEntity, for example relationships
356
         * @param e -- the Famix entity to add to the repository
357
         */
358
        public void famixRepoAdd(Entity e) {
359
                this.famixRepo.add(e);
4✔
360
        }
1✔
361

362

363
        /**
364
         * Returns a Famix ParametricClass with the given <b>name</b>, creating it if it does not exist yet
365
         * In the second case, sets some default properties: not Abstract, not Final, not Private, not Protected, not Public, not Interface
366
         * @param name -- the name of the Famix Class
367
         * @return the Famix Class or null in case of a Famix error
368
         */
369
        public ParametricClass ensureFamixParametricClass(ITypeBinding key, String name, TWithTypes owner) {
370
                ParametricClass fmx = ensureFamixEntity(ParametricClass.class, key, name);
7✔
371
                if(key != null) {
2✔
372
                        for (ITypeBinding tp : key.getErasure().getTypeParameters()) {
18✔
373
                                // If there is a type parameter, then fmx will be a Famix ParametricClass
374
                                // note: in Famix, the owner of the TypeParameter is the ParametricClass
375
                                TypeParameter fmxParam = ensureFamixTypeParameter(tp,
5✔
376
                                                tp.getName(), fmx);
2✔
377
                                fmxParam.setGenericEntity((TParametricEntity)fmx);
3✔
378
                fmxParam.setIsStub(fmx.getIsStub());
4✔
379
            }
380
                }
381
                
382
                fmx.setTypeContainer(owner);
3✔
383
                return fmx;
2✔
384
        }
385

386
        /**
387
         * Returns a Famix ParametricInterface with the given <b>name</b>, creating it if it does not exist yet
388
         * In the second case, sets some default properties: not Abstract, not Final, not Private, not Protected, not Public, not Interface
389
         * @param name -- the name of the Famix Class
390
         * @return the Famix Class or null in case of a Famix error
391
         */
392
        public ParametricInterface ensureFamixParametricInterface(ITypeBinding key, String name, TWithTypes owner) {
393
                ParametricInterface fmx = ensureFamixEntity(ParametricInterface.class, key, name);
7✔
394
                if(key != null) {
2!
395
                        for (ITypeBinding tp : key.getTypeParameters()) {
17✔
396
                                // If there is a type parameter, then fmx will be a Famix ParametricInterface
397
                                // note: in Famix, the owner of the TypeParameter is the ParametricInterface
398
                                TypeParameter fmxParam = ensureFamixTypeParameter(tp,
5✔
399
                                                tp.getName(), fmx);
2✔
400
                                fmxParam.setGenericEntity(fmx);
3✔
401
                fmxParam.setIsStub(false);
4✔
402
            }
403
                }
404
                fmx.setTypeContainer(owner);
3✔
405
                return fmx;
2✔
406
        }
407

408
        public AnnotationInstanceAttribute createFamixAnnotationInstanceAttribute(AnnotationTypeAttribute att, String value) {
409
                AnnotationInstanceAttribute fmx = null;
2✔
410
                if ( (att != null) && (value != null) ) {
4!
411
                        fmx = new AnnotationInstanceAttribute();
4✔
412
                        fmx.setAnnotationTypeAttribute(att);
3✔
413
                        fmx.setValue(value);
3✔
414
                        this.famixRepo.add(fmx);
4✔
415
                }
416
                return fmx;
2✔
417
        }
418

419
        public AnnotationInstance addFamixAnnotationInstance(TWithAnnotationInstances fmx, AnnotationType annType, Collection<AnnotationInstanceAttribute> annAtts) {
420
                AnnotationInstance inst = null;
2✔
421
                if ( (fmx != null) && (annType != null) ) {
4!
422
                        inst = new AnnotationInstance();
4✔
423
                        inst.setAnnotatedEntity(fmx);
3✔
424
                        inst.setAnnotationType(annType);
3✔
425
                        inst.addAttributes(annAtts);
3✔
426
                        this.famixRepo.add(inst);
4✔
427
                }
428
                return inst;
2✔
429
        }
430

431
        ///// ensure Famix Relationships /////
432

433
        /**
434
         * Returns a Famix Inheritance relationship between two Famix Classes creating it if needed
435
         * @param sup -- the super class
436
         * @param sub -- the sub class
437
         * @param prev -- previous inheritance relationship in the same context
438
         * @return the Inheritance relationship
439
         */
440
        public Inheritance ensureFamixInheritance(TWithInheritances sup, TWithInheritances sub, TAssociation prev, ITypeBinding supBnd) {
441
                if ( (sup == null) || (sub == null) ) {
4!
442
                        return null;
×
443
                }
444

445
                // Does the inheritance already exist?
446
                for (TInheritance i : (sup).getSubInheritances()) {                        
11✔
447
                        if (i.getSubclass() == sub) {
4✔
448
                                return (Inheritance) i;
3✔
449
                        }
450
                }
1✔
451

452
                Inheritance inh;
453
                if (supBnd != null && supBnd.isParameterizedType()) { // Needs checks and tests.
5✔
454
                        inh = (ParametricInheritance)buildFamixParametricAssociation(new ParametricInheritance(), supBnd.getErasure().getTypeParameters(), supBnd.getTypeArguments());
13✔
455
                } else {
456
                        inh = new Inheritance();
4✔
457
                }
458

459
                inh.setSuperclass(sup);
3✔
460
                inh.setSubclass(sub);
3✔
461
                chainPrevNext(prev, inh);
4✔
462
                famixRepoAdd(inh);
3✔
463
                return inh;
2✔
464
        }
465
        
466
        /**
467
         * Creates the concretization between the type parameters of the generic entity that is target of an association 
468
         * and the concrete types that concretize them in this association.
469
         * @param association -- the association that must be linked to # or several concretizations
470
         * @param typeParameters -- the collection of type parameters declared in the generic entity
471
         * @param typeArguments -- the collection of concrete types linked to this association
472
         * @return the parametric association
473
         */
474
        public  <T extends TParametricEntity> TParametricAssociation buildFamixParametricAssociation(TParametricAssociation association, ITypeBinding[] typeParameters, ITypeBinding[] typeArguments
475
        ) {
476
                
477
                Iterator<ITypeBinding> genericIterator = Arrays.asList(typeParameters).iterator();
4✔
478
                Iterator<ITypeBinding> concreteIterator = Arrays.asList(typeArguments).iterator();
4✔
479

480
                while (concreteIterator.hasNext() && genericIterator.hasNext()) {
6✔
481
                        TTypeArgument typeArgument = (TTypeArgument)ensureFamixType(concreteIterator.next());
7✔
482
                        TypeParameter typeParameter = (TypeParameter)ensureFamixType(genericIterator.next());
7✔
483

484
                        Concretization concretization = ensureFamixConcretization(typeArgument, typeParameter);
5✔
485
                        association.addConcretization(concretization);
3✔
486
                }
1✔
487

488
                return association;
2✔
489
        }
490

491
        /**
492
         * Returns a Famix Concretization relationship between a Concrete Type and a ParameterType
493
         * @param typeArgument -- the concrete type
494
         * @param typeParameter -- the generic type parameter
495
         * @return the Concretization relationship
496
         */
497
        public Concretization ensureFamixConcretization(TTypeArgument typeArgument, TypeParameter typeParameter ) {
498
                if ( (typeArgument == null) || (typeParameter == null) ) {
4!
499
                        return null;
×
500
                }
501

502
                Concretization concretization = new Concretization();
4✔
503
                concretization.setTypeArgument(typeArgument);
3✔
504
                concretization.setTypeParameter(typeParameter);
3✔
505

506
                famixRepoAdd(concretization);
3✔
507
                return concretization;
2✔
508
        }
509
        
510
                /**
511
         * Returns a Famix Implementation relationship between two Famix Classes creating it if needed
512
         * @param myInterface -- the implemented interface
513
         * @param implementingClass -- the implementing class
514
         * @param prev -- previous inheritance relationship in the same context
515
         * @return the Inheritance relationship
516
         */
517
        public Implementation ensureFamixImplementation(TImplementable myInterface, TCanImplement implementingClass, TAssociation prev, ITypeBinding supBnd) {
518
                if ( (myInterface == null) || (implementingClass == null) ) {
4!
519
                        return null;
×
520
                }
521

522
                for (TImplementation imp : myInterface.getImplementations()) {
11✔
523
                        if (imp.getImplementingClass() == implementingClass) {
4✔
524
                                return (Implementation) imp;
3✔
525
                        }
526
                }
1✔
527
                
528
                Implementation implementation;
529
                if (supBnd != null && supBnd.isParameterizedType()) { // Needs checks and tests.
5!
530
                        implementation = (ParametricImplementation)buildFamixParametricAssociation(new ParametricImplementation(), supBnd.getErasure().getTypeParameters(), supBnd.getTypeArguments());
13✔
531
                } else {
532
                        implementation = new Implementation();
4✔
533
                }
534

535
                implementation.setImplementingClass(implementingClass);
3✔
536
                implementation.setMyInterface(myInterface);
3✔
537
                chainPrevNext(prev, implementation);
4✔
538
                famixRepoAdd(implementation);
3✔
539
                return implementation;
2✔
540
        }
541

542
        public void ensureImplementedInterfaces(ITypeBinding bnd, TType fmx, TWithTypes owner, TAssociation lastAssociation) {
543
                for (ITypeBinding intbnd : bnd.getInterfaces()) {
17✔
544
                        Type superTyp;
545
                        if(intbnd.isClass()){
3✔
546
                                superTyp = this.ensureFamixInterface(intbnd, intbnd.getName(), null, intbnd.isGenericType() || intbnd.isParameterizedType() || intbnd.isRawType(), intbnd.getModifiers());
22!
547
                        }else {
548
                                superTyp = this.ensureFamixType(intbnd);
4✔
549
                        }
550
                        
551
                        if (bnd.isInterface()) {
3✔
552
                                // in Java "subtyping" link between 2 interfaces is call inheritance 
553
                                lastAssociation = ensureFamixInheritance((TWithInheritances)superTyp, (TWithInheritances)fmx, lastAssociation, intbnd);
10✔
554
                        }
555
                        else {
556
                                lastAssociation = ensureFamixImplementation((TImplementable)superTyp, (TCanImplement)fmx, lastAssociation, intbnd);
9✔
557
                        }
558
                }
559
        }
1✔
560

561
        /**
562
         * Returns a Famix Reference between two Famix Entities creating it if needed.<br>
563
         * If <code>prev == null</code> and a similar reference already exist (same <code>src</code>, same <code>tgt</code>), does not create a new one
564
         * @param src -- source of the reference
565
         * @param tgt -- target of the reference
566
         * @param prev -- previous reference relationship in the same context
567
         * @return the FamixReference
568
         */
569
        public Reference addFamixReference(Method src, TType tgt, TAssociation prev, ITypeBinding referredTypeBnd) {
570
                Reference ref = null;
2✔
571
                
572
                if ( (src == null) || (tgt == null) ) {
4!
573
                        return null;
×
574
                }
575

576
                if (prev == null) {
2✔
577
                        for (TReference existingRef : src.getOutgoingReferences()) {
7!
578
                                if (existingRef.getReferredEntity() == tgt) {
×
579
                                        return (Reference) existingRef;
×
580
                                }
581
                        }
×
582
                }
583

584
                if (referredTypeBnd != null) {
2✔
585
                        if (referredTypeBnd.isParameterizedType()) {
3✔
586
                                ref = (ParametricReference)buildFamixParametricAssociation(new ParametricReference(), referredTypeBnd.getErasure().getTypeParameters(), referredTypeBnd.getTypeArguments());
13✔
587
                        } else if (referredTypeBnd.isArray()) {
3✔
588
                                ref = new ParametricReference();
4✔
589
                        
590
                                TTypeArgument typeArgument  = (TTypeArgument)ensureFamixType(referredTypeBnd.getElementType());
6✔
591
                                TypeParameter typeParameter = (TypeParameter) ((TParametricEntity)tgt).getTypeParameters().iterator().next();
7✔
592

593
                                Concretization concretization = ensureFamixConcretization(typeArgument, typeParameter);
5✔
594
                                ((ParametricReference)ref).addConcretization(concretization);
4✔
595
                        }
596
                }
597

598
                if (ref == null){
2✔
599
                        ref = new Reference();
4✔
600
                }
601

602
                ref.setReferredEntity(tgt);
3✔
603
                ref.setReferencer(src);
3✔
604
                chainPrevNext(prev,ref);
4✔
605
                famixRepoAdd(ref);
3✔
606

607
                return ref;
2✔
608
        }
609

610
        /**
611
         * Returns a Famix Invocation between two Famix Entities creating it if needed
612
         * @param tMethod of the invocation
613
         * @param invoked -- method invoked
614
         * @param receiver of the invocation
615
         * @param signature -- i.e. actual invocation code
616
         * @param prev -- previous invocation relationship in the same context
617
         * @return the FamixInvocation
618
         */
619
        public Invocation addFamixInvocation(TMethod tMethod, TMethod invoked, TInvocationsReceiver receiver, String signature, TAssociation prev, IMethodBinding invokedBnd) {
620
                if ( (tMethod == null) || (invoked == null) ) {
4!
621
                        return null;
×
622
                }
623
                Invocation invocation;
624
                if (invokedBnd != null && invokedBnd.isParameterizedMethod()) {
5✔
625
                        invocation = (ParametricInvocation)buildFamixParametricAssociation(new ParametricInvocation(), invokedBnd.getMethodDeclaration().getTypeParameters(), invokedBnd.getTypeArguments());
13✔
626
                } else if ( invokedBnd != null && isConstructorBinding(invokedBnd) && invokedBnd.getMethodDeclaration().getDeclaringClass().isGenericType()) {
11✔
627
                        invocation = (ParametricInvocation)buildFamixParametricAssociation(new ParametricInvocation(), invokedBnd.getMethodDeclaration().getDeclaringClass().getTypeParameters(), invokedBnd.getDeclaringClass().getTypeArguments());
15✔
628
                } else {
629
                        invocation = new Invocation();
4✔
630
                }
631

632
                invocation.setReceiver(receiver);
3✔
633
                invocation.setSender(tMethod);
3✔
634
                invocation.setSignature((signature == null) ? invoked.getSignature() : signature);
5!
635
                invocation.addCandidates(invoked);
3✔
636
                chainPrevNext(prev,invocation);
4✔
637
                famixRepoAdd(invocation);
3✔
638
                
639
                return invocation;
2✔
640
        }
641

642
        /**
643
         * Returns a Famix Access between two Famix Entities creating it if needed
644
         * @param accessor -- the entity (presumably a method) accessing the attribute
645
         * @param var -- the variable accessed
646
         * @param isWrite -- whether this is an access for reading or writing in the variable
647
         * @param prev -- previous access relationship in the same context
648
         * @return the FamixAccess
649
         */
650
        public Access addFamixAccess(TWithAccesses accessor, TStructuralEntity var, boolean isWrite, TAssociation prev) {
651
                if ( (accessor == null) || (var == null) ) {
4!
652
                        return null;
×
653
                }
654
                Access acc = new Access();
4✔
655
                acc.setAccessor(accessor);
3✔
656
                acc.addCandidates((TAccessible) var);
3✔
657
                acc.setIsWrite(isWrite);
4✔
658
                chainPrevNext(prev, acc);
4✔
659
                famixRepoAdd(acc);
3✔
660
                
661
                return acc;
2✔
662
        }
663

664
        protected void chainPrevNext(TAssociation prev, TAssociation next) {
665
                if (prev != null) {
2✔
666
                        next.setPrevious(prev);  // not yet implemented in importer
3✔
667
                }
668
        }
1✔
669
        
670
        /**
671
         * Returns a Famix DeclaredException between a method and an Exception that it declares to throw
672
         * @param meth -- the method throwing the exception
673
         * @param excep -- the exception declared to be thrown
674
         * @return the DeclaredException
675
         */
676
        public TThrowable createFamixDeclaredException(Method meth, TThrowable excep) {
677
                if ( (meth == null) || (excep == null) ) {
4!
678
                        return null;
×
679
                }
680
                meth.getDeclaredExceptions().add(excep);
5✔
681
                return excep;
2✔
682
        }
683

684
        /**
685
         * Returns a Famix CaughtException between a method and an Exception that is caught
686
         * @param meth -- the method catching the exception
687
         * @param excep -- the exception caught
688
         * @return the CaughtException
689
         */
690
        public TThrowable createFamixCaughtException(Method meth, TThrowable excep) {
691
                if ( (meth == null) || (excep == null) ) {
4!
692
                        return null;
×
693
                }
694
                meth.getCaughtExceptions().add(excep);
5✔
695
                return excep;
2✔
696
        }
697

698
        /**
699
         * Returns a Famix ThrownException between a method and an Exception that it (actually) throws.
700
         * Note: DeclaredException indicates that the method declares it can throw the exception,
701
         * here we state that the exception is actually thrown
702
         * @param meth -- the method throwing the exception
703
         * @param excep -- the exception thrown
704
         * @return the ThrownException
705
         */
706
        public TThrowable createFamixThrownException(Method meth, TThrowable excep) {
707
                if ( (meth == null) || (excep == null) ) {
4!
708
                        return null;
×
709
                }
710
                meth.getThrownExceptions().add(excep);
5✔
711
                return excep;
2✔
712
        }
713

714

715
        /**
716
         * Returns a Famix EntityTyping between a typed entity and a type.
717
         * @param typedEntity -- the typed entity
718
         * @param declaredType -- the declared type
719
         * @return the FamixEntityTyping
720
         */
721
        public EntityTyping ensureFamixEntityTyping(ITypeBinding declaredTypeBnd, TTypedEntity typedEntity, TType declaredType) {
722
                if ( (typedEntity == null) || (declaredType == null) ) {
4!
723
                        return null;
2✔
724
                }
725
                EntityTyping typing = null;
2✔
726
                
727
                if (declaredTypeBnd != null) {
2✔
728
                        if (declaredTypeBnd.isParameterizedType()) {
3✔
729
                                typing = (ParametricEntityTyping)buildFamixParametricAssociation(new ParametricEntityTyping(), declaredTypeBnd.getErasure().getTypeParameters(), declaredTypeBnd.getTypeArguments());
13✔
730
                        }else if (declaredTypeBnd.isArray()) {
3✔
731
                                typing = new ParametricEntityTyping();
4✔
732
                                
733
                                TTypeArgument typeArgument  = (TTypeArgument)ensureFamixType(declaredTypeBnd.getElementType());
6✔
734
                                TypeParameter typeParameter = (TypeParameter) ((ParametricClass)declaredType).getTypeParameters().iterator().next();
7✔
735

736
                                Concretization concretization = ensureFamixConcretization(typeArgument, typeParameter);
5✔
737
                                ((ParametricEntityTyping)typing).addConcretization(concretization);
4✔
738
                        }
739
                }
740

741
                // If we did not set a typing because not parameterized nor array, set a default one
742
                if (typing == null) {
2✔
743
                        typing = new EntityTyping();
4✔
744
                }
745
                
746
                typing.setTypedEntity(typedEntity);
3✔
747
                typing.setDeclaredType(declaredType);
3✔
748
                famixRepoAdd(typing);
3✔
749
                
750
                return typing;
2✔
751
        }
752

753

754
        ///// Special Case: ImplicitVariables /////
755

756
        /**
757
         * Returns the Famix ImplicitVariable associated to the given binding and name (self or super).
758
         * See also {@link #getEntityByKey(IBinding)}
759
         * @param bnd -- the binding
760
         * @return the Famix Entity associated to the binding or null if not found
761
         */
762
        @Deprecated
763
        public ImplicitVariable getImplicitVariableByBinding(IBinding bnd, String iv_name) {
764
                return getImplicitVariableByType((Class)getEntityByKey(bnd), iv_name);
×
765
        }
766
        
767
        /**
768
         * Returns the Famix ImplicitVariable associated to the given FamixType.
769
         * @param type -- the FamixType
770
         * @param name -- name of the ImplicitVariable (should be Dictionary.SELF_NAME or Dictionary.SUPER_NAME)
771
         * @return the Famix ImplicitVariable associated to the Type or null if not found
772
         */
773
        @Deprecated
774
        public ImplicitVariable getImplicitVariableByType(Type type, String name) {
775
                ImplicitVars iv = typeToImpVar.get(type);
×
776
                ImplicitVariable ret = null;
×
777
                
778
                if (iv == null) {
×
779
                        iv = new ImplicitVars();
×
780
                }
781
                
782
                if (name.equals(THIS_NAME)) {
×
783
                        ret = iv.self_iv;
×
784
                }
785
                else if (name.equals(SUPER_NAME)) {
×
786
                        ret = iv.super_iv;
×
787
                }
788

789
                return ret;
×
790
        }
791

792
        ///// Special Case: "Uniq" Entities /////
793

794
        /**
795
         * Creates or recovers a Famix Named Entity uniq for the given name.
796
         * For some specific entities we don't allow two of them with the same name.
797
         * This is the case e.g. for the default package, or the Java class "Object" and its package "java.lang".
798
         * @param fmxClass -- the Famix class of the instance to create
799
         * @param key -- a potential binding for the entity
800
         * @param name -- the name of the new instance (used if <pre>{@code bnd == null}</pre>)
801
         * @return the uniq Famix Entity for this binding and/or name
802
         */
803
        @SuppressWarnings("unchecked")
804
        public <T extends NamedEntity> T ensureFamixUniqEntity(java.lang.Class<T> fmxClass, IBinding key, String name) {
805
                T fmx = null;
2✔
806
                
807
                if (name == null) {
2!
808
                        return null;
×
809
                }
810
                
811
                if (key != null) {
2✔
812
                        fmx = (T) getEntityByKey(key);
5✔
813
                }
814
                
815
                if (fmx == null) {
2✔
816
                        Collection<T> l = getEntityByName( fmxClass, name);
5✔
817
                        if (l.size() > 0) {
3✔
818
                                fmx = l.iterator().next();
6✔
819
                        }
820
                        else {
821
                                fmx = createFamixEntity(fmxClass, name);
6✔
822
                        }
823
                        
824
                        if (key != null) {
2✔
825
                                // may happen for example if the entity was first created without binding
826
                                // and we find a binding for it later
827
                                keyToEntity.put(key, fmx);
6✔
828
                        }
829
                }
830

831
                return fmx;
2✔
832
        }
833

834
        /**
835
         * Creates or recovers the Famix Class that will own all stub methods (for which the real owner is unknown)
836
         *
837
         * @return a Famix class
838
         */
839
        public Class ensureFamixClassStubOwner() {
840
                Class fmx =  ensureFamixUniqEntity(Class.class, null, STUB_METHOD_CONTAINER_NAME);
7✔
841
                if (fmx != null) {
2!
842
                        fmx.setTypeContainer( ensureFamixPackageDefault());
4✔
843
                }
844
                ensureFamixInheritance(ensureFamixClassObject(), fmx, /*prev*/null, null);
8✔
845

846
                return fmx;
2✔
847
        }
848

849
        public Type searchTypeInContext(String name, TWithTypes ctxt) {
850
                if (ctxt == null) {
×
851
                        return null;
×
852
                }
853
                
854
                for (TType candidate : ctxt.getTypes()) {
×
855
                        if (candidate.getName().equals(name) ) {
×
856
                                return (Type) candidate;
×
857
                        }
858
                }
×
859
                
860
                return searchTypeInContext(name, Util.getOwner((TNamedEntity)ctxt));
×
861
        }
862

863
        /**
864
         * Returns a Famix Package associated with its IPackageBinding and/or fully qualified name.
865
         * The Entity is created if it does not exist.
866
         * We assume that Namespaces must be uniq for a given name
867
         * Also creates or recovers recusively it's parent namespaces.<br>
868
         * At least one of <b>bnd</b> and <b>name</b> must be non null.
869
         *
870
         * @param bnd  -- the JDT Binding that may be used as a uniq key to recover this namespace
871
         * @param name -- fully qualified name of the namespace (e.g. 'java.lang')
872
         * @return the Famix Namespace found or created. May return null in case of a Famix error
873
         */
874
        public Package ensureFamixPackage(IPackageBinding bnd, String name) {
875
                Package fmx;
876
                Package parent;
877

878
                if ((name == null) && (bnd != null)) {
4!
879
                        name = bnd.getName();
3✔
880
                }
881

882
                if ((name == null) || name.equals("")) {
6!
883
                        return ensureFamixPackageDefault();
3✔
884
                } else {
885
                        /* Note: Packages are created with their fully-qualified name to simplify recovering when we don't have a binding
886
                         * (for example when creating parent packages of a package we have a binding for).
887
                         * Because the preferred solution in Moose is to give their simple names to packages, they must be post-processed when
888
                         * all is said and done. */
889
                        fmx = ensureFamixUniqEntity(Package.class, bnd, name);
7✔
890
                        String parentName = removeLastPartOfPackageName(name);
4✔
891
                        if (parentName.length() > 0) {
3✔
892
                                parent = ensureFamixPackage(null, parentName);
5✔
893
                                // set the parentscope relationship
894
                                if ((parent != null) && (fmx != null) && (fmx.getParentPackage() == null)) {
7!
895
                                        parent.addChildEntities(fmx);
3✔
896
                                }
897
                        }
898
                }
899

900
                return fmx;
2✔
901
        }
902

903
        /**
904
         * Creates or recovers a default Famix Package.
905
         * Because this package does not really exist, it has no binding.
906
         *
907
         * @return a Famix Namespace
908
         */
909
        public Package ensureFamixPackageDefault() {
910
        return ensureFamixUniqEntity(Package.class, null, DEFAULT_PCKG_NAME);
7✔
911
        }
912

913
        /**
914
         * Creates or recovers a Famix Package for the package of Java class "Object" (i.e. "java.lang").
915
         * Because "Object" is the root of the inheritance tree, it needs to be treated differently.
916
         *
917
         * @param bnd -- a potential binding for the "java.lang" package
918
         * @return a Famix Namespace for "java.lang"
919
         */
920
        public Package ensureFamixPackageJavaLang(IPackageBinding bnd) {
921

922
        return this.ensureFamixPackage(bnd, OBJECT_PACKAGE_NAME);
5✔
923
        }
924

925
        /**
926
         * Returns the Package with {@link #DEFAULT_PCKG_NAME} or <code>null</code> if not found
927
         */
928
        public Package getFamixPackageDefault() {
929
                Collection<Package> l = getEntityByName(Package.class, DEFAULT_PCKG_NAME);
5✔
930
                if (l.size() > 0) {
3!
931
                        return l.iterator().next();
5✔
932
                } else {
933
                        return null;
×
934
                }
935
        }
936

937
        /**
938
         * Returns a Famix Type with the given <b>name</b>, creating it if it does not exist yet.
939
         * In the second case, sets some default properties: not Abstract, not Final, not Private, not Protected, not Public, not Interface
940
         * @param bnd -- binding for the type to create
941
         * @param name of the type
942
         * @param owner of the type
943
         * @param ctxt -- context of use of the type
944
         */
945
        public Type ensureFamixType(ITypeBinding bnd, String name, TWithTypes owner, TWithTypes ctxt, int modifiers) {
946
                
947
                Type fmx;
948

949
                if (bnd == null) {
2!
950
                        if (name == null) {
×
951
                                return null;
×
952
                        }
953
                        fmx = searchTypeInContext(name, ctxt); // WildCard Types don't have binding
×
954
                        if (fmx != null) {
×
955
                                return fmx;
×
956
                        }
957

958
                        if ((owner instanceof TParametricEntity)) {
×
959
                                return this.ensureFamixTypeParameter(null, name, owner);
×
960
                        }
961
                        else {
962
                                fmx = ensureFamixEntity(Type.class, bnd, name);
×
963
                                fmx.setTypeContainer(owner);
×
964
                                return fmx;
×
965
                        }
966
                }
967

968
                // bnd != null
969

970
                fmx = (Type) getEntityByKey(bnd);
5✔
971
                if (fmx != null) {
2✔
972
                        return fmx;
2✔
973
                }
974

975
                if (bnd.isArray()) {
3!
976
                        bnd = bnd.getElementType();
×
977
                }
978

979
                if (bnd.isPrimitive()) {
3✔
980
                        return this.ensureFamixPrimitiveType(bnd, name);
5✔
981
                }
982

983
                if (bnd.isEnum()) {
3!
984
                        return this.ensureFamixEnum(bnd, name, owner);
×
985
                }
986
 
987
                if ((bnd.isRawType() || bnd.isGenericType()) && !bnd.isInterface() ) {
9✔
988
                        return this.ensureFamixClass(bnd.getErasure(), name, (TNamedEntity) owner, /*isGeneric*/true, modifiers);
10✔
989
                }
990

991
                if (bnd.isCapture()) {
3✔
992
                        if (bnd.getErasure().isInterface()) {
4!
993
                                return this.ensureFamixInterface(bnd.getErasure(), name, owner, /*isGeneric*/true, modifiers);
9✔
994
                        }
995
                        else {
996
                                return this.ensureFamixClass(bnd.getErasure(), name, (TNamedEntity) owner, /*isGeneric*/true, modifiers);
×
997
                        }
998
                }
999

1000
                if (bnd.isAnnotation()) {
3✔
1001
                        return this.ensureFamixAnnotationType(bnd, name, (ContainerEntity) owner);
7✔
1002
                }
1003

1004
                if (bnd.isInterface()) {
3✔
1005
                        return this.ensureFamixInterface(bnd, name, owner, /*isGeneric*/bnd.isGenericType() || bnd.isParameterizedType() || bnd.isRawType(), modifiers);
19✔
1006
                }
1007

1008
                if (isThrowable(bnd)) {
4✔
1009
                        return this.ensureFamixException(bnd, name, owner, /*isGeneric*/false, modifiers);
8✔
1010
                }
1011
                if (bnd.isClass()) {
3✔
1012
                        return this.ensureFamixClass(bnd, name, (TNamedEntity) owner, /*isGeneric*/bnd.isGenericType() || bnd.isParameterizedType() || bnd.isRawType(), modifiers);
20!
1013
                }
1014
                if(bnd.isWildcardType()) {
3✔
1015
                        return this.ensureFamixWildcardType(bnd, name, (TParametricEntity)owner, ctxt);
8✔
1016
                }
1017

1018
                //otherwise (none of the above)
1019

1020
                if (name == null) {
2✔
1021
                        name = bnd.getName();
3✔
1022
                }
1023

1024
                if (owner == null) {
2!
1025
                        owner = (TWithTypes) this.ensureOwner(bnd);
5✔
1026
                }
1027

1028
                if (bnd.isTypeVariable() ) {
3!
1029
                        fmx = ensureFamixTypeParameter(bnd, name, owner);
6✔
1030
                        return fmx;
2✔
1031
                }
1032

1033
                fmx = ensureFamixEntity(Type.class, bnd, name);
×
1034
                fmx.setTypeContainer(owner);
×
1035
                return fmx;
×
1036
        }
1037

1038
        public Type ensureFamixType(ITypeBinding bnd, TWithTypes context) {
1039
        int modifiers = (bnd != null) ? bnd.getModifiers() : UNKNOWN_MODIFIERS;
6!
1040
                return ensureFamixType(bnd, /*name*/null, /*owner*/null, context, modifiers);
8✔
1041
        }
1042
        
1043
        public Type ensureFamixType(ITypeBinding bnd) {
1044
                return ensureFamixType(bnd, /*ctxt*/null);
5✔
1045
        }
1046

1047
        public boolean isThrowable(ITypeBinding bnd) {
1048
                if (bnd == null) {
2!
1049
                        return false;
×
1050
                }
1051
                if (bnd.getQualifiedName().equals("java.lang.Throwable")) {
5✔
1052
                        return true;
2✔
1053
                } else if (bnd.getQualifiedName().equals("java.lang.Object")) {
5✔
1054
                        return false;
2✔
1055
                }
1056
                else {
1057
                        return isThrowable(bnd.getSuperclass());
5✔
1058
                }
1059
        }
1060

1061
        /**
1062
         * Returns a Famix Class associated with the ITypeBinding.
1063
         * The Entity is created if it does not exist.
1064
         * @param name -- the name of the Famix Class (MUST NOT be null, but this is not checked)
1065
         * @param owner -- package defining the class (should not be null, but it will work if it is)
1066
         * @return the Famix Entity found or created. May return null if "bnd" is null or in case of a Famix error
1067
         */
1068
        @SuppressWarnings("deprecation")
1069
        public Class ensureFamixClass(ITypeBinding bnd, String name, TNamedEntity owner, boolean isGeneric, int modifiers) {
1070
                Class fmx;
1071

1072
                // --------------- some special cases
1073
                if (bnd != null) {
2✔
1074
                        if (bnd.isArray()) {
3!
1075
                                bnd = bnd.getElementType();
×
1076
                        }
1077

1078
                        // for inner classes defined in generics !!! For others should not change anything
1079
                        bnd = bnd.getErasure();
3✔
1080
                }
1081

1082
                // ---------------- to avoid useless computations if we can
1083
                fmx = getEntityByKey(bnd, Class.class);
6✔
1084
                if (fmx != null) {
2✔
1085
                        return fmx;
2✔
1086
                }
1087

1088
                // --------------- name
1089
                if (name == null) {
2✔
1090
                        if (bnd == null) {
2!
1091
                                return null;  // not much we can do
×
1092
                        } else if (!bnd.isAnonymous()) {
3!
1093
                                name = bnd.getErasure().getName();  // for generics, will give the "core" type name, for normal type, won't change anything
5✔
1094
                        } else { // anonymous class
1095
                                if (bnd.getSuperclass() != null) {
×
1096
                                        name = bnd.getSuperclass().getName();
×
1097
                                }
1098
                                if ((name == null) || name.equals(OBJECT_NAME)) {
×
1099
                                        ITypeBinding[] intfcs = bnd.getInterfaces();
×
1100
                                        if ((intfcs != null) && (intfcs.length > 0)) {
×
1101
                                                name = bnd.getInterfaces()[0].getName();
×
1102
                                        }
1103
                                        else {
1104
                                                name = "???";
×
1105
                                        }
1106
                                }
1107
                                name = ANONYMOUS_NAME_PREFIX + "(" + name + ")";
×
1108
                        }
1109
                }
1110

1111
        // If we have java.lang.Object we should ensure we create this class
1112
        if (bnd != null && bnd.getQualifiedName().equals("java.lang.Object")) {
7✔
1113
                        return ensureFamixClassObject();
3✔
1114
                }
1115

1116
                // --------------- owner
1117
                if (owner == null) {
2✔
1118
                        if (bnd != null) {
2✔
1119
                                owner = ensureOwner(bnd);
4✔
1120
                        }
1121
                        /*                                owner = ensureFamixPackageDefault();
1122
                        } else {*/
1123
                }
1124

1125
                // --------------- recover from name ?
1126
                if (owner != null) {
2✔
1127
                        for (Class candidate : this.getEntityByName(Class.class, name)) {
13✔
1128
                                if (matchAndMapClass(bnd, name, owner, candidate)) {
7✔
1129
                                        fmx = candidate;
2✔
1130
                                        break;
1✔
1131
                                }
1132
                        }
1✔
1133
                }
1134

1135
                // ---------------- create
1136
                if (fmx == null) {
2✔
1137
                        if (isGeneric) {
2✔
1138
                                fmx = ensureFamixParametricClass(bnd, name, (TWithTypes) owner);
8✔
1139
                        }
1140
                        else {
1141
                                fmx = ensureFamixEntity(Class.class, bnd, name);
7✔
1142
                                fmx.setTypeContainer((TWithTypes)owner);
4✔
1143
                        }
1144
                }
1145

1146
                // ---------------- modifiers and super-classes
1147
                if (fmx!=null) {
2!
1148
                        // we just created it, or it was not bound so we make sure it has the right information in it
1149
                        if (bnd != null) {
2✔
1150
                                setClassModifiers(fmx, bnd.getDeclaredModifiers(), (TWithTypes) owner);
7✔
1151
                        }
1152

1153
                        TAssociation lastAssoc = null;
2✔
1154

1155
                        if (bnd != null) {
2✔
1156
                                ITypeBinding supbnd = bnd.getSuperclass();
3✔
1157
                                if (supbnd != null) {
2✔
1158
                                        lastAssoc = ensureFamixInheritance((TWithInheritances) ensureFamixType(supbnd), fmx, lastAssoc, supbnd);
11✔
1159
                                }
1160
                                else {
1161
                                        lastAssoc = ensureFamixInheritance(ensureFamixClassObject(), fmx, lastAssoc, null);
8✔
1162
                                }
1163
                                ensureImplementedInterfaces(bnd, fmx, (TWithTypes) owner, lastAssoc);
7✔
1164
                        }
1165
                }
1166

1167
                return fmx;
2✔
1168
        }
1169

1170
        /**
1171
         * Returns a Famix Exception associated with the ITypeBinding.
1172
         * The Entity is created if it does not exist.
1173
         * @param name -- the name of the Famix Exception
1174
         * @param owner -- type defining the Exception (should not be null, but it will work if it is) 
1175
         *
1176
         * @return the Famix Entity found or created. May return null if "bnd" is null or in case of a Famix error
1177
         */
1178
        public <T extends TWithTypes & TNamedEntity> Exception ensureFamixException(ITypeBinding bnd, String name, TWithTypes owner, boolean isGeneric, int modifiers) {
1179
                Exception fmx;
1180

1181
                // --------------- some special cases
1182
                if (bnd != null) {
2✔
1183
                        if (bnd.isArray()) {
3!
1184
                                bnd = bnd.getElementType();
×
1185
                        }
1186

1187
                        // for inner classes defined in generics !!! For others should not change anything
1188
                        bnd = bnd.getErasure();
3✔
1189
                }
1190

1191
                // ---------------- to avoid useless computations if we can
1192
                fmx = (Exception) getEntityByKey(bnd);
5✔
1193
                if (fmx != null) {
2✔
1194
                        return fmx;
2✔
1195
                }
1196

1197
                // --------------- name
1198
                if (name == null) {
2✔
1199
                        if (bnd == null) {
2!
1200
                                return null;  // not much we can do
×
1201
                        } else if (!bnd.isAnonymous()) {
3!
1202
                                name = bnd.getErasure().getName();  // for generics, will give the "core" type name, for normal type, won't change anything
5✔
1203
                        } else { // anonymous class
1204
                                if (bnd.getSuperclass() != null) {
×
1205
                                        name = bnd.getSuperclass().getName();
×
1206
                                }
1207
                                if ((name == null) || name.equals(OBJECT_NAME)) {
×
1208
                                        ITypeBinding[] intfcs = bnd.getInterfaces();
×
1209
                                        if ((intfcs != null) && (intfcs.length > 0)) {
×
1210
                                                name = bnd.getInterfaces()[0].getName();
×
1211
                                        }
1212
                                        else {
1213
                                                name = "???";
×
1214
                                        }
1215
                                }
1216
                                name = ANONYMOUS_NAME_PREFIX + "(" + name + ")";
×
1217
                        }
1218
                }
1219

1220
                // --------------- owner
1221
                if (owner == null) {
2✔
1222
                        if (bnd == null) {
2✔
1223
                                owner = ensureFamixPackageDefault();
4✔
1224
                        } else {
1225
                                owner = (TWithTypes) ensureOwner(bnd);
5✔
1226
                        }
1227
                }
1228

1229
                // --------------- recover from name ?
1230
                for (Exception candidate : this.getEntityByName(Exception.class, name)) {
13✔
1231
                        if (matchAndMapClass(bnd, name, (T) owner, candidate)) {
8!
1232
                                fmx = candidate;
×
1233
                                break;
×
1234
                        }
1235
                }
1✔
1236

1237
                // ---------------- create
1238
                if (fmx == null) {
2!
1239
                        fmx = ensureFamixEntity(Exception.class, bnd, name);
7✔
1240
                        fmx.setTypeContainer(owner);
3✔
1241
                }
1242

1243
        // we just created it or it was not bound, so we make sure it has the right information in it
1244
        TAssociation lastAssoc = null;
2✔
1245
        if (bnd != null) {
2✔
1246
            ITypeBinding supbnd = bnd.getSuperclass();
3✔
1247
            if (supbnd != null) {
2!
1248
                lastAssoc = ensureFamixInheritance((TWithInheritances) ensureFamixType(supbnd), fmx, lastAssoc, supbnd);
11✔
1249
            }
1250
            else {
1251
                lastAssoc = ensureFamixInheritance(ensureFamixClassObject(), fmx, lastAssoc, null);
×
1252
            }
1253
            ensureImplementedInterfaces(bnd, fmx, owner, lastAssoc);
6✔
1254
        }
1255

1256
        return fmx;
2✔
1257
        }
1258

1259
        /**
1260
         * Returns a FAMIX Interface with the given <b>name</b>, creating it if it does not exist yet.
1261
         * @param name -- the name of the FAMIX Method
1262
         * @param owner -- type defining the method (should not be null, but it will work if it is) 
1263
         * @return the FAMIX Class or null in case of a FAMIX error
1264
         */
1265
        public <T extends TWithTypes & TNamedEntity> Interface ensureFamixInterface(ITypeBinding bnd, String name, TWithTypes owner, boolean isGeneric, int modifiers) {
1266
                Interface fmx;
1267

1268
                // --------------- some special cases
1269
                if (bnd != null) {
2!
1270
                        if (bnd.isArray()) {
3!
1271
                                bnd = bnd.getElementType();
×
1272
                        }
1273

1274
                        // for inner classes defined in generics !!! For others should not change anything
1275
                        bnd = bnd.getErasure();
3✔
1276
                }
1277

1278
                // ---------------- to avoid useless computations if we can
1279
                fmx = getEntityByKey(bnd, Interface.class);
6✔
1280
                if (fmx != null) {
2✔
1281
                        return fmx;
2✔
1282
                }
1283

1284
                // --------------- name
1285
                if (name == null) {
2✔
1286
                        if (bnd == null) {
2!
1287
                                return null;  // not much we can do
×
1288
                        } else if (!bnd.isAnonymous()) {
3!
1289
                                name = bnd.getErasure().getName();  // for generics, will give the "core" type name, for normal type, won't change anything
5✔
1290
                        } else { // anonymous class
1291
                                if (bnd.getSuperclass() != null) {
×
1292
                                        name = bnd.getSuperclass().getName();
×
1293
                                }
1294
                                if ((name == null) || name.equals(OBJECT_NAME)) {
×
1295
                                        ITypeBinding[] intfcs = bnd.getInterfaces();
×
1296
                                        if ((intfcs != null) && (intfcs.length > 0)) {
×
1297
                                                name = bnd.getInterfaces()[0].getName();
×
1298
                                        }
1299
                                        else {
1300
                                                name = "???";
×
1301
                                        }
1302
                                }
1303
                                name = ANONYMOUS_NAME_PREFIX + "(" + name + ")";
×
1304
                        }
1305
                }
1306

1307
                // --------------- owner
1308
                if (owner == null) {
2✔
1309
                        if (bnd == null) {
2!
1310
                                owner = ensureFamixPackageDefault();
×
1311
                        } else {
1312
                                owner = (TWithTypes) ensureOwner(bnd);
5✔
1313
                        }
1314
                }
1315

1316
                // --------------- recover from name ?
1317
                for (Interface candidate : this.getEntityByName(Interface.class, name)) {
13✔
1318
                        if (matchAndMapInterface(bnd, name, (T) owner, candidate)) {
8✔
1319
                                fmx = candidate;
2✔
1320
                                break;
1✔
1321
                        }
1322
                }
1✔
1323

1324
                // ---------------- create
1325
                if (fmx == null) {
2✔
1326
                        if (isGeneric) {
2✔
1327
                                fmx = ensureFamixParametricInterface(bnd, name, owner);
7✔
1328
                        }
1329
                        else {
1330
                                fmx = ensureFamixEntity(Interface.class, bnd, name);
7✔
1331
                                fmx.setTypeContainer(owner);
3✔
1332
                        }
1333
                }
1334

1335
                // ---------------- modifiers and "super interfaces"
1336
                if (fmx!=null) {
2!
1337
                        // we just created it or it was not bound, so we make sure it has the right information in it
1338
                        if (bnd != null) {
2!
1339
                                setInterfaceModifiers(fmx, bnd.getModifiers());
5✔
1340
                        }
1341
                        TAssociation lastAssociation = null;
2✔
1342
                        if (bnd != null) {
2!
1343
                                ensureImplementedInterfaces(bnd, fmx, owner, lastAssociation);
6✔
1344
                        }
1345
                }
1346
                return fmx;
2✔
1347
        }
1348

1349
        /**
1350
         * "Converts" (if needed) a TTYpe entity to be a TThrowable. Might involve removing the existing entity, recreating a new one and migrating
1351
         * all the relationship of the former to the later
1352
         */
1353
        public TThrowable asException(TType fmxType) {
1354
                if (fmxType instanceof Exception) {
3✔
1355
                        return (Exception) fmxType;
3✔
1356
                }
1357
                if(fmxType instanceof TypeParameter) {
3✔
1358
                        return (TypeParameter) fmxType;
3✔
1359
                }
1360

1361
                Exception fmxException = null;
2✔
1362
                IBinding key;
1363

1364
                try {
1365
                        key = entityToKey.get(fmxType);
6✔
1366

1367
                        /* Remove entity immediately so that its key and name are not "reassigned" in the various cache dictionaries
1368
                         * the object still exists and its properties are still accessible */
1369
                        removeEntity((NamedEntity) fmxType);
4✔
1370

1371
                        TWithTypes owner = fmxType.getTypeContainer();
3✔
1372
                        fmxType.setTypeContainer(null);
3✔
1373
                        fmxException = ensureFamixException((ITypeBinding) key, fmxType.getName(), owner, /*isGeneric*/false, UNKNOWN_MODIFIERS);
10✔
1374

1375
                        fmxException.addMethods( new ArrayList<>( ((TWithMethods)fmxType).getMethods() ) );
8✔
1376
                        if (fmxType instanceof TWithAttributes) {
3!
1377
                                fmxException.addAttributes( new ArrayList<>( ((TWithAttributes)fmxType).getAttributes() ) );
8✔
1378
                        }
1379

1380
                        if (fmxType instanceof TWithInheritances) {
3!
1381
                                fmxException.addSuperInheritances( new ArrayList<>( ((TWithInheritances) fmxType).getSuperInheritances() ) );
8✔
1382
                                fmxException.addSubInheritances( new ArrayList<>( ((TWithInheritances) fmxType).getSubInheritances() ) );
8✔
1383
                        }
1384
                        fmxException.setComments(new ArrayList<>( ((TWithComments) fmxType).getComments() ));
8✔
1385
                        fmxException.setSourceAnchor(fmxType.getSourceAnchor());
4✔
1386
                        fmxException.addIncomingTypings( new ArrayList<>( fmxType.getIncomingTypings() ) );
7✔
1387
                        fmxException.addAnnotationInstances( new ArrayList<>( ((NamedEntity)fmxType).getAnnotationInstances() ) );
8✔
1388
                        fmxException.addIncomingReferences( new ArrayList<>( fmxType.getIncomingReferences() ) );
7✔
1389
                        fmxException.setIsStub(fmxType.getIsStub());
4✔
1390
                        fmxException.addTypes( new ArrayList<>( ((ContainerEntity) fmxType).getTypes() ) );
8✔
1391
                }
1392
                catch( ConcurrentModificationException e) {
×
1393
                        e.printStackTrace();
×
1394
                }
1✔
1395

1396
                return fmxException;
2✔
1397
        }
1398

1399
        /**
1400
         * helper method, we know the type exists, ensureFamixClass will recover it
1401
         */
1402
        public Class getFamixClass(ITypeBinding bnd, String name, TNamedEntity owner) {
1403
                return ensureFamixClass(bnd, name, owner, /*isGeneric*/false, UNKNOWN_MODIFIERS);
8✔
1404
        }
1405

1406
        /**
1407
         * helper method, we know the type exists, ensureFamixInterface will recover it
1408
         */
1409
        public Interface getFamixInterface(ITypeBinding bnd, String name, ContainerEntity owner) {
1410
                return ensureFamixInterface(bnd, name, owner, /*isGeneric*/false, UNKNOWN_MODIFIERS);
8✔
1411
        }
1412

1413
        /**
1414
         * helper method, we know the type exists, ensureFamixInterface will recover it
1415
         */
1416
        public Exception getFamixException(ITypeBinding bnd, String name, TWithTypes owner) {
1417
                return ensureFamixException(bnd, name, owner, /*isGeneric*/false, UNKNOWN_MODIFIERS);
8✔
1418
        }
1419

1420
        /**
1421
         * Ensures a famix entity for the owner of a binding.<br>
1422
         * This owner can be a method, a class or a namespace
1423
         * @param bnd -- binding for the owned entity
1424
         * @return a famix entity for the owner
1425
         */
1426
        private TNamedEntity ensureOwner(ITypeBinding bnd) {
1427
                TNamedEntity owner;
1428
                IMethodBinding parentMtd = bnd.getDeclaringMethod();
3✔
1429
                if (parentMtd != null) {
2✔
1430
                        owner = this.ensureFamixMethod(parentMtd);  // cast needed to desambiguate the call
5✔
1431
                }
1432
                else {
1433
                        ITypeBinding parentClass = bnd.getDeclaringClass();
3✔
1434
                        if (parentClass != null) {
2✔
1435
                owner = this.ensureFamixType(parentClass);
5✔
1436
            }
1437
                        else {
1438
                                IPackageBinding parentPckg = bnd.getPackage();
3✔
1439
                                if (parentPckg != null) {
2!
1440
                                        owner = this.ensureFamixPackage(parentPckg, null);
6✔
1441
                                } else {
1442
                                        owner = this.ensureFamixPackageDefault();
×
1443
                                }
1444
                        }
1445
                }
1446
                return owner;
2✔
1447
        }
1448

1449

1450
        /**
1451
         * Returns a FAMIX PrimitiveType with the given <b>name</b>, creating it if it does not exist yet
1452
         * We assume that PrimitiveType must be uniq for a given name
1453
         * @param name -- the name of the FAMIX PrimitiveType
1454
         * @return the FAMIX PrimitiveType or null in case of a FAMIX error
1455
         */
1456
        public PrimitiveType ensureFamixPrimitiveType(ITypeBinding bnd, String name) {
1457
                if (name == null) {
2✔
1458
                        if (bnd == null) {
2!
1459
                                return null;
×
1460
                        } else {
1461
                                name = bnd.getName();
3✔
1462
                        }
1463
                }
1464
                return ensureFamixUniqEntity(PrimitiveType.class, bnd, name);
7✔
1465
        }
1466

1467
        public <T extends TWithTypes & TNamedEntity> org.moosetechnology.model.famix.famixjavaentities.Enum ensureFamixEnum(ITypeBinding bnd, String name, TWithTypes owner) {
1468
                org.moosetechnology.model.famix.famixjavaentities.Enum fmx = null;
2✔
1469

1470
                // --------------- to avoid useless computations if we can
1471
                fmx = (org.moosetechnology.model.famix.famixjavaentities.Enum) getEntityByKey(bnd);
5✔
1472
                if (fmx != null) {
2✔
1473
                        return fmx;
2✔
1474
                }
1475

1476
                // --------------- name
1477
                if (name == null) {
2✔
1478
                        if (bnd == null) {
2!
1479
                                return null;
×
1480
                        }
1481
                        else {
1482
                                name = bnd.getName();
3✔
1483
                        }
1484
                }
1485

1486
                // --------------- owner
1487
                if (owner == null) {
2✔
1488
                        if (bnd == null) {
2!
1489
                                owner = ensureFamixPackageDefault();  // not really sure what to do here
×
1490
                        } else {
1491
                                owner = (TWithTypes) ensureOwner(bnd);
5✔
1492
                        }
1493
                }
1494

1495
                // --------------- recover from name ?
1496
                for (org.moosetechnology.model.famix.famixjavaentities.Enum candidate : getEntityByName(org.moosetechnology.model.famix.famixjavaentities.Enum.class, name)) {
9!
1497
                        if (matchAndMapType(bnd, name, (T) owner, candidate)) {
×
1498
                                fmx = candidate;
×
1499
                                break;
×
1500
                        }
1501
                }
×
1502

1503
                if (fmx == null) {
2!
1504
                        fmx = ensureFamixEntity(Enum.class, bnd, name);
7✔
1505
                        fmx.setTypeContainer(owner);
3✔
1506
                }
1507

1508
                if (bnd != null) {
2!
1509
                        setVisibility(fmx, bnd.getModifiers(), owner);
6✔
1510
                }
1511

1512
                return fmx;
2✔
1513
        }
1514

1515
        /**
1516
         * helper method, we know the type exists, ensureFamixEnum will recover it
1517
         */
1518
        public org.moosetechnology.model.famix.famixjavaentities.Enum getFamixEnum(ITypeBinding bnd, String name, TWithTypes owner) {
1519
                return ensureFamixEnum(bnd, name, owner);
6✔
1520
        }
1521

1522
        public EnumValue ensureFamixEnumValue(IVariableBinding bnd,        String name, Enum owner) {
1523
                EnumValue fmx;
1524

1525
                // --------------- to avoid useless computations if we can
1526
                fmx = (EnumValue)getEntityByKey(bnd);
5✔
1527
                if (fmx != null) {
2✔
1528
                        return fmx;
2✔
1529
                }
1530

1531
                // --------------- name
1532
                if (name == null) {
2!
1533
                        if (bnd == null) {
×
1534
                                return null;
×
1535
                        }
1536
                        else {
1537
                                name = bnd.getName();
×
1538
                        }
1539
                }
1540

1541
                // --------------- owner
1542
                if (owner == null) {
2✔
1543
                        if (bnd == null) {
2!
1544
                                return null;  // what would be the interest of creating an EnumValue without a declaring Enum type?
×
1545
                        }
1546
                        else {
1547
                                owner = ensureFamixEnum(bnd.getDeclaringClass(), null, null);
7✔
1548
                        }
1549
                }
1550

1551
                // --------------- recover from name ?
1552
                for (EnumValue candidate : getEntityByName(EnumValue.class, name) ) {
9!
1553
                        if ( matchAndMapVariable(bnd, name, owner, candidate) ) {
×
1554
                                fmx = candidate;
×
1555
                                break;
×
1556
                        }
1557
                }
×
1558
                if (fmx == null) {
2!
1559
                        fmx = ensureFamixEntity(EnumValue.class, bnd, name);
7✔
1560
                        fmx.setParentEnum(owner);
3✔
1561
                }
1562

1563
        fmx.setParentEnum(owner);
3✔
1564

1565
        return fmx;
2✔
1566
        }
1567

1568
    /**
1569
         * e.g. see {@link EntityDictionary#ensureFamixClass}
1570
         */
1571
        public AnnotationType ensureFamixAnnotationType(ITypeBinding bnd, String name, ContainerEntity owner) {
1572
                AnnotationType fmx;
1573

1574
                // --------------- to avoid useless computations if we can
1575
                fmx = (AnnotationType)getEntityByKey(bnd);
5✔
1576
                if (fmx != null) {
2✔
1577
                        return fmx;
2✔
1578
                }
1579

1580
                // --------------- name
1581
                if (name == null) {
2✔
1582
                        if (bnd == null) {
2!
1583
                                return null;
×
1584
                        }
1585
                        else {
1586
                                name = bnd.getName();
3✔
1587
                        }
1588
                }
1589

1590
                // --------------- owner
1591
                if (owner == null) {
2✔
1592
                        if (bnd == null) {
2!
1593
                                owner = ensureFamixPackageDefault();
×
1594
                        }
1595
                        else {
1596
                                IPackageBinding parentPckg = bnd.getPackage();
3✔
1597
                                if (parentPckg != null) {
2!
1598
                                        owner = this.ensureFamixPackage(parentPckg, null);
6✔
1599
                                } else {
1600
                                        owner = this.ensureFamixPackageDefault();
×
1601
                                }
1602
                        }
1603
                }
1604

1605
                // --------------- recover from name ?
1606
                for (AnnotationType candidate : getEntityByName(AnnotationType.class, name) ) {
13✔
1607
                        if ( matchAndMapType(bnd, name, owner, candidate) ) {
7✔
1608
                                fmx = candidate;
2✔
1609
                                break;
1✔
1610
                        }
1611
                }
1✔
1612

1613
                // --------------- create
1614
                if (fmx == null) {
2✔
1615
                        fmx = ensureFamixEntity(AnnotationType.class, bnd, name);
7✔
1616
                        fmx.setAnnotationTypesContainer(owner);
3✔
1617
                }
1618

1619
                if (bnd != null) {
2!
1620
                        // Not supported in Famix
1621

1622
                        // setVisibility(fmx, bnd.getModifiers());
1623
                }
1624

1625
                return fmx;
2✔
1626
        }
1627

1628
        /**
1629
         * helper method, we know the type exists, ensureFamixAnnotationType will recover it
1630
         */
1631
        public AnnotationType getFamixAnnotationType(ITypeBinding bnd, String name, ContainerEntity owner) {
1632
                return ensureFamixAnnotationType(bnd, name, owner);
6✔
1633
        }
1634

1635
        public AnnotationTypeAttribute ensureFamixAnnotationTypeAttribute(IMethodBinding bnd, String name, AnnotationType owner) {
1636
                AnnotationTypeAttribute fmx = null;
2✔
1637

1638
                // --------------- to avoid useless computations if we can
1639
                fmx = (AnnotationTypeAttribute)getEntityByKey(bnd);
5✔
1640
                if (fmx != null) {
2✔
1641
                        return fmx;
2✔
1642
                }
1643

1644
                // --------------- name
1645
                if (name == null) {
2!
1646
                        if (bnd == null) {
×
1647
                                return null;
×
1648
                        }
1649
                        else {
1650
                                name = bnd.getName();
×
1651
                        }
1652
                }
1653

1654
                // --------------- owner
1655
                if (owner == null) {
2!
1656
                        if (bnd == null) {
×
1657
                                return null;  // what would be the use of an AnnotationTypeAttribute without AnnotationType ?
×
1658
                        }
1659
                        else {
1660
                                ITypeBinding parentType = bnd.getDeclaringClass();
×
1661
                                if (parentType != null) {
×
1662
                                        owner = this.ensureFamixAnnotationType(parentType, null, null);
×
1663
                                }
1664
                                else  {
1665
                                        return null;  // what would be the use of an AnnotationTypeAttribute without AnnotationType ?
×
1666
                                }
1667
                        }
1668
                }
1669

1670
                // --------------- recover from name ?
1671
                for (AnnotationTypeAttribute candidate : getEntityByName(AnnotationTypeAttribute.class, name) ) {
13✔
1672
                        // JDT treats annotation type attributes as methods ...
1673
                        // checkAndMapMethod wants a signature as 2nd argument so we add empty param list
1674
                        if ( (bnd != null) && matchAndMapMethod(bnd, name+"()", null, owner, candidate) ) {
11!
1675
                                fmx = candidate;
×
1676
                                break;
×
1677
                        }
1678
                        // if the binding is null, the annotationTypeAttribute migth have been created
1679
                        else if ( (bnd == null) && matchAndMapVariable(null, name, owner, candidate)) {
2!
1680
                                fmx = candidate;
×
1681
                                break;
×
1682
                        }
1683
                }
1✔
1684

1685
                if (fmx == null) {
2!
1686
                        fmx = ensureFamixEntity(AnnotationTypeAttribute.class, bnd, name);
7✔
1687
                        fmx.setParentType(owner);
3✔
1688
                }
1689

1690
                if (bnd != null) {
2!
1691
                        // Not suopp
1692

1693
                        // setVisibility(fmx, bnd.getModifiers());
1694
                }
1695

1696
                return fmx;
2✔
1697
        }
1698

1699
        /**
1700
         * helper method, we know the attribute exists, ensureFamixAnnotationTypeAttribute will recover it
1701
         */
1702
        public AnnotationTypeAttribute getFamixAnnotationTypeAttribute(IMethodBinding bnd, String name, AnnotationType owner) {
1703
                return ensureFamixAnnotationTypeAttribute( bnd, name, owner);
6✔
1704
        }
1705
        
1706
        
1707
        /**
1708
         * Returns a FAMIX Wildcard with its bounds
1709
         * @param bnd
1710
         * @param name
1711
         * @param owner
1712
         * @return
1713
         */
1714
        public Wildcard ensureFamixWildcardType(ITypeBinding bnd, String name, TParametricEntity owner, TWithTypes ctxt) {
1715
                Wildcard fmx = this.ensureFamixEntity(Wildcard.class, bnd, bnd.getName());
8✔
1716
                if(bnd.getBound() != null) {
3✔
1717
                        Type bound = this.ensureFamixType(bnd.getBound());
5✔
1718
                        if(bnd.isUpperbound()) {
3✔
1719
                                fmx.setUpperBound(bound);
3✔
1720
                                bound.addUpperBoundedWildcards(fmx);
4✔
1721
                        }else{
1722
                                fmx.setLowerBound(bound);
3✔
1723
                                bound.addLowerBoundedWildcards(fmx);
3✔
1724
                        }
1725
                }
1726
                return fmx;
2✔
1727
        }
1728

1729
        /**
1730
         * Returns a Famix TypeParameter (created by a Famix ParametricEntity) with the given <b>name</b>, creating it if it does not exist yet
1731
         * In the second case, sets some default properties: not Abstract, not Final, not Private, not Protected, not Public
1732
         * @param name -- the name of the Famix TypeParameter
1733
         * @return the Famix TypeParameter or null in case of a Famix error
1734
         */
1735
        public TypeParameter ensureFamixTypeParameter(ITypeBinding bnd,        String name, TWithTypes owner) {
1736
                TypeParameter fmx;
1737

1738
                // --------------- to avoid useless computations if we can
1739
                fmx = (TypeParameter)getEntityByKey(bnd);
5✔
1740
                if (fmx != null) {
2✔
1741
                        return fmx;
2✔
1742
                }
1743

1744
                // --------------- name
1745
                if (name == null) {
2✔
1746
                        if (bnd == null) {
2!
1747
                                return null;
×
1748
                        }
1749
                        else {
1750
                                name = bnd.getName();
3✔
1751
                        }
1752
                }
1753

1754
                // --------------- owner
1755
                if (owner == null && bnd != null) {
2!
1756
            if (bnd.getDeclaringClass() != null) {
×
1757
                owner = this.ensureFamixType(bnd.getDeclaringClass());
×
1758
            } else if(bnd.getDeclaringMethod() != null) {
×
1759
                owner = this.ensureFamixMethod(bnd.getDeclaringMethod());
×
1760
            }
1761
                }
1762

1763
                // --------------- recover from name ?
1764
                for (Type candidate : this.getEntityByName(Type.class, name)) {
13✔
1765
                        if ( matchAndMapType(bnd, name, (ContainerEntity) owner, candidate) ) {
8✔
1766
                                fmx = (TypeParameter) candidate;
3✔
1767
                                break;
1✔
1768
                        }
1769
                }
1✔
1770

1771
                // --------------- create
1772
                if (fmx == null) {
2✔
1773
                        fmx = ensureFamixEntity(TypeParameter.class, bnd, name);
7✔
1774
                        if(bnd != null && bnd.getSuperclass() != null) {
5!
1775
                                Type upperBound = ensureFamixType(bnd.getSuperclass());
5✔
1776
                                fmx.setUpperBound(upperBound);
3✔
1777
                        }
1778
                        if(bnd != null) {
2✔
1779
                for (ITypeBinding intbnd : bnd.getInterfaces()) {
17✔
1780
                    Type upperBound = ensureFamixType(intbnd);
4✔
1781
                    fmx.setUpperBound(upperBound);
3✔
1782
                }
1783
            }
1784
                        fmx.setTypeContainer(owner);
3✔
1785
                }
1786

1787
                return fmx;
2✔
1788
        }
1789

1790
        /**
1791
         * Checks whether the existing unmapped Famix Namespace matches the binding.
1792
         * Checks that the candidate has the same name as the JDT bound package, and checks recursively that owners also match.
1793
         *
1794
         * @param bnd       -- a JDT binding that we are trying to match to the candidate
1795
         * @param name      of the package
1796
         * @param owner     of the package
1797
         * @param candidate -- a Famix Entity
1798
         * @return whether the binding matches the candidate (if <b>true</b>, the mapping is recorded)
1799
         */
1800
        private boolean matchAndMapPackage(IPackageBinding bnd, String name, Package owner, NamedEntity candidate) {
1801
                if (!(candidate instanceof Package)) {
3!
1802
                        return false;
×
1803
                }
1804

1805
                // check whether bnd and candidate are already bound
1806
                CheckResult res = checkKeyMatch(bnd, candidate);
5✔
1807
                if (res == CheckResult.MATCH) {
3✔
1808
                        return true;
2✔
1809
                } else if (res == CheckResult.FAIL) {
3!
1810
                        return false;
×
1811
                }
1812

1813
                if (checkNameMatch(bnd, name, candidate) == CheckResult.FAIL) {
7✔
1814
                        return false;
2✔
1815
                }
1816

1817
                // names match, not need to look at owner because names of Namespaces are their fully qualified name
1818
                conditionalMapToKey(bnd, candidate);
4✔
1819
                return true;
2✔
1820
        }
1821

1822
        /**
1823
         * Checks whether the existing unmapped Famix Type matches the binding.
1824
         * Checks that the candidate has the same name as the JDT bound type, and checks recursively that owners also match.
1825
         * We also check that the actual class of the candidate matches (can be a sub-class of FamixType).
1826
         * @param bnd -- a JDT binding that we are trying to match to the candidate
1827
         * @param name of the type
1828
         * @param owner of the type
1829
         * @param candidate -- a Famix NamedEntity (Class, Type, PrimitiveType, Enum, AnnotationType)
1830
         * @return whether the binding matches the candidate (if <b>true</b>, the mapping is recorded)
1831
         */
1832
        private <T extends TWithTypes & TNamedEntity> boolean matchAndMapType(ITypeBinding bnd, String name, TNamedEntity owner, TNamedEntity candidate) {
1833
                if (! (candidate instanceof Type) ) {
3!
1834
                        return false;
×
1835
                }
1836

1837
                // check whether bnd and candidate are already bound
1838
                CheckResult res = checkKeyMatch(bnd, candidate);
5✔
1839
                if (res == CheckResult.MATCH) {
3✔
1840
                        return true;
2✔
1841
                }
1842
                else if (res == CheckResult.FAIL) {
3✔
1843
                        return false;
2✔
1844
                }
1845

1846
                if ( (bnd != null) && (bnd.isArray()) ) {
5!
1847
                                bnd = bnd.getElementType();
×
1848
                }
1849

1850
                // checking names
1851
                if ( (bnd != null) && (bnd.isParameterizedType() || bnd.isRawType()) ) {
8!
1852
                        name = bnd.getErasure().getName();
×
1853
                }
1854
                else if (bnd != null) {
2✔
1855
                        name = bnd.getName();
3✔
1856
                }
1857
                // else name = name
1858
                if (checkNameMatch(null, name, candidate) == CheckResult.FAIL) {
7✔
1859
                        return false;
2✔
1860
                }
1861

1862
                // special case of primitive types
1863
                if (candidate instanceof PrimitiveType) {
3!
1864
                        if ( (bnd != null) && bnd.isPrimitive() ) {
×
1865
                                // names are equal so it's OK
1866
                                conditionalMapToKey(bnd, candidate);
×
1867
                                return true;
×
1868
                        }
1869
                        else if ( (bnd == null) && (owner == null) ) {
×
1870
                                return true;
×
1871
                        }
1872
                }
1873

1874
                // check owners without bnd
1875
                if (bnd == null) {
2✔
1876
                        return matchAndMapTypeOwner(bnd, owner, (Type) candidate);
7✔
1877
                }
1878

1879
                // check owners with bnd
1880
                // type is an annotation
1881
                if (bnd.isAnnotation() && (candidate instanceof AnnotationType)) {
6!
1882
                        if (matchAndMapPackage(bnd.getPackage(), owner.getName(), (Package) Util.getOwner(owner), Util.getOwner(candidate))) {
13!
1883
                                conditionalMapToKey(bnd, candidate);
4✔
1884
                                return true;
2✔
1885
                        } else {
1886
                                return false;
×
1887
                        }
1888
                }
1889

1890
                // check owners with bnd
1891
                // type is a Parameterized type
1892
                if ((bnd.isParameterizedType() || bnd.isRawType()) && (candidate instanceof ParametricClass)) {
6!
1893
                        return matchAndMapTypeOwner(bnd, owner, (Type) candidate);
×
1894
                }
1895

1896
                // check owners with bnd
1897
                // type is an Enum
1898
                if (bnd.isEnum() && (candidate instanceof Enum)) {
3!
1899
                        return matchAndMapTypeOwner(bnd, owner, (Type) candidate);
×
1900
                }
1901

1902
                // check owners with bnd
1903
                // type is something elae (a class or interface)
1904
                // Annotation are interfaces too, so we should check this one after isAnnotation
1905
                if ( bnd.isClass()) {
3!
1906
                        return matchAndMapClass(bnd, name, owner, (Type) candidate);
×
1907
                }
1908

1909
                if(bnd.isInterface()) {
3!
1910
                        return matchAndMapInterface(bnd, name, owner, (Type) candidate);
×
1911
                }
1912

1913
                return false;
2✔
1914
        }
1915

1916
        /**
1917
         * Checks whether the existing unmapped Famix Class (or Interface) matches the binding.
1918
         * Checks that the candidate has the same name as the JDT bound type, and checks recursively that owners also match.
1919
         * @param bnd -- a JDT binding that we are trying to match to the candidate
1920
         * @param name of the class
1921
         * @param owner of the class
1922
         * @param candidate -- a Famix Entity
1923
         * @return whether the binding matches the candidate (if <b>true</b>, the mapping is recorded)
1924
         */
1925
        private boolean matchAndMapClass(ITypeBinding bnd, String name, TNamedEntity owner, TType candidate) {
1926
                if (!(candidate instanceof Class)) {
3!
1927
                        return false;
×
1928
                }
1929

1930
                // check whether bnd and candidate are already bound
1931
                CheckResult res = checkKeyMatch(bnd, candidate);
5✔
1932
                if (res == CheckResult.MATCH) {
3!
1933
                        return true;
×
1934
                } else if (res == CheckResult.FAIL) {
3✔
1935
                        return false;
2✔
1936
                }
1937

1938
                if (checkNameMatch(bnd, name, candidate) == CheckResult.FAIL) {
7!
1939
                        return false;
×
1940
                }
1941

1942
                // checking owner
1943
                return matchAndMapTypeOwner(bnd, owner, (Type) candidate);
7✔
1944
        }
1945

1946
        /**
1947
         * Checks whether the existing unmapped Famix Class (or Interface) matches the binding.
1948
         * Checks that the candidate has the same name as the JDT bound type, and checks recursively that owners also match.
1949
         * @param bnd -- a JDT binding that we are trying to match to the candidate
1950
         * @param name of the class
1951
         * @param owner of the class
1952
         * @param candidate -- a Famix Entity
1953
         * @return whether the binding matches the candidate (if <b>true</b>, the mapping is recorded)
1954
         */
1955
        private boolean matchAndMapInterface(ITypeBinding bnd, String name, TNamedEntity owner, Type candidate) {
1956
                if (!(candidate instanceof Interface)) {
3!
1957
                        return false;
×
1958
                }
1959

1960
                // check whether bnd and candidate are already bound
1961
                CheckResult res = checkKeyMatch(bnd, candidate);
5✔
1962
                if (res == CheckResult.MATCH) {
3!
1963
                        return true;
×
1964
                } else if (res == CheckResult.FAIL) {
3✔
1965
                        return false;
2✔
1966
                }
1967

1968
                if (checkNameMatch(bnd, name, candidate) == CheckResult.FAIL) {
7!
1969
                        return false;
×
1970
                }
1971

1972
                // checking owner
1973
                return matchAndMapTypeOwner(bnd, owner, candidate);
6✔
1974
        }
1975

1976
        /**
1977
         * Checks whether the existing unmapped Famix "Method" matches the binding.
1978
         * Checks that the candidate has the same name and same signature as the JDT bound method, and checks recursively that owners also match.
1979
         * Note that AnnotationTypeAttribute are treated as methods by JDT, so they are checked here.
1980
         * @param bnd -- a JDT binding that we are trying to match to the candidate
1981
         * @param sig -- signature of the method
1982
         * @param retTyp -- return type of the method
1983
         * @param owner of the method
1984
         * @param candidate -- a Famix Entity (regular Method or AnnotationTypeAttribute)
1985
         * @return whether the binding matches the candidate (if <b>true</b>, the mapping is recorded)
1986
         */
1987
        private  boolean matchAndMapMethod(IMethodBinding bnd, String sig, TType retTyp, TNamedEntity owner, NamedEntity candidate) {
1988
                if (! (candidate instanceof Method) ) {
3✔
1989
                        return false;
2✔
1990
                }
1991

1992
                // check whether bnd and candidate are already bound
1993
                CheckResult res = checkKeyMatch(bnd, candidate);
5✔
1994
                if (res == CheckResult.MATCH) {
3!
1995
                        return true;
×
1996
                }
1997
                else if (res == CheckResult.FAIL) {
3✔
1998
                        return false;
2✔
1999
                }
2000

2001
                // checking names
2002
                String name = (sig != null) ? sig.substring(0, sig.indexOf('(')) : null;
10!
2003
                if (checkNameMatch(bnd, name, candidate) == CheckResult.FAIL) {
7!
2004
                        return false;
×
2005
                }
2006

2007
                // for methods, the name is not enough, we must test the signature also
2008
                // but not for AnnotationTypeAttribute
2009

2010
                        if (bnd != null) {
2✔
2011
                                sig = bnd.getName() + "(" + signatureParamsFromBinding(bnd) + ")";
7✔
2012
                        }
2013
                        if (! ((Method) candidate).getSignature().equals(sig)) {
6✔
2014
                                return false;
2✔
2015
                        }
2016

2017
                        // and still for method, must also check the return type
2018
                        if (bnd != null) {
2✔
2019
                                if (isConstructorBinding(bnd)) {
4!
2020
                                        if ( ((Method) candidate).getDeclaredType() != null ) {
×
2021
                                                return false;
×
2022
                                        }
2023
                                        // else OK for now
2024
                                }
2025
                                else { // not a constructor
2026
                                        if ( ((Method) candidate).getDeclaredType() == null ) {
4!
2027
                                                return false;
×
2028
                                        }
2029
                                        else if (! matchAndMapType(bnd.getReturnType(), null, null, ((Method) candidate).getDeclaredType()) ) {
10!
2030
                                                return false;
×
2031
                                        }
2032
                                        // else OK for now
2033
                                }
2034
                        }
2035
                        else {  // bnd == null
2036
                                if (retTyp == null) { // similar to (bnd.isConstructor())
2!
2037
                                        if ( ((Method) candidate).getDeclaredType() != null ) {
4✔
2038
                                                return false;
2✔
2039
                                        }
2040
                                        // else OK for now
2041
                                } else { // (ret != null)  i.e. not a constructor
2042
                                        if (((Method) candidate).getDeclaredType() == null) {
×
2043
                                                return false;
×
2044
                                        } else if (!matchAndMapType(null, retTyp.getName(), Util.getOwner(retTyp), (NamedEntity) ((Method) candidate).getDeclaredType())) {
×
2045
                                                return false;
×
2046
                                        }
2047
                                        // else OK for now
2048
                                }
2049
                        }
2050

2051

2052
                // check owner
2053
                if (matchAndMapOwnerAsType(((bnd != null) ? bnd.getDeclaringClass() : null), owner, Util.getOwner(candidate)) == CheckResult.MATCH) {
13✔
2054
                        conditionalMapToKey(bnd, candidate);
4✔
2055
                        return true;
2✔
2056
                } else {
2057
                        return false;
2✔
2058
                }
2059
        }
2060

2061
        /** testing that a method binding is for a constructor
2062
         * There is a special case for "diamond constructors" (eg: <code>new HashSet<>()</code>)
2063
         */
2064
        protected boolean isConstructorBinding(IMethodBinding bnd) {
2065
                if (bnd.isConstructor()) {
3✔
2066
                        return true;
2✔
2067
                }
2068
                if ( bnd.getName().equals("<factory>") ) {
5✔
2069
                        return true;
2✔
2070
                }
2071
                return false;
2✔
2072
        }
2073

2074
        /**
2075
         * Checks whether the candidate (an existing unmapped Famix "Variable" like Attribute, Parameter, ...) matches the binding.
2076
         * Checks that the candidate has the same name as the JDT bound variable, and checks recursively that owners also match.
2077
         * The Famix candidate is a NamedEntity and not a StructuralEntity to allow dealing with Famix EnumValue that JDT treats as variables
2078
         * @param bnd -- a JDT binding that we are trying to match to the candidate
2079
         * @param name of the variable
2080
         * @param owner of the variable
2081
         * @param candidate -- a Famix Entity (a StructuralEntity or an EnumValue)
2082
         * @return whether the binding matches the candidate (if <b>true</b>, the mapping is recorded)
2083
         */
2084
        private boolean matchAndMapVariable(IVariableBinding bnd, String name, TNamedEntity owner, TNamedEntity candidate) {
2085
                if (!(candidate instanceof TStructuralEntity)) {
3!
2086
                        return false;
×
2087
                }
2088

2089
                // check whether bnd and candidate are already bound
2090
                CheckResult keyMatch = checkKeyMatch(bnd, candidate);
5✔
2091
                if (keyMatch == CheckResult.MATCH) {
3!
2092
                        return true;
×
2093
                } else if (keyMatch == CheckResult.FAIL) {
3✔
2094
                        return false;
2✔
2095
                }
2096

2097
                if (checkNameMatch(bnd, name, candidate) == CheckResult.FAIL) {
7!
2098
                        return false;
×
2099
                }
2100

2101
                // check owner
2102
                TNamedEntity candidateOwner = Util.getOwner(candidate);
3✔
2103

2104
                // local variable or parameter ?
2105
                // owner is a Method? (for example in case of an anonymous class)
2106
                CheckResult res = matchAndMapOwnerAsMethod(((bnd != null) ? bnd.getDeclaringMethod() : null), owner, candidateOwner);
11✔
2107
                if (res == CheckResult.FAIL) {
3✔
2108
                        return false;
2✔
2109
                } else if (res == CheckResult.MATCH) {
3!
2110
                        conditionalMapToKey(bnd, candidate);
×
2111
                        return true;
×
2112
                }
2113

2114
                // check owner
2115
                // <anArray>.length field?
2116
                if (name.equals("length")) {
4!
2117
                        boolean isArrayLengthField = ((bnd != null) && (bnd.getDeclaringClass() == null)) ||
×
2118
                                                                                 ((bnd == null) && (owner.getName().equals(EntityDictionary.ARRAYS_NAME)));
×
2119
                        if (isArrayLengthField) {
×
2120
                                if (candidateOwner.getName().equals(EntityDictionary.ARRAYS_NAME)) {
×
2121
                                        conditionalMapToKey(bnd, candidate);
×
2122
                                        return true;
×
2123
                                }
2124
                                else {
2125
                                        return false;
×
2126
                                }
2127
                        }
2128
                }
2129

2130
                // check owner
2131
                // "normal" field?
2132
                res = matchAndMapOwnerAsType( ((bnd != null) ? bnd.getDeclaringClass() : null), owner, candidateOwner);
11✔
2133
                if (res == CheckResult.MATCH) {
3!
2134
                        conditionalMapToKey(bnd, candidate);
4✔
2135
                        return true;
2✔
2136
                }
2137
                return false;
×
2138
        }
2139

2140
        /**
2141
         * Checks whether the existing unmapped Famix Type's parent (or owner) matches the binding's owner.
2142
         * Checks that the candidate has the same name as the JDT bound type, and checks recursively that owners also match.
2143
         * @param bnd -- a JDT binding whose owner we are trying to match to the candidate's owner
2144
         * @param owner -- the owner of the type
2145
         * @param candidate -- a Famix Entity
2146
         * @return whether we found a match (if <b>true</b>, the mapping is recorded)
2147
         */
2148
        private boolean matchAndMapTypeOwner(ITypeBinding bnd, TNamedEntity owner, Type candidate) {
2149
                ContainerEntity candidateOwner = Util.getOwner(candidate);
4✔
2150

2151
                // owner is a Method? (for example in case of an anonymous class)
2152
                CheckResult res = matchAndMapOwnerAsMethod(((bnd != null) ? bnd.getDeclaringMethod() : null), owner, candidate);
11✔
2153
                if (res == CheckResult.MATCH) {
3!
2154
                        conditionalMapToKey(bnd, candidate);
×
2155
                        return true;
×
2156
                } else if (res == CheckResult.FAIL) {
3!
2157
                        return false;
×
2158
                }
2159

2160
                // owner is a class ?
2161
                res = matchAndMapOwnerAsType(((bnd != null) ? bnd.getDeclaringClass() : null), owner, candidateOwner);
11✔
2162
                if (res == CheckResult.MATCH) {
3✔
2163
                        conditionalMapToKey(bnd, candidate);
4✔
2164
                        return true;
2✔
2165
                }
2166
                else if (res == CheckResult.FAIL) {
3✔
2167
                        return false;
2✔
2168
                }
2169

2170
                // owner must be a package
2171
                if (matchAndMapOwnerAsNamespace( ((bnd != null)?bnd.getPackage():null), owner, candidateOwner) == CheckResult.MATCH) {
12✔
2172
                        conditionalMapToKey(bnd, candidate);
4✔
2173
                        return true;
2✔
2174
                }
2175
                return false;
2✔
2176
        }
2177

2178
        /**
2179
         * Check whether the owner of candidates is a method macthinf either methBnd or owner
2180
         * @param methBnd
2181
         * @param owner
2182
         * @param candidateOwner
2183
         * @return a {@link CheckResult}
2184
         */
2185
        private  <T extends TNamedEntity> CheckResult matchAndMapOwnerAsMethod(IMethodBinding methBnd, T owner, T candidateOwner) {
2186
                if ((methBnd != null) || (owner instanceof Method)) {
5!
2187
                        if (!(candidateOwner instanceof Method)) {
3!
2188
                                return CheckResult.FAIL;
×
2189
                        }
2190

2191
                        ContainerEntity ownerOwner = (owner != null) ? (ContainerEntity) Util.getOwner(owner) : null;
7!
2192
                        String ownerSig = (owner != null) ? ((Method) owner).getSignature() : null;
7!
2193
                        Type ownerReturn = (owner != null) ? (Type) ((Method) owner).getDeclaredType() : null;
8!
2194

2195
                        if (matchAndMapMethod(methBnd, ownerSig, ownerReturn, ownerOwner, (Method) candidateOwner)) {
9!
2196
                                return CheckResult.MATCH;
×
2197
                        } else {
2198
                                return CheckResult.FAIL;
2✔
2199
                        }
2200
                }
2201
                return CheckResult.UNDECIDED;
2✔
2202
        }
2203

2204
        /**
2205
         * @param typBnd
2206
         * @param owner
2207
         * @param candidateOwner
2208
         * @return a {@link CheckResult}
2209
         */
2210
        private CheckResult matchAndMapOwnerAsType(ITypeBinding typBnd, TNamedEntity owner, TNamedEntity candidateOwner) {
2211
                if ((typBnd != null) || (owner instanceof Type)) {
5✔
2212
                        if (!(candidateOwner instanceof Type)) {
3✔
2213
                                return CheckResult.FAIL;
2✔
2214
                        }
2215

2216
                        TNamedEntity ownerOwner = (owner != null) ? Util.getOwner(owner) : null;
6!
2217
                        String ownerName = (owner != null) ? owner.getName() : null;
6!
2218

2219
                        if (matchAndMapType(typBnd, ownerName, ownerOwner, candidateOwner)) {
7✔
2220
                                return CheckResult.MATCH;
2✔
2221
                        } else {
2222
                                return CheckResult.FAIL;
2✔
2223
                        }
2224
                }
2225
                return CheckResult.UNDECIDED;
2✔
2226
        }
2227

2228
        private CheckResult matchAndMapOwnerAsNamespace(IPackageBinding pckgBnd, TNamedEntity owner, ContainerEntity candidateOwner) {
2229
                if ((pckgBnd != null) || (owner instanceof Package)) {
5!
2230
                        if (!(candidateOwner instanceof Package)) {
3!
2231
                                return CheckResult.FAIL;
×
2232
                        }
2233

2234
                        Package ownerOwner = (owner != null) ? (Package) Util.getOwner(owner) : null;
7!
2235
                        String ownerName = (owner != null) ? owner.getName() : null;
6!
2236

2237
                        if (matchAndMapPackage(pckgBnd, ownerName, ownerOwner, candidateOwner)) {
7✔
2238
                                return CheckResult.MATCH;
2✔
2239
                        } else {
2240
                                return CheckResult.FAIL;
2✔
2241
                        }
2242
                }
2243
                return CheckResult.UNDECIDED;
×
2244
        }
2245

2246
        /**
2247
         * Checks whether the name and the candidate matches the name of the entity (given either by 'bnd' or 'name')<br>
2248
         * 'name' and 'bnd' cannot be null together
2249
         * @param bnd -- binding associated with the entity may be null
2250
         * @param name -- name of the entity may be null
2251
         * @param candidate
2252
         * @return true if names match, false if not
2253
         */
2254
        private CheckResult checkNameMatch(IBinding bnd, String name, TNamedEntity candidate) {
2255
                if ( (bnd != null) && (! bnd.getName().equals(candidate.getName())) ) {
8!
2256
                        return CheckResult.FAIL;
×
2257
                }
2258
                else if ( (bnd == null) && (name != null) && (! name.equals(candidate.getName())) ) {
9!
2259
                        return CheckResult.FAIL;
2✔
2260
                }
2261
                else {
2262
                        return CheckResult.MATCH;
2✔
2263
                }
2264
        }
2265

2266
        /**
2267
         * Check whether key and candidate are already bound together, whether either is bound to something else, or whether none is bound
2268
         * @param key
2269
         * @param candidate
2270
         * @return <ul><li><b>-1</b>, if either is bound to something else</li><li><b>0</b>, if none is bound (or key is null)</li><li><b>1</b>, if they are bound to each other</li></ul>
2271
         */
2272
        private CheckResult checkKeyMatch(IBinding key, TNamedEntity candidate) {
2273
                if (key == null) {
2✔
2274
                        return CheckResult.UNDECIDED;
2✔
2275
                }
2276

2277
                NamedEntity bound = (NamedEntity)getEntityByKey(key);
5✔
2278
                if (bound == candidate) {
3✔
2279
                        return CheckResult.MATCH;
2✔
2280
                }
2281
                else if (bound != null) {
2✔
2282
                        return CheckResult.FAIL;
2✔
2283
                }
2284
                else if (getEntityKey(candidate) != null) {
4✔
2285
                        // candidate already bound, and not to this binding
2286
                        return CheckResult.FAIL;
2✔
2287
                }
2288
                else {
2289
                        return CheckResult.UNDECIDED;
2✔
2290
                }
2291
        }
2292

2293
        private void conditionalMapToKey(IBinding bnd, TNamedEntity ent) {
2294
                if (bnd != null) {
2✔
2295
                        mapEntityToKey(bnd, ent);
4✔
2296
                }
2297
        }
1✔
2298

2299
        public Method ensureFamixMethod(IMethodBinding bnd) {
2300
                return ensureFamixMethod(
8✔
2301
                                bnd,
2302
                                /*name*/null,
2303
                                /*paramsType*/null,
2304
                                /*returnType*/null,
2305
                                /*owner*/null,
2306
                                (bnd == null) ? UNKNOWN_MODIFIERS : bnd.getModifiers());
6✔
2307
        }
2308

2309
        /**
2310
         * Returns a Famix Method associated with the IMethodBinding.
2311
         * The Entity is created if it does not exist.
2312
         * @param name -- the name of the Famix Method (MUST NOT be null, but this is not checked)
2313
         * @param ret -- Famix Type returned by the method (ideally should only be null in case of a constructor, but will accept it in any case)
2314
         * @param owner -- type defining the method (should not be null, but it will work if it is)
2315
         * @return the Famix Entity found or created. May return null if "bnd" is null or in case of a Famix error
2316
         */
2317
        public Method ensureFamixMethod(IMethodBinding bnd, String name, Collection<String> paramTypes, TType ret, TWithMethods owner, int modifiers) {
2318
                Method fmx;
2319
                String signature;
2320
                boolean delayedRetTyp;
2321

2322
                // --------------- to avoid useless computations if we can
2323
                fmx = (Method)getEntityByKey(bnd);
5✔
2324
                if (fmx != null) {
2✔
2325
                        return fmx;
2✔
2326
                }
2327

2328
                // --------------- name
2329
                if (name == null) {
2✔
2330
                        if (bnd == null) {
2✔
2331
                                return null;
2✔
2332
                        }
2333
                        else {
2334
                                name = bnd.getName();
3✔
2335
                        }
2336
                }
2337

2338
                // --------------- signature
2339
                signature = name + "(";
3✔
2340
                 if (bnd != null) {
2✔
2341
                    signature += signatureParamsFromBinding(bnd);
7✔
2342
                }
2343
        else if (paramTypes != null) {
2!
2344
                        signature += signatureParamsFromStringCollection(paramTypes);
7✔
2345
                }
2346
                else {
2347
                        signature += "???";
×
2348
                }
2349
                signature += ")";
3✔
2350

2351
                // --------------- return type
2352
                delayedRetTyp = false;
2✔
2353
                ITypeBinding retTypBnd = null;
2✔
2354
                if (ret == null) {
2!
2355
                        if (bnd != null) {
2✔
2356
                // must create the return type
2357
                // but for method like "<T> T mtd()" where T belongs to mtd and mtd returns T,
2358
                // we need T to create the method and the method to create T ...
2359
                // so we need to test the situation and deal with it
2360
                retTypBnd = bnd.getReturnType();
3✔
2361
                if ( (retTypBnd != null) && retTypBnd.isTypeVariable() && (retTypBnd.getDeclaringMethod() == bnd) ) {
9✔
2362
                    delayedRetTyp = true;
3✔
2363
                }
2364
                else {
2365
                        ret = this.referredType(retTypBnd, fmx);
5✔
2366
                }
2367
                        }
2368
                }
2369

2370
                // --------------- owner
2371
                if (owner == null) {
2✔
2372
                        if (bnd == null) {
2✔
2373
                                owner = ensureFamixClassStubOwner();
4✔
2374
                        }
2375
                        else {
2376
                                ITypeBinding classBnd = bnd.getDeclaringClass().getErasure();
4✔
2377
                                if (classBnd != null) {
2!
2378
                                        owner = ensureFamixType(classBnd);
5✔
2379
                                }
2380
                                else {
2381
                                        owner = ensureFamixClassStubOwner();
×
2382
                                }
2383
                        }
2384
                }
2385

2386
                // --------------- recover from name ?
2387
                for (Method candidate : this.getEntityByName(Method.class, name)) {
13✔
2388
                        if (matchAndMapMethod(bnd, signature, ret, (TNamedEntity) owner, candidate)) {
9✔
2389
                                fmx = candidate;
2✔
2390
                                break;
1✔
2391
                        }
2392
                }
1✔
2393

2394
                if (fmx == null) {
2✔
2395
                        if(bnd != null && bnd.isGenericMethod() && !isConstructorBinding(bnd)) {
9✔
2396
                                fmx = ensureFamixEntity(ParametricMethod.class, bnd, name);
7✔
2397
                                for(ITypeBinding param: bnd.getTypeParameters()) {
18✔
2398
                                        TypeParameter fmxParam = this.ensureFamixTypeParameter(param, null, fmx);
6✔
2399
                                        fmxParam.setGenericEntity((ParametricMethod)fmx);
4✔
2400
                                }
2401
                        // Parameterized method binding = when the method is the target of an invocation.
2402
                        } else if (bnd != null && bnd.isParameterizedMethod()) {
5✔
2403
                                fmx = this.ensureFamixMethod(bnd.getMethodDeclaration());
6✔
2404
                        } else {
2405
                if (bnd != null && isConstructorBinding(bnd)) {
6✔
2406
                    fmx = ensureFamixEntity(Initializer.class, bnd, name);
8✔
2407
                } else {
2408
                    fmx = ensureFamixEntity(Method.class, bnd, name);
7✔
2409
                }
2410
            }
2411

2412
                        fmx.setSignature(signature);
3✔
2413
                        ITypeBinding returnTypeBnd = (bnd == null) ? null : bnd.getReturnType();
7✔
2414
                        ensureFamixEntityTyping(returnTypeBnd, fmx, ret);
6✔
2415
                        fmx.setParentType(owner);
3✔
2416
                }
2417

2418
                if (fmx != null) {
2!
2419
                        setMethodModifiers(fmx, modifiers);
4✔
2420
                }
2421

2422
        //If it has the #default keywork, we mark it as default implementation
2423
        if (Modifier.isDefault(modifiers)) {
3✔
2424
            fmx.setKind(DEFAULT_IMPLEMENTATION_KIND_MARKER);
3✔
2425
        }
2426

2427
        if (delayedRetTyp) {
2✔
2428
                        int retTypModifiers = retTypBnd.getModifiers();
3✔
2429
                        ITypeBinding returnTypeBnd = bnd.getReturnType();
3✔
2430
                        ensureFamixEntityTyping(returnTypeBnd, fmx, this.ensureFamixType(retTypBnd, /*name*/null, /*owner*/fmx, /*ctxt*/(ContainerEntity) owner, retTypModifiers));
13✔
2431
                }
2432

2433
                return fmx;
2✔
2434
        }
2435

2436

2437
        /**
2438
         * Creates or recovers the initializer method containing the attribute initializations of a type.
2439
         * @param owner Type containing the initializer
2440
         * @param isStatic Modifier of the initializer. A type can have 2 initializers for attribute initialization: 1 static and 1 not.
2441
         * @param isInitializationBlock True if the entity is an initialization block. False for the artificial method containing all field initializations.
2442
         * @return the FamixInitializer
2443
         */
2444
        public Initializer ensureFamixInitializer(TWithMethods owner, Boolean isStatic, Boolean isInitializationBlock) {
2445
                Initializer fmx = null;
2✔
2446

2447
                if (owner != null) {
2!
2448
                        Optional<TMethod> existingInitializer = owner.getMethods().stream()
6✔
2449
                                        .filter(meth ->
1✔
2450
                                                        ((Method) meth).getIsInitializer() &&
8✔
2451
                                                        ((Method) meth).getIsConstructor().equals(false) &&
7✔
2452
                                                        ((Method) meth).getIsClassSide().equals(isStatic) &&
6✔
2453
                                                        ((Initializer) meth).getIsInitializationBlock().equals(isInitializationBlock))
7✔
2454
                                        .findFirst();
2✔
2455
                        if (existingInitializer.isPresent()) {
3✔
2456
                                fmx = (Initializer) existingInitializer.get();
4✔
2457
                        }
2458
                }
2459

2460
                if (fmx == null) {
2✔
2461
                        fmx = createFamixEntity(Initializer.class, INIT_BLOCK_NAME);
6✔
2462
                        fmx.setSignature(INIT_BLOCK_NAME + "()" );
3✔
2463
                        fmx.setVisibility(MODIFIER_PRIVATE);
3✔
2464
                        fmx.setParentType(owner);
3✔
2465
                        fmx.setIsClassSide(isStatic);
3✔
2466
                        fmx.setIsInitializationBlock(isInitializationBlock);
3✔
2467
                }
2468

2469
                return fmx;
2✔
2470
        }
2471

2472

2473
        public Initializer ensureImplicitConstructor(IMethodBinding binding, TWithMethods owner, String name, Collection<String> parameterTypesNames) {
2474
                Initializer fmx = null;
2✔
2475

2476
                if (fmx == null) {
2!
2477
                        int modifiers = (binding != null) ? binding.getModifiers() : EntityDictionary.UNKNOWN_MODIFIERS;
7✔
2478
                        if (binding == null) {
2✔
2479
                                // OK! Binding is null, this is the default constructor!!
2480
                                // It has no source code :)
2481
                                fmx = ensureFamixEntity(Initializer.class, null, name);
7✔
2482
                                fmx.setParentType(owner);
3✔
2483
                                fmx.setSignature(name + "()");                                
5✔
2484
                        } else {
2485
                                // But, if we have the binding, that means the constructor exists. Let's just go the normal way
2486
                                fmx = (Initializer) this.ensureFamixMethod(binding, name, parameterTypesNames, /*ret type*/null, owner, modifiers);
10✔
2487
                        }
2488
                }
2489

2490
                return fmx;
2✔
2491
        }
2492

2493
        /**
2494
         * Creates or recovers a stub Famix Method
2495
         * @param name of the method
2496
         * @return the Famix Method
2497
         */
2498
        public Method ensureFamixStubMethod(String name) {
2499
                return ensureFamixMethod(null, name, /*paramType*/null, /*returnType*/null, ensureFamixClassStubOwner(), /*modifiers*/0);
×
2500
        }
2501

2502
        public void setAttributeModifiers(Attribute fmx, int mod) {
2503
                setCommonModifiers(fmx, mod, null);
5✔
2504
                fmx.setIsTransient(Modifier.isTransient(mod));
5✔
2505
                fmx.setIsVolatile(Modifier.isVolatile(mod));
5✔
2506
        }
1✔
2507

2508
        public void setMethodModifiers(Method fmx, int mod) {
2509
                setCommonModifiers(fmx, mod, (TWithTypes) fmx.getParentType());
7✔
2510
                fmx.setIsAbstract(Modifier.isAbstract(mod));
5✔
2511
                fmx.setIsSynchronized(Modifier.isSynchronized(mod));
5✔
2512
        }
1✔
2513

2514
        public void setClassModifiers(Class fmx, int mod, TWithTypes owner) {
2515
                setCommonModifiers(fmx, mod, null);
5✔
2516
                fmx.setIsAbstract(Modifier.isAbstract(mod));
5✔
2517
        }
1✔
2518

2519
        public void setInterfaceModifiers(Interface fmx, int mod) {
2520
                setCommonModifiers(fmx, mod, null);
5✔
2521
        }
1✔
2522

2523
        private void setCommonModifiers(Entity fmx, int mod, TWithTypes owner) {
2524
                setVisibility((THasVisibility)fmx, mod, owner);
6✔
2525
                ((TCanBeClassSide)fmx).setIsClassSide(Modifier.isStatic(mod));
6✔
2526
                ((TCanBeFinal)fmx).setIsFinal(Modifier.isFinal(mod));
6✔
2527
        }
1✔
2528

2529
        /**
2530
         * Sets the visibility of a FamixNamedEntity
2531
         *
2532
         * @param fmx -- the FamixNamedEntity
2533
         * @param mod -- a description of the modifiers as understood by org.eclipse.jdt.core.dom.Modifier
2534
         */
2535
        public void setVisibility(THasVisibility fmx, int mod, TWithTypes owner) {
2536
                if (Modifier.isPublic(mod)) {
3✔
2537
                        fmx.setVisibility(MODIFIER_PUBLIC);
4✔
2538
                } else if (Modifier.isPrivate(mod)) {
3✔
2539
                        fmx.setVisibility(MODIFIER_PRIVATE);
4✔
2540
                } else if (Modifier.isProtected(mod)) {
3✔
2541
                        fmx.setVisibility(MODIFIER_PROTECTED);
4✔
2542
                } else {
2543
                        //Default visibility!
2544
                        //If we are in an interface, default visibility is public, otherwise package.
2545
                        if (owner instanceof Interface) {
3✔
2546
                                fmx.setVisibility(MODIFIER_PUBLIC);
4✔
2547
                        } else {
2548
                                fmx.setVisibility(MODIFIER_PACKAGE);
3✔
2549
                        }
2550
                }
2551
        }
1✔
2552

2553
        /**
2554
         * Returns a Famix Attribute associated with the IVariableBinding.
2555
         * The Entity is created if it does not exist.<br>
2556
         * @param name -- the name of the FAMIX Attribute (MUST NOT be null, but this is not checked)
2557
         * @param type -- Famix Type of the Attribute (should not be null, but it will work if it is)
2558
         * @param owner -- type defining the Attribute (should not be null, but it will work if it is)
2559
         * @return the Famix Entity found or created. May return null if "bnd" is null or in case of a Famix error
2560
         */
2561
        public Attribute ensureFamixAttribute(IVariableBinding bnd, String name, Type type, TWithAttributes owner) {
2562
                Attribute fmx;
2563

2564
                // --------------- to avoid useless computations if we can
2565
                fmx = (Attribute)getEntityByKey(bnd);
5✔
2566
                if (fmx != null) {
2✔
2567
                        return fmx;
2✔
2568
                }
2569

2570
                // --------------- name
2571
                if (name == null) {
2!
2572
                        if (bnd == null) {
×
2573
                                return null;
×
2574
                        }
2575
                        else {
2576
                                name = bnd.getName();
×
2577
                        }
2578
                }
2579

2580
                // --------------- owner
2581
                if (owner == null) {
2✔
2582
                        if (bnd == null) {
2!
2583
                                return null;  // what would be the interest of creating an attribute for which we ignore the declaring class?
×
2584
                        }
2585
                        else {
2586
                                if (bnd.getDeclaringClass() != null && bnd.getDeclaringClass().getErasure() != null) {
7!
2587
                                        // Declaring class is the generic one if the class is parametric.
2588
                                        owner = (TWithAttributes)ensureFamixType(bnd.getDeclaringClass().getErasure());
8✔
2589
                                } else {
2590
                                        return null;  // what would be the interest of creating an attribute for which we ignore the declaring class?
2✔
2591
                                }
2592
                        }
2593
                }
2594

2595
                // --------------- recover from name ?
2596
                for (Attribute candidate : getEntityByName(Attribute.class, name)) {
13✔
2597
                        if (matchAndMapVariable(bnd, name, (TNamedEntity) owner, candidate)) {
8✔
2598
                                fmx = candidate;
2✔
2599
                                break;
1✔
2600
                        }
2601
                }
1✔
2602

2603
                if (fmx == null) {
2✔
2604
                        fmx = ensureFamixEntity(Attribute.class, bnd, name);
7✔
2605
                        fmx.setParentType( owner);
3✔
2606
                }
2607

2608
        fmx.setParentType(owner);
3✔
2609
        ITypeBinding declaredTypeBinding = (bnd == null) ? null : bnd.getType();
7✔
2610
        ensureFamixEntityTyping(declaredTypeBinding, fmx, type);
6✔
2611
        if (bnd != null) {
2✔
2612
            int mod = bnd.getModifiers();
3✔
2613
            setAttributeModifiers(fmx, mod);
4✔
2614
        }
2615

2616
        return fmx;
2✔
2617
        }
2618

2619
        public Attribute ensureFamixAttribute(IVariableBinding bnd, String name, TWithAttributes owner) {
2620
                return ensureFamixAttribute(bnd, name, /*declared type*/null, owner);
7✔
2621
        }
2622

2623
        /**
2624
         * helper method, we know the var exists, ensureFamixAttribute will recover it
2625
         */
2626
        public Attribute getFamixAttribute(IVariableBinding bnd, String name, TWithAttributes owner) {
2627
                return ensureFamixAttribute(bnd, name, /*declared type*/null, owner);
7✔
2628
        }
2629

2630
        /**
2631
         * Returns a Famix Parameter associated with the IVariableBinding.
2632
         * The Entity is created if it does not exist.<br>
2633
         * @return the Famix Entity found or created. May return null if "bnd" is null or in case of a Famix error
2634
         */
2635
        public Parameter ensureFamixParameter(IVariableBinding bnd, String name, Type typ, TMethod tMethod) {
2636
                Parameter fmx = null;
2✔
2637

2638
                // --------------- to avoid useless computations if we can
2639
                try {
2640
                        fmx = (Parameter)getEntityByKey(bnd);
5✔
2641
                }catch(Throwable e) {
×
2642
                        e.printStackTrace();
×
2643
                }
1✔
2644
                if (fmx != null) {
2✔
2645
                        return fmx;
2✔
2646
                }
2647

2648
                // --------------- name
2649
                if (name == null) {
2!
2650
                        if (bnd == null) {
×
2651
                                return null;
×
2652
                        }
2653
                        else {
2654
                                name = bnd.getName();
×
2655
                        }
2656
                }
2657

2658
                // --------------- owner
2659
                if (tMethod == null) {
2!
2660
                        if (bnd == null) {
×
2661
                                tMethod = ensureFamixStubMethod("<"+name+"_owner>");
×
2662
                        }
2663
                        else {
2664
                                tMethod = ensureFamixMethod(bnd.getDeclaringMethod());
×
2665
                        }
2666
                }
2667

2668
                // --------------- recover from name ?
2669
                for (Parameter candidate : getEntityByName(Parameter.class, name) ) {
13✔
2670
                        if ( matchAndMapVariable(bnd, name, tMethod, candidate) ) {
7!
2671
                                fmx = candidate;
×
2672
                                break;
×
2673
                        }
2674
                }
1✔
2675

2676
                if (fmx == null) {
2!
2677
                        fmx = ensureFamixEntity(Parameter.class, bnd, name);
7✔
2678
                }
2679

2680
                if (fmx != null) {
2!
2681
                        fmx.setParentBehaviouralEntity(tMethod);
3✔
2682
                        ITypeBinding declaredTypeBnd = (bnd == null) ? null : bnd.getType();
5!
2683
                        ensureFamixEntityTyping(declaredTypeBnd, fmx, typ);
6✔
2684
                }
2685

2686
                return fmx;
2✔
2687
        }
2688

2689
    /**
2690
         * Returns a Famix LocalVariable associated with the IVariableBinding.
2691
         * The Entity is created if it does not exist.<br>
2692
         * @param name -- the name of the FAMIX LocalVariable
2693
         * @return the Famix Entity found or created. May return null if <b>bnd</b> and <b>name</b> are null, or <b>bnd</b> and <b>owner</b> are null, or in case of a Famix error
2694
         */
2695
        public LocalVariable ensureFamixLocalVariable(IVariableBinding bnd, String name, TWithLocalVariables owner) {
2696
                LocalVariable fmx;
2697

2698
                // --------------- to avoid useless computations if we can
2699
                fmx = (LocalVariable)getEntityByKey(bnd);
5✔
2700
                if (fmx != null) {
2!
2701
                        return fmx;
×
2702
                }
2703

2704
                // --------------- name
2705
                if (name == null) {
2!
2706
                        if (bnd == null) {
×
2707
                                return null;
×
2708
                        }
2709
                        else {
2710
                                name = bnd.getName();
×
2711
                        }
2712
                }
2713

2714
                // --------------- owner
2715
                if (owner == null) {
2!
2716
                        if (bnd == null) {
×
2717
                                return null;  // what would be the interest of a local variable for which we ignore the declaring method?
×
2718
                        }
2719
                        else {
2720
                                owner = ensureFamixMethod(bnd.getDeclaringMethod());
×
2721
                        }
2722
                }
2723

2724
                // --------------- recover from name ?
2725
                for (LocalVariable candidate : getEntityByName(LocalVariable.class, name) ) {
13✔
2726
                        if ( matchAndMapVariable(bnd, name, (TNamedEntity) owner, candidate) ) {
8!
2727
                                fmx = candidate;
×
2728
                                break;
×
2729
                        }
2730
                }
1✔
2731

2732
                if (fmx == null) {
2!
2733
                        fmx = ensureFamixEntity(LocalVariable.class, bnd, name);
7✔
2734
                        fmx.setParentBehaviouralEntity(owner);
3✔
2735
                }
2736

2737
        // we just created it or it was not bound, so we make sure it has the right information in it
2738
        fmx.setParentBehaviouralEntity(owner);
3✔
2739

2740
        return fmx;
2✔
2741
        }
2742

2743
    /**
2744
         * Returns a FAMIX ImplicitVariable with the given <b>name</b> ("self" or "super") and corresponding to the <b>type</b>.
2745
         * If this ImplicitVariable does not exist yet, it is created
2746
         * @param name -- the name of the FAMIX ImplicitVariable (should be Dictionary.SELF_NAME or Dictionary.SUPER_NAME)
2747
         * @param type -- the Famix Type for this ImplicitVariable (should not be null)
2748
         * @param tMethod -- the ContainerEntity where the implicit variable appears (should be a method inside <b>type</b>)
2749
         * @return the FAMIX ImplicitVariable or null in case of a FAMIX error
2750
         */
2751
        public ImplicitVariable ensureFamixImplicitVariable(IBinding key, String name, TType type, TMethod tMethod) {
2752
                ImplicitVariable fmx;
2753
                fmx = ensureFamixEntity(ImplicitVariable.class, key, name);
7✔
2754
                fmx.setParentBehaviouralEntity(tMethod);
3✔
2755
                return fmx;
2✔
2756
        }
2757

2758
        public ImplicitVariable ensureFamixImplicitVariable(String name, TType tType, TMethod tMethod) {
2759
                IBinding bnd = ImplicitVarBinding.getInstance(tMethod, name);
4✔
2760
                return ensureFamixImplicitVariable(bnd, name, tType, tMethod);
7✔
2761
        }
2762

2763
        /**
2764
         * Creates and returns a Famix Comment and associates it with an Entity (ex: for Javadocs)
2765
         * @param jCmt -- the content (String) of the comment 
2766
         * @param owner -- the entity that is commented
2767
         * @return the Famix Comment
2768
         */
2769
        public Comment createFamixComment(org.eclipse.jdt.core.dom.Comment jCmt, TWithComments owner) {
2770
                Comment cmt = null;
2✔
2771

2772
                if ( (jCmt != null) && (owner != null) ) {
4!
2773
                        
2774
                        cmt = new Comment();
4✔
2775
                        addSourceAnchor(cmt, jCmt);
5✔
2776
                        famixRepoAdd(cmt);
3✔
2777
                        cmt.setCommentedEntity(owner);
3✔
2778
                }
2779

2780
                return cmt;
2✔
2781
        }
2782

2783
        /**
2784
         * Creates and returns a Famix Comment and associates it with an Entity
2785
         * @param jCmt -- the content (String) of the comment 
2786
         * @param owner -- the entity that is commented
2787
         * @param content -- the text of the comment
2788
         * @return the Famix Comment
2789
         */
2790
        public Comment createFamixComment(org.eclipse.jdt.core.dom.Comment jCmt, TWithComments owner, String content) {
2791
                Comment cmt = null;
2✔
2792

2793
                if ( (jCmt != null) && (owner != null) ) {
4!
2794
                        cmt = new Comment();
4✔
2795
                        cmt.setContent(content );
3✔
2796
                        famixRepoAdd(cmt);
3✔
2797
                        cmt.setCommentedEntity(owner);
3✔
2798
                }
2799

2800
                return cmt;
2✔
2801
        }
2802

2803
        /**
2804
         * Adds location information to a Famix Entity.
2805
         * Location informations are: <b>name</b> of the source file and <b>line</b> position in this file. They are found in the JDT ASTNode: ast.
2806
         * This method also creates some basic links between the entity and others (e.g. declaring container, return type, ...)
2807
         * @param fmx -- Famix Entity to add the anchor to
2808
         * @param node -- JDT ASTNode, where the information is extracted
2809
         * @return the Famix SourceAnchor added to fmx. May be null in case of incorrect parameter ('fmx' or 'ast' == null)
2810
         */
2811
        public SourceAnchor addSourceAnchor(TSourceEntity fmx, ASTNode node) {
2812
                IndexedFileAnchor fa;
2813

2814
                fa = createIndexedFileAnchor(node);
4✔
2815
                if ((fmx != null) && (fa != null)) {
4!
2816
                        fmx.setSourceAnchor(fa);
3✔
2817
                        famixRepoAdd(fa);
3✔
2818
                }
2819

2820
                return fa;
2✔
2821
        }
2822

2823
        /**
2824
         * Special case of  {@linkplain #addSourceAnchor(TSourceEntity, ASTNode)} to add location information to a Famix Method.
2825
         */
2826
        public SourceAnchor addSourceAnchor(Method fmx, MethodDeclaration node) {
2827
                IndexedFileAnchor fa;
2828

2829
                fa = createIndexedFileAnchor(node);
4✔
2830
                if ((fmx != null) && (fa != null)) {
4!
2831

2832
                        // may change the positions
2833
                        List<ASTNode> methodDeclarationModifiers = new ArrayList<>();
4✔
2834
                        methodDeclarationModifiers.addAll(node.modifiers());
5✔
2835
                        if (node.getName() != null) {
3!
2836
                                methodDeclarationModifiers.add(node.getName());
5✔
2837
                        }
2838
                        if (node.getReturnType2() != null) {
3✔
2839
                                methodDeclarationModifiers.add(node.getReturnType2());
5✔
2840
                        }
2841
                        int beg = (methodDeclarationModifiers.stream().mapToInt(el -> el.getStartPosition()).min().getAsInt()) + 1;
12✔
2842
                        int end = node.getStartPosition() + node.getLength();
6✔
2843

2844
                        fa.setStartPos(beg);
4✔
2845
                        fa.setEndPos(end);
4✔
2846

2847
                        fmx.setSourceAnchor(fa);
3✔
2848
                        famixRepoAdd(fa);
3✔
2849
                }
2850

2851
                return fa;
2✔
2852
        }
2853

2854
        /**
2855
         * Gets the file name holding <code>node</code> and its start and end positions in the file.
2856
         * Information returned in the form of an IndexedFileAnchor
2857
         */
2858
        protected IndexedFileAnchor createIndexedFileAnchor(ASTNode node) {
2859
                IndexedFileAnchor fa;
2860
                
2861
                if (node == null) {
2!
2862
                        return null;
×
2863
                }
2864

2865
                // position in source file
2866
                int beg = node.getStartPosition() + 1; // Java starts at 0, Moose at 1
5✔
2867
                int end = beg + node.getLength() - 1;
7✔
2868

2869
                // find source Compilation Unit
2870
                // there is a special case for the JDT Comment Nodes
2871
                if (node instanceof org.eclipse.jdt.core.dom.Comment) {
3✔
2872
                        node = ((org.eclipse.jdt.core.dom.Comment) node).getAlternateRoot();
5✔
2873
                } else {
2874
                        node = node.getRoot();
3✔
2875
                }
2876

2877
                fa = new IndexedFileAnchor();
4✔
2878
                fa.setStartPos(beg);
4✔
2879
                fa.setEndPos(end);
4✔
2880

2881
                fa.setFileName((String) node.getProperty(SOURCE_FILENAME_PROPERTY));
6✔
2882

2883
                return fa;
2✔
2884
        }
2885

2886
        /**
2887
         * Creates or recovers the Famix Class for "Object".
2888
         * Because "Object" is the root of the inheritance tree, it needs to be treated differently.
2889
         *
2890
         * @return a Famix class for "Object"
2891
         */
2892
        public Class ensureFamixClassObject() {
2893
        // In the past we used #ensureFamixUniqEntity but that does not check that the parent package is right and we got some Object from other packages than java.lang...
2894
        Collection<Class> objects = getEntityByName(Class.class, "Object");
5✔
2895

2896
        for (Class entity : objects) {
10✔
2897
                //We need to cast because the type container is a FamixTWithType but all implementors of this should be named in Java...
2898
                if ("java.lang".equals(((TNamedEntity) entity.getTypeContainer()).getName())) {
7✔
2899
                    return entity;
2✔
2900
                }
2901
        }
1✔
2902

2903
        Class fmx = createFamixEntity(Class.class, "Object");
6✔
2904
        fmx.setTypeContainer(ensureFamixPackageJavaLang(null));
5✔
2905
                return fmx;
2✔
2906
        }
2907

2908
        /***
2909
         * We treat array types as parametrized types Array<T>.
2910
         * This keeps the meta-model simple: a single way to model different concepts.
2911
         * @return
2912
         */
2913
        public ParametricClass ensureParametricArrayClass() {
2914
                
2915
                Collection<ParametricClass> arrayClasses = getEntityByName(ParametricClass.class, "Array");
5✔
2916
                
2917
                for (ParametricClass entity : arrayClasses) {
10✔
2918
            //We need to cast because the type container is a FamixTWithType but all implementors of this should be named in Java...
2919
            if ("java.lang".equals(((TNamedEntity) entity.getTypeContainer()).getName())) {
7!
2920
                return entity;
2✔
2921
            }
2922
                }
×
2923
                
2924
                //Create the parametric class
2925
                ParametricClass arrayClass = createFamixEntity(ParametricClass.class, "Array"); 
6✔
2926

2927
                //And now add the type parameter
2928
                TypeParameter fmxParam = this.ensureFamixTypeParameter(null, "T", arrayClass);
6✔
2929
                fmxParam.setGenericEntity(arrayClass);
3✔
2930
                fmxParam.setIsStub(true);
4✔
2931
                
2932
                //And the class is in java.lang
2933
                arrayClass.setTypeContainer(ensureFamixPackageJavaLang(null));
5✔
2934
                
2935
                return arrayClass;
2✔
2936
        }
2937

2938
        /**
2939
         * Ensures the Java meta-class: <pre>{@code java.lang.Class<>}</pre>
2940
         */
2941
        public Class ensureFamixMetaClass(ITypeBinding bnd) {
2942
                Package javaLang = ensureFamixPackageJavaLang((bnd == null) ? null : bnd.getPackage());
7!
2943
                ParametricClass fmx = (ParametricClass) this.ensureFamixClass(null, METACLASS_NAME, javaLang, /*isGeneric*/true, Modifier.PUBLIC & Modifier.FINAL);
9✔
2944

2945
                if (fmx != null) {
2!
2946
                        fmx.addTypeParameters(ensureFamixTypeParameter(null, "T", fmx));
7✔
2947
                }
2948

2949
                if ((fmx != null) && (fmx.getSuperInheritances() == null)) {
5!
2950
                        ensureFamixInheritance(ensureFamixClassObject(), fmx, null, null);
×
2951
                }
2952

2953
                return fmx;
2✔
2954
        }
2955

2956
        public Class getFamixMetaClass(ITypeBinding bnd) {
2957
                Package javaLang = ensureFamixPackageJavaLang((bnd == null) ? null : bnd.getPackage());
7!
2958
                return this.ensureFamixClass(null, METACLASS_NAME, javaLang, /*isGeneric*/true, UNKNOWN_MODIFIERS);
8✔
2959
        }
2960

2961
        /**
2962
         * Creates or recovers the Famix Class for all arrays (<pre>{@code <some-type> []}</pre>)
2963
         * In java arrays or objects of special classes (i.e. "I[" for an array of int).
2964
         * JDT does not create a binding for these classes, so we create a stub one here.
2965
         *
2966
         * @return a Famix class
2967
         */
2968
        public Class ensureFamixClassArray() {
2969
                Class fmx = ensureFamixUniqEntity(Class.class, null, ARRAYS_NAME);
×
2970
                if (fmx != null) {
×
2971
                        ensureFamixInheritance(ensureFamixClassObject(), fmx, /*prev*/null, null);
×
2972
                        fmx.setTypeContainer(ensureFamixPackageDefault());
×
2973

2974
                        // may be not needed anymore now that we use modifiers
2975
                        /*fmx.setIsAbstract(Boolean.FALSE);
2976
                        fmx.setIsFinal(Boolean.FALSE);
2977
                        fmx.setIsInterface(Boolean.FALSE); 
2978
                        fmx.setIsPrivate(Boolean.FALSE);
2979
                        fmx.setIsProtected(Boolean.FALSE);*/
2980
                        fmx.setVisibility(MODIFIER_PUBLIC);
×
2981
                }
2982

2983
                return fmx;
×
2984
        }
2985

2986
        public String removeLastPartOfPackageName(String qualifiedName) {
2987
                String ret;
2988
                int last = qualifiedName.lastIndexOf('.');
4✔
2989
                if (last > 0) {
2✔
2990
                        // recursively creating the parent
2991
                        ret = qualifiedName.substring(0, last);
6✔
2992
                }
2993
                else {
2994
                        ret = "";
2✔
2995
                }
2996

2997
                return ret;
2✔
2998
        }
2999

3000
        /** Generates the list of parameters for a method signature
3001
         * @return a string
3002
         */
3003
        protected String signatureParamsFromBinding(IMethodBinding bnd) {
3004
                boolean first = true;
2✔
3005
                String sig = "";
2✔
3006

3007
                for (ITypeBinding parBnd : bnd.getParameterTypes()) {
17✔
3008
                        if (first) {
2✔
3009
                                sig = parBnd.getName();
3✔
3010
                                first = false;
3✔
3011
                        }
3012
                        else {
3013
                                sig += "," + parBnd.getName();
5✔
3014
                        }
3015
                }
3016
                return sig;
2✔
3017
        }
3018

3019
        private String signatureParamsFromStringCollection(Collection<String> paramTypes) {
3020
                boolean first = true;
2✔
3021
                String sig = "";
2✔
3022

3023
                for (String t : paramTypes) {
10✔
3024
                        if (first) {
2✔
3025
                                sig = t;
2✔
3026
                                first = false;
3✔
3027
                        }
3028
                        else {
3029
                                sig += "," + t;
4✔
3030
                        }
3031
                }
1✔
3032
                return sig;
2✔
3033
        }
3034

3035
        public String findTypeName(org.eclipse.jdt.core.dom.Type t) {
3036
                if (t == null) {
×
3037
                        return null;
×
3038
                }
3039

3040
                if (t.isPrimitiveType()) {
×
3041
                        return t.toString();
×
3042
                } else if (t.isSimpleType()) {
×
3043
                        String fullName = ((SimpleType) t).getName().getFullyQualifiedName();
×
3044
                        int i = fullName.lastIndexOf('.');
×
3045
                        if (i > 0) {
×
3046
                                return fullName.substring(i+1);
×
3047
                        }
3048
                        else {
3049
                                return fullName;
×
3050
                        }
3051
                } else if (t.isQualifiedType()) {
×
3052
                        return ((QualifiedType) t).getName().getIdentifier();
×
3053
                } else if (t.isArrayType()) {
×
3054
                        return findTypeName(((ArrayType) t).getElementType());
×
3055
                } else if (t.isParameterizedType()) {
×
3056
                        return findTypeName(((org.eclipse.jdt.core.dom.ParameterizedType) t).getType());
×
3057
                } else { // it is a WildCardType
3058
                        if (((org.eclipse.jdt.core.dom.WildcardType) t).isUpperBound()) {
×
3059
                                return findTypeName(((org.eclipse.jdt.core.dom.WildcardType) t).getBound());
×
3060
                        } else {
3061
                                return EntityDictionary.OBJECT_NAME;
×
3062
                        }
3063
                }
3064
        }
3065

3066
        /**
3067
         * Ensures the proper creation of a FamixType for JDT typ in the given context.
3068
         * Useful for parameterizedTypes, or classInstance.
3069
         *
3070
         * @param isClass we are sure that the type is actually a class
3071
         * @return a famix type or null
3072
         */
3073
        public <T extends TWithTypes & TNamedEntity> TType referredType(org.eclipse.jdt.core.dom.Type typ, T ctxt, boolean isClass) {
3074
                return this.referredType(typ, ctxt, isClass, /*isExcep*/false);
7✔
3075
        }
3076
        
3077
        /**
3078
         * Ensures the proper creation of a FamixType for JDT type in the given context.
3079
         * Useful for parameterizedTypes, or classInstance.
3080
         *
3081
         * @param isClass we are sure that the type is actually a class
3082
         * @return a famix type or null
3083
         */
3084
        public <T extends TWithTypes & TNamedEntity> TType referredType(org.eclipse.jdt.core.dom.Type typ, T ctxt, boolean isClass, boolean isException) {
3085
                if (typ == null) {
2!
3086
                        return null;
×
3087
                } else if (typ.resolveBinding() != null) {
3!
3088
                        return this.referredType(typ.resolveBinding(), ctxt);
7✔
3089
                }
3090
                // from here, we assume the owner is the context
3091
                else if (isClass && !isException) {
×
3092
                        return this.ensureFamixClass(null, findTypeName(typ), /*owner*/ctxt, /*isGeneric*/false,
×
3093
                                        EntityDictionary.UNKNOWN_MODIFIERS);
3094
                } else if (isException) {
×
3095
                        // return ensure FamixException
3096
                        return this.ensureFamixException(null, findTypeName(typ), (ContainerEntity) /*owner*/ctxt, /*isGeneric*/false,
×
3097
                                        EntityDictionary.UNKNOWN_MODIFIERS);
3098
                } else {
3099
                        while (typ.isArrayType()) {
×
3100
                                typ = ((ArrayType) typ).getElementType();
×
3101
                        }
3102

3103
                        if (typ.isPrimitiveType()) {
×
3104
                                return this.ensureFamixPrimitiveType(null, findTypeName(typ));
×
3105
                        } else {
3106
                                return this.ensureFamixType(null, findTypeName(typ), /*owner*/ctxt, /*container*/ctxt,
×
3107
                                                EntityDictionary.UNKNOWN_MODIFIERS);
3108
                        }
3109
                }
3110
        }
3111
        
3112
        public TType referredType(ITypeBinding bnd, TNamedEntity ctxt) {
3113
                return this.referredType(bnd, ctxt, 0);
6✔
3114
        }
3115
        
3116
        public TType referredType(ITypeBinding bnd, TNamedEntity ctxt, int extraDimensions) {
3117
                TType fmxTyp = null;
2✔
3118

3119
                if (bnd == null) {
2✔
3120
                        return null;
2✔
3121
                }
3122

3123
                String name;
3124
                //Three cases here:
3125
                // - the type binding knows it's an array
3126
                // - the binding is not declared as array, but it has dimensions > 0
3127
                // - the dimension is not in the type but in the variable declaration (thus extra)
3128
                if (bnd.isArray() || bnd.getDimensions() + extraDimensions > 0) {
8✔
3129
                        // We treat array types as parametrized types Array<T>.
3130
                        // This keeps the meta-model simple: a single way to model different concepts.
3131
                        return this.ensureParametricArrayClass();
3✔
3132
                }
3133
                name = bnd.getName();
3✔
3134

3135
                if ( bnd.isParameterizedType() ) {
3✔
3136
                        
3137
                        // remove type parameters from the name even for parameterized interfaces
3138
                        int i = name.indexOf('<');
4✔
3139
                        if (i > 0) {
2✔
3140
                                name = name.substring(0, i);
5✔
3141
                        }
3142

3143
                        ITypeBinding parameterizableBnd = bnd.getErasure();
3✔
3144
                        int modifiers = (parameterizableBnd != null) ? parameterizableBnd.getModifiers() : EntityDictionary.UNKNOWN_MODIFIERS;
6!
3145
                        
3146
                        if(parameterizableBnd != null && parameterizableBnd.isInterface()) {
5!
3147
                                fmxTyp = (ParametricInterface) this.ensureFamixInterface(parameterizableBnd, name, /*owner*/null, /*isGeneric*/true, modifiers);
10✔
3148
                        } else {
3149
                                fmxTyp = (ParametricClass) this.ensureFamixClass(parameterizableBnd, name, /*owner*/null, /*isGeneric*/true, modifiers);
9✔
3150
                        }
3151
                } else if ( (name != null) && name.equals("var") ) {
7!
UNCOV
3152
                        fmxTyp = this.ensureFamixUniqEntity(org.moosetechnology.model.famix.famixjavaentities.Type.class, /*binding*/null, EntityDictionary.IMPLICIT_VAR_TYPE_NAME);        
×
3153
                } else {
3154
                        fmxTyp = this.ensureFamixType(bnd, name, /*owner*/null, (TWithTypes) ctxt, bnd.getModifiers());
10✔
3155
                }
3156

3157
                return fmxTyp;
2✔
3158
        }
3159

3160
}
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