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

moosetechnology / VerveineJ / 38736888627

15 Oct 2026 12:43PM UTC coverage: 55.418% (+0.03%) from 55.387%
38736888627

Pull #294

github

web-flow
Merge 7cb333652 into be4830954
Pull Request #294: Preserve associations when recovering exceptions

2236 of 4190 branches covered (53.37%)

Branch coverage included in aggregate %.

6 of 6 new or added lines in 1 file covered. (100.0%)

1 existing line in 1 file now uncovered.

4832 of 8564 relevant lines covered (56.42%)

2.34 hits per line

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

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

3
import java.util.*;
4
import java.util.logging.Level;
5
import java.util.logging.Logger;
6

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

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

58
/**
59
 * A dictionary of FamixJava entities to help create them and find them back
60
 * Entities are mapped to keys which are the "binding" provided by the JDT pars
61
 * 
62
 * @author anquetil
63
 */
64
public class EntityDictionary {
65
        private static final Logger LOGGER = Logger.getLogger(EntityDictionary.class.getName());
5 ✔
66

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

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

86
        public static final int UNKNOWN_MODIFIERS = 0;
87
        public static final String MODIFIER_PUBLIC   = "public";
88
        public static final String MODIFIER_PRIVATE  = "private";
89
        public static final String MODIFIER_PROTECTED= "protected";
90
        public static final String MODIFIER_PACKAGE = "package";
91

92
    /**
93
     * The symbol kind to use to define that a method is a default implementation in an interface
94
     */
95
    public static final String DEFAULT_IMPLEMENTATION_KIND_MARKER = "default";
96

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

102
        /**
103
         * The FAMIX repository where all FAMIX entities are created and stored
104
         */
105
        protected Repository famixRepo;
106

107
        /**
108
         * A dictionary to map a key (provided by the user) to FAMIX Entity
109
         */
110
        protected Map<IBinding,TNamedEntity> keyToEntity;
111
        /**
112
         * A reverse dictionary (see {@link #keyToEntity}) to find the key of an entity.
113
         */
114
        protected Map<TNamedEntity,IBinding> entityToKey;
115
        /**
116
         * Associates an enum constant with the anonymous type declared in its class body.
117
         * This is extraction state only; the FAMIX metamodel has no such association.
118
         */
119
        protected Map<IVariableBinding,ITypeBinding> enumConstantAnonymousTypes;
120

121
        /**
122
         * Another dictionary to map a name to FAMIX Entities with this name
123
         */
124
        protected Map<String,Collection<TNamedEntity>> nameToEntity;
125
        
126
        /**
127
         * Yet another dictionary for implicit variables ('self' and 'super')
128
         * Because they are implicit, they may not have a binding provided by the parser,
129
         * or may have the same binding as their associated type so they can't be kept easily in {@link #keyToEntity}
130
         */
131
        @Deprecated
132
        protected Map<Type,ImplicitVars> typeToImpVar;
133

134
        /**
135
         * Used to keep the two possible ImplicitVariable for a given Class binding
136
         * @author anquetil
137
         */
138
        @Deprecated
139
        protected class ImplicitVars {
×
140
                public ImplicitVariable self_iv;
141
                public ImplicitVariable super_iv;
142
        }
143
        
144
        /**
145
         * Result of utility methods for checking matching between two entities
146
         */
147
        private enum CheckResult {
3 ✔
148
                MATCH, UNDECIDED, FAIL;
18 ✔
149
        }
150

151
        /**
152
         * Option: If true, create stubs without package into a "Missing" package
153
         */
154
        protected boolean useMissingPackage;
155

156
        /** Constructor taking a FAMIX repository
157
         * @param famixRepo
158
         */
159
        public EntityDictionary(Repository famixRepo) {
2 ✔
160
                        this.famixRepo = famixRepo;
3 ✔
161
                        
162
                        this.keyToEntity = new Hashtable<IBinding,TNamedEntity>();
5 ✔
163
                        this.entityToKey = new Hashtable<TNamedEntity,IBinding>();
5 ✔
164
                        this.nameToEntity = new Hashtable<String,Collection<TNamedEntity>>();
5 ✔
165
                        this.typeToImpVar = new Hashtable<Type,ImplicitVars>();
5 ✔
166
                        this.enumConstantAnonymousTypes = new HashMap<>();
5 ✔
167
                        
168
                        if (! this.famixRepo.isEmpty()) {
4 ✔
169
                                recoverExistingRepository();
2 ✔
170
                        }
171
                }
1 ✔
172
        
173
        /** Constructor taking a FAMIX repository and the useMissingPackage option
174
         * @param famixRepo
175
         * @param useMissingPackage
176
         */
177
        public EntityDictionary(Repository famixRepo, boolean useMissingPackage) {
2 ✔
178
                        this.famixRepo = famixRepo;
3 ✔
179
                        this.useMissingPackage = useMissingPackage;
3 ✔
180
                        
181
                        this.keyToEntity = new Hashtable<IBinding,TNamedEntity>();
5 ✔
182
                        this.entityToKey = new Hashtable<TNamedEntity,IBinding>();
5 ✔
183
                        this.nameToEntity = new Hashtable<String,Collection<TNamedEntity>>();
5 ✔
184
                        this.typeToImpVar = new Hashtable<Type,ImplicitVars>();
5 ✔
185
                        this.enumConstantAnonymousTypes = new HashMap<>();
5 ✔
186
                        
187
                        if (! this.famixRepo.isEmpty()) {
4 ✔
188
                                recoverExistingRepository();
2 ✔
189
                        }
190
                }
1 ✔
191

192
    /**
193
         * Resets the dictionnary in a proper state after loading entities from an existing MSE file:
194
         * <UL>
195
         * <li>map all named entities to their names in <b>mapName</b></li>
196
         * <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>
197
         * </ul>
198
         */
199
        protected void recoverExistingRepository() {
200
                for (NamedEntity ent : famixRepo.all(NamedEntity.class)) {
13 ✔
201
                        try {
202
                                mapEntityToName(ent.getName(), ent);
5 ✔
203
                        } catch (java.lang.Exception e) {
×
204
                                LOGGER.log(Level.WARNING, "Error recovering entity " + ent.getName() + " from repository " + famixRepo, e);
×
205
                        };
1 ✔
206
                        // for the Exception to be raised, the return value must be tested
207
                        try { if (((TCanBeStub) ent).getIsStub()) {} }
5 ✔
208
                        catch (NullPointerException e) { ((TCanBeStub)ent).setIsStub(Boolean.FALSE); }
6 ✔
209
                }
1 ✔
210

211
                for (Access acc : famixRepo.all(Access.class)) {
13 ✔
212
                        // for the Exception to be raised, the return value must be tested
213
                        try { if (acc.getIsWrite()) {}        }
4 ✔
214
                        catch (NullPointerException e) { acc.setIsWrite(Boolean.FALSE); }
5 ✔
215
                }
1 ✔
216
        }
1 ✔
217

218
        protected void mapEntityToName(String name, TNamedEntity ent) {
219
                
220
                Collection<TNamedEntity> l_ent = nameToEntity.get(name);
6 ✔
221
                if (l_ent == null) {
2 ✔
222
                        l_ent = new LinkedList<>();
4 ✔
223
                }
224
                l_ent.add(ent);
4 ✔
225
                nameToEntity.put(name, l_ent);
6 ✔
226
        }
1 ✔
227

228
        public void removeEntity( NamedEntity ent) {
229
                IBinding key;
230
                key = entityToKey.get(ent);
6 ✔
231
                if (key != null) {
2 ✔
232
                        entityToKey.remove(ent);
5 ✔
233
                        keyToEntity.remove(key);
5 ✔
234
                }
235

236
                Collection<TNamedEntity> l_ent = nameToEntity.get(ent.getName());
7 ✔
237
                if (l_ent != null) {
2 ✔
238
                        l_ent.remove(ent);
4 ✔
239
                }
240

241
                famixRepo.getElements().remove(ent);
6 ✔
242
        }
1 ✔
243
        
244
        protected void mapEntityToKey(IBinding key, TNamedEntity ent) {
245
                TNamedEntity old = keyToEntity.get(key);
6 ✔
246
                if (old != null) {
2 ✔
247
                        entityToKey.remove(old);
5 ✔
248
                }
249
                keyToEntity.put(key, ent);
6 ✔
250
                entityToKey.put(ent, key);
6 ✔
251
        }
1 ✔
252
        
253
        /**
254
         * Returns all the Famix Entity with the given name and class 
255
         * @param fmxClass -- the subtype of Famix Entity we are looking for
256
         * @param name -- the name of the entity
257
         * @return the Collection of Famix Entities with the given name and class (possibly empty)
258
         */
259
        @SuppressWarnings("unchecked")
260
        public <T extends TNamedEntity> Collection<T> getEntityByName(java.lang.Class<T> fmxClass, String name) {
261
                Collection<T> ret = new LinkedList<T>();
4 ✔
262
                Collection<TNamedEntity> l_name = nameToEntity.get(name);
6 ✔
263
                
264
                if (l_name != null ) {
2 ✔
265
                        for (TNamedEntity obj : l_name) {
10 ✔
266
                                if (fmxClass.isInstance(obj)) {
4 ✔
267
                                        ret.add((T) obj);
4 ✔
268
                                }
269
                        }
1 ✔
270
                }
271

272
                return ret;
2 ✔
273
        }
274

275
        /**
276
         * Returns the Famix Entity associated to the given key.
277
         * <b>Note</b>: Be careful that ImplicitVariables share the same binding as their associated Class and cannot be retrieved with this method.
278
         * In such a case, this method will always retrieve the Class associated to the key.
279
         * To get an ImplicitVariable from the key, use {@link #getImplicitVariableByBinding(IBinding, String)}
280
         * @param key -- the key
281
         * @return the Famix Entity associated to the binding or null if not found
282
         */
283
        public TNamedEntity getEntityByKey(IBinding key) {
284
                if (key == null) {
2 ✔
285
                        return null;
2 ✔
286
                }
287
                else {
288
                        return keyToEntity.get(key);
6 ✔
289
                }
290
        }
291

292
        public void registerEnumConstantAnonymousType(IVariableBinding enumConstant, ITypeBinding anonymousType) {
293
                if (enumConstant != null && anonymousType != null) {
4 !
294
                        enumConstantAnonymousTypes.put(enumConstant.getVariableDeclaration(), anonymousType);
7 ✔
295
                }
296
        }
1 ✔
297

298
        public IMethodBinding getEnumConstantMethodOverride(EnumValue enumConstant, IMethodBinding calledMethod) {
299
                if (calledMethod == null) {
2 !
300
                        return null;
×
301
                }
302

303
                IBinding enumConstantBinding = getEntityKey(enumConstant);
4 ✔
304
                ITypeBinding anonymousType = enumConstantBinding instanceof IVariableBinding
3 !
305
                                ? enumConstantAnonymousTypes.get(((IVariableBinding) enumConstantBinding).getVariableDeclaration())
8 ✔
306
                                : null;
1 ✔
307
                if (anonymousType == null) {
2 ✔
308
                        return calledMethod;
2 ✔
309
                }
310

311
                // JDT resolves the enum declaration, but this receiver identifies the runtime override.
312
                IMethodBinding declaredMethod = calledMethod.getMethodDeclaration();
3 ✔
313
                for (IMethodBinding declaredInAnonymousType : anonymousType.getDeclaredMethods()) {
17 !
314
                        if (declaredInAnonymousType.overrides(declaredMethod)) {
4 ✔
315
                                return declaredInAnonymousType;
2 ✔
316
                        }
317
                }
318
                return calledMethod;
×
319
        }
320

321
        /**
322
         * Returns the Famix Entity associated to the given key if it is an instance of the given Famix class.
323
         * This avoids recovering a binding as another kind of entity than the one requested.
324
         * @param key -- the key
325
         * @param fmxClass -- the expected Famix class
326
         * @return the Famix Entity associated to the binding or null if not found or not an instance of <b>fmxClass</b>
327
         */
328
        protected <T extends TNamedEntity> T getEntityByKey(IBinding key, java.lang.Class<T> fmxClass) {
329
                TNamedEntity fmx = getEntityByKey(key);
4 ✔
330
                if (fmxClass.isInstance(fmx)) {
4 ✔
331
                        return fmxClass.cast(fmx);
5 ✔
332
                }
333
                return null;
2 ✔
334
        }
335

336
        /**
337
         * Returns the key associated to a Famix Entity.
338
         * @param e -- the Named entity
339
         * @return the key associated to this entity or null if none
340
         */
341
        public IBinding getEntityKey(TNamedEntity e) {
342
                return entityToKey.get(e);
6 ✔
343
        }
344

345
        /**
346
         * Creates and returns a FAMIX Entity of the type <b>fmxjava.lang.Class</b>.
347
         * The Entity is always created.
348
         * @param fmxClass -- the FAMIX class of the instance to create
349
         * @param name -- the name of the new instance must not be null (and this is not tested)
350
         * @return the FAMIX Entity or null in case of a FAMIX error
351
         */
352
        protected <T extends TNamedEntity & TSourceEntity> T createFamixEntity(java.lang.Class<T> fmxClass, String name) {
353
                T fmx = null;
2 ✔
354

355
                if (name == null) {
2 !
356
                        return null;
×
357
                }
358
                
359
                try {
360
                        fmx = fmxClass.getDeclaredConstructor().newInstance();
9 ✔
361
                } catch (java.lang.Exception e) {
×
362
                        LOGGER.log(Level.WARNING, "Unexpected error, could not create a FAMIX entity", e);
×
363
                }
1 ✔
364
                
365
                if (fmx != null) {
2 !
366
                        fmx.setName(name);
3 ✔
367
                        if (fmx instanceof TCanBeStub) {
3 !
368
                                ((TCanBeStub)fmx).setIsStub(Boolean.TRUE);
4 ✔
369
                        }
370

371
                        mapEntityToName(name, fmx);
4 ✔
372
                        
373
                        // put new entity in Famix repository
374
                        famixRepoAdd((Entity) fmx);
4 ✔
375
                }
376

377
                return fmx;
2 ✔
378
        }
379
        
380
        /**
381
         * Returns a Famix Entity of the type <b>fmxjava.lang.Class</b> and maps it to its binding <b>bnd</b> (if not null).
382
         * The Entity is created if it did not exist.
383
         * @param fmxClass -- the Famix class of the instance to create
384
         * @param bnd -- the binding to map to the new instance
385
         * @param name -- the name of the new instance (used if <pre>{@code bnd == null}</pre>)
386
         * @return the Famix Entity or null if <b>bnd</b> was null or in case of a Famix error
387
         */
388
        @SuppressWarnings("unchecked")
389
        protected <T extends TNamedEntity & TSourceEntity> T ensureFamixEntity(java.lang.Class<T> fmxClass, IBinding bnd, String name) {
390
                T fmx = null;
2 ✔
391
                
392
                /* 
393
                 * Unfortunately different entities with the same name and same type may exist
394
                 * e.g. 2 parameters of 2 different methods but having the same name
395
                 * so we cannot recover just from the name
396
                 */
397

398
                if (bnd != null) {
2 ✔
399
                        fmx = getEntityByKey(bnd, fmxClass);
5 ✔
400
                        if (fmx != null) {
2 ✔
401
                                return fmx;
2 ✔
402
                        }
403
                }
404

405
                // else
406
                fmx = createFamixEntity(fmxClass, name);
5 ✔
407
                if ( (bnd != null) && (fmx != null) ) {
4 !
408
                        mapEntityToKey(bnd, fmx);
4 ✔
409
                }
410
                
411
                return fmx;
2 ✔
412
        }
413

414
        /**
415
         * Adds an already created Entity to the Famix repository
416
         * Used mainly for non-NamedEntity, for example relationships
417
         * @param e -- the Famix entity to add to the repository
418
         */
419
        public void famixRepoAdd(Entity e) {
420
                this.famixRepo.add(e);
4 ✔
421
        }
1 ✔
422

423

424
        /**
425
         * Returns a Famix ParametricClass with the given <b>name</b>, creating it if it does not exist yet
426
         * In the second case, sets some default properties: not Abstract, not Final, not Private, not Protected, not Public, not Interface
427
         * @param name -- the name of the Famix Class
428
         * @return the Famix Class or null in case of a Famix error
429
         */
430
        public ParametricClass ensureFamixParametricClass(ITypeBinding key, String name, TWithTypes owner) {
431
                ParametricClass fmx = ensureFamixEntity(ParametricClass.class, key, name);
7 ✔
432
                if(key != null) {
2 ✔
433
                        for (ITypeBinding tp : key.getErasure().getTypeParameters()) {
18 ✔
434
                                // If there is a type parameter, then fmx will be a Famix ParametricClass
435
                                // note: in Famix, the owner of the TypeParameter is the ParametricClass
436
                                TypeParameter fmxParam = ensureFamixTypeParameter(tp,
5 ✔
437
                                                tp.getName(), fmx);
2 ✔
438
                                fmxParam.setGenericEntity((TParametricEntity)fmx);
3 ✔
439
                fmxParam.setIsStub(fmx.getIsStub());
4 ✔
440
            }
441
                }
442
                
443
                fmx.setTypeContainer(owner);
3 ✔
444
                return fmx;
2 ✔
445
        }
446

447
        /**
448
         * Returns a Famix ParametricInterface with the given <b>name</b>, creating it if it does not exist yet
449
         * In the second case, sets some default properties: not Abstract, not Final, not Private, not Protected, not Public, not Interface
450
         * @param name -- the name of the Famix Class
451
         * @return the Famix Class or null in case of a Famix error
452
         */
453
        public ParametricInterface ensureFamixParametricInterface(ITypeBinding key, String name, TWithTypes owner) {
454
                ParametricInterface fmx = ensureFamixEntity(ParametricInterface.class, key, name);
7 ✔
455
                if(key != null) {
2 !
456
                        for (ITypeBinding tp : key.getTypeParameters()) {
17 ✔
457
                                // If there is a type parameter, then fmx will be a Famix ParametricInterface
458
                                // note: in Famix, the owner of the TypeParameter is the ParametricInterface
459
                                TypeParameter fmxParam = ensureFamixTypeParameter(tp,
5 ✔
460
                                                tp.getName(), fmx);
2 ✔
461
                                fmxParam.setGenericEntity(fmx);
3 ✔
462
                fmxParam.setIsStub(false);
4 ✔
463
            }
464
                }
465
                fmx.setTypeContainer(owner);
3 ✔
466
                return fmx;
2 ✔
467
        }
468

469
        public AnnotationInstanceAttribute createFamixAnnotationInstanceAttribute(AnnotationTypeAttribute att, String value) {
470
                AnnotationInstanceAttribute fmx = null;
2 ✔
471
                if ( (att != null) && (value != null) ) {
4 !
472
                        fmx = new AnnotationInstanceAttribute();
4 ✔
473
                        fmx.setAnnotationTypeAttribute(att);
3 ✔
474
                        fmx.setValue(value);
3 ✔
475
                        this.famixRepo.add(fmx);
4 ✔
476
                }
477
                return fmx;
2 ✔
478
        }
479

480
        public AnnotationInstance addFamixAnnotationInstance(TWithAnnotationInstances fmx, AnnotationType annType, Collection<AnnotationInstanceAttribute> annAtts) {
481
                AnnotationInstance inst = null;
2 ✔
482
                if ( (fmx != null) && (annType != null) ) {
4 !
483
                        inst = new AnnotationInstance();
4 ✔
484
                        inst.setAnnotatedEntity(fmx);
3 ✔
485
                        inst.setAnnotationType(annType);
3 ✔
486
                        inst.addAttributes(annAtts);
3 ✔
487
                        this.famixRepo.add(inst);
4 ✔
488
                }
489
                return inst;
2 ✔
490
        }
491

492
        ///// ensure Famix Relationships /////
493

494
        /**
495
         * Returns a Famix Inheritance relationship between two Famix Classes creating it if needed
496
         * @param sup -- the super class
497
         * @param sub -- the sub class
498
         * @param prev -- previous inheritance relationship in the same context
499
         * @return the Inheritance relationship
500
         */
501
        public Inheritance ensureFamixInheritance(TWithInheritances sup, TWithInheritances sub, TAssociation prev, ITypeBinding supBnd) {
502
                if ( (sup == null) || (sub == null) ) {
4 !
503
                        return null;
×
504
                }
505

506
                // Does the inheritance already exist?
507
                for (TInheritance i : (sup).getSubInheritances()) {                        
11 ✔
508
                        if (i.getSubclass() == sub) {
4 ✔
509
                                return (Inheritance) i;
3 ✔
510
                        }
511
                }
1 ✔
512

513
                Inheritance inh;
514
                if (supBnd != null && supBnd.isParameterizedType()) { // Needs checks and tests.
5 ✔
515
                        inh = (ParametricInheritance)buildFamixParametricAssociation(new ParametricInheritance(), supBnd.getErasure().getTypeParameters(), supBnd.getTypeArguments());
13 ✔
516
                } else {
517
                        inh = new Inheritance();
4 ✔
518
                }
519

520
                inh.setSuperclass(sup);
3 ✔
521
                inh.setSubclass(sub);
3 ✔
522
                chainPrevNext(prev, inh);
4 ✔
523
                famixRepoAdd(inh);
3 ✔
524
                return inh;
2 ✔
525
        }
526
        
527
        /**
528
         * Creates the concretization between the type parameters of the generic entity that is target of an association 
529
         * and the concrete types that concretize them in this association.
530
         * @param association -- the association that must be linked to # or several concretizations
531
         * @param typeParameters -- the collection of type parameters declared in the generic entity
532
         * @param typeArguments -- the collection of concrete types linked to this association
533
         * @return the parametric association
534
         */
535
        public  <T extends TParametricEntity> TParametricAssociation buildFamixParametricAssociation(TParametricAssociation association, ITypeBinding[] typeParameters, ITypeBinding[] typeArguments
536
        ) {
537
                
538
                Iterator<ITypeBinding> genericIterator = Arrays.asList(typeParameters).iterator();
4 ✔
539
                Iterator<ITypeBinding> concreteIterator = Arrays.asList(typeArguments).iterator();
4 ✔
540

541
                while (concreteIterator.hasNext() && genericIterator.hasNext()) {
6 ✔
542
                        TTypeArgument typeArgument = (TTypeArgument)ensureFamixType(concreteIterator.next());
7 ✔
543
                        TypeParameter typeParameter = (TypeParameter)ensureFamixType(genericIterator.next());
7 ✔
544

545
                        Concretization concretization = ensureFamixConcretization(typeArgument, typeParameter);
5 ✔
546
                        association.addConcretization(concretization);
3 ✔
547
                }
1 ✔
548

549
                return association;
2 ✔
550
        }
551

552
        /**
553
         * Returns a Famix Concretization relationship between a Concrete Type and a ParameterType
554
         * @param typeArgument -- the concrete type
555
         * @param typeParameter -- the generic type parameter
556
         * @return the Concretization relationship
557
         */
558
        public Concretization ensureFamixConcretization(TTypeArgument typeArgument, TypeParameter typeParameter ) {
559
                if ( (typeArgument == null) || (typeParameter == null) ) {
4 !
560
                        return null;
×
561
                }
562

563
                Concretization concretization = new Concretization();
4 ✔
564
                concretization.setTypeArgument(typeArgument);
3 ✔
565
                concretization.setTypeParameter(typeParameter);
3 ✔
566

567
                famixRepoAdd(concretization);
3 ✔
568
                return concretization;
2 ✔
569
        }
570
        
571
                /**
572
         * Returns a Famix Implementation relationship between two Famix Classes creating it if needed
573
         * @param myInterface -- the implemented interface
574
         * @param implementingClass -- the implementing class
575
         * @param prev -- previous inheritance relationship in the same context
576
         * @return the Inheritance relationship
577
         */
578
        public Implementation ensureFamixImplementation(TImplementable myInterface, TCanImplement implementingClass, TAssociation prev, ITypeBinding supBnd) {
579
                if ( (myInterface == null) || (implementingClass == null) ) {
4 !
580
                        return null;
×
581
                }
582

583
                for (TImplementation imp : myInterface.getImplementations()) {
11 ✔
584
                        if (imp.getImplementingClass() == implementingClass) {
4 ✔
585
                                return (Implementation) imp;
3 ✔
586
                        }
587
                }
1 ✔
588
                
589
                Implementation implementation;
590
                if (supBnd != null && supBnd.isParameterizedType()) { // Needs checks and tests.
5 !
591
                        implementation = new ParametricImplementation();
5 ✔
592
                } else {
593
                        implementation = new Implementation();
4 ✔
594
                }
595

596
                implementation.setImplementingClass(implementingClass);
3 ✔
597
                implementation.setMyInterface(myInterface);
3 ✔
598
                chainPrevNext(prev, implementation);
4 ✔
599
                famixRepoAdd(implementation);
3 ✔
600
                if (implementation instanceof ParametricImplementation) {
3 ✔
601
                        buildFamixParametricAssociation((ParametricImplementation) implementation,
6 ✔
602
                                        supBnd.getErasure().getTypeParameters(), supBnd.getTypeArguments());
4 ✔
603
                }
604
                return implementation;
2 ✔
605
        }
606

607
        public void ensureImplementedInterfaces(ITypeBinding bnd, TType fmx, TWithTypes owner, TAssociation lastAssociation) {
608
                for (ITypeBinding intbnd : bnd.getInterfaces()) {
17 ✔
609
                        Type superTyp;
610
                        if(intbnd.isClass()){
3 ✔
611
                                superTyp = this.ensureFamixInterface(intbnd, intbnd.getName(), null, intbnd.isGenericType() || intbnd.isParameterizedType() || intbnd.isRawType(), intbnd.getModifiers());
22 !
612
                        }else {
613
                                superTyp = this.ensureFamixType(intbnd);
4 ✔
614
                        }
615
                        
616
                        if (bnd.isInterface()) {
3 ✔
617
                                // in Java "subtyping" link between 2 interfaces is call inheritance 
618
                                lastAssociation = ensureFamixInheritance((TWithInheritances)superTyp, (TWithInheritances)fmx, lastAssociation, intbnd);
10 ✔
619
                        }
620
                        else {
621
                                lastAssociation = ensureFamixImplementation((TImplementable)superTyp, (TCanImplement)fmx, lastAssociation, intbnd);
9 ✔
622
                        }
623
                }
624
        }
1 ✔
625

626
        /**
627
         * Returns a Famix Reference between two Famix Entities creating it if needed.<br>
628
         * 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
629
         * @param src -- source of the reference
630
         * @param tgt -- target of the reference
631
         * @param prev -- previous reference relationship in the same context
632
         * @return the FamixReference
633
         */
634
        public Reference addFamixReference(Method src, TType tgt, TAssociation prev, ITypeBinding referredTypeBnd) {
635
                Reference ref = null;
2 ✔
636
                
637
                if ( (src == null) || (tgt == null) ) {
4 !
638
                        return null;
×
639
                }
640

641
                if (prev == null) {
2 ✔
642
                        for (TReference existingRef : src.getOutgoingReferences()) {
7 !
643
                                if (existingRef.getReferredEntity() == tgt) {
×
644
                                        return (Reference) existingRef;
×
645
                                }
646
                        }
×
647
                }
648

649
                if (referredTypeBnd != null) {
2 ✔
650
                        if (referredTypeBnd.isParameterizedType()) {
3 ✔
651
                                ref = (ParametricReference)buildFamixParametricAssociation(new ParametricReference(), referredTypeBnd.getErasure().getTypeParameters(), referredTypeBnd.getTypeArguments());
13 ✔
652
                        } else if (referredTypeBnd.isArray()) {
3 ✔
653
                                ref = new ParametricReference();
4 ✔
654
                        
655
                                TTypeArgument typeArgument  = (TTypeArgument)ensureFamixType(referredTypeBnd.getElementType());
6 ✔
656
                                TypeParameter typeParameter = (TypeParameter) ((TParametricEntity)tgt).getTypeParameters().iterator().next();
7 ✔
657

658
                                Concretization concretization = ensureFamixConcretization(typeArgument, typeParameter);
5 ✔
659
                                ((ParametricReference)ref).addConcretization(concretization);
4 ✔
660
                        }
661
                }
662

663
                if (ref == null){
2 ✔
664
                        ref = new Reference();
4 ✔
665
                }
666

667
                ref.setReferredEntity(tgt);
3 ✔
668
                ref.setReferencer(src);
3 ✔
669
                chainPrevNext(prev,ref);
4 ✔
670
                famixRepoAdd(ref);
3 ✔
671

672
                return ref;
2 ✔
673
        }
674

675
        /**
676
         * Returns a Famix Invocation between two Famix Entities creating it if needed
677
         * @param tMethod of the invocation
678
         * @param invoked -- method invoked
679
         * @param receiver of the invocation
680
         * @param signature -- i.e. actual invocation code
681
         * @param prev -- previous invocation relationship in the same context
682
         * @return the FamixInvocation
683
         */
684
        public Invocation addFamixInvocation(TMethod tMethod, TMethod invoked, TInvocationsReceiver receiver, String signature, TAssociation prev, IMethodBinding invokedBnd) {
685
                if ( (tMethod == null) || (invoked == null) ) {
4 !
686
                        return null;
×
687
                }
688
                Invocation invocation;
689
                if (invokedBnd != null && invokedBnd.isParameterizedMethod()) {
5 ✔
690
                        invocation = (ParametricInvocation)buildFamixParametricAssociation(new ParametricInvocation(), invokedBnd.getMethodDeclaration().getTypeParameters(), invokedBnd.getTypeArguments());
13 ✔
691
                } else if ( invokedBnd != null && isConstructorBinding(invokedBnd) && invokedBnd.getMethodDeclaration().getDeclaringClass().isGenericType()) {
11 ✔
692
                        invocation = (ParametricInvocation)buildFamixParametricAssociation(new ParametricInvocation(), invokedBnd.getMethodDeclaration().getDeclaringClass().getTypeParameters(), invokedBnd.getDeclaringClass().getTypeArguments());
15 ✔
693
                } else {
694
                        invocation = new Invocation();
4 ✔
695
                }
696

697
                invocation.setReceiver(receiver);
3 ✔
698
                invocation.setSender(tMethod);
3 ✔
699
                invocation.setSignature((signature == null) ? invoked.getSignature() : signature);
5 !
700
                invocation.addCandidates(invoked);
3 ✔
701
                chainPrevNext(prev,invocation);
4 ✔
702
                famixRepoAdd(invocation);
3 ✔
703
                
704
                return invocation;
2 ✔
705
        }
706

707
        /**
708
         * Returns a Famix Access between two Famix Entities creating it if needed
709
         * @param accessor -- the entity (presumably a method) accessing the attribute
710
         * @param var -- the variable accessed
711
         * @param isWrite -- whether this is an access for reading or writing in the variable
712
         * @param prev -- previous access relationship in the same context
713
         * @return the FamixAccess
714
         */
715
        public Access addFamixAccess(TWithAccesses accessor, TStructuralEntity var, boolean isWrite, TAssociation prev) {
716
                if ( (accessor == null) || (var == null) ) {
4 !
717
                        return null;
×
718
                }
719
                Access acc = new Access();
4 ✔
720
                acc.setAccessor(accessor);
3 ✔
721
                acc.addCandidates((TAccessible) var);
3 ✔
722
                acc.setIsWrite(isWrite);
4 ✔
723
                chainPrevNext(prev, acc);
4 ✔
724
                famixRepoAdd(acc);
3 ✔
725
                
726
                return acc;
2 ✔
727
        }
728

729
        protected void chainPrevNext(TAssociation prev, TAssociation next) {
730
                if (prev != null) {
2 ✔
731
                        next.setPrevious(prev);  // not yet implemented in importer
3 ✔
732
                }
733
        }
1 ✔
734
        
735
        /**
736
         * Returns a Famix DeclaredException between a method and an Exception that it declares to throw
737
         * @param meth -- the method throwing the exception
738
         * @param excep -- the exception declared to be thrown
739
         * @return the DeclaredException
740
         */
741
        public TThrowable createFamixDeclaredException(Method meth, TThrowable excep) {
742
                if ( (meth == null) || (excep == null) ) {
4 !
743
                        return null;
×
744
                }
745
                meth.getDeclaredExceptions().add(excep);
5 ✔
746
                return excep;
2 ✔
747
        }
748

749
        /**
750
         * Returns a Famix CaughtException between a method and an Exception that is caught
751
         * @param meth -- the method catching the exception
752
         * @param excep -- the exception caught
753
         * @return the CaughtException
754
         */
755
        public TThrowable createFamixCaughtException(Method meth, TThrowable excep) {
756
                if ( (meth == null) || (excep == null) ) {
4 !
757
                        return null;
×
758
                }
759
                meth.getCaughtExceptions().add(excep);
5 ✔
760
                return excep;
2 ✔
761
        }
762

763
        /**
764
         * Returns a Famix ThrownException between a method and an Exception that it (actually) throws.
765
         * Note: DeclaredException indicates that the method declares it can throw the exception,
766
         * here we state that the exception is actually thrown
767
         * @param meth -- the method throwing the exception
768
         * @param excep -- the exception thrown
769
         * @return the ThrownException
770
         */
771
        public TThrowable createFamixThrownException(Method meth, TThrowable excep) {
772
                if ( (meth == null) || (excep == null) ) {
4 !
773
                        return null;
×
774
                }
775
                meth.getThrownExceptions().add(excep);
5 ✔
776
                return excep;
2 ✔
777
        }
778

779

780
        /**
781
         * Returns a Famix EntityTyping between a typed entity and a type.
782
         * @param typedEntity -- the typed entity
783
         * @param declaredType -- the declared type
784
         * @return the FamixEntityTyping
785
         */
786
        public EntityTyping ensureFamixEntityTyping(ITypeBinding declaredTypeBnd, TTypedEntity typedEntity, TType declaredType) {
787
                if ( (typedEntity == null) || (declaredType == null) ) {
4 !
788
                        return null;
2 ✔
789
                }
790
                EntityTyping typing = null;
2 ✔
791
                
792
                if (declaredTypeBnd != null) {
2 ✔
793
                        if (declaredTypeBnd.isParameterizedType()) {
3 ✔
794
                                typing = (ParametricEntityTyping)buildFamixParametricAssociation(new ParametricEntityTyping(), declaredTypeBnd.getErasure().getTypeParameters(), declaredTypeBnd.getTypeArguments());
13 ✔
795
                        }else if (declaredTypeBnd.isArray()) {
3 ✔
796
                                typing = new ParametricEntityTyping();
4 ✔
797
                                
798
                                TTypeArgument typeArgument  = (TTypeArgument)ensureFamixType(declaredTypeBnd.getElementType());
6 ✔
799
                                TypeParameter typeParameter = (TypeParameter) ((ParametricClass)declaredType).getTypeParameters().iterator().next();
7 ✔
800

801
                                Concretization concretization = ensureFamixConcretization(typeArgument, typeParameter);
5 ✔
802
                                ((ParametricEntityTyping)typing).addConcretization(concretization);
4 ✔
803
                        }
804
                }
805

806
                // If we did not set a typing because not parameterized nor array, set a default one
807
                if (typing == null) {
2 ✔
808
                        typing = new EntityTyping();
4 ✔
809
                }
810
                
811
                typing.setTypedEntity(typedEntity);
3 ✔
812
                typing.setDeclaredType(declaredType);
3 ✔
813
                famixRepoAdd(typing);
3 ✔
814
                
815
                return typing;
2 ✔
816
        }
817

818

819
        ///// Special Case: ImplicitVariables /////
820

821
        /**
822
         * Returns the Famix ImplicitVariable associated to the given binding and name (self or super).
823
         * See also {@link #getEntityByKey(IBinding)}
824
         * @param bnd -- the binding
825
         * @return the Famix Entity associated to the binding or null if not found
826
         */
827
        @Deprecated
828
        public ImplicitVariable getImplicitVariableByBinding(IBinding bnd, String iv_name) {
829
                return getImplicitVariableByType((Class)getEntityByKey(bnd), iv_name);
×
830
        }
831
        
832
        /**
833
         * Returns the Famix ImplicitVariable associated to the given FamixType.
834
         * @param type -- the FamixType
835
         * @param name -- name of the ImplicitVariable (should be Dictionary.SELF_NAME or Dictionary.SUPER_NAME)
836
         * @return the Famix ImplicitVariable associated to the Type or null if not found
837
         */
838
        @Deprecated
839
        public ImplicitVariable getImplicitVariableByType(Type type, String name) {
840
                ImplicitVars iv = typeToImpVar.get(type);
×
841
                ImplicitVariable ret = null;
×
842
                
843
                if (iv == null) {
×
844
                        iv = new ImplicitVars();
×
845
                }
846
                
847
                if (name.equals(THIS_NAME)) {
×
848
                        ret = iv.self_iv;
×
849
                }
850
                else if (name.equals(SUPER_NAME)) {
×
851
                        ret = iv.super_iv;
×
852
                }
853

854
                return ret;
×
855
        }
856

857
        ///// Special Case: "Uniq" Entities /////
858

859
        /**
860
         * Creates or recovers a Famix Named Entity uniq for the given name.
861
         * For some specific entities we don't allow two of them with the same name.
862
         * This is the case e.g. for the default package, or the Java class "Object" and its package "java.lang".
863
         * @param fmxClass -- the Famix class of the instance to create
864
         * @param key -- a potential binding for the entity
865
         * @param name -- the name of the new instance (used if <pre>{@code bnd == null}</pre>)
866
         * @return the uniq Famix Entity for this binding and/or name
867
         */
868
        @SuppressWarnings("unchecked")
869
        public <T extends NamedEntity> T ensureFamixUniqEntity(java.lang.Class<T> fmxClass, IBinding key, String name) {
870
                T fmx = null;
2 ✔
871
                
872
                if (name == null) {
2 !
873
                        return null;
×
874
                }
875
                
876
                if (key != null) {
2 ✔
877
                        fmx = (T) getEntityByKey(key);
5 ✔
878
                }
879
                
880
                if (fmx == null) {
2 ✔
881
                        Collection<T> l = getEntityByName( fmxClass, name);
5 ✔
882
                        if (l.size() > 0) {
3 ✔
883
                                fmx = l.iterator().next();
6 ✔
884
                        }
885
                        else {
886
                                fmx = createFamixEntity(fmxClass, name);
6 ✔
887
                        }
888
                        
889
                        if (key != null) {
2 ✔
890
                                // may happen for example if the entity was first created without binding
891
                                // and we find a binding for it later
892
                                keyToEntity.put(key, fmx);
6 ✔
893
                        }
894
                }
895

896
                return fmx;
2 ✔
897
        }
898

899
        /**
900
         * Creates or recovers the Famix Class that will own all stub methods (for which the real owner is unknown)
901
         *
902
         * @return a Famix class
903
         */
904
        public Class ensureFamixClassStubOwner() {
905
                Class fmx =  ensureFamixUniqEntity(Class.class, null, STUB_METHOD_CONTAINER_NAME);
7 ✔
906
                if (fmx != null) {
2 !
907
                        /*if the option `useMissingPackage` is activated, we put all the stubs with unknown packages in a package `Missing*/
908
                        if(this.useMissingPackage) {
3 ✔
909
                                fmx.setTypeContainer(ensureFamixPackageMissing());
5 ✔
910
                        }
911
                        /*otherwise we put them in the default package*/
912
                        else {
913
                                fmx.setTypeContainer( ensureFamixPackageDefault());
4 ✔
914
                        }
915
                }
916
                ensureFamixInheritance(ensureFamixClassObject(), fmx, /*prev*/null, null);
8 ✔
917

918
                return fmx;
2 ✔
919
        }
920

921
        public Type searchTypeInContext(String name, TWithTypes ctxt) {
922
                if (ctxt == null) {
×
923
                        return null;
×
924
                }
925
                
926
                for (TType candidate : ctxt.getTypes()) {
×
927
                        if (candidate.getName().equals(name) ) {
×
928
                                return (Type) candidate;
×
929
                        }
930
                }
×
931
                
932
                return searchTypeInContext(name, Util.getOwner((TNamedEntity)ctxt));
×
933
        }
934

935
        /**
936
         * Returns a Famix Package associated with its IPackageBinding and/or fully qualified name.
937
         * The Entity is created if it does not exist.
938
         * We assume that Namespaces must be uniq for a given name
939
         * Also creates or recovers recusively it's parent namespaces.<br>
940
         * At least one of <b>bnd</b> and <b>name</b> must be non null.
941
         *
942
         * @param bnd  -- the JDT Binding that may be used as a uniq key to recover this namespace
943
         * @param name -- fully qualified name of the namespace (e.g. 'java.lang')
944
         * @return the Famix Namespace found or created. May return null in case of a Famix error
945
         */
946
        public Package ensureFamixPackage(IPackageBinding bnd, String name) {
947
                Package fmx;
948
                Package parent;
949

950
                if ((name == null) && (bnd != null)) {
4 !
951
                        name = bnd.getName();
3 ✔
952
                }
953

954
                if ((name == null) || name.equals("")) {
6 !
955
                        return ensureFamixPackageDefault();
3 ✔
956
                } else {
957
                        /* Note: Packages are created with their fully-qualified name to simplify recovering when we don't have a binding
958
                         * (for example when creating parent packages of a package we have a binding for).
959
                         * Because the preferred solution in Moose is to give their simple names to packages, they must be post-processed when
960
                         * all is said and done. */
961
                        fmx = ensureFamixUniqEntity(Package.class, bnd, name);
7 ✔
962
                        String parentName = removeLastPartOfPackageName(name);
4 ✔
963
                        if (parentName.length() > 0) {
3 ✔
964
                                parent = ensureFamixPackage(null, parentName);
5 ✔
965
                                // set the parentscope relationship
966
                                if ((parent != null) && (fmx != null) && (fmx.getParentPackage() == null)) {
7 !
967
                                        parent.addChildEntities(fmx);
3 ✔
968
                                }
969
                        }
970
                }
971

972
                return fmx;
2 ✔
973
        }
974

975
        /**
976
         * Creates or recovers a default Famix Package.
977
         * Because this package does not really exist, it has no binding.
978
         *
979
         * @return a Famix Namespace
980
         */
981
        public Package ensureFamixPackageDefault() {
982
        return ensureFamixUniqEntity(Package.class, null, DEFAULT_PCKG_NAME);
7 ✔
983
        }
984
        
985
        /**
986
         * Returns an artificial Package to contain stubs with an unknown package.
987
         * Always returns the same package. Create it lazily if not existing.
988
         * @return a Famix Namespace
989
         */
990
        public Package ensureFamixPackageMissing( ) {
991
                return ensureFamixUniqEntity(Package.class, null, MISSING_PCKG_NAME);
7 ✔
992
        }
993

994
        /**
995
         * Creates or recovers a Famix Package for the package of Java class "Object" (i.e. "java.lang").
996
         * Because "Object" is the root of the inheritance tree, it needs to be treated differently.
997
         *
998
         * @param bnd -- a potential binding for the "java.lang" package
999
         * @return a Famix Namespace for "java.lang"
1000
         */
1001
        public Package ensureFamixPackageJavaLang(IPackageBinding bnd) {
1002

1003
        return this.ensureFamixPackage(bnd, OBJECT_PACKAGE_NAME);
5 ✔
1004
        }
1005

1006
        /**
1007
         * Returns the Package with {@link #DEFAULT_PCKG_NAME} or <code>null</code> if not found
1008
         */
1009
        public Package getFamixPackageDefault() {
1010
                Collection<Package> l = getEntityByName(Package.class, DEFAULT_PCKG_NAME);
5 ✔
1011
                if (l.size() > 0) {
3 !
1012
                        return l.iterator().next();
5 ✔
1013
                } else {
1014
                        return null;
×
1015
                }
1016
        }
1017

1018
        /**
1019
         * Returns a Famix Type with the given <b>name</b>, creating it if it does not exist yet.
1020
         * In the second case, sets some default properties: not Abstract, not Final, not Private, not Protected, not Public, not Interface
1021
         * @param bnd -- binding for the type to create
1022
         * @param name of the type
1023
         * @param owner of the type
1024
         * @param ctxt -- context of use of the type
1025
         */
1026
        public Type ensureFamixType(ITypeBinding bnd, String name, TWithTypes owner, TWithTypes ctxt, int modifiers) {
1027
                
1028
                Type fmx;
1029

1030
                if (bnd == null) {
2 !
1031
                        if (name == null) {
×
1032
                                return null;
×
1033
                        }
1034
                        fmx = searchTypeInContext(name, ctxt); // WildCard Types don't have binding
×
1035
                        if (fmx != null) {
×
1036
                                return fmx;
×
1037
                        }
1038

1039
                        if ((owner instanceof TParametricEntity)) {
×
1040
                                return this.ensureFamixTypeParameter(null, name, owner);
×
1041
                        }
1042
                        else {
1043
                                fmx = ensureFamixEntity(Type.class, bnd, name);
×
1044
                                fmx.setTypeContainer(owner);
×
1045
                                return fmx;
×
1046
                        }
1047
                }
1048

1049
                // bnd != null
1050

1051
                fmx = (Type) getEntityByKey(bnd);
5 ✔
1052
                if (fmx != null) {
2 ✔
1053
                        return fmx;
2 ✔
1054
                }
1055

1056
                if (bnd.isArray()) {
3 !
1057
                        bnd = bnd.getElementType();
×
1058
                }
1059

1060
                if (bnd.isPrimitive()) {
3 ✔
1061
                        return this.ensureFamixPrimitiveType(bnd, name);
5 ✔
1062
                }
1063

1064
                if (bnd.isEnum()) {
3 !
1065
                        return this.ensureFamixEnum(bnd, name, owner);
×
1066
                }
1067
 
1068
                if ((bnd.isRawType() || bnd.isGenericType()) && !bnd.isInterface() ) {
9 ✔
1069
                        return this.ensureFamixClass(bnd.getErasure(), name, (TNamedEntity) owner, /*isGeneric*/true, modifiers);
10 ✔
1070
                }
1071

1072
                if (bnd.isCapture()) {
3 ✔
1073
                        if (bnd.getErasure().isInterface()) {
4 !
1074
                                return this.ensureFamixInterface(bnd.getErasure(), name, owner, /*isGeneric*/true, modifiers);
9 ✔
1075
                        }
1076
                        else {
1077
                                return this.ensureFamixClass(bnd.getErasure(), name, (TNamedEntity) owner, /*isGeneric*/true, modifiers);
×
1078
                        }
1079
                }
1080

1081
                if (bnd.isAnnotation()) {
3 ✔
1082
                        return this.ensureFamixAnnotationType(bnd, name, (ContainerEntity) owner);
7 ✔
1083
                }
1084

1085
                if (bnd.isInterface()) {
3 ✔
1086
                        return this.ensureFamixInterface(bnd, name, owner, /*isGeneric*/bnd.isGenericType() || bnd.isParameterizedType() || bnd.isRawType(), modifiers);
19 ✔
1087
                }
1088

1089
                if (isThrowable(bnd)) {
4 ✔
1090
                        return this.ensureFamixException(bnd, name, owner, /*isGeneric*/false, modifiers);
8 ✔
1091
                }
1092
                if (bnd.isClass()) {
3 ✔
1093
                        return this.ensureFamixClass(bnd, name, (TNamedEntity) owner, /*isGeneric*/bnd.isGenericType() || bnd.isParameterizedType() || bnd.isRawType(), modifiers);
20 !
1094
                }
1095
                if(bnd.isWildcardType()) {
3 ✔
1096
                        return this.ensureFamixWildcardType(bnd, name, (TParametricEntity)owner, ctxt);
8 ✔
1097
                }
1098

1099
                //otherwise (none of the above)
1100

1101
                if (name == null) {
2 ✔
1102
                        name = bnd.getName();
3 ✔
1103
                }
1104

1105
                if (owner == null) {
2 !
1106
                        owner = (TWithTypes) this.ensureOwner(bnd);
5 ✔
1107
                }
1108

1109
                if (bnd.isTypeVariable() ) {
3 !
1110
                        fmx = ensureFamixTypeParameter(bnd, name, owner);
6 ✔
1111
                        return fmx;
2 ✔
1112
                }
1113

1114
                fmx = ensureFamixEntity(Type.class, bnd, name);
×
1115
                fmx.setTypeContainer(owner);
×
1116
                return fmx;
×
1117
        }
1118

1119
        public Type ensureFamixType(ITypeBinding bnd, TWithTypes context) {
1120
        int modifiers = (bnd != null) ? bnd.getModifiers() : UNKNOWN_MODIFIERS;
6 !
1121
                return ensureFamixType(bnd, /*name*/null, /*owner*/null, context, modifiers);
8 ✔
1122
        }
1123
        
1124
        public Type ensureFamixType(ITypeBinding bnd) {
1125
                return ensureFamixType(bnd, /*ctxt*/null);
5 ✔
1126
        }
1127

1128
        public boolean isThrowable(ITypeBinding bnd) {
1129
                if (bnd == null) {
2 ✔
1130
                        return false;
2 ✔
1131
                }
1132
                if (bnd.getQualifiedName().equals("java.lang.Throwable")) {
5 ✔
1133
                        return true;
2 ✔
1134
                } else if (bnd.getQualifiedName().equals("java.lang.Object")) {
5 ✔
1135
                        return false;
2 ✔
1136
                }
1137
                else {
1138
                        return isThrowable(bnd.getSuperclass());
5 ✔
1139
                }
1140
        }
1141

1142
        /**
1143
         * Returns a Famix Class associated with the ITypeBinding.
1144
         * The Entity is created if it does not exist.
1145
         * @param name -- the name of the Famix Class (MUST NOT be null, but this is not checked)
1146
         * @param owner -- package defining the class (should not be null, but it will work if it is)
1147
         * @return the Famix Entity found or created. May return null if "bnd" is null or in case of a Famix error
1148
         */
1149
        @SuppressWarnings("deprecation")
1150
        public Class ensureFamixClass(ITypeBinding bnd, String name, TNamedEntity owner, boolean isGeneric, int modifiers) {
1151
                Class fmx;
1152

1153
                // --------------- some special cases
1154
                if (bnd != null) {
2 ✔
1155
                        if (bnd.isArray()) {
3 !
1156
                                bnd = bnd.getElementType();
×
1157
                        }
1158

1159
                        // for inner classes defined in generics !!! For others should not change anything
1160
                        bnd = bnd.getErasure();
3 ✔
1161
                }
1162

1163
                // ---------------- to avoid useless computations if we can
1164
                fmx = getEntityByKey(bnd, Class.class);
6 ✔
1165
                if (fmx != null) {
2 ✔
1166
                        // Famix cannot represent a parametric exception.
1167
                        if (isGeneric && !(fmx instanceof ParametricClass) && !(fmx instanceof Exception)) {
8 ✔
1168
                                return promoteFamixClass(fmx, bnd);
5 ✔
1169
                        }
1170
                        return fmx;
2 ✔
1171
                }
1172

1173
                // --------------- name
1174
                if (name == null) {
2 ✔
1175
                        if (bnd == null) {
2 !
1176
                                return null;  // not much we can do
×
1177
                        } else if (!bnd.isAnonymous()) {
3 !
1178
                                name = bnd.getErasure().getName();  // for generics, will give the "core" type name, for normal type, won't change anything
5 ✔
1179
                        } else { // anonymous class
1180
                                if (bnd.getSuperclass() != null) {
×
1181
                                        name = bnd.getSuperclass().getName();
×
1182
                                }
1183
                                if ((name == null) || name.equals(OBJECT_NAME)) {
×
1184
                                        ITypeBinding[] intfcs = bnd.getInterfaces();
×
1185
                                        if ((intfcs != null) && (intfcs.length > 0)) {
×
1186
                                                name = bnd.getInterfaces()[0].getName();
×
1187
                                        }
1188
                                        else {
1189
                                                name = "???";
×
1190
                                        }
1191
                                }
1192
                                name = ANONYMOUS_NAME_PREFIX + "(" + name + ")";
×
1193
                        }
1194
                }
1195

1196
        // If we have java.lang.Object we should ensure we create this class
1197
        if (bnd != null && bnd.getQualifiedName().equals("java.lang.Object")) {
7 ✔
1198
                        return ensureFamixClassObject();
3 ✔
1199
                }
1200

1201
                // --------------- owner
1202
                if (owner == null) {
2 ✔
1203
                        if (bnd != null) {
2 ✔
1204
                                owner = ensureOwner(bnd);
4 ✔
1205
                        }
1206
                        /*                                owner = ensureFamixPackageDefault();
1207
                        } else {*/
1208
                }
1209

1210
                // --------------- recover from name ?
1211
                if (owner != null) {
2 ✔
1212
                        for (Class candidate : this.getEntityByName(Class.class, name)) {
13 ✔
1213
                                if (matchAndMapClass(bnd, name, owner, candidate)) {
7 ✔
1214
                                        fmx = candidate;
2 ✔
1215
                                        break;
1 ✔
1216
                                }
1217
                        }
1 ✔
1218
                }
1219
                if (fmx == null && (bnd != null || owner != null)) {
6 ✔
1220
                        fmx = recoveredClassStub(name, (TWithTypes) owner);
6 ✔
1221
                }
1222
                // Famix cannot represent a parametric exception.
1223
                if (fmx != null && isGeneric && !(fmx instanceof ParametricClass) && !(fmx instanceof Exception)) {
10 !
1224
                        fmx = promoteFamixClass(fmx, bnd);
5 ✔
1225
                }
1226

1227
                // ---------------- create
1228
                if (fmx == null) {
2 ✔
1229
                        if (isGeneric) {
2 ✔
1230
                                fmx = ensureFamixParametricClass(bnd, name, (TWithTypes) owner);
8 ✔
1231
                        }
1232
                        else {
1233
                                fmx = ensureFamixEntity(Class.class, bnd, name);
7 ✔
1234
                                fmx.setTypeContainer((TWithTypes)owner);
4 ✔
1235
                        }
1236
                }
1237

1238
                // ---------------- modifiers and super-classes
1239
                if (fmx!=null) {
2 !
1240
                        // we just created it, or it was not bound so we make sure it has the right information in it
1241
                        if (bnd != null) {
2 ✔
1242
                                setClassModifiers(fmx, bnd.getDeclaredModifiers(), (TWithTypes) owner);
7 ✔
1243
                        }
1244

1245
                        TAssociation lastAssoc = null;
2 ✔
1246

1247
                        if (bnd != null) {
2 ✔
1248
                                ITypeBinding supbnd = bnd.getSuperclass();
3 ✔
1249
                                if (supbnd != null) {
2 ✔
1250
                                        lastAssoc = ensureFamixInheritance((TWithInheritances) ensureFamixType(supbnd), fmx, lastAssoc, supbnd);
11 ✔
1251
                                }
1252
                                else {
1253
                                        lastAssoc = ensureFamixInheritance(ensureFamixClassObject(), fmx, lastAssoc, null);
8 ✔
1254
                                }
1255
                                ensureImplementedInterfaces(bnd, fmx, (TWithTypes) owner, lastAssoc);
7 ✔
1256
                        }
1257
                }
1258

1259
                return fmx;
2 ✔
1260
        }
1261

1262
        /**
1263
         * Returns a Famix Exception associated with the ITypeBinding.
1264
         * The Entity is created if it does not exist.
1265
         * @param name -- the name of the Famix Exception
1266
         * @param owner -- type defining the Exception (should not be null, but it will work if it is) 
1267
         *
1268
         * @return the Famix Entity found or created. May return null if "bnd" is null or in case of a Famix error
1269
         */
1270
        public <T extends TWithTypes & TNamedEntity> Exception ensureFamixException(ITypeBinding bnd, String name, TWithTypes owner, boolean isGeneric, int modifiers) {
1271
                Exception fmx;
1272

1273
                // --------------- some special cases
1274
                if (bnd != null) {
2 ✔
1275
                        if (bnd.isArray()) {
3 !
1276
                                bnd = bnd.getElementType();
×
1277
                        }
1278

1279
                        // for inner classes defined in generics !!! For others should not change anything
1280
                        bnd = bnd.getErasure();
3 ✔
1281
                }
1282

1283
                // ---------------- to avoid useless computations if we can
1284
                fmx = (Exception) getEntityByKey(bnd);
5 ✔
1285
                if (fmx != null) {
2 ✔
1286
                        return fmx;
2 ✔
1287
                }
1288

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

1312
                // --------------- owner
1313
                if (owner == null) {
2 ✔
1314
                        if (bnd == null) {
2 ✔
1315
                                owner = ensureFamixPackageDefault();
4 ✔
1316
                        } else {
1317
                                owner = (TWithTypes) ensureOwner(bnd);
5 ✔
1318
                        }
1319
                }
1320

1321
                // --------------- recover from name ?
1322
                for (Exception candidate : this.getEntityByName(Exception.class, name)) {
13 ✔
1323
                        if (matchAndMapClass(bnd, name, (T) owner, candidate)) {
8 !
1324
                                fmx = candidate;
×
1325
                                break;
×
1326
                        }
1327
                }
1 ✔
1328

1329
                // ---------------- create
1330
                if (fmx == null) {
2 !
1331
                        fmx = ensureFamixEntity(Exception.class, bnd, name);
7 ✔
1332
                        fmx.setTypeContainer(owner);
3 ✔
1333
                }
1334

1335
        // we just created it or it was not bound, so we make sure it has the right information in it
1336
        TAssociation lastAssoc = null;
2 ✔
1337
        if (bnd != null) {
2 ✔
1338
            ITypeBinding supbnd = bnd.getSuperclass();
3 ✔
1339
            if (supbnd != null) {
2 ✔
1340
                lastAssoc = ensureFamixInheritance((TWithInheritances) ensureFamixType(supbnd), fmx, lastAssoc, supbnd);
11 ✔
1341
            }
1342
            else {
1343
                lastAssoc = ensureFamixInheritance(ensureFamixClassObject(), fmx, lastAssoc, null);
8 ✔
1344
            }
1345
            ensureImplementedInterfaces(bnd, fmx, owner, lastAssoc);
6 ✔
1346
        }
1347

1348
        return fmx;
2 ✔
1349
        }
1350

1351
        /**
1352
         * Returns a FAMIX Interface with the given <b>name</b>, creating it if it does not exist yet.
1353
         * @param name -- the name of the FAMIX Method
1354
         * @param owner -- type defining the method (should not be null, but it will work if it is) 
1355
         * @return the FAMIX Class or null in case of a FAMIX error
1356
         */
1357
        public <T extends TWithTypes & TNamedEntity> Interface ensureFamixInterface(ITypeBinding bnd, String name, TWithTypes owner, boolean isGeneric, int modifiers) {
1358
                Interface fmx;
1359

1360
                // --------------- some special cases
1361
                if (bnd != null) {
2 !
1362
                        if (bnd.isArray()) {
3 !
1363
                                bnd = bnd.getElementType();
×
1364
                        }
1365

1366
                        // for inner classes defined in generics !!! For others should not change anything
1367
                        bnd = bnd.getErasure();
3 ✔
1368
                }
1369

1370
                // ---------------- to avoid useless computations if we can
1371
                fmx = getEntityByKey(bnd, Interface.class);
6 ✔
1372
                if (fmx != null) {
2 ✔
1373
                        if (isGeneric && !(fmx instanceof ParametricInterface)) {
5 ✔
1374
                                return promoteFamixInterface(fmx, bnd);
5 ✔
1375
                        }
1376
                        return fmx;
2 ✔
1377
                }
1378

1379
                // --------------- name
1380
                if (name == null) {
2 ✔
1381
                        if (bnd == null) {
2 !
1382
                                return null;  // not much we can do
×
1383
                        } else if (!bnd.isAnonymous()) {
3 !
1384
                                name = bnd.getErasure().getName();  // for generics, will give the "core" type name, for normal type, won't change anything
5 ✔
1385
                        } else { // anonymous class
1386
                                if (bnd.getSuperclass() != null) {
×
1387
                                        name = bnd.getSuperclass().getName();
×
1388
                                }
1389
                                if ((name == null) || name.equals(OBJECT_NAME)) {
×
1390
                                        ITypeBinding[] intfcs = bnd.getInterfaces();
×
1391
                                        if ((intfcs != null) && (intfcs.length > 0)) {
×
1392
                                                name = bnd.getInterfaces()[0].getName();
×
1393
                                        }
1394
                                        else {
1395
                                                name = "???";
×
1396
                                        }
1397
                                }
1398
                                name = ANONYMOUS_NAME_PREFIX + "(" + name + ")";
×
1399
                        }
1400
                }
1401

1402
                // --------------- owner
1403
                if (owner == null) {
2 ✔
1404
                        if (bnd == null) {
2 !
1405
                                owner = ensureFamixPackageDefault();
×
1406
                        } else {
1407
                                owner = (TWithTypes) ensureOwner(bnd);
5 ✔
1408
                        }
1409
                }
1410

1411
                // --------------- recover from name ?
1412
                for (Interface candidate : this.getEntityByName(Interface.class, name)) {
13 ✔
1413
                        if (matchAndMapInterface(bnd, name, (T) owner, candidate)) {
8 ✔
1414
                                fmx = candidate;
2 ✔
1415
                                break;
1 ✔
1416
                        }
1417
                }
1 ✔
1418
                if (fmx == null && (bnd != null || owner != null)) {
4 !
1419
                        fmx = recoveredInterfaceStub(name, owner);
5 ✔
1420
                }
1421
                if (fmx != null && isGeneric && !(fmx instanceof ParametricInterface)) {
7 !
1422
                        fmx = promoteFamixInterface(fmx, bnd);
×
1423
                }
1424

1425
                // ---------------- create
1426
                if (fmx == null) {
2 ✔
1427
                        if (isGeneric) {
2 ✔
1428
                                fmx = ensureFamixParametricInterface(bnd, name, owner);
7 ✔
1429
                        }
1430
                        else {
1431
                                fmx = ensureFamixEntity(Interface.class, bnd, name);
7 ✔
1432
                                fmx.setTypeContainer(owner);
3 ✔
1433
                        }
1434
                }
1435

1436
                // ---------------- modifiers and "super interfaces"
1437
                if (fmx!=null) {
2 !
1438
                        // we just created it or it was not bound, so we make sure it has the right information in it
1439
                        if (bnd != null) {
2 !
1440
                                setInterfaceModifiers(fmx, bnd.getModifiers());
5 ✔
1441
                        }
1442
                        TAssociation lastAssociation = null;
2 ✔
1443
                        if (bnd != null) {
2 !
1444
                                ensureImplementedInterfaces(bnd, fmx, owner, lastAssociation);
6 ✔
1445
                        }
1446
                }
1447
                return fmx;
2 ✔
1448
        }
1449

1450
        // JDT can assign distinct recovery bindings to the same unresolved type.
1451
        private Class recoveredClassStub(String name, TWithTypes owner) {
1452
                Class recovered = null;
2 ✔
1453
                for (Class candidate : getEntityByName(Class.class, name)) {
13 ✔
1454
                        if (sameTypeOwner(candidate.getTypeContainer(), owner) && Boolean.TRUE.equals(candidate.getIsStub())) {
11 ✔
1455
                                if (recovered != null) {
2 !
1456
                                        return null;
×
1457
                                }
1458
                                recovered = candidate;
2 ✔
1459
                        }
1460
                }
1 ✔
1461
                return recovered;
2 ✔
1462
        }
1463

1464
        private Interface recoveredInterfaceStub(String name, TWithTypes owner) {
1465
                Interface recovered = null;
2 ✔
1466
                for (Interface candidate : getEntityByName(Interface.class, name)) {
13 ✔
1467
                        if (sameTypeOwner(candidate.getTypeContainer(), owner) && Boolean.TRUE.equals(candidate.getIsStub())) {
11 !
1468
                                if (recovered != null) {
2 !
1469
                                        return null;
×
1470
                                }
1471
                                recovered = candidate;
2 ✔
1472
                        }
1473
                }
1 ✔
1474
                return recovered;
2 ✔
1475
        }
1476

1477
        private boolean sameTypeOwner(TWithTypes first, TWithTypes second) {
1478
                if (first == second) {
3 ✔
1479
                        return true;
2 ✔
1480
                }
1481
                if (!(first instanceof TNamedEntity) || !(second instanceof TNamedEntity)
8 !
1482
                                || !((TNamedEntity) first).getName().equals(((TNamedEntity) second).getName())) {
6 !
1483
                        return false;
2 ✔
1484
                }
1485
                if (first instanceof Type && second instanceof Type) {
×
1486
                        return sameTypeOwner(((Type) first).getTypeContainer(), ((Type) second).getTypeContainer());
×
1487
                }
1488
                return first.getClass() == second.getClass();
×
1489
        }
1490

1491
        private ParametricClass promoteFamixClass(Class former, ITypeBinding bnd) {
1492
                TWithTypes owner = former.getTypeContainer();
3 ✔
1493
                removeEntity(former);
3 ✔
1494
                former.setTypeContainer(null);
3 ✔
1495

1496
                ParametricClass promoted = ensureFamixParametricClass(bnd, former.getName(), owner);
7 ✔
1497
                transferTypeAssociations(former, promoted);
4 ✔
1498
                promoted.addAttributes(new ArrayList<>(former.getAttributes()));
7 ✔
1499
                promoted.addComments(new ArrayList<>(former.getComments()));
7 ✔
1500
                promoted.addImports(new ArrayList<>(former.getImports()));
7 ✔
1501
                promoted.addIncomingImports(new ArrayList<>(former.getIncomingImports()));
7 ✔
1502
                promoted.addInterfaceImplementations(new ArrayList<>(former.getInterfaceImplementations()));
7 ✔
1503
                promoted.addOutgoingConcretizations(new ArrayList<>(former.getOutgoingConcretizations()));
7 ✔
1504
                promoted.addReceivingInvocations(new ArrayList<>(former.getReceivingInvocations()));
7 ✔
1505
                promoted.addSubInheritances(new ArrayList<>(former.getSubInheritances()));
7 ✔
1506
                promoted.addSuperInheritances(new ArrayList<>(former.getSuperInheritances()));
7 ✔
1507
                promoted.setParentPackage(former.getParentPackage());
4 ✔
1508
                promoted.setIsAbstract(former.getIsAbstract());
4 ✔
1509
                promoted.setIsClassSide(former.getIsClassSide());
4 ✔
1510
                promoted.setIsFinal(former.getIsFinal());
4 ✔
1511
                promoted.setVisibility(former.getVisibility());
4 ✔
1512
                return promoted;
2 ✔
1513
        }
1514

1515
        private ParametricInterface promoteFamixInterface(Interface former, ITypeBinding bnd) {
1516
                TWithTypes owner = former.getTypeContainer();
3 ✔
1517
                removeEntity(former);
3 ✔
1518
                former.setTypeContainer(null);
3 ✔
1519

1520
                ParametricInterface promoted = ensureFamixParametricInterface(bnd, former.getName(), owner);
7 ✔
1521
                transferTypeAssociations(former, promoted);
4 ✔
1522
                promoted.addAttributes(new ArrayList<>(former.getAttributes()));
7 ✔
1523
                promoted.addComments(new ArrayList<>(former.getComments()));
7 ✔
1524
                promoted.addImplementations(new ArrayList<>(former.getImplementations()));
7 ✔
1525
                promoted.addImports(new ArrayList<>(former.getImports()));
7 ✔
1526
                promoted.addIncomingImports(new ArrayList<>(former.getIncomingImports()));
7 ✔
1527
                promoted.addOutgoingConcretizations(new ArrayList<>(former.getOutgoingConcretizations()));
7 ✔
1528
                promoted.addReceivingInvocations(new ArrayList<>(former.getReceivingInvocations()));
7 ✔
1529
                promoted.addSubInheritances(new ArrayList<>(former.getSubInheritances()));
7 ✔
1530
                promoted.addSuperInheritances(new ArrayList<>(former.getSuperInheritances()));
7 ✔
1531
                promoted.setParentPackage(former.getParentPackage());
4 ✔
1532
                promoted.setIsClassSide(former.getIsClassSide());
4 ✔
1533
                promoted.setIsFinal(former.getIsFinal());
4 ✔
1534
                promoted.setVisibility(former.getVisibility());
4 ✔
1535
                return promoted;
2 ✔
1536
        }
1537

1538
        private void transferTypeAssociations(Type former, Type promoted) {
1539
                promoted.addTypes(new ArrayList<>(former.getTypes()));
7 ✔
1540
                promoted.addDefinedAnnotationTypes(new ArrayList<>(former.getDefinedAnnotationTypes()));
7 ✔
1541
                promoted.addAnnotationInstances(new ArrayList<>(former.getAnnotationInstances()));
7 ✔
1542
                promoted.addIncomingReferences(new ArrayList<>(former.getIncomingReferences()));
7 ✔
1543
                promoted.addIncomingTypings(new ArrayList<>(former.getIncomingTypings()));
7 ✔
1544
                promoted.addLowerBoundedWildcards(new ArrayList<>(former.getLowerBoundedWildcards()));
7 ✔
1545
                promoted.addMethods(new ArrayList<>(former.getMethods()));
7 ✔
1546
                promoted.addUpperBoundedWildcards(new ArrayList<>(former.getUpperBoundedWildcards()));
7 ✔
1547
                promoted.setSourceAnchor(former.getSourceAnchor());
4 ✔
1548
                promoted.setNumberOfLinesOfCode(former.getNumberOfLinesOfCode());
4 ✔
1549
                promoted.setIsStub(former.getIsStub());
4 ✔
1550
        }
1 ✔
1551

1552
        /**
1553
         * "Converts" (if needed) a TTYpe entity to be a TThrowable. Might involve removing the existing entity, recreating a new one and migrating
1554
         * all the relationship of the former to the later
1555
         */
1556
        public TThrowable asException(TType fmxType) {
1557
                if (fmxType instanceof Exception) {
3 ✔
1558
                        return (Exception) fmxType;
3 ✔
1559
                }
1560
                if(fmxType instanceof TypeParameter) {
3 ✔
1561
                        return (TypeParameter) fmxType;
3 ✔
1562
                }
1563

1564
                Exception fmxException = null;
2 ✔
1565
                IBinding key;
1566

1567
                try {
1568
                        key = entityToKey.get(fmxType);
6 ✔
1569

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

1574
                        TWithTypes owner = fmxType.getTypeContainer();
3 ✔
1575
                        fmxType.setTypeContainer(null);
3 ✔
1576
                        fmxException = ensureFamixException((ITypeBinding) key, fmxType.getName(), owner, /*isGeneric*/false, UNKNOWN_MODIFIERS);
10 ✔
1577
                        transferTypeAssociations((Type) fmxType, fmxException);
5 ✔
1578
                        fmxException.setComments(new ArrayList<>( ((TWithComments) fmxType).getComments() ));
8 ✔
1579
                        if (fmxType instanceof TTypeArgument) {
3 !
1580
                                fmxException.addOutgoingConcretizations(new ArrayList<>( ((TTypeArgument) fmxType).getOutgoingConcretizations() ));
8 ✔
1581
                        }
1582

1583
                        if (fmxType instanceof TWithAttributes) {
3 !
1584
                                fmxException.addAttributes( new ArrayList<>( ((TWithAttributes)fmxType).getAttributes() ) );
8 ✔
1585
                        }
1586

1587
                        if (fmxType instanceof TWithInheritances) {
3 !
1588
                                fmxException.addSuperInheritances( new ArrayList<>( ((TWithInheritances) fmxType).getSuperInheritances() ) );
8 ✔
1589
                                fmxException.addSubInheritances( new ArrayList<>( ((TWithInheritances) fmxType).getSubInheritances() ) );
8 ✔
1590
                        }
1591
                }
UNCOV
1592
                catch( ConcurrentModificationException e) {
×
1593
                        e.printStackTrace();
×
1594
                }
1 ✔
1595

1596
                return fmxException;
2 ✔
1597
        }
1598

1599
        /**
1600
         * helper method, we know the type exists, ensureFamixClass will recover it
1601
         */
1602
        public Class getFamixClass(ITypeBinding bnd, String name, TNamedEntity owner) {
1603
                return ensureFamixClass(bnd, name, owner, /*isGeneric*/false, UNKNOWN_MODIFIERS);
8 ✔
1604
        }
1605

1606
        /**
1607
         * helper method, we know the type exists, ensureFamixInterface will recover it
1608
         */
1609
        public Interface getFamixInterface(ITypeBinding bnd, String name, ContainerEntity owner) {
1610
                return ensureFamixInterface(bnd, name, owner, /*isGeneric*/false, UNKNOWN_MODIFIERS);
8 ✔
1611
        }
1612

1613
        /**
1614
         * helper method, we know the type exists, ensureFamixInterface will recover it
1615
         */
1616
        public Exception getFamixException(ITypeBinding bnd, String name, TWithTypes owner) {
1617
                return ensureFamixException(bnd, name, owner, /*isGeneric*/false, UNKNOWN_MODIFIERS);
8 ✔
1618
        }
1619

1620
        /**
1621
         * Ensures a famix entity for the owner of a binding.<br>
1622
         * This owner can be a method, a class or a namespace
1623
         * @param bnd -- binding for the owned entity
1624
         * @return a famix entity for the owner
1625
         */
1626
        private TNamedEntity ensureOwner(ITypeBinding bnd) {
1627
                TNamedEntity owner;
1628
                IMethodBinding parentMtd = bnd.getDeclaringMethod();
3 ✔
1629
                if (parentMtd != null) {
2 ✔
1630
                        owner = this.ensureFamixMethod(parentMtd);  // cast needed to desambiguate the call
5 ✔
1631
                }
1632
                else {
1633
                        ITypeBinding parentClass = bnd.getDeclaringClass();
3 ✔
1634
                        if (parentClass != null) {
2 ✔
1635
                owner = this.ensureFamixType(parentClass);
5 ✔
1636
            }
1637
                        else {
1638
                                IPackageBinding parentPckg = bnd.getPackage();
3 ✔
1639
                                if (parentPckg != null) {
2 !
1640
                                        owner = this.ensureFamixPackage(parentPckg, null);
6 ✔
1641
                                } else {
1642
                                        owner = this.ensureFamixPackageDefault();
×
1643
                                }
1644
                        }
1645
                }
1646
                return owner;
2 ✔
1647
        }
1648

1649

1650
        /**
1651
         * Returns a FAMIX PrimitiveType with the given <b>name</b>, creating it if it does not exist yet
1652
         * We assume that PrimitiveType must be uniq for a given name
1653
         * @param name -- the name of the FAMIX PrimitiveType
1654
         * @return the FAMIX PrimitiveType or null in case of a FAMIX error
1655
         */
1656
        public PrimitiveType ensureFamixPrimitiveType(ITypeBinding bnd, String name) {
1657
                if (name == null) {
2 ✔
1658
                        if (bnd == null) {
2 !
1659
                                return null;
×
1660
                        } else {
1661
                                name = bnd.getName();
3 ✔
1662
                        }
1663
                }
1664
                return ensureFamixUniqEntity(PrimitiveType.class, bnd, name);
7 ✔
1665
        }
1666

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

1670
                // --------------- to avoid useless computations if we can
1671
                fmx = (org.moosetechnology.model.famix.famixjavaentities.Enum) getEntityByKey(bnd);
5 ✔
1672
                if (fmx != null) {
2 ✔
1673
                        return fmx;
2 ✔
1674
                }
1675

1676
                // --------------- name
1677
                if (name == null) {
2 ✔
1678
                        if (bnd == null) {
2 !
1679
                                return null;
×
1680
                        }
1681
                        else {
1682
                                name = bnd.getName();
3 ✔
1683
                        }
1684
                }
1685

1686
                // --------------- owner
1687
                if (owner == null) {
2 ✔
1688
                        if (bnd == null) {
2 !
1689
                                owner = ensureFamixPackageDefault();  // not really sure what to do here
×
1690
                        } else {
1691
                                owner = (TWithTypes) ensureOwner(bnd);
5 ✔
1692
                        }
1693
                }
1694

1695
                // --------------- recover from name ?
1696
                for (org.moosetechnology.model.famix.famixjavaentities.Enum candidate : getEntityByName(org.moosetechnology.model.famix.famixjavaentities.Enum.class, name)) {
9 !
1697
                        if (matchAndMapType(bnd, name, (T) owner, candidate)) {
×
1698
                                fmx = candidate;
×
1699
                                break;
×
1700
                        }
1701
                }
×
1702

1703
                if (fmx == null) {
2 !
1704
                        fmx = ensureFamixEntity(Enum.class, bnd, name);
7 ✔
1705
                        fmx.setTypeContainer(owner);
3 ✔
1706
                }
1707

1708
                if (bnd != null) {
2 !
1709
                        setVisibility(fmx, bnd.getModifiers(), owner);
6 ✔
1710
                }
1711

1712
                return fmx;
2 ✔
1713
        }
1714

1715
        /**
1716
         * helper method, we know the type exists, ensureFamixEnum will recover it
1717
         */
1718
        public org.moosetechnology.model.famix.famixjavaentities.Enum getFamixEnum(ITypeBinding bnd, String name, TWithTypes owner) {
1719
                return ensureFamixEnum(bnd, name, owner);
6 ✔
1720
        }
1721

1722
        public EnumValue ensureFamixEnumValue(IVariableBinding bnd,        String name, Enum owner) {
1723
                EnumValue fmx;
1724

1725
                // --------------- to avoid useless computations if we can
1726
                fmx = (EnumValue)getEntityByKey(bnd);
5 ✔
1727
                if (fmx != null) {
2 ✔
1728
                        return fmx;
2 ✔
1729
                }
1730

1731
                // --------------- name
1732
                if (name == null) {
2 !
1733
                        if (bnd == null) {
×
1734
                                return null;
×
1735
                        }
1736
                        else {
1737
                                name = bnd.getName();
×
1738
                        }
1739
                }
1740

1741
                // --------------- owner
1742
                if (owner == null) {
2 ✔
1743
                        if (bnd == null) {
2 !
1744
                                return null;  // what would be the interest of creating an EnumValue without a declaring Enum type?
×
1745
                        }
1746
                        else {
1747
                                owner = ensureFamixEnum(bnd.getDeclaringClass(), null, null);
7 ✔
1748
                        }
1749
                }
1750

1751
                // --------------- recover from name ?
1752
                for (EnumValue candidate : getEntityByName(EnumValue.class, name) ) {
9 !
1753
                        if ( matchAndMapVariable(bnd, name, owner, candidate) ) {
×
1754
                                fmx = candidate;
×
1755
                                break;
×
1756
                        }
1757
                }
×
1758
                if (fmx == null) {
2 !
1759
                        fmx = ensureFamixEntity(EnumValue.class, bnd, name);
7 ✔
1760
                        fmx.setParentEnum(owner);
3 ✔
1761
                }
1762

1763
        fmx.setParentEnum(owner);
3 ✔
1764

1765
        return fmx;
2 ✔
1766
        }
1767

1768
    /**
1769
         * e.g. see {@link EntityDictionary#ensureFamixClass}
1770
         */
1771
        public AnnotationType ensureFamixAnnotationType(ITypeBinding bnd, String name, ContainerEntity owner) {
1772
                AnnotationType fmx;
1773

1774
                // --------------- to avoid useless computations if we can
1775
                fmx = (AnnotationType)getEntityByKey(bnd);
5 ✔
1776
                if (fmx != null) {
2 ✔
1777
                        return fmx;
2 ✔
1778
                }
1779

1780
                // --------------- name
1781
                if (name == null) {
2 ✔
1782
                        if (bnd == null) {
2 !
1783
                                return null;
×
1784
                        }
1785
                        else {
1786
                                name = bnd.getName();
3 ✔
1787
                        }
1788
                }
1789

1790
                // --------------- owner
1791
                if (owner == null) {
2 ✔
1792
                        if (bnd == null) {
2 !
1793
                                owner = ensureFamixPackageDefault();
×
1794
                        }
1795
                        else {
1796
                                IPackageBinding parentPckg = bnd.getPackage();
3 ✔
1797
                                if (parentPckg != null) {
2 !
1798
                                        owner = this.ensureFamixPackage(parentPckg, null);
6 ✔
1799
                                } else {
1800
                                        owner = this.ensureFamixPackageDefault();
×
1801
                                }
1802
                        }
1803
                }
1804

1805
                // --------------- recover from name ?
1806
                for (AnnotationType candidate : getEntityByName(AnnotationType.class, name) ) {
13 ✔
1807
                        if ( matchAndMapType(bnd, name, owner, candidate) ) {
7 !
1808
                                fmx = candidate;
2 ✔
1809
                                break;
1 ✔
1810
                        }
1811
                }
×
1812

1813
                // --------------- create
1814
                if (fmx == null) {
2 ✔
1815
                        fmx = ensureFamixEntity(AnnotationType.class, bnd, name);
7 ✔
1816
                        fmx.setAnnotationTypesContainer(owner);
3 ✔
1817
                }
1818

1819
                if (bnd != null) {
2 !
1820
                        // Not supported in Famix
1821

1822
                        // setVisibility(fmx, bnd.getModifiers());
1823
                }
1824

1825
                return fmx;
2 ✔
1826
        }
1827

1828
        /**
1829
         * helper method, we know the type exists, ensureFamixAnnotationType will recover it
1830
         */
1831
        public AnnotationType getFamixAnnotationType(ITypeBinding bnd, String name, ContainerEntity owner) {
1832
                return ensureFamixAnnotationType(bnd, name, owner);
6 ✔
1833
        }
1834

1835
        public AnnotationTypeAttribute ensureFamixAnnotationTypeAttribute(IMethodBinding bnd, String name, AnnotationType owner) {
1836
                AnnotationTypeAttribute fmx = null;
2 ✔
1837

1838
                // --------------- to avoid useless computations if we can
1839
                fmx = (AnnotationTypeAttribute)getEntityByKey(bnd);
5 ✔
1840
                if (fmx != null) {
2 ✔
1841
                        return fmx;
2 ✔
1842
                }
1843

1844
                // --------------- name
1845
                if (name == null) {
2 !
1846
                        if (bnd == null) {
×
1847
                                return null;
×
1848
                        }
1849
                        else {
1850
                                name = bnd.getName();
×
1851
                        }
1852
                }
1853

1854
                // --------------- owner
1855
                if (owner == null) {
2 !
1856
                        if (bnd == null) {
×
1857
                                return null;  // what would be the use of an AnnotationTypeAttribute without AnnotationType ?
×
1858
                        }
1859
                        else {
1860
                                ITypeBinding parentType = bnd.getDeclaringClass();
×
1861
                                if (parentType != null) {
×
1862
                                        owner = this.ensureFamixAnnotationType(parentType, null, null);
×
1863
                                }
1864
                                else  {
1865
                                        return null;  // what would be the use of an AnnotationTypeAttribute without AnnotationType ?
×
1866
                                }
1867
                        }
1868
                }
1869

1870
                // --------------- recover from name ?
1871
                for (AnnotationTypeAttribute candidate : getEntityByName(AnnotationTypeAttribute.class, name) ) {
13 ✔
1872
                        // JDT treats annotation type attributes as methods ...
1873
                        // checkAndMapMethod wants a signature as 2nd argument so we add empty param list
1874
                        if ( (bnd != null) && matchAndMapMethod(bnd, name+"()", null, owner, candidate) ) {
11 !
1875
                                fmx = candidate;
×
1876
                                break;
×
1877
                        }
1878
                        // if the binding is null, the annotationTypeAttribute migth have been created
1879
                        else if ( (bnd == null) && matchAndMapVariable(null, name, owner, candidate)) {
2 !
1880
                                fmx = candidate;
×
1881
                                break;
×
1882
                        }
1883
                }
1 ✔
1884

1885
                if (fmx == null) {
2 !
1886
                        fmx = ensureFamixEntity(AnnotationTypeAttribute.class, bnd, name);
7 ✔
1887
                        fmx.setParentType(owner);
3 ✔
1888
                }
1889

1890
                if (bnd != null) {
2 !
1891
                        // Not suopp
1892

1893
                        // setVisibility(fmx, bnd.getModifiers());
1894
                }
1895

1896
                return fmx;
2 ✔
1897
        }
1898

1899
        /**
1900
         * helper method, we know the attribute exists, ensureFamixAnnotationTypeAttribute will recover it
1901
         */
1902
        public AnnotationTypeAttribute getFamixAnnotationTypeAttribute(IMethodBinding bnd, String name, AnnotationType owner) {
1903
                return ensureFamixAnnotationTypeAttribute( bnd, name, owner);
6 ✔
1904
        }
1905
        
1906
        
1907
        /**
1908
         * Returns a FAMIX Wildcard with its bounds
1909
         * @param bnd
1910
         * @param name
1911
         * @param owner
1912
         * @return
1913
         */
1914
        public Wildcard ensureFamixWildcardType(ITypeBinding bnd, String name, TParametricEntity owner, TWithTypes ctxt) {
1915
                Wildcard fmx = this.ensureFamixEntity(Wildcard.class, bnd, bnd.getName());
8 ✔
1916
                if(bnd.getBound() != null) {
3 ✔
1917
                        Type bound = this.ensureFamixType(bnd.getBound());
5 ✔
1918
                        if(bnd.isUpperbound()) {
3 ✔
1919
                                fmx.setUpperBound(bound);
3 ✔
1920
                                bound.addUpperBoundedWildcards(fmx);
4 ✔
1921
                        }else{
1922
                                fmx.setLowerBound(bound);
3 ✔
1923
                                bound.addLowerBoundedWildcards(fmx);
3 ✔
1924
                        }
1925
                }
1926
                return fmx;
2 ✔
1927
        }
1928

1929
        /**
1930
         * Returns a Famix TypeParameter (created by a Famix ParametricEntity) with the given <b>name</b>, creating it if it does not exist yet
1931
         * In the second case, sets some default properties: not Abstract, not Final, not Private, not Protected, not Public
1932
         * @param name -- the name of the Famix TypeParameter
1933
         * @return the Famix TypeParameter or null in case of a Famix error
1934
         */
1935
        public TypeParameter ensureFamixTypeParameter(ITypeBinding bnd,        String name, TWithTypes owner) {
1936
                TypeParameter fmx;
1937

1938
                // --------------- to avoid useless computations if we can
1939
                fmx = (TypeParameter)getEntityByKey(bnd);
5 ✔
1940
                if (fmx != null) {
2 ✔
1941
                        return fmx;
2 ✔
1942
                }
1943

1944
                // --------------- name
1945
                if (name == null) {
2 ✔
1946
                        if (bnd == null) {
2 !
1947
                                return null;
×
1948
                        }
1949
                        else {
1950
                                name = bnd.getName();
3 ✔
1951
                        }
1952
                }
1953

1954
                // --------------- owner
1955
                if (owner == null && bnd != null) {
2 !
1956
            if (bnd.getDeclaringClass() != null) {
×
1957
                owner = this.ensureFamixType(bnd.getDeclaringClass());
×
1958
            } else if(bnd.getDeclaringMethod() != null) {
×
1959
                owner = this.ensureFamixMethod(bnd.getDeclaringMethod());
×
1960
            }
1961
                }
1962

1963
                // --------------- recover from name ?
1964
                for (Type candidate : this.getEntityByName(Type.class, name)) {
13 ✔
1965
                        if ( matchAndMapType(bnd, name, (ContainerEntity) owner, candidate) ) {
8 ✔
1966
                                fmx = (TypeParameter) candidate;
3 ✔
1967
                                break;
1 ✔
1968
                        }
1969
                }
1 ✔
1970

1971
                // --------------- create
1972
                if (fmx == null) {
2 ✔
1973
                        fmx = ensureFamixEntity(TypeParameter.class, bnd, name);
7 ✔
1974
                        if(bnd != null && bnd.getSuperclass() != null) {
5 !
1975
                                Type upperBound = ensureFamixType(bnd.getSuperclass());
5 ✔
1976
                                fmx.setUpperBound(upperBound);
3 ✔
1977
                        }
1978
                        if(bnd != null) {
2 ✔
1979
                for (ITypeBinding intbnd : bnd.getInterfaces()) {
17 ✔
1980
                    Type upperBound = ensureFamixType(intbnd);
4 ✔
1981
                    fmx.setUpperBound(upperBound);
3 ✔
1982
                }
1983
            }
1984
                        fmx.setTypeContainer(owner);
3 ✔
1985
                }
1986

1987
                return fmx;
2 ✔
1988
        }
1989

1990
        /**
1991
         * Checks whether the existing unmapped Famix Namespace matches the binding.
1992
         * Checks that the candidate has the same name as the JDT bound package, and checks recursively that owners also match.
1993
         *
1994
         * @param bnd       -- a JDT binding that we are trying to match to the candidate
1995
         * @param name      of the package
1996
         * @param owner     of the package
1997
         * @param candidate -- a Famix Entity
1998
         * @return whether the binding matches the candidate (if <b>true</b>, the mapping is recorded)
1999
         */
2000
        private boolean matchAndMapPackage(IPackageBinding bnd, String name, Package owner, NamedEntity candidate) {
2001
                if (!(candidate instanceof Package)) {
3 !
2002
                        return false;
×
2003
                }
2004

2005
                // check whether bnd and candidate are already bound
2006
                CheckResult res = checkKeyMatch(bnd, candidate);
5 ✔
2007
                if (res == CheckResult.MATCH) {
3 ✔
2008
                        return true;
2 ✔
2009
                } else if (res == CheckResult.FAIL) {
3 ✔
2010
                        return false;
2 ✔
2011
                }
2012

2013
                if (checkNameMatch(bnd, name, candidate) == CheckResult.FAIL) {
7 ✔
2014
                        return false;
2 ✔
2015
                }
2016

2017
                // names match, not need to look at owner because names of Namespaces are their fully qualified name
2018
                conditionalMapToKey(bnd, candidate);
4 ✔
2019
                return true;
2 ✔
2020
        }
2021

2022
        /**
2023
         * Checks whether the existing unmapped Famix Type matches the binding.
2024
         * Checks that the candidate has the same name as the JDT bound type, and checks recursively that owners also match.
2025
         * We also check that the actual class of the candidate matches (can be a sub-class of FamixType).
2026
         * @param bnd -- a JDT binding that we are trying to match to the candidate
2027
         * @param name of the type
2028
         * @param owner of the type
2029
         * @param candidate -- a Famix NamedEntity (Class, Type, PrimitiveType, Enum, AnnotationType)
2030
         * @return whether the binding matches the candidate (if <b>true</b>, the mapping is recorded)
2031
         */
2032
        private <T extends TWithTypes & TNamedEntity> boolean matchAndMapType(ITypeBinding bnd, String name, TNamedEntity owner, TNamedEntity candidate) {
2033
                if (! (candidate instanceof Type) ) {
3 !
2034
                        return false;
×
2035
                }
2036

2037
                // check whether bnd and candidate are already bound
2038
                CheckResult res = checkKeyMatch(bnd, candidate);
5 ✔
2039
                if (res == CheckResult.MATCH) {
3 ✔
2040
                        return true;
2 ✔
2041
                }
2042
                else if (res == CheckResult.FAIL) {
3 ✔
2043
                        return false;
2 ✔
2044
                }
2045

2046
                if ( (bnd != null) && (bnd.isArray()) ) {
5 ✔
2047
                                bnd = bnd.getElementType();
3 ✔
2048
                }
2049

2050
                // checking names
2051
                if ( (bnd != null) && (bnd.isParameterizedType() || bnd.isRawType()) ) {
8 !
2052
                        name = bnd.getErasure().getName();
×
2053
                }
2054
                else if (bnd != null) {
2 ✔
2055
                        name = bnd.getName();
3 ✔
2056
                }
2057
                // else name = name
2058
                if (checkNameMatch(null, name, candidate) == CheckResult.FAIL) {
7 ✔
2059
                        return false;
2 ✔
2060
                }
2061

2062
                // special case of primitive types
2063
                if (candidate instanceof PrimitiveType) {
3 !
2064
                        if ( (bnd != null) && bnd.isPrimitive() ) {
×
2065
                                // names are equal so it's OK
2066
                                conditionalMapToKey(bnd, candidate);
×
2067
                                return true;
×
2068
                        }
2069
                        else if ( (bnd == null) && (owner == null) ) {
×
2070
                                return true;
×
2071
                        }
2072
                }
2073

2074
                // check owners without bnd
2075
                if (bnd == null) {
2 ✔
2076
                        return matchAndMapTypeOwner(bnd, owner, (Type) candidate);
7 ✔
2077
                }
2078

2079
                // check owners with bnd
2080
                // type is an annotation
2081
                if (bnd.isAnnotation() && (candidate instanceof AnnotationType)) {
6 !
2082
                        if (matchAndMapPackage(bnd.getPackage(), owner.getName(), (Package) Util.getOwner(owner), Util.getOwner(candidate))) {
13 !
2083
                                conditionalMapToKey(bnd, candidate);
4 ✔
2084
                                return true;
2 ✔
2085
                        } else {
2086
                                return false;
×
2087
                        }
2088
                }
2089

2090
                // check owners with bnd
2091
                // type is a Parameterized type
2092
                if ((bnd.isParameterizedType() || bnd.isRawType()) && (candidate instanceof ParametricClass)) {
6 !
2093
                        return matchAndMapTypeOwner(bnd, owner, (Type) candidate);
×
2094
                }
2095

2096
                // check owners with bnd
2097
                // type is an Enum
2098
                if (bnd.isEnum() && (candidate instanceof Enum)) {
3 !
2099
                        return matchAndMapTypeOwner(bnd, owner, (Type) candidate);
×
2100
                }
2101

2102
                // check owners with bnd
2103
                // type is something elae (a class or interface)
2104
                // Annotation are interfaces too, so we should check this one after isAnnotation
2105
                if ( bnd.isClass()) {
3 !
2106
                        return matchAndMapClass(bnd, name, owner, (Type) candidate);
×
2107
                }
2108

2109
                if(bnd.isInterface()) {
3 !
2110
                        return matchAndMapInterface(bnd, name, owner, (Type) candidate);
×
2111
                }
2112

2113
                return false;
2 ✔
2114
        }
2115

2116
        /**
2117
         * Checks whether the existing unmapped Famix Class (or Interface) matches the binding.
2118
         * Checks that the candidate has the same name as the JDT bound type, and checks recursively that owners also match.
2119
         * @param bnd -- a JDT binding that we are trying to match to the candidate
2120
         * @param name of the class
2121
         * @param owner of the class
2122
         * @param candidate -- a Famix Entity
2123
         * @return whether the binding matches the candidate (if <b>true</b>, the mapping is recorded)
2124
         */
2125
        private boolean matchAndMapClass(ITypeBinding bnd, String name, TNamedEntity owner, TType candidate) {
2126
                if (!(candidate instanceof Class)) {
3 !
2127
                        return false;
×
2128
                }
2129

2130
                // check whether bnd and candidate are already bound
2131
                CheckResult res = checkKeyMatch(bnd, candidate);
5 ✔
2132
                if (res == CheckResult.MATCH) {
3 ✔
2133
                        return true;
2 ✔
2134
                } else if (res == CheckResult.FAIL) {
3 ✔
2135
                        return false;
2 ✔
2136
                }
2137

2138
                if (checkNameMatch(bnd, name, candidate) == CheckResult.FAIL) {
7 !
2139
                        return false;
×
2140
                }
2141

2142
                // checking owner
2143
                return matchAndMapTypeOwner(bnd, owner, (Type) candidate);
7 ✔
2144
        }
2145

2146
        /**
2147
         * Checks whether the existing unmapped Famix Class (or Interface) matches the binding.
2148
         * Checks that the candidate has the same name as the JDT bound type, and checks recursively that owners also match.
2149
         * @param bnd -- a JDT binding that we are trying to match to the candidate
2150
         * @param name of the class
2151
         * @param owner of the class
2152
         * @param candidate -- a Famix Entity
2153
         * @return whether the binding matches the candidate (if <b>true</b>, the mapping is recorded)
2154
         */
2155
        private boolean matchAndMapInterface(ITypeBinding bnd, String name, TNamedEntity owner, Type candidate) {
2156
                if (!(candidate instanceof Interface)) {
3 !
2157
                        return false;
×
2158
                }
2159

2160
                // check whether bnd and candidate are already bound
2161
                CheckResult res = checkKeyMatch(bnd, candidate);
5 ✔
2162
                if (res == CheckResult.MATCH) {
3 ✔
2163
                        return true;
2 ✔
2164
                } else if (res == CheckResult.FAIL) {
3 ✔
2165
                        return false;
2 ✔
2166
                }
2167

2168
                if (checkNameMatch(bnd, name, candidate) == CheckResult.FAIL) {
7 !
2169
                        return false;
×
2170
                }
2171

2172
                // checking owner
2173
                return matchAndMapTypeOwner(bnd, owner, candidate);
6 ✔
2174
        }
2175

2176
        /**
2177
         * Checks whether the existing unmapped Famix "Method" matches the binding.
2178
         * Checks that the candidate has the same name and same signature as the JDT bound method, and checks recursively that owners also match.
2179
         * Note that AnnotationTypeAttribute are treated as methods by JDT, so they are checked here.
2180
         * @param bnd -- a JDT binding that we are trying to match to the candidate
2181
         * @param sig -- signature of the method
2182
         * @param retTyp -- return type of the method
2183
         * @param owner of the method
2184
         * @param candidate -- a Famix Entity (regular Method or AnnotationTypeAttribute)
2185
         * @return whether the binding matches the candidate (if <b>true</b>, the mapping is recorded)
2186
         */
2187
        private  boolean matchAndMapMethod(IMethodBinding bnd, String sig, TType retTyp, TNamedEntity owner, NamedEntity candidate) {
2188
                if (! (candidate instanceof Method) ) {
3 ✔
2189
                        return false;
2 ✔
2190
                }
2191

2192
                // check whether bnd and candidate are already bound
2193
                CheckResult res = checkKeyMatch(bnd, candidate);
5 ✔
2194
                if (res == CheckResult.MATCH) {
3 ✔
2195
                        return true;
2 ✔
2196
                }
2197
                else if (res == CheckResult.FAIL) {
3 ✔
2198
                        return false;
2 ✔
2199
                }
2200

2201
                // checking names
2202
                String name = (sig != null) ? sig.substring(0, sig.indexOf('(')) : null;
10 !
2203
                if (checkNameMatch(bnd, name, candidate) == CheckResult.FAIL) {
7 ✔
2204
                        return false;
2 ✔
2205
                }
2206

2207
                // for methods, the name is not enough, we must test the signature also
2208
                // but not for AnnotationTypeAttribute
2209

2210
                        if (bnd != null) {
2 ✔
2211
                                sig = bnd.getName() + "(" + signatureParamsFromBinding(bnd) + ")";
7 ✔
2212
                        }
2213
                        if (! ((Method) candidate).getSignature().equals(sig)) {
6 ✔
2214
                                return false;
2 ✔
2215
                        }
2216

2217
                        // and still for method, must also check the return type
2218
                        if (bnd != null) {
2 ✔
2219
                                if (isConstructorBinding(bnd)) {
4 !
2220
                                        if ( ((Method) candidate).getDeclaredType() != null ) {
×
2221
                                                return false;
×
2222
                                        }
2223
                                        // else OK for now
2224
                                }
2225
                                else { // not a constructor
2226
                                        if ( ((Method) candidate).getDeclaredType() == null ) {
4 !
2227
                                                return false;
×
2228
                                        }
2229
                                        else if (! matchAndMapType(bnd.getReturnType(), null, null, ((Method) candidate).getDeclaredType()) ) {
10 ✔
2230
                                                return false;
2 ✔
2231
                                        }
2232
                                        // else OK for now
2233
                                }
2234
                        }
2235
                        else {  // bnd == null
2236
                                if (retTyp == null) { // similar to (bnd.isConstructor())
2 !
2237
                                        if ( ((Method) candidate).getDeclaredType() != null ) {
4 ✔
2238
                                                return false;
2 ✔
2239
                                        }
2240
                                        // else OK for now
2241
                                } else { // (ret != null)  i.e. not a constructor
2242
                                        if (((Method) candidate).getDeclaredType() == null) {
×
2243
                                                return false;
×
2244
                                        } else if (!matchAndMapType(null, retTyp.getName(), Util.getOwner(retTyp), (NamedEntity) ((Method) candidate).getDeclaredType())) {
×
2245
                                                return false;
×
2246
                                        }
2247
                                        // else OK for now
2248
                                }
2249
                        }
2250

2251

2252
                // check owner
2253
                if (matchAndMapOwnerAsType(((bnd != null) ? bnd.getDeclaringClass() : null), owner, Util.getOwner(candidate)) == CheckResult.MATCH) {
13 ✔
2254
                        conditionalMapToKey(bnd, candidate);
4 ✔
2255
                        return true;
2 ✔
2256
                } else {
2257
                        return false;
2 ✔
2258
                }
2259
        }
2260

2261
        /** testing that a method binding is for a constructor
2262
         * There is a special case for "diamond constructors" (eg: <code>new HashSet<>()</code>)
2263
         */
2264
        protected boolean isConstructorBinding(IMethodBinding bnd) {
2265
                if (bnd.isConstructor()) {
3 ✔
2266
                        return true;
2 ✔
2267
                }
2268
                if ( bnd.getName().equals("<factory>") ) {
5 ✔
2269
                        return true;
2 ✔
2270
                }
2271
                return false;
2 ✔
2272
        }
2273

2274
        /**
2275
         * Checks whether the candidate (an existing unmapped Famix "Variable" like Attribute, Parameter, ...) matches the binding.
2276
         * Checks that the candidate has the same name as the JDT bound variable, and checks recursively that owners also match.
2277
         * The Famix candidate is a NamedEntity and not a StructuralEntity to allow dealing with Famix EnumValue that JDT treats as variables
2278
         * @param bnd -- a JDT binding that we are trying to match to the candidate
2279
         * @param name of the variable
2280
         * @param owner of the variable
2281
         * @param candidate -- a Famix Entity (a StructuralEntity or an EnumValue)
2282
         * @return whether the binding matches the candidate (if <b>true</b>, the mapping is recorded)
2283
         */
2284
        private boolean matchAndMapVariable(IVariableBinding bnd, String name, TNamedEntity owner, TNamedEntity candidate) {
2285
                if (!(candidate instanceof TStructuralEntity)) {
3 !
2286
                        return false;
×
2287
                }
2288

2289
                // check whether bnd and candidate are already bound
2290
                CheckResult keyMatch = checkKeyMatch(bnd, candidate);
5 ✔
2291
                if (keyMatch == CheckResult.MATCH) {
3 !
2292
                        return true;
×
2293
                } else if (keyMatch == CheckResult.FAIL) {
3 ✔
2294
                        return false;
2 ✔
2295
                }
2296

2297
                if (checkNameMatch(bnd, name, candidate) == CheckResult.FAIL) {
7 !
2298
                        return false;
×
2299
                }
2300

2301
                // check owner
2302
                TNamedEntity candidateOwner = Util.getOwner(candidate);
3 ✔
2303

2304
                // local variable or parameter ?
2305
                // owner is a Method? (for example in case of an anonymous class)
2306
                CheckResult res = matchAndMapOwnerAsMethod(((bnd != null) ? bnd.getDeclaringMethod() : null), owner, candidateOwner);
11 ✔
2307
                if (res == CheckResult.FAIL) {
3 ✔
2308
                        return false;
2 ✔
2309
                } else if (res == CheckResult.MATCH) {
3 !
2310
                        conditionalMapToKey(bnd, candidate);
×
2311
                        return true;
×
2312
                }
2313

2314
                // check owner
2315
                // <anArray>.length field?
2316
                if (name.equals("length")) {
4 !
2317
                        boolean isArrayLengthField = ((bnd != null) && (bnd.getDeclaringClass() == null)) ||
×
2318
                                                                                 ((bnd == null) && (owner.getName().equals(EntityDictionary.ARRAYS_NAME)));
×
2319
                        if (isArrayLengthField) {
×
2320
                                if (candidateOwner.getName().equals(EntityDictionary.ARRAYS_NAME)) {
×
2321
                                        conditionalMapToKey(bnd, candidate);
×
2322
                                        return true;
×
2323
                                }
2324
                                else {
2325
                                        return false;
×
2326
                                }
2327
                        }
2328
                }
2329

2330
                // check owner
2331
                // "normal" field?
2332
                res = matchAndMapOwnerAsType( ((bnd != null) ? bnd.getDeclaringClass() : null), owner, candidateOwner);
11 ✔
2333
                if (res == CheckResult.MATCH) {
3 !
2334
                        conditionalMapToKey(bnd, candidate);
4 ✔
2335
                        return true;
2 ✔
2336
                }
2337
                return false;
×
2338
        }
2339

2340
        /**
2341
         * Checks whether the existing unmapped Famix Type's parent (or owner) matches the binding's owner.
2342
         * Checks that the candidate has the same name as the JDT bound type, and checks recursively that owners also match.
2343
         * @param bnd -- a JDT binding whose owner we are trying to match to the candidate's owner
2344
         * @param owner -- the owner of the type
2345
         * @param candidate -- a Famix Entity
2346
         * @return whether we found a match (if <b>true</b>, the mapping is recorded)
2347
         */
2348
        private boolean matchAndMapTypeOwner(ITypeBinding bnd, TNamedEntity owner, Type candidate) {
2349
                ContainerEntity candidateOwner = Util.getOwner(candidate);
4 ✔
2350

2351
                // owner is a Method? (for example in case of an anonymous class)
2352
                CheckResult res = matchAndMapOwnerAsMethod(((bnd != null) ? bnd.getDeclaringMethod() : null), owner, candidate);
11 ✔
2353
                if (res == CheckResult.MATCH) {
3 !
2354
                        conditionalMapToKey(bnd, candidate);
×
2355
                        return true;
×
2356
                } else if (res == CheckResult.FAIL) {
3 !
2357
                        return false;
×
2358
                }
2359

2360
                // owner is a class ?
2361
                res = matchAndMapOwnerAsType(((bnd != null) ? bnd.getDeclaringClass() : null), owner, candidateOwner);
11 ✔
2362
                if (res == CheckResult.MATCH) {
3 ✔
2363
                        conditionalMapToKey(bnd, candidate);
4 ✔
2364
                        return true;
2 ✔
2365
                }
2366
                else if (res == CheckResult.FAIL) {
3 ✔
2367
                        return false;
2 ✔
2368
                }
2369

2370
                // owner must be a package
2371
                if (matchAndMapOwnerAsNamespace( ((bnd != null)?bnd.getPackage():null), owner, candidateOwner) == CheckResult.MATCH) {
12 ✔
2372
                        conditionalMapToKey(bnd, candidate);
4 ✔
2373
                        return true;
2 ✔
2374
                }
2375
                return false;
2 ✔
2376
        }
2377

2378
        /**
2379
         * Check whether the owner of candidates is a method macthinf either methBnd or owner
2380
         * @param methBnd
2381
         * @param owner
2382
         * @param candidateOwner
2383
         * @return a {@link CheckResult}
2384
         */
2385
        private  <T extends TNamedEntity> CheckResult matchAndMapOwnerAsMethod(IMethodBinding methBnd, T owner, T candidateOwner) {
2386
                if ((methBnd != null) || (owner instanceof Method)) {
5 !
2387
                        if (!(candidateOwner instanceof Method)) {
3 !
2388
                                return CheckResult.FAIL;
×
2389
                        }
2390

2391
                        ContainerEntity ownerOwner = (owner != null) ? (ContainerEntity) Util.getOwner(owner) : null;
7 !
2392
                        String ownerSig = (owner != null) ? ((Method) owner).getSignature() : null;
7 !
2393
                        Type ownerReturn = (owner != null) ? (Type) ((Method) owner).getDeclaredType() : null;
8 !
2394

2395
                        if (matchAndMapMethod(methBnd, ownerSig, ownerReturn, ownerOwner, (Method) candidateOwner)) {
9 !
2396
                                return CheckResult.MATCH;
×
2397
                        } else {
2398
                                return CheckResult.FAIL;
2 ✔
2399
                        }
2400
                }
2401
                return CheckResult.UNDECIDED;
2 ✔
2402
        }
2403

2404
        /**
2405
         * @param typBnd
2406
         * @param owner
2407
         * @param candidateOwner
2408
         * @return a {@link CheckResult}
2409
         */
2410
        private CheckResult matchAndMapOwnerAsType(ITypeBinding typBnd, TNamedEntity owner, TNamedEntity candidateOwner) {
2411
                if ((typBnd != null) || (owner instanceof Type)) {
5 ✔
2412
                        if (!(candidateOwner instanceof Type)) {
3 ✔
2413
                                return CheckResult.FAIL;
2 ✔
2414
                        }
2415

2416
                        TNamedEntity ownerOwner = (owner != null) ? Util.getOwner(owner) : null;
6 !
2417
                        String ownerName = (owner != null) ? owner.getName() : null;
6 !
2418

2419
                        if (matchAndMapType(typBnd, ownerName, ownerOwner, candidateOwner)) {
7 ✔
2420
                                return CheckResult.MATCH;
2 ✔
2421
                        } else {
2422
                                return CheckResult.FAIL;
2 ✔
2423
                        }
2424
                }
2425
                return CheckResult.UNDECIDED;
2 ✔
2426
        }
2427

2428
        private CheckResult matchAndMapOwnerAsNamespace(IPackageBinding pckgBnd, TNamedEntity owner, ContainerEntity candidateOwner) {
2429
                if ((pckgBnd != null) || (owner instanceof Package)) {
5 !
2430
                        if (!(candidateOwner instanceof Package)) {
3 !
2431
                                return CheckResult.FAIL;
×
2432
                        }
2433

2434
                        Package ownerOwner = (owner != null) ? (Package) Util.getOwner(owner) : null;
7 !
2435
                        String ownerName = (owner != null) ? owner.getName() : null;
6 !
2436

2437
                        if (matchAndMapPackage(pckgBnd, ownerName, ownerOwner, candidateOwner)) {
7 ✔
2438
                                return CheckResult.MATCH;
2 ✔
2439
                        } else {
2440
                                return CheckResult.FAIL;
2 ✔
2441
                        }
2442
                }
2443
                return CheckResult.UNDECIDED;
×
2444
        }
2445

2446
        /**
2447
         * Checks whether the name and the candidate matches the name of the entity (given either by 'bnd' or 'name')<br>
2448
         * 'name' and 'bnd' cannot be null together
2449
         * @param bnd -- binding associated with the entity may be null
2450
         * @param name -- name of the entity may be null
2451
         * @param candidate
2452
         * @return true if names match, false if not
2453
         */
2454
        private CheckResult checkNameMatch(IBinding bnd, String name, TNamedEntity candidate) {
2455
                if ( (bnd != null) && (! bnd.getName().equals(candidate.getName())) ) {
8 ✔
2456
                        return CheckResult.FAIL;
2 ✔
2457
                }
2458
                else if ( (bnd == null) && (name != null) && (! name.equals(candidate.getName())) ) {
9 !
2459
                        return CheckResult.FAIL;
2 ✔
2460
                }
2461
                else {
2462
                        return CheckResult.MATCH;
2 ✔
2463
                }
2464
        }
2465

2466
        /**
2467
         * Check whether key and candidate are already bound together, whether either is bound to something else, or whether none is bound
2468
         * @param key
2469
         * @param candidate
2470
         * @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>
2471
         */
2472
        private CheckResult checkKeyMatch(IBinding key, TNamedEntity candidate) {
2473
                if (key == null) {
2 ✔
2474
                        return CheckResult.UNDECIDED;
2 ✔
2475
                }
2476

2477
                NamedEntity bound = (NamedEntity)getEntityByKey(key);
5 ✔
2478
                if (bound == candidate) {
3 ✔
2479
                        return CheckResult.MATCH;
2 ✔
2480
                }
2481
                else if (bound != null) {
2 ✔
2482
                        return CheckResult.FAIL;
2 ✔
2483
                }
2484
                else if (getEntityKey(candidate) != null) {
4 ✔
2485
                        //we check if the both bindings are linked to the same entity
2486
                        IBinding candidateBinding = getEntityKey(candidate);
4 ✔
2487
                        if(candidateBinding.getKey().equals(key.getKey())) {
6 ✔
2488
                                return CheckResult.MATCH;
2 ✔
2489
                        }
2490
                        
2491
                        //if candidate is a stub, we accept multiple recovery binding
2492
                        //this code will be upgraded with the possibility to add manually code to entities
2493
                        if (candidate instanceof TCanBeStub && ((TCanBeStub) candidate).getIsStub()) {
8 !
2494
                                return CheckResult.UNDECIDED;
2 ✔
2495
                        }
2496
      
2497
                        return CheckResult.FAIL;
2 ✔
2498
                }
2499
                else {
2500
                        return CheckResult.UNDECIDED;
2 ✔
2501
                }
2502
        }
2503

2504
        private void conditionalMapToKey(IBinding bnd, TNamedEntity ent) {
2505
                if (bnd != null) {
2 ✔
2506
                        mapEntityToKey(bnd, ent);
4 ✔
2507
                }
2508
        }
1 ✔
2509

2510
        public Method ensureFamixMethod(IMethodBinding bnd) {
2511
                return ensureFamixMethod(
8 ✔
2512
                                bnd,
2513
                                /*name*/null,
2514
                                /*paramsType*/null,
2515
                                /*returnType*/null,
2516
                                /*owner*/null,
2517
                                (bnd == null) ? UNKNOWN_MODIFIERS : bnd.getModifiers());
6 ✔
2518
        }
2519

2520
        /**
2521
         * Returns a Famix Method associated with the IMethodBinding.
2522
         * The Entity is created if it does not exist.
2523
         * @param name -- the name of the Famix Method (MUST NOT be null, but this is not checked)
2524
         * @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)
2525
         * @param owner -- type defining the method (should not be null, but it will work if it is)
2526
         * @return the Famix Entity found or created. May return null if "bnd" is null or in case of a Famix error
2527
         */
2528
        public Method ensureFamixMethod(IMethodBinding bnd, String name, Collection<String> paramTypes, TType ret, TWithMethods owner, int modifiers) {
2529
                Method fmx;
2530
                String signature;
2531
                boolean delayedRetTyp;
2532

2533
                // --------------- to avoid useless computations if we can
2534
                fmx = (Method)getEntityByKey(bnd);
5 ✔
2535
                if (fmx != null) {
2 ✔
2536
                        return fmx;
2 ✔
2537
                }
2538

2539
                // --------------- name
2540
                if (name == null) {
2 ✔
2541
                        if (bnd == null) {
2 ✔
2542
                                return null;
2 ✔
2543
                        }
2544
                        else {
2545
                                name = bnd.getName();
3 ✔
2546
                        }
2547
                }
2548

2549
                // --------------- signature
2550
                signature = name + "(";
3 ✔
2551
                 if (bnd != null) {
2 ✔
2552
                    signature += signatureParamsFromBinding(bnd);
7 ✔
2553
                }
2554
        else if (paramTypes != null) {
2 !
2555
                        signature += signatureParamsFromStringCollection(paramTypes);
7 ✔
2556
                }
2557
                else {
2558
                        signature += "???";
×
2559
                }
2560
                signature += ")";
3 ✔
2561

2562
                // --------------- return type
2563
                delayedRetTyp = false;
2 ✔
2564
                ITypeBinding retTypBnd = null;
2 ✔
2565
                if (ret == null) {
2 !
2566
                        if (bnd != null) {
2 ✔
2567
                // must create the return type
2568
                // but for method like "<T> T mtd()" where T belongs to mtd and mtd returns T,
2569
                // we need T to create the method and the method to create T ...
2570
                // so we need to test the situation and deal with it
2571
                retTypBnd = bnd.getReturnType();
3 ✔
2572
                if ( (retTypBnd != null) && retTypBnd.isTypeVariable() && (retTypBnd.getDeclaringMethod() == bnd) ) {
9 ✔
2573
                    delayedRetTyp = true;
3 ✔
2574
                }
2575
                else {
2576
                        ret = this.referredType(retTypBnd, fmx);
5 ✔
2577
                }
2578
                        }
2579
                }
2580

2581
                // --------------- owner
2582
                if (owner == null) {
2 ✔
2583
                        if (bnd == null) {
2 ✔
2584
                                owner = ensureFamixClassStubOwner();
4 ✔
2585
                        }
2586
                        else {
2587
                                ITypeBinding classBnd = bnd.getDeclaringClass().getErasure();
4 ✔
2588
                                if (classBnd != null) {
2 !
2589
                                        owner = ensureFamixType(classBnd);
5 ✔
2590
                                }
2591
                                else {
2592
                                        owner = ensureFamixClassStubOwner();
×
2593
                                }
2594
                        }
2595
                }
2596

2597
                // --------------- recover from name ?
2598
                for (Method candidate : this.getEntityByName(Method.class, name)) {
13 ✔
2599
                        if (matchAndMapMethod(bnd, signature, ret, (TNamedEntity) owner, candidate)) {
9 ✔
2600
                                fmx = candidate;
2 ✔
2601
                                break;
1 ✔
2602
                        }
2603
                }
1 ✔
2604

2605
                if (fmx == null) {
2 ✔
2606
                        if(bnd != null && bnd.isGenericMethod() && !isConstructorBinding(bnd)) {
9 ✔
2607
                                fmx = ensureFamixEntity(ParametricMethod.class, bnd, name);
7 ✔
2608
                                for(ITypeBinding param: bnd.getTypeParameters()) {
18 ✔
2609
                                        TypeParameter fmxParam = this.ensureFamixTypeParameter(param, null, fmx);
6 ✔
2610
                                        fmxParam.setGenericEntity((ParametricMethod)fmx);
4 ✔
2611
                                }
2612
                        // Parameterized method binding = when the method is the target of an invocation.
2613
                        } else if (bnd != null && bnd.isParameterizedMethod()) {
5 ✔
2614
                                fmx = this.ensureFamixMethod(bnd.getMethodDeclaration());
6 ✔
2615
                        } else {
2616
                if (bnd != null && isConstructorBinding(bnd)) {
6 ✔
2617
                    fmx = ensureFamixEntity(Initializer.class, bnd, name);
8 ✔
2618
                } else {
2619
                    fmx = ensureFamixEntity(Method.class, bnd, name);
7 ✔
2620
                }
2621
            }
2622

2623
                        fmx.setSignature(signature);
3 ✔
2624
                        ITypeBinding returnTypeBnd = (bnd == null) ? null : bnd.getReturnType();
7 ✔
2625
                        ensureFamixEntityTyping(returnTypeBnd, fmx, ret);
6 ✔
2626
                        fmx.setParentType(owner);
3 ✔
2627
                }
2628

2629
                if (fmx != null) {
2 !
2630
                        setMethodModifiers(fmx, modifiers);
4 ✔
2631
                }
2632

2633
        //If it has the #default keywork, we mark it as default implementation
2634
        if (Modifier.isDefault(modifiers)) {
3 ✔
2635
            fmx.setKind(DEFAULT_IMPLEMENTATION_KIND_MARKER);
3 ✔
2636
        }
2637

2638
        if (delayedRetTyp) {
2 ✔
2639
                        int retTypModifiers = retTypBnd.getModifiers();
3 ✔
2640
                        ITypeBinding returnTypeBnd = bnd.getReturnType();
3 ✔
2641
                        ensureFamixEntityTyping(returnTypeBnd, fmx, this.ensureFamixType(retTypBnd, /*name*/null, /*owner*/fmx, /*ctxt*/(ContainerEntity) owner, retTypModifiers));
13 ✔
2642
                }
2643

2644
                return fmx;
2 ✔
2645
        }
2646

2647

2648
        /**
2649
         * Creates or recovers the initializer method containing the attribute initializations of a type.
2650
         * @param owner Type containing the initializer
2651
         * @param isStatic Modifier of the initializer. A type can have 2 initializers for attribute initialization: 1 static and 1 not.
2652
         * @param isInitializationBlock True if the entity is an initialization block. False for the artificial method containing all field initializations.
2653
         * @return the FamixInitializer
2654
         */
2655
        public Initializer ensureFamixInitializer(TWithMethods owner, Boolean isStatic, Boolean isInitializationBlock) {
2656
                Initializer fmx = null;
2 ✔
2657

2658
                if (owner != null) {
2 !
2659
                        Optional<TMethod> existingInitializer = owner.getMethods().stream()
6 ✔
2660
                                        .filter(meth ->
1 ✔
2661
                                                        ((Method) meth).getIsInitializer() &&
8 ✔
2662
                                                        ((Method) meth).getIsConstructor().equals(false) &&
7 ✔
2663
                                                        ((Method) meth).getIsClassSide().equals(isStatic) &&
6 ✔
2664
                                                        ((Initializer) meth).getIsInitializationBlock().equals(isInitializationBlock))
7 ✔
2665
                                        .findFirst();
2 ✔
2666
                        if (existingInitializer.isPresent()) {
3 ✔
2667
                                fmx = (Initializer) existingInitializer.get();
4 ✔
2668
                        }
2669
                }
2670

2671
                if (fmx == null) {
2 ✔
2672
                        fmx = createFamixEntity(Initializer.class, INIT_BLOCK_NAME);
6 ✔
2673
                        fmx.setSignature(INIT_BLOCK_NAME + "()" );
3 ✔
2674
                        fmx.setVisibility(MODIFIER_PRIVATE);
3 ✔
2675
                        fmx.setParentType(owner);
3 ✔
2676
                        fmx.setIsClassSide(isStatic);
3 ✔
2677
                        fmx.setIsInitializationBlock(isInitializationBlock);
3 ✔
2678
                }
2679

2680
                return fmx;
2 ✔
2681
        }
2682

2683

2684
        public Initializer ensureImplicitConstructor(IMethodBinding binding, TWithMethods owner, String name, Collection<String> parameterTypesNames) {
2685
                Initializer fmx = null;
2 ✔
2686

2687
                if (fmx == null) {
2 !
2688
                        int modifiers = (binding != null) ? binding.getModifiers() : EntityDictionary.UNKNOWN_MODIFIERS;
7 ✔
2689
                        if (binding == null) {
2 ✔
2690
                                // OK! Binding is null, this is the default constructor!!
2691
                                // It has no source code :)
2692
                                fmx = ensureFamixEntity(Initializer.class, null, name);
7 ✔
2693
                                fmx.setParentType(owner);
3 ✔
2694
                                fmx.setSignature(name + "()");                                
5 ✔
2695
                        } else {
2696
                                // But, if we have the binding, that means the constructor exists. Let's just go the normal way
2697
                                fmx = (Initializer) this.ensureFamixMethod(binding, name, parameterTypesNames, /*ret type*/null, owner, modifiers);
10 ✔
2698
                        }
2699
                }
2700

2701
                return fmx;
2 ✔
2702
        }
2703

2704
        /**
2705
         * Creates or recovers a stub Famix Method
2706
         * @param name of the method
2707
         * @return the Famix Method
2708
         */
2709
        public Method ensureFamixStubMethod(String name) {
2710
                return ensureFamixMethod(null, name, /*paramType*/null, /*returnType*/null, ensureFamixClassStubOwner(), /*modifiers*/0);
×
2711
        }
2712

2713
        public void setAttributeModifiers(Attribute fmx, int mod) {
2714
                setCommonModifiers(fmx, mod, null);
5 ✔
2715
                fmx.setIsTransient(Modifier.isTransient(mod));
5 ✔
2716
                fmx.setIsVolatile(Modifier.isVolatile(mod));
5 ✔
2717
        }
1 ✔
2718

2719
        public void setMethodModifiers(Method fmx, int mod) {
2720
                setCommonModifiers(fmx, mod, (TWithTypes) fmx.getParentType());
7 ✔
2721
                fmx.setIsAbstract(Modifier.isAbstract(mod));
5 ✔
2722
                fmx.setIsSynchronized(Modifier.isSynchronized(mod));
5 ✔
2723
        }
1 ✔
2724

2725
        public void setClassModifiers(Class fmx, int mod, TWithTypes owner) {
2726
                setCommonModifiers(fmx, mod, null);
5 ✔
2727
                fmx.setIsAbstract(Modifier.isAbstract(mod));
5 ✔
2728
        }
1 ✔
2729

2730
        public void setInterfaceModifiers(Interface fmx, int mod) {
2731
                setCommonModifiers(fmx, mod, null);
5 ✔
2732
        }
1 ✔
2733

2734
        private void setCommonModifiers(Entity fmx, int mod, TWithTypes owner) {
2735
                setVisibility((THasVisibility)fmx, mod, owner);
6 ✔
2736
                ((TCanBeClassSide)fmx).setIsClassSide(Modifier.isStatic(mod));
6 ✔
2737
                ((TCanBeFinal)fmx).setIsFinal(Modifier.isFinal(mod));
6 ✔
2738
        }
1 ✔
2739

2740
        /**
2741
         * Sets the visibility of a FamixNamedEntity
2742
         *
2743
         * @param fmx -- the FamixNamedEntity
2744
         * @param mod -- a description of the modifiers as understood by org.eclipse.jdt.core.dom.Modifier
2745
         */
2746
        public void setVisibility(THasVisibility fmx, int mod, TWithTypes owner) {
2747
                if (Modifier.isPublic(mod)) {
3 ✔
2748
                        fmx.setVisibility(MODIFIER_PUBLIC);
4 ✔
2749
                } else if (Modifier.isPrivate(mod)) {
3 ✔
2750
                        fmx.setVisibility(MODIFIER_PRIVATE);
4 ✔
2751
                } else if (Modifier.isProtected(mod)) {
3 ✔
2752
                        fmx.setVisibility(MODIFIER_PROTECTED);
4 ✔
2753
                } else {
2754
                        //Default visibility!
2755
                        //If we are in an interface, default visibility is public, otherwise package.
2756
                        if (owner instanceof Interface) {
3 ✔
2757
                                fmx.setVisibility(MODIFIER_PUBLIC);
4 ✔
2758
                        } else {
2759
                                fmx.setVisibility(MODIFIER_PACKAGE);
3 ✔
2760
                        }
2761
                }
2762
        }
1 ✔
2763

2764
        /**
2765
         * Returns a Famix Attribute associated with the IVariableBinding.
2766
         * The Entity is created if it does not exist.<br>
2767
         * @param name -- the name of the FAMIX Attribute (MUST NOT be null, but this is not checked)
2768
         * @param type -- Famix Type of the Attribute (should not be null, but it will work if it is)
2769
         * @param owner -- type defining the Attribute (should not be null, but it will work if it is)
2770
         * @return the Famix Entity found or created. May return null if "bnd" is null or in case of a Famix error
2771
         */
2772
        public Attribute ensureFamixAttribute(IVariableBinding bnd, String name, Type type, TWithAttributes owner) {
2773
                Attribute fmx;
2774

2775
                // --------------- to avoid useless computations if we can
2776
                fmx = (Attribute)getEntityByKey(bnd);
5 ✔
2777
                if (fmx != null) {
2 ✔
2778
                        return fmx;
2 ✔
2779
                }
2780

2781
                // --------------- name
2782
                if (name == null) {
2 !
2783
                        if (bnd == null) {
×
2784
                                return null;
×
2785
                        }
2786
                        else {
2787
                                name = bnd.getName();
×
2788
                        }
2789
                }
2790

2791
                // --------------- owner
2792
                if (owner == null) {
2 ✔
2793
                        if (bnd == null) {
2 !
2794
                                return null;  // what would be the interest of creating an attribute for which we ignore the declaring class?
×
2795
                        }
2796
                        else {
2797
                                if (bnd.getDeclaringClass() != null && bnd.getDeclaringClass().getErasure() != null) {
7 !
2798
                                        // Declaring class is the generic one if the class is parametric.
2799
                                        owner = (TWithAttributes)ensureFamixType(bnd.getDeclaringClass().getErasure());
8 ✔
2800
                                } else {
2801
                                        return null;  // what would be the interest of creating an attribute for which we ignore the declaring class?
2 ✔
2802
                                }
2803
                        }
2804
                }
2805

2806
                // --------------- recover from name ?
2807
                for (Attribute candidate : getEntityByName(Attribute.class, name)) {
13 ✔
2808
                        if (matchAndMapVariable(bnd, name, (TNamedEntity) owner, candidate)) {
8 ✔
2809
                                fmx = candidate;
2 ✔
2810
                                break;
1 ✔
2811
                        }
2812
                }
1 ✔
2813

2814
                if (fmx == null) {
2 ✔
2815
                        fmx = ensureFamixEntity(Attribute.class, bnd, name);
7 ✔
2816
                        fmx.setParentType( owner);
3 ✔
2817
                }
2818

2819
        fmx.setParentType(owner);
3 ✔
2820
        ITypeBinding declaredTypeBinding = (bnd == null) ? null : bnd.getType();
7 ✔
2821
        ensureFamixEntityTyping(declaredTypeBinding, fmx, type);
6 ✔
2822
        if (bnd != null) {
2 ✔
2823
            int mod = bnd.getModifiers();
3 ✔
2824
            setAttributeModifiers(fmx, mod);
4 ✔
2825
        }
2826

2827
        return fmx;
2 ✔
2828
        }
2829

2830
        public Attribute ensureFamixAttribute(IVariableBinding bnd, String name, TWithAttributes owner) {
2831
                return ensureFamixAttribute(bnd, name, /*declared type*/null, owner);
7 ✔
2832
        }
2833

2834
        /**
2835
         * helper method, we know the var exists, ensureFamixAttribute will recover it
2836
         */
2837
        public Attribute getFamixAttribute(IVariableBinding bnd, String name, TWithAttributes owner) {
2838
                return ensureFamixAttribute(bnd, name, /*declared type*/null, owner);
7 ✔
2839
        }
2840

2841
        /**
2842
         * Returns a Famix Parameter associated with the IVariableBinding.
2843
         * The Entity is created if it does not exist.<br>
2844
         * @return the Famix Entity found or created. May return null if "bnd" is null or in case of a Famix error
2845
         */
2846
        public Parameter ensureFamixParameter(IVariableBinding bnd, String name, Type typ, TMethod tMethod) {
2847
                Parameter fmx = null;
2 ✔
2848

2849
                // --------------- to avoid useless computations if we can
2850
                try {
2851
                        fmx = (Parameter)getEntityByKey(bnd);
5 ✔
2852
                }catch(Throwable e) {
×
2853
                        e.printStackTrace();
×
2854
                }
1 ✔
2855
                if (fmx != null) {
2 ✔
2856
                        return fmx;
2 ✔
2857
                }
2858

2859
                // --------------- name
2860
                if (name == null) {
2 !
2861
                        if (bnd == null) {
×
2862
                                return null;
×
2863
                        }
2864
                        else {
2865
                                name = bnd.getName();
×
2866
                        }
2867
                }
2868

2869
                // --------------- owner
2870
                if (tMethod == null) {
2 !
2871
                        if (bnd == null) {
×
2872
                                tMethod = ensureFamixStubMethod("<"+name+"_owner>");
×
2873
                        }
2874
                        else {
2875
                                tMethod = ensureFamixMethod(bnd.getDeclaringMethod());
×
2876
                        }
2877
                }
2878

2879
                // --------------- recover from name ?
2880
                for (Parameter candidate : getEntityByName(Parameter.class, name) ) {
13 ✔
2881
                        if ( matchAndMapVariable(bnd, name, tMethod, candidate) ) {
7 !
2882
                                fmx = candidate;
×
2883
                                break;
×
2884
                        }
2885
                }
1 ✔
2886

2887
                if (fmx == null) {
2 !
2888
                        fmx = ensureFamixEntity(Parameter.class, bnd, name);
7 ✔
2889
                }
2890

2891
                if (fmx != null) {
2 !
2892
                        fmx.setParentBehaviouralEntity(tMethod);
3 ✔
2893
                        ITypeBinding declaredTypeBnd = (bnd == null) ? null : bnd.getType();
5 !
2894
                        ensureFamixEntityTyping(declaredTypeBnd, fmx, typ);
6 ✔
2895
                }
2896

2897
                return fmx;
2 ✔
2898
        }
2899

2900
    /**
2901
         * Returns a Famix LocalVariable associated with the IVariableBinding.
2902
         * The Entity is created if it does not exist.<br>
2903
         * @param name -- the name of the FAMIX LocalVariable
2904
         * @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
2905
         */
2906
        public LocalVariable ensureFamixLocalVariable(IVariableBinding bnd, String name, TWithLocalVariables owner) {
2907
                LocalVariable fmx;
2908

2909
                // --------------- to avoid useless computations if we can
2910
                fmx = (LocalVariable)getEntityByKey(bnd);
5 ✔
2911
                if (fmx != null) {
2 !
2912
                        return fmx;
×
2913
                }
2914

2915
                // --------------- name
2916
                if (name == null) {
2 !
2917
                        if (bnd == null) {
×
2918
                                return null;
×
2919
                        }
2920
                        else {
2921
                                name = bnd.getName();
×
2922
                        }
2923
                }
2924

2925
                // --------------- owner
2926
                if (owner == null) {
2 !
2927
                        if (bnd == null) {
×
2928
                                return null;  // what would be the interest of a local variable for which we ignore the declaring method?
×
2929
                        }
2930
                        else {
2931
                                owner = ensureFamixMethod(bnd.getDeclaringMethod());
×
2932
                        }
2933
                }
2934

2935
                // --------------- recover from name ?
2936
                for (LocalVariable candidate : getEntityByName(LocalVariable.class, name) ) {
13 ✔
2937
                        if ( matchAndMapVariable(bnd, name, (TNamedEntity) owner, candidate) ) {
8 !
2938
                                fmx = candidate;
×
2939
                                break;
×
2940
                        }
2941
                }
1 ✔
2942

2943
                if (fmx == null) {
2 !
2944
                        fmx = ensureFamixEntity(LocalVariable.class, bnd, name);
7 ✔
2945
                        fmx.setParentBehaviouralEntity(owner);
3 ✔
2946
                }
2947

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

2951
        return fmx;
2 ✔
2952
        }
2953

2954
    /**
2955
         * Returns a FAMIX ImplicitVariable with the given <b>name</b> ("self" or "super") and corresponding to the <b>type</b>.
2956
         * If this ImplicitVariable does not exist yet, it is created
2957
         * @param name -- the name of the FAMIX ImplicitVariable (should be Dictionary.SELF_NAME or Dictionary.SUPER_NAME)
2958
         * @param type -- the Famix Type for this ImplicitVariable (should not be null)
2959
         * @param tMethod -- the ContainerEntity where the implicit variable appears (should be a method inside <b>type</b>)
2960
         * @return the FAMIX ImplicitVariable or null in case of a FAMIX error
2961
         */
2962
        public ImplicitVariable ensureFamixImplicitVariable(IBinding key, String name, TType type, TMethod tMethod) {
2963
                ImplicitVariable fmx;
2964
                fmx = ensureFamixEntity(ImplicitVariable.class, key, name);
7 ✔
2965
                fmx.setParentBehaviouralEntity(tMethod);
3 ✔
2966
                fmx.setIsStub(tMethod.getIsStub());
4 ✔
2967
                if (fmx.getTyping() == null) {
3 ✔
2968
                        ensureFamixEntityTyping(null, fmx, type);
7 ✔
2969
                } else {
2970
                        fmx.getTyping().setDeclaredType(type);
4 ✔
2971
                }
2972
                return fmx;
2 ✔
2973
        }
2974

2975
        public ImplicitVariable ensureFamixImplicitVariable(String name, TType tType, TMethod tMethod) {
2976
                IBinding bnd = ImplicitVarBinding.getInstance(tMethod, name);
4 ✔
2977
                return ensureFamixImplicitVariable(bnd, name, tType, tMethod);
7 ✔
2978
        }
2979

2980
        /**
2981
         * Creates and returns a Famix Comment and associates it with an Entity (ex: for Javadocs)
2982
         * @param jCmt -- the content (String) of the comment 
2983
         * @param owner -- the entity that is commented
2984
         * @return the Famix Comment
2985
         */
2986
        public Comment createFamixComment(org.eclipse.jdt.core.dom.Comment jCmt, TWithComments owner) {
2987
                Comment cmt = null;
2 ✔
2988

2989
                if ( (jCmt != null) && (owner != null) ) {
4 !
2990
                        
2991
                        cmt = new Comment();
4 ✔
2992
                        addSourceAnchor(cmt, jCmt);
5 ✔
2993
                        famixRepoAdd(cmt);
3 ✔
2994
                        cmt.setCommentedEntity(owner);
3 ✔
2995
                }
2996

2997
                return cmt;
2 ✔
2998
        }
2999

3000
        /**
3001
         * Creates and returns a Famix Comment and associates it with an Entity
3002
         * @param jCmt -- the content (String) of the comment 
3003
         * @param owner -- the entity that is commented
3004
         * @param content -- the text of the comment
3005
         * @return the Famix Comment
3006
         */
3007
        public Comment createFamixComment(org.eclipse.jdt.core.dom.Comment jCmt, TWithComments owner, String content) {
3008
                Comment cmt = null;
2 ✔
3009

3010
                if ( (jCmt != null) && (owner != null) ) {
4 !
3011
                        cmt = new Comment();
4 ✔
3012
                        cmt.setContent(content );
3 ✔
3013
                        famixRepoAdd(cmt);
3 ✔
3014
                        cmt.setCommentedEntity(owner);
3 ✔
3015
                }
3016

3017
                return cmt;
2 ✔
3018
        }
3019

3020
        /**
3021
         * Adds location information to a Famix Entity.
3022
         * 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.
3023
         * This method also creates some basic links between the entity and others (e.g. declaring container, return type, ...)
3024
         * @param fmx -- Famix Entity to add the anchor to
3025
         * @param node -- JDT ASTNode, where the information is extracted
3026
         * @return the Famix SourceAnchor added to fmx. May be null in case of incorrect parameter ('fmx' or 'ast' == null)
3027
         */
3028
        public SourceAnchor addSourceAnchor(TSourceEntity fmx, ASTNode node) {
3029
                IndexedFileAnchor fa;
3030

3031
                fa = createIndexedFileAnchor(node);
4 ✔
3032
                if ((fmx != null) && (fa != null)) {
4 !
3033
                        fmx.setSourceAnchor(fa);
3 ✔
3034
                        famixRepoAdd(fa);
3 ✔
3035
                }
3036

3037
                return fa;
2 ✔
3038
        }
3039

3040
        /**
3041
         * Special case of  {@linkplain #addSourceAnchor(TSourceEntity, ASTNode)} to add location information to a Famix Method.
3042
         */
3043
        public SourceAnchor addSourceAnchor(Method fmx, MethodDeclaration node) {
3044
                IndexedFileAnchor fa;
3045

3046
                fa = createIndexedFileAnchor(node);
4 ✔
3047
                if ((fmx != null) && (fa != null)) {
4 !
3048

3049
                        // may change the positions
3050
                        List<ASTNode> methodDeclarationModifiers = new ArrayList<>();
4 ✔
3051
                        methodDeclarationModifiers.addAll(node.modifiers());
5 ✔
3052
                        if (node.getName() != null) {
3 !
3053
                                methodDeclarationModifiers.add(node.getName());
5 ✔
3054
                        }
3055
                        if (node.getReturnType2() != null) {
3 ✔
3056
                                methodDeclarationModifiers.add(node.getReturnType2());
5 ✔
3057
                        }
3058
                        int beg = (methodDeclarationModifiers.stream().mapToInt(el -> el.getStartPosition()).min().getAsInt()) + 1;
12 ✔
3059
                        int end = node.getStartPosition() + node.getLength();
6 ✔
3060

3061
                        fa.setStartPos(beg);
4 ✔
3062
                        fa.setEndPos(end);
4 ✔
3063

3064
                        fmx.setSourceAnchor(fa);
3 ✔
3065
                        famixRepoAdd(fa);
3 ✔
3066
                }
3067

3068
                return fa;
2 ✔
3069
        }
3070

3071
        /**
3072
         * Gets the file name holding <code>node</code> and its start and end positions in the file.
3073
         * Information returned in the form of an IndexedFileAnchor
3074
         */
3075
        protected IndexedFileAnchor createIndexedFileAnchor(ASTNode node) {
3076
                IndexedFileAnchor fa;
3077
                
3078
                if (node == null) {
2 !
3079
                        return null;
×
3080
                }
3081

3082
                // position in source file
3083
                int beg = node.getStartPosition() + 1; // Java starts at 0, Moose at 1
5 ✔
3084
                int end = beg + node.getLength() - 1;
7 ✔
3085

3086
                // find source Compilation Unit
3087
                // there is a special case for the JDT Comment Nodes
3088
                if (node instanceof org.eclipse.jdt.core.dom.Comment) {
3 ✔
3089
                        node = ((org.eclipse.jdt.core.dom.Comment) node).getAlternateRoot();
5 ✔
3090
                } else {
3091
                        node = node.getRoot();
3 ✔
3092
                }
3093

3094
                fa = new IndexedFileAnchor();
4 ✔
3095
                fa.setStartPos(beg);
4 ✔
3096
                fa.setEndPos(end);
4 ✔
3097

3098
                fa.setFileName((String) node.getProperty(SOURCE_FILENAME_PROPERTY));
6 ✔
3099

3100
                return fa;
2 ✔
3101
        }
3102

3103
        /**
3104
         * Creates or recovers the Famix Class for "Object".
3105
         * Because "Object" is the root of the inheritance tree, it needs to be treated differently.
3106
         *
3107
         * @return a Famix class for "Object"
3108
         */
3109
        public Class ensureFamixClassObject() {
3110
        // 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...
3111
        Collection<Class> objects = getEntityByName(Class.class, "Object");
5 ✔
3112

3113
        for (Class entity : objects) {
10 ✔
3114
                //We need to cast because the type container is a FamixTWithType but all implementors of this should be named in Java...
3115
                if ("java.lang".equals(((TNamedEntity) entity.getTypeContainer()).getName())) {
7 ✔
3116
                    return entity;
2 ✔
3117
                }
3118
        }
1 ✔
3119

3120
        Class fmx = createFamixEntity(Class.class, "Object");
6 ✔
3121
        fmx.setTypeContainer(ensureFamixPackageJavaLang(null));
5 ✔
3122
                return fmx;
2 ✔
3123
        }
3124

3125
        /***
3126
         * We treat array types as parametrized types Array<T>.
3127
         * This keeps the meta-model simple: a single way to model different concepts.
3128
         * @return
3129
         */
3130
        public ParametricClass ensureParametricArrayClass() {
3131
                
3132
                Collection<ParametricClass> arrayClasses = getEntityByName(ParametricClass.class, "Array");
5 ✔
3133
                
3134
                for (ParametricClass entity : arrayClasses) {
10 ✔
3135
            //We need to cast because the type container is a FamixTWithType but all implementors of this should be named in Java...
3136
            if ("java.lang".equals(((TNamedEntity) entity.getTypeContainer()).getName())) {
7 !
3137
                return entity;
2 ✔
3138
            }
3139
                }
×
3140
                
3141
                //Create the parametric class
3142
                ParametricClass arrayClass = createFamixEntity(ParametricClass.class, "Array"); 
6 ✔
3143

3144
                //And now add the type parameter
3145
                TypeParameter fmxParam = this.ensureFamixTypeParameter(null, "T", arrayClass);
6 ✔
3146
                fmxParam.setGenericEntity(arrayClass);
3 ✔
3147
                fmxParam.setIsStub(true);
4 ✔
3148
                
3149
                //And the class is in java.lang
3150
                arrayClass.setTypeContainer(ensureFamixPackageJavaLang(null));
5 ✔
3151
                
3152
                return arrayClass;
2 ✔
3153
        }
3154

3155
        /**
3156
         * Ensures the Java meta-class: <pre>{@code java.lang.Class<>}</pre>
3157
         */
3158
        public Class ensureFamixMetaClass(ITypeBinding bnd) {
3159
                Package javaLang = ensureFamixPackageJavaLang((bnd == null) ? null : bnd.getPackage());
7 !
3160
                ParametricClass fmx = (ParametricClass) this.ensureFamixClass(null, METACLASS_NAME, javaLang, /*isGeneric*/true, Modifier.PUBLIC & Modifier.FINAL);
9 ✔
3161

3162
                if (fmx != null) {
2 !
3163
                        fmx.addTypeParameters(ensureFamixTypeParameter(null, "T", fmx));
7 ✔
3164
                }
3165

3166
                if ((fmx != null) && (fmx.getSuperInheritances() == null)) {
5 !
3167
                        ensureFamixInheritance(ensureFamixClassObject(), fmx, null, null);
×
3168
                }
3169

3170
                return fmx;
2 ✔
3171
        }
3172

3173
        public Class getFamixMetaClass(ITypeBinding bnd) {
3174
                Package javaLang = ensureFamixPackageJavaLang((bnd == null) ? null : bnd.getPackage());
7 !
3175
                return this.ensureFamixClass(null, METACLASS_NAME, javaLang, /*isGeneric*/true, UNKNOWN_MODIFIERS);
8 ✔
3176
        }
3177

3178
        /**
3179
         * Creates or recovers the Famix Class for all arrays (<pre>{@code <some-type> []}</pre>)
3180
         * In java arrays or objects of special classes (i.e. "I[" for an array of int).
3181
         * JDT does not create a binding for these classes, so we create a stub one here.
3182
         *
3183
         * @return a Famix class
3184
         */
3185
        public Class ensureFamixClassArray() {
3186
                Class fmx = ensureFamixUniqEntity(Class.class, null, ARRAYS_NAME);
×
3187
                if (fmx != null) {
×
3188
                        ensureFamixInheritance(ensureFamixClassObject(), fmx, /*prev*/null, null);
×
3189
                        fmx.setTypeContainer(ensureFamixPackageDefault());
×
3190

3191
                        // may be not needed anymore now that we use modifiers
3192
                        /*fmx.setIsAbstract(Boolean.FALSE);
3193
                        fmx.setIsFinal(Boolean.FALSE);
3194
                        fmx.setIsInterface(Boolean.FALSE); 
3195
                        fmx.setIsPrivate(Boolean.FALSE);
3196
                        fmx.setIsProtected(Boolean.FALSE);*/
3197
                        fmx.setVisibility(MODIFIER_PUBLIC);
×
3198
                }
3199

3200
                return fmx;
×
3201
        }
3202

3203
        public String removeLastPartOfPackageName(String qualifiedName) {
3204
                String ret;
3205
                int last = qualifiedName.lastIndexOf('.');
4 ✔
3206
                if (last > 0) {
2 ✔
3207
                        // recursively creating the parent
3208
                        ret = qualifiedName.substring(0, last);
6 ✔
3209
                }
3210
                else {
3211
                        ret = "";
2 ✔
3212
                }
3213

3214
                return ret;
2 ✔
3215
        }
3216

3217
        /** Generates the list of parameters for a method signature
3218
         * @return a string
3219
         */
3220
        protected String signatureParamsFromBinding(IMethodBinding bnd) {
3221
                boolean first = true;
2 ✔
3222
                String sig = "";
2 ✔
3223

3224
                for (ITypeBinding parBnd : bnd.getParameterTypes()) {
17 ✔
3225
                        if (first) {
2 ✔
3226
                                sig = parBnd.getName();
3 ✔
3227
                                first = false;
3 ✔
3228
                        }
3229
                        else {
3230
                                sig += "," + parBnd.getName();
5 ✔
3231
                        }
3232
                }
3233
                return sig;
2 ✔
3234
        }
3235

3236
        private String signatureParamsFromStringCollection(Collection<String> paramTypes) {
3237
                boolean first = true;
2 ✔
3238
                String sig = "";
2 ✔
3239

3240
                for (String t : paramTypes) {
10 ✔
3241
                        if (first) {
2 ✔
3242
                                sig = t;
2 ✔
3243
                                first = false;
3 ✔
3244
                        }
3245
                        else {
3246
                                sig += "," + t;
4 ✔
3247
                        }
3248
                }
1 ✔
3249
                return sig;
2 ✔
3250
        }
3251

3252
        public String findTypeName(org.eclipse.jdt.core.dom.Type t) {
3253
                if (t == null) {
×
3254
                        return null;
×
3255
                }
3256

3257
                if (t.isPrimitiveType()) {
×
3258
                        return t.toString();
×
3259
                } else if (t.isSimpleType()) {
×
3260
                        String fullName = ((SimpleType) t).getName().getFullyQualifiedName();
×
3261
                        int i = fullName.lastIndexOf('.');
×
3262
                        if (i > 0) {
×
3263
                                return fullName.substring(i+1);
×
3264
                        }
3265
                        else {
3266
                                return fullName;
×
3267
                        }
3268
                } else if (t.isQualifiedType()) {
×
3269
                        return ((QualifiedType) t).getName().getIdentifier();
×
3270
                } else if (t.isArrayType()) {
×
3271
                        return findTypeName(((ArrayType) t).getElementType());
×
3272
                } else if (t.isParameterizedType()) {
×
3273
                        return findTypeName(((org.eclipse.jdt.core.dom.ParameterizedType) t).getType());
×
3274
                } else { // it is a WildCardType
3275
                        if (((org.eclipse.jdt.core.dom.WildcardType) t).isUpperBound()) {
×
3276
                                return findTypeName(((org.eclipse.jdt.core.dom.WildcardType) t).getBound());
×
3277
                        } else {
3278
                                return EntityDictionary.OBJECT_NAME;
×
3279
                        }
3280
                }
3281
        }
3282

3283
        /**
3284
         * Ensures the proper creation of a FamixType for JDT typ in the given context.
3285
         * Useful for parameterizedTypes, or classInstance.
3286
         *
3287
         * @param isClass we are sure that the type is actually a class
3288
         * @return a famix type or null
3289
         */
3290
        public <T extends TWithTypes & TNamedEntity> TType referredType(org.eclipse.jdt.core.dom.Type typ, T ctxt, boolean isClass) {
3291
                return this.referredType(typ, ctxt, isClass, /*isExcep*/false);
7 ✔
3292
        }
3293
        
3294
        /**
3295
         * Ensures the proper creation of a FamixType for JDT type in the given context.
3296
         * Useful for parameterizedTypes, or classInstance.
3297
         *
3298
         * @param isClass we are sure that the type is actually a class
3299
         * @return a famix type or null
3300
         */
3301
        public <T extends TWithTypes & TNamedEntity> TType referredType(org.eclipse.jdt.core.dom.Type typ, T ctxt, boolean isClass, boolean isException) {
3302
                if (typ == null) {
2 !
3303
                        return null;
×
3304
                } else if (typ.resolveBinding() != null) {
3 !
3305
                        return this.referredType(typ.resolveBinding(), ctxt);
7 ✔
3306
                }
3307
                // from here, we assume the owner is the context
3308
                else if (isClass && !isException) {
×
3309
                        return this.ensureFamixClass(null, findTypeName(typ), /*owner*/ctxt, /*isGeneric*/false,
×
3310
                                        EntityDictionary.UNKNOWN_MODIFIERS);
3311
                } else if (isException) {
×
3312
                        // return ensure FamixException
3313
                        return this.ensureFamixException(null, findTypeName(typ), (ContainerEntity) /*owner*/ctxt, /*isGeneric*/false,
×
3314
                                        EntityDictionary.UNKNOWN_MODIFIERS);
3315
                } else {
3316
                        while (typ.isArrayType()) {
×
3317
                                typ = ((ArrayType) typ).getElementType();
×
3318
                        }
3319

3320
                        if (typ.isPrimitiveType()) {
×
3321
                                return this.ensureFamixPrimitiveType(null, findTypeName(typ));
×
3322
                        } else {
3323
                                return this.ensureFamixType(null, findTypeName(typ), /*owner*/ctxt, /*container*/ctxt,
×
3324
                                                EntityDictionary.UNKNOWN_MODIFIERS);
3325
                        }
3326
                }
3327
        }
3328
        
3329
        public TType referredType(ITypeBinding bnd, TNamedEntity ctxt) {
3330
                return this.referredType(bnd, ctxt, 0);
6 ✔
3331
        }
3332
        
3333
        public TType referredType(ITypeBinding bnd, TNamedEntity ctxt, int extraDimensions) {
3334
                TType fmxTyp = null;
2 ✔
3335

3336
                if (bnd == null) {
2 ✔
3337
                        return null;
2 ✔
3338
                }
3339

3340
                String name;
3341
                //Three cases here:
3342
                // - the type binding knows it's an array
3343
                // - the binding is not declared as array, but it has dimensions > 0
3344
                // - the dimension is not in the type but in the variable declaration (thus extra)
3345
                if (bnd.isArray() || bnd.getDimensions() + extraDimensions > 0) {
8 ✔
3346
                        // We treat array types as parametrized types Array<T>.
3347
                        // This keeps the meta-model simple: a single way to model different concepts.
3348
                        return this.ensureParametricArrayClass();
3 ✔
3349
                }
3350
                name = bnd.getName();
3 ✔
3351

3352
                if ( bnd.isParameterizedType() ) {
3 ✔
3353
                        
3354
                        // remove type parameters from the name even for parameterized interfaces
3355
                        int i = name.indexOf('<');
4 ✔
3356
                        if (i > 0) {
2 ✔
3357
                                name = name.substring(0, i);
5 ✔
3358
                        }
3359

3360
                        ITypeBinding parameterizableBnd = bnd.getErasure();
3 ✔
3361
                        int modifiers = (parameterizableBnd != null) ? parameterizableBnd.getModifiers() : EntityDictionary.UNKNOWN_MODIFIERS;
6 !
3362
                        
3363
                        if(parameterizableBnd != null && parameterizableBnd.isInterface()) {
5 !
3364
                                fmxTyp = (ParametricInterface) this.ensureFamixInterface(parameterizableBnd, name, /*owner*/null, /*isGeneric*/true, modifiers);
10 ✔
3365
                        } else {
3366
                                fmxTyp = (ParametricClass) this.ensureFamixClass(parameterizableBnd, name, /*owner*/null, /*isGeneric*/true, modifiers);
9 ✔
3367
                        }
3368
                } else if ( (name != null) && name.equals("var") ) {
7 !
3369
                        fmxTyp = this.ensureFamixUniqEntity(org.moosetechnology.model.famix.famixjavaentities.Type.class, /*binding*/null, EntityDictionary.IMPLICIT_VAR_TYPE_NAME);        
×
3370
                } else {
3371
                        fmxTyp = this.ensureFamixType(bnd, name, /*owner*/null, (TWithTypes) ctxt, bnd.getModifiers());
10 ✔
3372
                }
3373

3374
                return fmxTyp;
2 ✔
3375
        }
3376

3377
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc