aboutsummaryrefslogtreecommitdiff
path: root/javaparser-symbol-solver-core/src
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2018-03-04 08:21:35 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2018-03-04 08:21:35 +0000
commitb4c1397d5df9370f6358d4f8e9efd27e0f67dec1 (patch)
tree6789ec288d344cf5fd5d057bcf1efc9545b1af28 /javaparser-symbol-solver-core/src
parent92d661a1d239131fb5c1e019a8f2ac7584d2d3f6 (diff)
parent1afe9e0652b9b53edade5aa276162abe27b32a67 (diff)
downloadjavaparser-pie-platform-release.tar.gz
Snap for 4632767 from 1afe9e0652b9b53edade5aa276162abe27b32a67 to pi-releaseandroid-wear-9.0.0_r9android-wear-9.0.0_r8android-wear-9.0.0_r7android-wear-9.0.0_r6android-wear-9.0.0_r5android-wear-9.0.0_r4android-wear-9.0.0_r34android-wear-9.0.0_r33android-wear-9.0.0_r32android-wear-9.0.0_r31android-wear-9.0.0_r30android-wear-9.0.0_r3android-wear-9.0.0_r29android-wear-9.0.0_r28android-wear-9.0.0_r27android-wear-9.0.0_r26android-wear-9.0.0_r25android-wear-9.0.0_r24android-wear-9.0.0_r23android-wear-9.0.0_r22android-wear-9.0.0_r21android-wear-9.0.0_r20android-wear-9.0.0_r2android-wear-9.0.0_r19android-wear-9.0.0_r18android-wear-9.0.0_r17android-wear-9.0.0_r16android-wear-9.0.0_r15android-wear-9.0.0_r14android-wear-9.0.0_r13android-wear-9.0.0_r12android-wear-9.0.0_r11android-wear-9.0.0_r10android-wear-9.0.0_r1android-vts-9.0_r9android-vts-9.0_r8android-vts-9.0_r7android-vts-9.0_r6android-vts-9.0_r5android-vts-9.0_r4android-vts-9.0_r19android-vts-9.0_r18android-vts-9.0_r17android-vts-9.0_r16android-vts-9.0_r15android-vts-9.0_r14android-vts-9.0_r13android-vts-9.0_r12android-vts-9.0_r11android-vts-9.0_r10android-security-9.0.0_r76android-security-9.0.0_r75android-security-9.0.0_r74android-security-9.0.0_r73android-security-9.0.0_r72android-security-9.0.0_r71android-security-9.0.0_r70android-security-9.0.0_r69android-security-9.0.0_r68android-security-9.0.0_r67android-security-9.0.0_r66android-security-9.0.0_r65android-security-9.0.0_r64android-security-9.0.0_r63android-security-9.0.0_r62android-cts-9.0_r9android-cts-9.0_r8android-cts-9.0_r7android-cts-9.0_r6android-cts-9.0_r5android-cts-9.0_r4android-cts-9.0_r3android-cts-9.0_r20android-cts-9.0_r2android-cts-9.0_r19android-cts-9.0_r18android-cts-9.0_r17android-cts-9.0_r16android-cts-9.0_r15android-cts-9.0_r14android-cts-9.0_r13android-cts-9.0_r12android-cts-9.0_r11android-cts-9.0_r10android-cts-9.0_r1android-9.0.0_r9android-9.0.0_r8android-9.0.0_r7android-9.0.0_r61android-9.0.0_r60android-9.0.0_r6android-9.0.0_r59android-9.0.0_r58android-9.0.0_r57android-9.0.0_r56android-9.0.0_r55android-9.0.0_r54android-9.0.0_r53android-9.0.0_r52android-9.0.0_r51android-9.0.0_r50android-9.0.0_r5android-9.0.0_r49android-9.0.0_r48android-9.0.0_r3android-9.0.0_r2android-9.0.0_r18android-9.0.0_r17android-9.0.0_r10android-9.0.0_r1security-pi-releasepie-vts-releasepie-security-releasepie-s2-releasepie-release-2pie-releasepie-r2-s2-releasepie-r2-s1-releasepie-r2-releasepie-platform-releasepie-gsipie-cuttlefish-testingpie-cts-release
Change-Id: Ibe65883e94ed5a7272dff3f100393987a1cf3da2
Diffstat (limited to 'javaparser-symbol-solver-core/src')
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/JavaSymbolSolver.java184
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/SourceFileInfoExtractor.java218
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/resolution/Context.java98
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/resolution/ContextHelper.java51
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/declarations/common/MethodDeclarationCommonLogic.java94
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparser/Navigator.java219
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparser/package-info.java20
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/DefaultVisitorAdapter.java475
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFacade.java562
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFactory.java139
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/LambdaArgumentTypePlaceholder.java72
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/TypeExtractor.java517
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/UnsolvedSymbolException.java71
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractJavaParserContext.java193
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractMethodLikeDeclarationContext.java101
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AnonymousClassDeclarationContext.java186
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/CatchClauseContext.java54
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ClassOrInterfaceDeclarationContext.java109
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/CompilationUnitContext.java291
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ConstructorContext.java35
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ContextHelper.java74
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/EnumDeclarationContext.java82
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/FieldAccessContext.java104
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ForStatementContext.java70
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ForechStatementContext.java61
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/JavaParserTypeDeclarationAdapter.java133
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/LambdaExprContext.java193
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/MethodCallExprContext.java433
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/MethodContext.java35
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/StatementContext.java200
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/SwitchEntryContext.java80
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/TryWithResourceContext.java88
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/DefaultConstructorDeclaration.java82
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/Helper.java85
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserAnnotationDeclaration.java103
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserAnnotationMemberDeclaration.java40
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserAnonymousClassDeclaration.java205
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserClassDeclaration.java397
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserConstructorDeclaration.java103
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserEnumConstantDeclaration.java59
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserEnumDeclaration.java350
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserFieldDeclaration.java121
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserInterfaceDeclaration.java335
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserMethodDeclaration.java167
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserParameterDeclaration.java100
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserSymbolDeclaration.java171
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeAdapter.java140
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeParameter.java223
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeVariableDeclaration.java187
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/AbstractSymbolDeclarator.java35
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/FieldSymbolDeclarator.java46
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/NoSymbolDeclarator.java40
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/ParameterSymbolDeclarator.java42
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/VariableSymbolDeclarator.java52
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/package-info.java20
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistClassDeclaration.java395
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistConstructorDeclaration.java146
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistEnumConstantDeclaration.java54
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistEnumDeclaration.java275
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistFactory.java85
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistFieldDeclaration.java94
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistInterfaceDeclaration.java292
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistMethodDeclaration.java198
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistParameterDeclaration.java80
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistTypeDeclarationAdapter.java93
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistTypeParameter.java114
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistUtils.java209
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/package-info.java20
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/model/typesystem/LazyType.java120
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/model/typesystem/ReferenceTypeImpl.java211
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/MyObjectProvider.java36
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionClassAdapter.java190
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionClassDeclaration.java344
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionConstructorDeclaration.java95
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionEnumConstantDeclaration.java31
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionEnumDeclaration.java201
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionFactory.java121
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionFieldDeclaration.java97
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionInterfaceDeclaration.java307
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionMethodDeclaration.java151
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionMethodResolutionLogic.java144
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionParameterDeclaration.java75
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionTypeParameter.java130
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/comparators/ClassComparator.java25
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/comparators/MethodComparator.java25
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/comparators/ParameterComparator.java19
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/package-info.java20
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/ConstructorResolutionLogic.java226
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/MethodResolutionLogic.java696
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/SymbolDeclarator.java30
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/SymbolSolver.java171
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/Bound.java106
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/BoundSet.java804
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ConstraintFormula.java125
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ConstraintFormulaSet.java53
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ControlFlowLogic.java290
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ExpressionHelper.java137
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/InferenceVariable.java93
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/InferenceVariableSubstitution.java36
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/Instantiation.java51
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/InstantiationSet.java68
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/MethodType.java55
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ProperLowerBound.java50
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ProperUpperBound.java50
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/Substitution.java45
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/TypeHelper.java405
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/TypeInference.java719
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/TypeInferenceCache.java56
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/CapturesBound.java69
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/FalseBound.java42
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/SameAsBound.java96
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/SubtypeOfBound.java98
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/ThrowsBound.java62
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/ExpressionCompatibleWithType.java335
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/LambdaThrowsCompatibleWithType.java67
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/MethodReferenceThrowsCompatibleWithType.java62
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeCompatibleWithType.java122
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeContainedByType.java95
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeSameAsType.java140
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeSubtypeOfType.java139
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/AarTypeSolver.java60
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/CombinedTypeSolver.java76
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/JarTypeSolver.java167
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/JavaParserTypeSolver.java178
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/MemoryTypeSolver.java86
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/ReflectionTypeSolver.java94
-rw-r--r--javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/utils/SymbolSolverQuickSetup.java152
127 files changed, 19148 insertions, 0 deletions
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/JavaSymbolSolver.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/JavaSymbolSolver.java
new file mode 100644
index 000000000..77550c736
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/JavaSymbolSolver.java
@@ -0,0 +1,184 @@
+package com.github.javaparser.symbolsolver;
+
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.body.*;
+import com.github.javaparser.ast.expr.Expression;
+import com.github.javaparser.ast.expr.MethodCallExpr;
+import com.github.javaparser.ast.expr.NameExpr;
+import com.github.javaparser.ast.expr.ThisExpr;
+import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt;
+import com.github.javaparser.ast.type.Type;
+import com.github.javaparser.resolution.SymbolResolver;
+import com.github.javaparser.resolution.UnsolvedSymbolException;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.*;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+/**
+ * This implementation of the SymbolResolver wraps the functionalities of the library to make them easily usable
+ * from JavaParser nodes.
+ *
+ * An instance of this class should be created once and then injected in all the CompilationUnit for which we
+ * want to enable symbol resolution. To do so the method inject can be used.
+ *
+ * @author Federico Tomassetti
+ */
+public class JavaSymbolSolver implements SymbolResolver {
+
+ private TypeSolver typeSolver;
+
+ public JavaSymbolSolver(TypeSolver typeSolver) {
+ this.typeSolver = typeSolver;
+ }
+
+ /**
+ * Register this SymbolResolver into a CompilationUnit, so that symbol resolution becomes available to
+ * all nodes part of the CompilationUnit.
+ */
+ public void inject(CompilationUnit destination) {
+ destination.setData(Node.SYMBOL_RESOLVER_KEY, this);
+ }
+
+ @Override
+ public <T> T resolveDeclaration(Node node, Class<T> resultClass) {
+ if (node instanceof MethodDeclaration) {
+ return resultClass.cast(new JavaParserMethodDeclaration((MethodDeclaration)node, typeSolver));
+ }
+ if (node instanceof ClassOrInterfaceDeclaration) {
+ ResolvedReferenceTypeDeclaration resolved = JavaParserFactory.toTypeDeclaration(node, typeSolver);
+ if (resultClass.isInstance(resolved)) {
+ return resultClass.cast(resolved);
+ }
+ }
+ if (node instanceof EnumDeclaration) {
+ ResolvedReferenceTypeDeclaration resolved = JavaParserFactory.toTypeDeclaration(node, typeSolver);
+ if (resultClass.isInstance(resolved)) {
+ return resultClass.cast(resolved);
+ }
+ }
+ if (node instanceof EnumConstantDeclaration) {
+ ResolvedEnumDeclaration enumDeclaration = node.findParent(EnumDeclaration.class).get().resolve().asEnum();
+ ResolvedEnumConstantDeclaration resolved = enumDeclaration.getEnumConstants().stream().filter(c -> ((JavaParserEnumConstantDeclaration)c).getWrappedNode() == node).findFirst().get();
+ if (resultClass.isInstance(resolved)) {
+ return resultClass.cast(resolved);
+ }
+ }
+ if (node instanceof ConstructorDeclaration) {
+ ConstructorDeclaration constructorDeclaration = (ConstructorDeclaration)node;
+ ClassOrInterfaceDeclaration classOrInterfaceDeclaration = (ClassOrInterfaceDeclaration)node.getParentNode().get();
+ ResolvedClassDeclaration resolvedClass = resolveDeclaration(classOrInterfaceDeclaration, ResolvedClassDeclaration.class).asClass();
+ ResolvedConstructorDeclaration resolved = resolvedClass.getConstructors().stream().filter(c -> ((JavaParserConstructorDeclaration)c).getWrappedNode() == constructorDeclaration).findFirst().get();
+ if (resultClass.isInstance(resolved)) {
+ return resultClass.cast(resolved);
+ }
+ }
+ if (node instanceof AnnotationDeclaration) {
+ ResolvedReferenceTypeDeclaration resolved = JavaParserFactory.toTypeDeclaration(node, typeSolver);
+ if (resultClass.isInstance(resolved)) {
+ return resultClass.cast(resolved);
+ }
+ }
+ if (node instanceof AnnotationMemberDeclaration) {
+ ResolvedAnnotationDeclaration annotationDeclaration = node.findParent(AnnotationDeclaration.class).get().resolve();
+ ResolvedAnnotationMemberDeclaration resolved = annotationDeclaration.getAnnotationMembers().stream().filter(c -> ((JavaParserAnnotationMemberDeclaration)c).getWrappedNode() == node).findFirst().get();
+ if (resultClass.isInstance(resolved)) {
+ return resultClass.cast(resolved);
+ }
+ }
+ if (node instanceof FieldDeclaration) {
+ FieldDeclaration fieldDeclaration = (FieldDeclaration)node;
+ if (fieldDeclaration.getVariables().size() != 1) {
+ throw new RuntimeException("Cannot resolve a Field Declaration including multiple variable declarators. Resolve the single variable declarators");
+ }
+ ResolvedFieldDeclaration resolved = new JavaParserFieldDeclaration(fieldDeclaration.getVariable(0), typeSolver);
+ if (resultClass.isInstance(resolved)) {
+ return resultClass.cast(resolved);
+ }
+ }
+ if (node instanceof VariableDeclarator) {
+ ResolvedFieldDeclaration resolved = new JavaParserFieldDeclaration((VariableDeclarator)node, typeSolver);
+ if (resultClass.isInstance(resolved)) {
+ return resultClass.cast(resolved);
+ }
+ }
+ if (node instanceof MethodCallExpr) {
+ SymbolReference<ResolvedMethodDeclaration> result = JavaParserFacade.get(typeSolver).solve((MethodCallExpr)node);
+ if (result.isSolved()) {
+ if (resultClass.isInstance(result.getCorrespondingDeclaration())) {
+ return resultClass.cast(result.getCorrespondingDeclaration());
+ }
+ } else {
+ throw new UnsolvedSymbolException("We are unable to find the method declaration corresponding to " + node);
+ }
+ }
+ if (node instanceof NameExpr) {
+ SymbolReference<? extends ResolvedValueDeclaration> result = JavaParserFacade.get(typeSolver).solve((NameExpr) node);
+ if (result.isSolved()) {
+ if (resultClass.isInstance(result.getCorrespondingDeclaration())) {
+ return resultClass.cast(result.getCorrespondingDeclaration());
+ }
+ } else {
+ throw new UnsolvedSymbolException("We are unable to find the value declaration corresponding to " + node);
+ }
+ }
+ if (node instanceof ThisExpr) {
+ SymbolReference<ResolvedTypeDeclaration> result = JavaParserFacade.get(typeSolver).solve((ThisExpr) node);
+ if (result.isSolved()) {
+ if (resultClass.isInstance(result.getCorrespondingDeclaration())) {
+ return resultClass.cast(result.getCorrespondingDeclaration());
+ }
+ } else {
+ throw new UnsolvedSymbolException("We are unable to find the type declaration corresponding to " + node);
+ }
+ }
+ if (node instanceof ExplicitConstructorInvocationStmt) {
+ SymbolReference<ResolvedConstructorDeclaration> result = JavaParserFacade.get(typeSolver).solve((ExplicitConstructorInvocationStmt) node);
+ if (result.isSolved()) {
+ if (resultClass.isInstance(result.getCorrespondingDeclaration())) {
+ return resultClass.cast(result.getCorrespondingDeclaration());
+ }
+ } else {
+ throw new UnsolvedSymbolException("We are unable to find the constructor declaration corresponding to " + node);
+ }
+ }
+ if (node instanceof Parameter) {
+ if (ResolvedParameterDeclaration.class.equals(resultClass)) {
+ Parameter parameter = (Parameter)node;
+ CallableDeclaration callableDeclaration = node.findParent(CallableDeclaration.class).get();
+ ResolvedMethodLikeDeclaration resolvedMethodLikeDeclaration;
+ if (callableDeclaration.isConstructorDeclaration()) {
+ resolvedMethodLikeDeclaration = callableDeclaration.asConstructorDeclaration().resolve();
+ } else {
+ resolvedMethodLikeDeclaration = callableDeclaration.asMethodDeclaration().resolve();
+ }
+ for (int i=0;i<resolvedMethodLikeDeclaration.getNumberOfParams();i++) {
+ if (resolvedMethodLikeDeclaration.getParam(i).getName().equals(parameter.getNameAsString())) {
+ return resultClass.cast(resolvedMethodLikeDeclaration.getParam(i));
+ }
+ }
+ }
+ }
+ throw new UnsupportedOperationException("Unable to find the declaration of type " + resultClass.getSimpleName()
+ + " from " + node.getClass().getSimpleName());
+ }
+
+ @Override
+ public <T> T toResolvedType(Type javaparserType, Class<T> resultClass) {
+ ResolvedType resolvedType = JavaParserFacade.get(typeSolver).convertToUsage(javaparserType, javaparserType);
+ if (resultClass.isInstance(resolvedType)) {
+ return resultClass.cast(resolvedType);
+ }
+ throw new UnsupportedOperationException("Unable to get the resolved type of class "
+ + resultClass.getSimpleName() + " from " + javaparserType);
+ }
+
+ @Override
+ public ResolvedType calculateType(Expression expression) {
+ return JavaParserFacade.get(typeSolver).getType(expression);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/SourceFileInfoExtractor.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/SourceFileInfoExtractor.java
new file mode 100644
index 000000000..8d2c07b05
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/SourceFileInfoExtractor.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver;
+
+import com.github.javaparser.JavaParser;
+import com.github.javaparser.ParseException;
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.PackageDeclaration;
+import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
+import com.github.javaparser.ast.body.MethodDeclaration;
+import com.github.javaparser.ast.body.VariableDeclarator;
+import com.github.javaparser.ast.expr.Expression;
+import com.github.javaparser.ast.expr.MethodCallExpr;
+import com.github.javaparser.ast.ImportDeclaration;
+import com.github.javaparser.ast.stmt.Statement;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.LinkedList;
+import java.util.List;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentNode;
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode;
+
+/**
+ * It prints information extracted from a source file. It is mainly intended as an example usage of JavaSymbolSolver.
+ *
+ * @author Federico Tomassetti
+ */
+public class SourceFileInfoExtractor {
+
+ private TypeSolver typeSolver;
+
+ private int ok = 0;
+ private int ko = 0;
+ private int unsupported = 0;
+ private boolean printFileName = true;
+ private PrintStream out = System.out;
+ private PrintStream err = System.err;
+
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ private boolean verbose = false;
+
+ public void setPrintFileName(boolean printFileName) {
+ this.printFileName = printFileName;
+ }
+
+ public void clear() {
+ ok = 0;
+ ko = 0;
+ unsupported = 0;
+ }
+
+ public void setOut(PrintStream out) {
+ this.out = out;
+ }
+
+ public void setErr(PrintStream err) {
+ this.err = err;
+ }
+
+ public int getOk() {
+ return ok;
+
+ }
+
+ public int getUnsupported() {
+ return unsupported;
+ }
+
+ public int getKo() {
+ return ko;
+ }
+
+ private void solveTypeDecl(ClassOrInterfaceDeclaration node) {
+ ResolvedTypeDeclaration typeDeclaration = JavaParserFacade.get(typeSolver).getTypeDeclaration(node);
+ if (typeDeclaration.isClass()) {
+ out.println("\n[ Class " + typeDeclaration.getQualifiedName() + " ]");
+ for (ResolvedReferenceType sc : typeDeclaration.asClass().getAllSuperClasses()) {
+ out.println(" superclass: " + sc.getQualifiedName());
+ }
+ for (ResolvedReferenceType sc : typeDeclaration.asClass().getAllInterfaces()) {
+ out.println(" interface: " + sc.getQualifiedName());
+ }
+ }
+ }
+
+ private void solve(Node node) {
+ if (node instanceof ClassOrInterfaceDeclaration) {
+ solveTypeDecl((ClassOrInterfaceDeclaration) node);
+ } else if (node instanceof Expression) {
+ if ((requireParentNode(node) instanceof ImportDeclaration) || (requireParentNode(node) instanceof Expression)
+ || (requireParentNode(node) instanceof MethodDeclaration)
+ || (requireParentNode(node) instanceof PackageDeclaration)) {
+ // skip
+ } else if ((requireParentNode(node) instanceof Statement) || (requireParentNode(node) instanceof VariableDeclarator)) {
+ try {
+ ResolvedType ref = JavaParserFacade.get(typeSolver).getType(node);
+ out.println(" Line " + node.getRange().get().begin.line + ") " + node + " ==> " + ref.describe());
+ ok++;
+ } catch (UnsupportedOperationException upe) {
+ unsupported++;
+ err.println(upe.getMessage());
+ throw upe;
+ } catch (RuntimeException re) {
+ ko++;
+ err.println(re.getMessage());
+ throw re;
+ }
+ }
+ }
+ }
+
+ private void solveMethodCalls(Node node) {
+ if (node instanceof MethodCallExpr) {
+ out.println(" Line " + node.getBegin().get().line + ") " + node + " ==> " + toString((MethodCallExpr) node));
+ }
+ for (Node child : node.getChildNodes()) {
+ solveMethodCalls(child);
+ }
+ }
+
+ private String toString(MethodCallExpr node) {
+ try {
+ return toString(JavaParserFacade.get(typeSolver).solve(node));
+ } catch (Exception e) {
+ if (verbose) {
+ System.err.println("Error resolving call at L" + node.getBegin().get().line + ": " + node);
+ e.printStackTrace();
+ }
+ return "ERROR";
+ }
+ }
+
+ private String toString(SymbolReference<ResolvedMethodDeclaration> methodDeclarationSymbolReference) {
+ if (methodDeclarationSymbolReference.isSolved()) {
+ return methodDeclarationSymbolReference.getCorrespondingDeclaration().getQualifiedSignature();
+ } else {
+ return "UNSOLVED";
+ }
+ }
+
+ private List<Node> collectAllNodes(Node node) {
+ List<Node> nodes = new LinkedList<>();
+ collectAllNodes(node, nodes);
+ nodes.sort((n1, n2) -> n1.getBegin().get().compareTo(n2.getBegin().get()));
+ return nodes;
+ }
+
+ private void collectAllNodes(Node node, List<Node> nodes) {
+ nodes.add(node);
+ node.getChildNodes().forEach(c -> collectAllNodes(c, nodes));
+ }
+
+ public void solve(File file) throws IOException, ParseException {
+ if (file.isDirectory()) {
+ for (File f : file.listFiles()) {
+ solve(f);
+ }
+ } else {
+ if (file.getName().endsWith(".java")) {
+ if (printFileName) {
+ out.println("- parsing " + file.getAbsolutePath());
+ }
+ CompilationUnit cu = JavaParser.parse(file);
+ List<Node> nodes = collectAllNodes(cu);
+ nodes.forEach(n -> solve(n));
+ }
+ }
+ }
+
+ public void solveMethodCalls(File file) throws IOException, ParseException {
+ if (file.isDirectory()) {
+ for (File f : file.listFiles()) {
+ solveMethodCalls(f);
+ }
+ } else {
+ if (file.getName().endsWith(".java")) {
+ if (printFileName) {
+ out.println("- parsing " + file.getAbsolutePath());
+ }
+ CompilationUnit cu = JavaParser.parse(file);
+ solveMethodCalls(cu);
+ }
+ }
+ }
+
+ public void setTypeSolver(TypeSolver typeSolver) {
+ this.typeSolver = typeSolver;
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/resolution/Context.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/resolution/Context.java
new file mode 100644
index 000000000..93b754b06
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/resolution/Context.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.core.resolution;
+
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.resolution.Value;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Context is very similar to scope.
+ * In the context we look for solving symbols.
+ *
+ * @author Federico Tomassetti
+ */
+public interface Context {
+
+ Context getParent();
+
+ /* Type resolution */
+
+ default Optional<ResolvedType> solveGenericType(String name, TypeSolver typeSolver) {
+ return Optional.empty();
+ }
+
+ default SymbolReference<ResolvedTypeDeclaration> solveType(String name, TypeSolver typeSolver) {
+ Context parent = getParent();
+ if (parent == null) {
+ return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class);
+ } else {
+ return parent.solveType(name, typeSolver);
+ }
+ }
+
+ /* Symbol resolution */
+
+ SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver);
+
+ default Optional<Value> solveSymbolAsValue(String name, TypeSolver typeSolver) {
+ SymbolReference<? extends ResolvedValueDeclaration> ref = solveSymbol(name, typeSolver);
+ if (ref.isSolved()) {
+ Value value = Value.from(ref.getCorrespondingDeclaration());
+ return Optional.of(value);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ /* Constructor resolution */
+
+ /**
+ * We find the method declaration which is the best match for the given name and list of typeParametersValues.
+ */
+ default SymbolReference<ResolvedConstructorDeclaration> solveConstructor(List<ResolvedType> argumentsTypes, TypeSolver typeSolver) {
+ throw new IllegalArgumentException("Constructor resolution is available only on Class Context");
+ }
+
+ /* Methods resolution */
+
+ /**
+ * We find the method declaration which is the best match for the given name and list of typeParametersValues.
+ */
+ SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> argumentsTypes, boolean staticOnly, TypeSolver typeSolver);
+
+ /**
+ * Similar to solveMethod but we return a MethodUsage. A MethodUsage corresponds to a MethodDeclaration plus the
+ * resolved type variables.
+ */
+ default Optional<MethodUsage> solveMethodAsUsage(String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver) {
+ SymbolReference<ResolvedMethodDeclaration> methodSolved = solveMethod(name, argumentsTypes, false, typeSolver);
+ if (methodSolved.isSolved()) {
+ ResolvedMethodDeclaration methodDeclaration = methodSolved.getCorrespondingDeclaration();
+ MethodUsage methodUsage = ContextHelper.resolveTypeVariables(this, methodDeclaration, argumentsTypes);//methodDeclaration.resolveTypeVariables(this, argumentsTypes);
+ return Optional.of(methodUsage);
+ } else {
+ return Optional.empty();
+ }
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/resolution/ContextHelper.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/resolution/ContextHelper.java
new file mode 100644
index 000000000..f3140521d
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/resolution/ContextHelper.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.core.resolution;
+
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserEnumDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserMethodDeclaration;
+import com.github.javaparser.symbolsolver.javassistmodel.JavassistMethodDeclaration;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionMethodDeclaration;
+
+import java.util.List;
+
+/**
+ * @author Federico Tomassetti
+ */
+class ContextHelper {
+
+ private ContextHelper() {
+ // prevent instantiation
+ }
+
+ static MethodUsage resolveTypeVariables(Context context, ResolvedMethodDeclaration methodDeclaration, List<ResolvedType> parameterTypes) {
+ if (methodDeclaration instanceof JavaParserMethodDeclaration) {
+ return ((JavaParserMethodDeclaration) methodDeclaration).resolveTypeVariables(context, parameterTypes);
+ } else if (methodDeclaration instanceof JavassistMethodDeclaration) {
+ return ((JavassistMethodDeclaration) methodDeclaration).resolveTypeVariables(context, parameterTypes);
+ } else if (methodDeclaration instanceof JavaParserEnumDeclaration.ValuesMethod) {
+ return ((JavaParserEnumDeclaration.ValuesMethod) methodDeclaration).resolveTypeVariables(context, parameterTypes);
+ } else if (methodDeclaration instanceof ReflectionMethodDeclaration) {
+ return ((ReflectionMethodDeclaration) methodDeclaration).resolveTypeVariables(context, parameterTypes);
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/declarations/common/MethodDeclarationCommonLogic.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/declarations/common/MethodDeclarationCommonLogic.java
new file mode 100644
index 000000000..5d0063576
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/declarations/common/MethodDeclarationCommonLogic.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.declarations.common;
+
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.resolution.types.ResolvedTypeVariable;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.logic.InferenceContext;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.reflectionmodel.MyObjectProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class MethodDeclarationCommonLogic {
+
+ private ResolvedMethodDeclaration methodDeclaration;
+ private TypeSolver typeSolver;
+
+ public MethodDeclarationCommonLogic(ResolvedMethodDeclaration methodDeclaration, TypeSolver typeSolver) {
+ this.methodDeclaration = methodDeclaration;
+ this.typeSolver = typeSolver;
+ }
+
+ public MethodUsage resolveTypeVariables(Context context, List<ResolvedType> parameterTypes) {
+ ResolvedType returnType = replaceTypeParams(methodDeclaration.getReturnType(), typeSolver, context);
+ List<ResolvedType> params = new ArrayList<>();
+ for (int i = 0; i < methodDeclaration.getNumberOfParams(); i++) {
+ ResolvedType replaced = replaceTypeParams(methodDeclaration.getParam(i).getType(), typeSolver, context);
+ params.add(replaced);
+ }
+
+ // We now look at the type parameter for the method which we can derive from the parameter types
+ // and then we replace them in the return type
+ // Map<TypeParameterDeclaration, Type> determinedTypeParameters = new HashMap<>();
+ InferenceContext inferenceContext = new InferenceContext(MyObjectProvider.INSTANCE);
+ for (int i = 0; i < methodDeclaration.getNumberOfParams() - (methodDeclaration.hasVariadicParameter() ? 1 : 0); i++) {
+ ResolvedType formalParamType = methodDeclaration.getParam(i).getType();
+ ResolvedType actualParamType = parameterTypes.get(i);
+ inferenceContext.addPair(formalParamType, actualParamType);
+ }
+
+ returnType = inferenceContext.resolve(inferenceContext.addSingle(returnType));
+
+ return new MethodUsage(methodDeclaration, params, returnType);
+ }
+
+ private ResolvedType replaceTypeParams(ResolvedType type, TypeSolver typeSolver, Context context) {
+ if (type.isTypeVariable()) {
+ ResolvedTypeParameterDeclaration typeParameter = type.asTypeParameter();
+ if (typeParameter.declaredOnType()) {
+ Optional<ResolvedType> typeParam = typeParamByName(typeParameter.getName(), typeSolver, context);
+ if (typeParam.isPresent()) {
+ type = typeParam.get();
+ }
+ }
+ }
+
+ if (type.isReferenceType()) {
+ type.asReferenceType().transformTypeParameters(tp -> replaceTypeParams(tp, typeSolver, context));
+ }
+
+ return type;
+ }
+
+ protected Optional<ResolvedType> typeParamByName(String name, TypeSolver typeSolver, Context context) {
+ return methodDeclaration.getTypeParameters().stream().filter(tp -> tp.getName().equals(name)).map(tp -> toType(tp)).findFirst();
+ }
+
+ protected ResolvedType toType(ResolvedTypeParameterDeclaration typeParameterDeclaration) {
+ return new ResolvedTypeVariable(typeParameterDeclaration);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparser/Navigator.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparser/Navigator.java
new file mode 100644
index 000000000..dc3ea62e8
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparser/Navigator.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparser;
+
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.body.*;
+import com.github.javaparser.ast.expr.MethodCallExpr;
+import com.github.javaparser.ast.expr.NameExpr;
+import com.github.javaparser.ast.expr.SimpleName;
+import com.github.javaparser.ast.stmt.ReturnStmt;
+import com.github.javaparser.ast.stmt.SwitchStmt;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * This class can be used to easily retrieve nodes from a JavaParser AST.
+ *
+ * @author Federico Tomassetti
+ */
+public final class Navigator {
+
+ private Navigator() {
+ // prevent instantiation
+ }
+
+ /**
+ * @deprecated use Node.getParentNode
+ */
+ @Deprecated
+ public static Node getParentNode(Node node) {
+ return node.getParentNode().orElse(null);
+ }
+
+ public static Node requireParentNode(Node node) {
+ return node.getParentNode().orElseThrow(() -> new IllegalStateException("Parent not found, the node does not appear to be inserted in a correct AST"));
+ }
+
+ public static Optional<TypeDeclaration<?>> findType(CompilationUnit cu, String qualifiedName) {
+ if (cu.getTypes().isEmpty()) {
+ return Optional.empty();
+ }
+
+ final String typeName = getOuterTypeName(qualifiedName);
+ Optional<TypeDeclaration<?>> type = cu.getTypes().stream().filter((t) -> t.getName().getId().equals(typeName)).findFirst();
+
+ final String innerTypeName = getInnerTypeName(qualifiedName);
+ if (type.isPresent() && !innerTypeName.isEmpty()) {
+ return findType(type.get(), innerTypeName);
+ }
+ return type;
+ }
+
+ public static Optional<TypeDeclaration<?>> findType(TypeDeclaration<?> td, String qualifiedName) {
+ final String typeName = getOuterTypeName(qualifiedName);
+
+ Optional<TypeDeclaration<?>> type = Optional.empty();
+ for (Node n : td.getMembers()) {
+ if (n instanceof TypeDeclaration && ((TypeDeclaration<?>) n).getName().getId().equals(typeName)) {
+ type = Optional.of((TypeDeclaration<?>) n);
+ break;
+ }
+ }
+ final String innerTypeName = getInnerTypeName(qualifiedName);
+ if (type.isPresent() && !innerTypeName.isEmpty()) {
+ return findType(type.get(), innerTypeName);
+ }
+ return type;
+ }
+
+ public static ClassOrInterfaceDeclaration demandClass(CompilationUnit cu, String qualifiedName) {
+ ClassOrInterfaceDeclaration cd = demandClassOrInterface(cu, qualifiedName);
+ if (cd.isInterface()) {
+ throw new IllegalStateException("Type is not a class");
+ }
+ return cd;
+ }
+
+ public static EnumDeclaration demandEnum(CompilationUnit cu, String qualifiedName) {
+ Optional<TypeDeclaration<?>> res = findType(cu, qualifiedName);
+ if (!res.isPresent()) {
+ throw new IllegalStateException("No type found");
+ }
+ if (!(res.get() instanceof EnumDeclaration)) {
+ throw new IllegalStateException("Type is not an enum");
+ }
+ return (EnumDeclaration) res.get();
+ }
+
+ public static MethodDeclaration demandMethod(TypeDeclaration<?> cd, String name) {
+ MethodDeclaration found = null;
+ for (BodyDeclaration<?> bd : cd.getMembers()) {
+ if (bd instanceof MethodDeclaration) {
+ MethodDeclaration md = (MethodDeclaration) bd;
+ if (md.getNameAsString().equals(name)) {
+ if (found != null) {
+ throw new IllegalStateException("Ambiguous getName");
+ }
+ found = md;
+ }
+ }
+ }
+ if (found == null) {
+ throw new IllegalStateException("No method called " + name);
+ }
+ return found;
+ }
+
+ public static VariableDeclarator demandField(ClassOrInterfaceDeclaration cd, String name) {
+ for (BodyDeclaration<?> bd : cd.getMembers()) {
+ if (bd instanceof FieldDeclaration) {
+ FieldDeclaration fd = (FieldDeclaration) bd;
+ for (VariableDeclarator vd : fd.getVariables()) {
+ if (vd.getName().getId().equals(name)) {
+ return vd;
+ }
+ }
+ }
+ }
+ throw new IllegalStateException("No field with given name");
+ }
+
+ public static Optional<NameExpr> findNameExpression(Node node, String name) {
+ return node.findFirst(NameExpr.class, n -> n.getNameAsString().equals(name));
+ }
+
+ public static Optional<SimpleName> findSimpleName(Node node, String name) {
+ return node.findFirst(SimpleName.class, n -> n.asString().equals(name));
+ }
+
+
+ public static Optional<MethodCallExpr> findMethodCall(Node node, String methodName) {
+ return node.findFirst(MethodCallExpr.class, n -> n.getNameAsString().equals(methodName));
+ }
+
+ public static Optional<VariableDeclarator> demandVariableDeclaration(Node node, String name) {
+ return node.findFirst(VariableDeclarator.class, n -> n.getNameAsString().equals(name));
+ }
+
+ public static ClassOrInterfaceDeclaration demandClassOrInterface(CompilationUnit compilationUnit, String qualifiedName) {
+ return findType(compilationUnit, qualifiedName)
+ .map(res -> res.toClassOrInterfaceDeclaration().orElseThrow(() -> new IllegalStateException("Type is not a class or an interface, it is " + res.getClass().getCanonicalName())))
+ .orElseThrow(() -> new IllegalStateException("No type named '" + qualifiedName + "'found"));
+ }
+
+ // TODO should be demand or requireSwitch
+ public static SwitchStmt findSwitch(Node node) {
+ return findSwitchHelper(node).orElseThrow(IllegalArgumentException::new);
+ }
+
+ public static <N extends Node> N findNodeOfGivenClass(Node node, Class<N> clazz) {
+ return node.findFirst(clazz).orElseThrow(IllegalArgumentException::new);
+ }
+
+ /**
+ * @deprecated use Node.findAll instead
+ */
+ @Deprecated
+ public static <N extends Node> List<N> findAllNodesOfGivenClass(Node node, Class<N> clazz) {
+ return node.findAll(clazz);
+ }
+
+ // TODO should be demand or require...
+ public static ReturnStmt findReturnStmt(MethodDeclaration method) {
+ return findNodeOfGivenClass(method, ReturnStmt.class);
+ }
+
+ /**
+ * @deprecated use Node.findParent instead
+ */
+ @Deprecated
+ public static <N extends Node> Optional<N> findAncestor(Node node, Class<N> clazz) {
+ return node.findParent(clazz);
+ }
+
+ ///
+ /// Private methods
+ ///
+
+ private static String getOuterTypeName(String qualifiedName) {
+ return qualifiedName.split("\\.", 2)[0];
+ }
+
+ private static String getInnerTypeName(String qualifiedName) {
+ if (qualifiedName.contains(".")) {
+ return qualifiedName.split("\\.", 2)[1];
+ }
+ return "";
+ }
+
+ private static Optional<SwitchStmt> findSwitchHelper(Node node) {
+ // TODO can be replaced by findFirst with the correct algorithm.
+ if (node instanceof SwitchStmt) {
+ return Optional.of((SwitchStmt) node);
+ }
+ for (Node child : node.getChildNodes()) {
+ Optional<SwitchStmt> resChild = findSwitchHelper(child);
+ if (resChild.isPresent()) {
+ return resChild;
+ }
+ }
+ return Optional.empty();
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparser/package-info.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparser/package-info.java
new file mode 100644
index 000000000..16ef20239
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparser/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains utility to use JavaParser.
+ */
+package com.github.javaparser.symbolsolver.javaparser; \ No newline at end of file
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/DefaultVisitorAdapter.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/DefaultVisitorAdapter.java
new file mode 100644
index 000000000..499ef3bd0
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/DefaultVisitorAdapter.java
@@ -0,0 +1,475 @@
+package com.github.javaparser.symbolsolver.javaparsermodel;
+
+import com.github.javaparser.ast.*;
+import com.github.javaparser.ast.body.*;
+import com.github.javaparser.ast.comments.BlockComment;
+import com.github.javaparser.ast.comments.JavadocComment;
+import com.github.javaparser.ast.comments.LineComment;
+import com.github.javaparser.ast.expr.*;
+import com.github.javaparser.ast.modules.*;
+import com.github.javaparser.ast.stmt.*;
+import com.github.javaparser.ast.type.*;
+import com.github.javaparser.ast.visitor.GenericVisitor;
+import com.github.javaparser.resolution.types.ResolvedType;
+
+public class DefaultVisitorAdapter implements GenericVisitor<ResolvedType, Boolean> {
+ @Override
+ public ResolvedType visit(CompilationUnit node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(PackageDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(TypeParameter node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(LineComment node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(BlockComment node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ClassOrInterfaceDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(EnumDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(EnumConstantDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(AnnotationDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(AnnotationMemberDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(FieldDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(VariableDeclarator node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ConstructorDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(MethodDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(Parameter node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(InitializerDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(JavadocComment node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ClassOrInterfaceType node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(PrimitiveType node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ArrayType node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ArrayCreationLevel node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(IntersectionType node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(UnionType node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(VoidType node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(WildcardType node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(UnknownType node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ArrayAccessExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ArrayCreationExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ArrayInitializerExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(AssignExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(BinaryExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(CastExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ClassExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ConditionalExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(EnclosedExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(FieldAccessExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(InstanceOfExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(StringLiteralExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(IntegerLiteralExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(LongLiteralExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(CharLiteralExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(DoubleLiteralExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(BooleanLiteralExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(NullLiteralExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(MethodCallExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(NameExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ObjectCreationExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ThisExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(SuperExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(UnaryExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(VariableDeclarationExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(MarkerAnnotationExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(SingleMemberAnnotationExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(NormalAnnotationExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(MemberValuePair node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ExplicitConstructorInvocationStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(LocalClassDeclarationStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(AssertStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(BlockStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(LabeledStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(EmptyStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ExpressionStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(SwitchStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(SwitchEntryStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(BreakStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ReturnStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(IfStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(WhileStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ContinueStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(DoStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ForeachStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ForStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ThrowStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(SynchronizedStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(TryStmt node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(CatchClause node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(LambdaExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(MethodReferenceExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(TypeExpr node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(NodeList node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(Name node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(SimpleName node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ImportDeclaration node, Boolean aBoolean) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ModuleDeclaration node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ModuleRequiresStmt node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ModuleExportsStmt node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ModuleProvidesStmt node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ModuleUsesStmt node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ModuleOpensStmt node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(UnparsableStmt node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(ReceiverParameter node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(VarType node, Boolean arg) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFacade.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFacade.java
new file mode 100644
index 000000000..f73dd986c
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFacade.java
@@ -0,0 +1,562 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel;
+
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.NodeList;
+import com.github.javaparser.ast.body.*;
+import com.github.javaparser.ast.body.EnumDeclaration;
+import com.github.javaparser.ast.expr.*;
+import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt;
+import com.github.javaparser.ast.type.*;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.UnsolvedSymbolException;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.*;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.contexts.FieldAccessContext;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.*;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.*;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionClassDeclaration;
+import com.github.javaparser.symbolsolver.resolution.ConstructorResolutionLogic;
+import com.github.javaparser.symbolsolver.resolution.SymbolSolver;
+
+import java.util.*;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode;
+
+/**
+ * Class to be used by final users to solve symbols for JavaParser ASTs.
+ *
+ * @author Federico Tomassetti
+ */
+public class JavaParserFacade {
+
+ private static Logger logger = Logger.getLogger(JavaParserFacade.class.getCanonicalName());
+
+ static {
+ logger.setLevel(Level.INFO);
+ ConsoleHandler consoleHandler = new ConsoleHandler();
+ consoleHandler.setLevel(Level.INFO);
+ logger.addHandler(consoleHandler);
+ }
+
+ private static Map<TypeSolver, JavaParserFacade> instances = new WeakHashMap<>();
+ private TypeSolver typeSolver;
+ private SymbolSolver symbolSolver;
+ private Map<Node, ResolvedType> cacheWithLambdasSolved = new IdentityHashMap<>();
+ private Map<Node, ResolvedType> cacheWithoutLambdasSolved = new IdentityHashMap<>();
+ private TypeExtractor typeExtractor;
+
+ private JavaParserFacade(TypeSolver typeSolver) {
+ this.typeSolver = typeSolver.getRoot();
+ this.symbolSolver = new SymbolSolver(typeSolver);
+ this.typeExtractor = new TypeExtractor(typeSolver, this);
+ }
+
+ public TypeSolver getTypeSolver() {
+ return typeSolver;
+ }
+
+ public SymbolSolver getSymbolSolver() {
+ return symbolSolver;
+ }
+
+ public static JavaParserFacade get(TypeSolver typeSolver) {
+ return instances.computeIfAbsent(typeSolver, JavaParserFacade::new);
+ }
+
+ /**
+ * This method is used to clear internal caches for the sake of releasing memory.
+ */
+ public static void clearInstances() {
+ instances.clear();
+ }
+
+ protected static ResolvedType solveGenericTypes(ResolvedType type, Context context, TypeSolver typeSolver) {
+ if (type.isTypeVariable()) {
+ return context.solveGenericType(type.describe(), typeSolver).orElse(type);
+ }
+ if (type.isWildcard()) {
+ if (type.asWildcard().isExtends() || type.asWildcard().isSuper()) {
+ ResolvedWildcard wildcardUsage = type.asWildcard();
+ ResolvedType boundResolved = solveGenericTypes(wildcardUsage.getBoundedType(), context, typeSolver);
+ if (wildcardUsage.isExtends()) {
+ return ResolvedWildcard.extendsBound(boundResolved);
+ } else {
+ return ResolvedWildcard.superBound(boundResolved);
+ }
+ }
+ }
+ return type;
+ }
+
+ public SymbolReference<? extends ResolvedValueDeclaration> solve(NameExpr nameExpr) {
+ return symbolSolver.solveSymbol(nameExpr.getName().getId(), nameExpr);
+ }
+
+ public SymbolReference<? extends ResolvedValueDeclaration> solve(SimpleName nameExpr) {
+ return symbolSolver.solveSymbol(nameExpr.getId(), nameExpr);
+ }
+
+ public SymbolReference<? extends ResolvedValueDeclaration> solve(Expression expr) {
+ return expr.toNameExpr().map(this::solve).orElseThrow(() -> new IllegalArgumentException(expr.getClass().getCanonicalName()));
+ }
+
+ public SymbolReference<ResolvedMethodDeclaration> solve(MethodCallExpr methodCallExpr) {
+ return solve(methodCallExpr, true);
+ }
+
+ public SymbolReference<ResolvedConstructorDeclaration> solve(ObjectCreationExpr objectCreationExpr) {
+ return solve(objectCreationExpr, true);
+ }
+
+ public SymbolReference<ResolvedConstructorDeclaration> solve(ExplicitConstructorInvocationStmt explicitConstructorInvocationStmt) {
+ return solve(explicitConstructorInvocationStmt, true);
+ }
+
+ public SymbolReference<ResolvedConstructorDeclaration> solve(ExplicitConstructorInvocationStmt explicitConstructorInvocationStmt, boolean solveLambdas) {
+ List<ResolvedType> argumentTypes = new LinkedList<>();
+ List<LambdaArgumentTypePlaceholder> placeholders = new LinkedList<>();
+
+ solveArguments(explicitConstructorInvocationStmt, explicitConstructorInvocationStmt.getArguments(), solveLambdas, argumentTypes, placeholders);
+
+ Optional<ClassOrInterfaceDeclaration> optAncestor = explicitConstructorInvocationStmt.getAncestorOfType(ClassOrInterfaceDeclaration.class);
+ if (!optAncestor.isPresent()) {
+ return SymbolReference.unsolved(ResolvedConstructorDeclaration.class);
+ }
+ ClassOrInterfaceDeclaration classNode = optAncestor.get();
+ ResolvedTypeDeclaration typeDecl = null;
+ if (!explicitConstructorInvocationStmt.isThis()) {
+ ResolvedType classDecl = JavaParserFacade.get(typeSolver).convert(classNode.getExtendedTypes(0), classNode);
+ if (classDecl.isReferenceType()) {
+ typeDecl = classDecl.asReferenceType().getTypeDeclaration();
+ }
+ } else {
+ SymbolReference<ResolvedTypeDeclaration> sr = JavaParserFactory.getContext(classNode, typeSolver).solveType(classNode.getNameAsString(), typeSolver);
+ if (sr.isSolved()) {
+ typeDecl = sr.getCorrespondingDeclaration();
+ }
+ }
+ if (typeDecl == null) {
+ return SymbolReference.unsolved(ResolvedConstructorDeclaration.class);
+ }
+ SymbolReference<ResolvedConstructorDeclaration> res = ConstructorResolutionLogic.findMostApplicable(((ResolvedClassDeclaration) typeDecl).getConstructors(), argumentTypes, typeSolver);
+ for (LambdaArgumentTypePlaceholder placeholder : placeholders) {
+ placeholder.setMethod(res);
+ }
+ return res;
+ }
+
+ public SymbolReference<ResolvedTypeDeclaration> solve(ThisExpr node) {
+ // If 'this' is prefixed by a class eg. MyClass.this
+ if (node.getClassExpr().isPresent()) {
+ // Get the class name
+ String className = node.getClassExpr().get().toString();
+ // Attempt to resolve using a typeSolver
+ SymbolReference<ResolvedReferenceTypeDeclaration> clazz = typeSolver.tryToSolveType(className);
+ if (clazz.isSolved()) {
+ return SymbolReference.solved(clazz.getCorrespondingDeclaration());
+ }
+ // Attempt to resolve locally in Compilation unit
+ Optional<CompilationUnit> cu = node.getAncestorOfType(CompilationUnit.class);
+ if (cu.isPresent()) {
+ Optional<ClassOrInterfaceDeclaration> classByName = cu.get().getClassByName(className);
+ if (classByName.isPresent()) {
+ return SymbolReference.solved(getTypeDeclaration(classByName.get()));
+ }
+ }
+ }
+ return SymbolReference.solved(getTypeDeclaration(findContainingTypeDeclOrObjectCreationExpr(node)));
+ }
+
+ /**
+ * Given a constructor call find out to which constructor declaration it corresponds.
+ */
+ public SymbolReference<ResolvedConstructorDeclaration> solve(ObjectCreationExpr objectCreationExpr, boolean solveLambdas) {
+ List<ResolvedType> argumentTypes = new LinkedList<>();
+ List<LambdaArgumentTypePlaceholder> placeholders = new LinkedList<>();
+
+ solveArguments(objectCreationExpr, objectCreationExpr.getArguments(), solveLambdas, argumentTypes, placeholders);
+
+ ResolvedType classDecl = JavaParserFacade.get(typeSolver).convert(objectCreationExpr.getType(), objectCreationExpr);
+ if (!classDecl.isReferenceType()) {
+ return SymbolReference.unsolved(ResolvedConstructorDeclaration.class);
+ }
+ SymbolReference<ResolvedConstructorDeclaration> res = ConstructorResolutionLogic.findMostApplicable(((ResolvedClassDeclaration) classDecl.asReferenceType().getTypeDeclaration()).getConstructors(), argumentTypes, typeSolver);
+ for (LambdaArgumentTypePlaceholder placeholder : placeholders) {
+ placeholder.setMethod(res);
+ }
+ return res;
+ }
+
+ private void solveArguments(Node node, NodeList<Expression> args, boolean solveLambdas, List<ResolvedType> argumentTypes,
+ List<LambdaArgumentTypePlaceholder> placeholders) {
+ int i = 0;
+ for (Expression parameterValue : args) {
+ if (parameterValue instanceof LambdaExpr || parameterValue instanceof MethodReferenceExpr) {
+ LambdaArgumentTypePlaceholder placeholder = new LambdaArgumentTypePlaceholder(i);
+ argumentTypes.add(placeholder);
+ placeholders.add(placeholder);
+ } else {
+ try {
+ argumentTypes.add(JavaParserFacade.get(typeSolver).getType(parameterValue, solveLambdas));
+ } catch (com.github.javaparser.resolution.UnsolvedSymbolException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new RuntimeException(String.format("Unable to calculate the type of a parameter of a method call. Method call: %s, Parameter: %s",
+ node, parameterValue), e);
+ }
+ }
+ i++;
+ }
+ }
+
+ /**
+ * Given a method call find out to which method declaration it corresponds.
+ */
+ public SymbolReference<ResolvedMethodDeclaration> solve(MethodCallExpr methodCallExpr, boolean solveLambdas) {
+ List<ResolvedType> argumentTypes = new LinkedList<>();
+ List<LambdaArgumentTypePlaceholder> placeholders = new LinkedList<>();
+
+ solveArguments(methodCallExpr, methodCallExpr.getArguments(), solveLambdas, argumentTypes, placeholders);
+
+ SymbolReference<ResolvedMethodDeclaration> res = JavaParserFactory.getContext(methodCallExpr, typeSolver).solveMethod(methodCallExpr.getName().getId(), argumentTypes, false, typeSolver);
+ for (LambdaArgumentTypePlaceholder placeholder : placeholders) {
+ placeholder.setMethod(res);
+ }
+ return res;
+ }
+
+ public SymbolReference<ResolvedAnnotationDeclaration> solve(AnnotationExpr annotationExpr) {
+ Context context = JavaParserFactory.getContext(annotationExpr, typeSolver);
+ SymbolReference<ResolvedTypeDeclaration> typeDeclarationSymbolReference = context.solveType(annotationExpr.getNameAsString(), typeSolver);
+ ResolvedAnnotationDeclaration annotationDeclaration = (ResolvedAnnotationDeclaration) typeDeclarationSymbolReference.getCorrespondingDeclaration();
+ if (typeDeclarationSymbolReference.isSolved()) {
+ return SymbolReference.solved(annotationDeclaration);
+ } else {
+ return SymbolReference.unsolved(ResolvedAnnotationDeclaration.class);
+ }
+ }
+
+ public SymbolReference<ResolvedFieldDeclaration> solve(FieldAccessExpr fieldAccessExpr) {
+ return ((FieldAccessContext) JavaParserFactory.getContext(fieldAccessExpr, typeSolver)).solveField(fieldAccessExpr.getName().getId(), typeSolver);
+ }
+
+ public ResolvedType getType(Node node) {
+ return getType(node, true);
+ }
+
+ public ResolvedType getType(Node node, boolean solveLambdas) {
+ if (solveLambdas) {
+ if (!cacheWithLambdasSolved.containsKey(node)) {
+ ResolvedType res = getTypeConcrete(node, solveLambdas);
+
+ cacheWithLambdasSolved.put(node, res);
+
+ boolean secondPassNecessary = false;
+ if (node instanceof MethodCallExpr) {
+ MethodCallExpr methodCallExpr = (MethodCallExpr) node;
+ for (Node arg : methodCallExpr.getArguments()) {
+ if (!cacheWithLambdasSolved.containsKey(arg)) {
+ getType(arg, true);
+ secondPassNecessary = true;
+ }
+ }
+ }
+ if (secondPassNecessary) {
+ cacheWithLambdasSolved.remove(node);
+ cacheWithLambdasSolved.put(node, getType(node, true));
+ }
+ logger.finer("getType on " + node + " -> " + res);
+ }
+ return cacheWithLambdasSolved.get(node);
+ } else {
+ Optional<ResolvedType> res = find(cacheWithLambdasSolved, node);
+ if (res.isPresent()) {
+ return res.get();
+ }
+ res = find(cacheWithoutLambdasSolved, node);
+ if (!res.isPresent()) {
+ ResolvedType resType = getTypeConcrete(node, solveLambdas);
+ cacheWithoutLambdasSolved.put(node, resType);
+ logger.finer("getType on " + node + " (no solveLambdas) -> " + res);
+ return resType;
+ }
+ return res.get();
+ }
+ }
+
+ private Optional<ResolvedType> find(Map<Node, ResolvedType> map, Node node) {
+ if (map.containsKey(node)) {
+ return Optional.of(map.get(node));
+ }
+ if (node instanceof LambdaExpr) {
+ return find(map, (LambdaExpr) node);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ /**
+ * For some reasons LambdaExprs are duplicate and the equals method is not implemented correctly.
+ */
+ private Optional<ResolvedType> find(Map<Node, ResolvedType> map, LambdaExpr lambdaExpr) {
+ for (Node key : map.keySet()) {
+ if (key instanceof LambdaExpr) {
+ LambdaExpr keyLambdaExpr = (LambdaExpr) key;
+ if (keyLambdaExpr.toString().equals(lambdaExpr.toString()) && requireParentNode(keyLambdaExpr) == requireParentNode(lambdaExpr)) {
+ return Optional.of(map.get(keyLambdaExpr));
+ }
+ }
+ }
+ return Optional.empty();
+ }
+
+ protected MethodUsage toMethodUsage(MethodReferenceExpr methodReferenceExpr) {
+ if (!(methodReferenceExpr.getScope() instanceof TypeExpr)) {
+ throw new UnsupportedOperationException();
+ }
+ TypeExpr typeExpr = (TypeExpr) methodReferenceExpr.getScope();
+ if (!(typeExpr.getType() instanceof com.github.javaparser.ast.type.ClassOrInterfaceType)) {
+ throw new UnsupportedOperationException(typeExpr.getType().getClass().getCanonicalName());
+ }
+ ClassOrInterfaceType classOrInterfaceType = (ClassOrInterfaceType) typeExpr.getType();
+ SymbolReference<ResolvedTypeDeclaration> typeDeclarationSymbolReference = JavaParserFactory.getContext(classOrInterfaceType, typeSolver).solveType(classOrInterfaceType.getName().getId(), typeSolver);
+ if (!typeDeclarationSymbolReference.isSolved()) {
+ throw new UnsupportedOperationException();
+ }
+ List<MethodUsage> methodUsages = ((ResolvedReferenceTypeDeclaration) typeDeclarationSymbolReference.getCorrespondingDeclaration()).getAllMethods().stream().filter(it -> it.getName().equals(methodReferenceExpr.getIdentifier())).collect(Collectors.toList());
+ switch (methodUsages.size()) {
+ case 0:
+ throw new UnsupportedOperationException();
+ case 1:
+ return methodUsages.get(0);
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ protected ResolvedType getBinaryTypeConcrete(Node left, Node right, boolean solveLambdas) {
+ ResolvedType leftType = getTypeConcrete(left, solveLambdas);
+ ResolvedType rightType = getTypeConcrete(right, solveLambdas);
+ if (rightType.isAssignableBy(leftType)) {
+ return rightType;
+ }
+ return leftType;
+ }
+
+
+ /**
+ * Should return more like a TypeApplication: a TypeDeclaration and possible typeParametersValues or array
+ * modifiers.
+ */
+ private ResolvedType getTypeConcrete(Node node, boolean solveLambdas) {
+ if (node == null) throw new IllegalArgumentException();
+ return node.accept(typeExtractor, solveLambdas);
+ }
+
+ protected com.github.javaparser.ast.body.TypeDeclaration<?> findContainingTypeDecl(Node node) {
+ if (node instanceof ClassOrInterfaceDeclaration) {
+ return (ClassOrInterfaceDeclaration) node;
+ }
+ if (node instanceof EnumDeclaration) {
+ return (EnumDeclaration) node;
+ }
+ return findContainingTypeDecl(requireParentNode(node));
+
+ }
+
+ protected Node findContainingTypeDeclOrObjectCreationExpr(Node node) {
+ if (node instanceof ClassOrInterfaceDeclaration) {
+ return node;
+ }
+ if (node instanceof EnumDeclaration) {
+ return node;
+ }
+ Node parent = requireParentNode(node);
+ if (parent instanceof ObjectCreationExpr && !((ObjectCreationExpr) parent).getArguments().contains(node)) {
+ return parent;
+ }
+ return findContainingTypeDeclOrObjectCreationExpr(parent);
+ }
+
+ public ResolvedType convertToUsageVariableType(VariableDeclarator var) {
+ return get(typeSolver).convertToUsage(var.getType(), var);
+ }
+
+ public ResolvedType convertToUsage(com.github.javaparser.ast.type.Type type, Node context) {
+ if (type.isUnknownType()) {
+ throw new IllegalArgumentException("Inferred lambda parameter type");
+ }
+ return convertToUsage(type, JavaParserFactory.getContext(context, typeSolver));
+ }
+
+ public ResolvedType convertToUsage(com.github.javaparser.ast.type.Type type) {
+ return convertToUsage(type, type);
+ }
+
+ // This is an hack around an issue in JavaParser
+ private String qName(ClassOrInterfaceType classOrInterfaceType) {
+ String name = classOrInterfaceType.getName().getId();
+ if (classOrInterfaceType.getScope().isPresent()) {
+ return qName(classOrInterfaceType.getScope().get()) + "." + name;
+ }
+ return name;
+ }
+
+ protected ResolvedType convertToUsage(com.github.javaparser.ast.type.Type type, Context context) {
+ if (context == null) {
+ throw new NullPointerException("Context should not be null");
+ }
+ if (type instanceof ClassOrInterfaceType) {
+ ClassOrInterfaceType classOrInterfaceType = (ClassOrInterfaceType) type;
+ String name = qName(classOrInterfaceType);
+ SymbolReference<ResolvedTypeDeclaration> ref = context.solveType(name, typeSolver);
+ if (!ref.isSolved()) {
+ throw new UnsolvedSymbolException(name);
+ }
+ ResolvedTypeDeclaration typeDeclaration = ref.getCorrespondingDeclaration();
+ List<ResolvedType> typeParameters = Collections.emptyList();
+ if (classOrInterfaceType.getTypeArguments().isPresent()) {
+ typeParameters = classOrInterfaceType.getTypeArguments().get().stream().map((pt) -> convertToUsage(pt, context)).collect(Collectors.toList());
+ }
+ if (typeDeclaration.isTypeParameter()) {
+ if (typeDeclaration instanceof ResolvedTypeParameterDeclaration) {
+ return new ResolvedTypeVariable((ResolvedTypeParameterDeclaration) typeDeclaration);
+ } else {
+ JavaParserTypeVariableDeclaration javaParserTypeVariableDeclaration = (JavaParserTypeVariableDeclaration) typeDeclaration;
+ return new ResolvedTypeVariable(javaParserTypeVariableDeclaration.asTypeParameter());
+ }
+ } else {
+ return new ReferenceTypeImpl((ResolvedReferenceTypeDeclaration) typeDeclaration, typeParameters, typeSolver);
+ }
+ } else if (type instanceof com.github.javaparser.ast.type.PrimitiveType) {
+ return ResolvedPrimitiveType.byName(((com.github.javaparser.ast.type.PrimitiveType) type).getType().name());
+ } else if (type instanceof WildcardType) {
+ WildcardType wildcardType = (WildcardType) type;
+ if (wildcardType.getExtendedType().isPresent() && !wildcardType.getSuperType().isPresent()) {
+ return ResolvedWildcard.extendsBound(convertToUsage(wildcardType.getExtendedType().get(), context)); // removed (ReferenceTypeImpl)
+ } else if (!wildcardType.getExtendedType().isPresent() && wildcardType.getSuperType().isPresent()) {
+ return ResolvedWildcard.superBound(convertToUsage(wildcardType.getSuperType().get(), context)); // removed (ReferenceTypeImpl)
+ } else if (!wildcardType.getExtendedType().isPresent() && !wildcardType.getSuperType().isPresent()) {
+ return ResolvedWildcard.UNBOUNDED;
+ } else {
+ throw new UnsupportedOperationException(wildcardType.toString());
+ }
+ } else if (type instanceof com.github.javaparser.ast.type.VoidType) {
+ return ResolvedVoidType.INSTANCE;
+ } else if (type instanceof com.github.javaparser.ast.type.ArrayType) {
+ com.github.javaparser.ast.type.ArrayType jpArrayType = (com.github.javaparser.ast.type.ArrayType) type;
+ return new ResolvedArrayType(convertToUsage(jpArrayType.getComponentType(), context));
+ } else if (type instanceof UnionType) {
+ UnionType unionType = (UnionType) type;
+ return new ResolvedUnionType(unionType.getElements().stream().map(el -> convertToUsage(el, context)).collect(Collectors.toList()));
+ } else if (type instanceof VarType) {
+ Node parent = type.getParentNode().get();
+ if (!(parent instanceof VariableDeclarator)) {
+ throw new IllegalStateException("Trying to resolve a `var` which is not in a variable declaration.");
+ }
+ final VariableDeclarator variableDeclarator = (VariableDeclarator) parent;
+ return variableDeclarator.getInitializer()
+ .map(Expression::calculateResolvedType)
+ .orElseThrow(() -> new IllegalStateException("Cannot resolve `var` which has no initializer."));
+ } else {
+ throw new UnsupportedOperationException(type.getClass().getCanonicalName());
+ }
+ }
+
+
+ public ResolvedType convert(Type type, Node node) {
+ return convert(type, JavaParserFactory.getContext(node, typeSolver));
+ }
+
+ public ResolvedType convert(com.github.javaparser.ast.type.Type type, Context context) {
+ return convertToUsage(type, context);
+ }
+
+ public MethodUsage solveMethodAsUsage(MethodCallExpr call) {
+ List<ResolvedType> params = new ArrayList<>();
+ if (call.getArguments() != null) {
+ for (Expression param : call.getArguments()) {
+ //getTypeConcrete(Node node, boolean solveLambdas)
+ try {
+ params.add(getType(param, false));
+ } catch (Exception e) {
+ throw new RuntimeException(String.format("Error calculating the type of parameter %s of method call %s", param, call), e);
+ }
+ //params.add(getTypeConcrete(param, false));
+ }
+ }
+ Context context = JavaParserFactory.getContext(call, typeSolver);
+ Optional<MethodUsage> methodUsage = context.solveMethodAsUsage(call.getName().getId(), params, typeSolver);
+ if (!methodUsage.isPresent()) {
+ throw new RuntimeException("Method '" + call.getName() + "' cannot be resolved in context "
+ + call + " (line: " + call.getRange().map(r -> "" + r.begin.line).orElse("??") + ") " + context + ". Parameter types: " + params);
+ }
+ return methodUsage.get();
+ }
+
+ public ResolvedReferenceTypeDeclaration getTypeDeclaration(Node node) {
+ if (node instanceof TypeDeclaration) {
+ return getTypeDeclaration((TypeDeclaration) node);
+ } else if (node instanceof ObjectCreationExpr) {
+ return new JavaParserAnonymousClassDeclaration((ObjectCreationExpr) node, typeSolver);
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ public ResolvedReferenceTypeDeclaration getTypeDeclaration(ClassOrInterfaceDeclaration classOrInterfaceDeclaration) {
+ return JavaParserFactory.toTypeDeclaration(classOrInterfaceDeclaration, typeSolver);
+ }
+
+ /**
+ * "this" inserted in the given point, which type would have?
+ */
+ public ResolvedType getTypeOfThisIn(Node node) {
+ // TODO consider static methods
+ if (node instanceof ClassOrInterfaceDeclaration) {
+ return new ReferenceTypeImpl(getTypeDeclaration((ClassOrInterfaceDeclaration) node), typeSolver);
+ } else if (node instanceof EnumDeclaration) {
+ JavaParserEnumDeclaration enumDeclaration = new JavaParserEnumDeclaration((EnumDeclaration) node, typeSolver);
+ return new ReferenceTypeImpl(enumDeclaration, typeSolver);
+ } else if (node instanceof ObjectCreationExpr && ((ObjectCreationExpr) node).getAnonymousClassBody().isPresent()) {
+ JavaParserAnonymousClassDeclaration anonymousDeclaration = new JavaParserAnonymousClassDeclaration((ObjectCreationExpr) node, typeSolver);
+ return new ReferenceTypeImpl(anonymousDeclaration, typeSolver);
+ }
+ return getTypeOfThisIn(requireParentNode(node));
+ }
+
+ public ResolvedReferenceTypeDeclaration getTypeDeclaration(com.github.javaparser.ast.body.TypeDeclaration<?> typeDeclaration) {
+ return JavaParserFactory.toTypeDeclaration(typeDeclaration, typeSolver);
+ }
+
+ public ResolvedType classToResolvedType(Class<?> clazz) {
+ if (clazz.isPrimitive()) {
+ return ResolvedPrimitiveType.byName(clazz.getName());
+ }
+ return new ReferenceTypeImpl(new ReflectionClassDeclaration(String.class, typeSolver), typeSolver);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFactory.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFactory.java
new file mode 100644
index 000000000..8bd3496fd
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFactory.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel;
+
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.body.*;
+import com.github.javaparser.ast.expr.*;
+import com.github.javaparser.ast.stmt.*;
+import com.github.javaparser.ast.type.TypeParameter;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.contexts.*;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserAnnotationDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserClassDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserEnumDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserInterfaceDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserTypeParameter;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarators.FieldSymbolDeclarator;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarators.NoSymbolDeclarator;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarators.ParameterSymbolDeclarator;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarators.VariableSymbolDeclarator;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.resolution.SymbolDeclarator;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentNode;
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavaParserFactory {
+
+ public static Context getContext(Node node, TypeSolver typeSolver) {
+ if (node == null) {
+ throw new NullPointerException("Node should not be null");
+ } else if (node instanceof CompilationUnit) {
+ return new CompilationUnitContext((CompilationUnit) node, typeSolver);
+ } else if (node instanceof ForeachStmt) {
+ return new ForechStatementContext((ForeachStmt) node, typeSolver);
+ } else if (node instanceof ForStmt) {
+ return new ForStatementContext((ForStmt) node, typeSolver);
+ } else if (node instanceof LambdaExpr) {
+ return new LambdaExprContext((LambdaExpr) node, typeSolver);
+ } else if (node instanceof MethodDeclaration) {
+ return new MethodContext((MethodDeclaration) node, typeSolver);
+ } else if (node instanceof ConstructorDeclaration) {
+ return new ConstructorContext((ConstructorDeclaration) node, typeSolver);
+ } else if (node instanceof ClassOrInterfaceDeclaration) {
+ return new ClassOrInterfaceDeclarationContext((ClassOrInterfaceDeclaration) node, typeSolver);
+ } else if (node instanceof MethodCallExpr) {
+ return new MethodCallExprContext((MethodCallExpr) node, typeSolver);
+ } else if (node instanceof EnumDeclaration) {
+ return new EnumDeclarationContext((EnumDeclaration) node, typeSolver);
+ } else if (node instanceof FieldAccessExpr) {
+ return new FieldAccessContext((FieldAccessExpr) node, typeSolver);
+ } else if (node instanceof SwitchEntryStmt) {
+ return new SwitchEntryContext((SwitchEntryStmt) node, typeSolver);
+ } else if (node instanceof TryStmt) {
+ return new TryWithResourceContext((TryStmt) node, typeSolver);
+ } else if (node instanceof Statement) {
+ return new StatementContext<>((Statement) node, typeSolver);
+ } else if (node instanceof CatchClause) {
+ return new CatchClauseContext((CatchClause) node, typeSolver);
+ } else if (node instanceof ObjectCreationExpr &&
+ ((ObjectCreationExpr) node).getAnonymousClassBody().isPresent()) {
+ return new AnonymousClassDeclarationContext((ObjectCreationExpr) node, typeSolver);
+ } else {
+ if (node instanceof NameExpr) {
+ // to resolve a name when in a fieldAccess context, we can get to the grand parent to prevent a infinite loop if the name is the same as the field (ie x.x)
+ if (node.getParentNode().isPresent() && node.getParentNode().get() instanceof FieldAccessExpr && node.getParentNode().get().getParentNode().isPresent()) {
+ return getContext(node.getParentNode().get().getParentNode().get(), typeSolver);
+ }
+ }
+ final Node parentNode = requireParentNode(node);
+ if (parentNode instanceof ObjectCreationExpr && node == ((ObjectCreationExpr) parentNode).getType()) {
+ return getContext(requireParentNode(parentNode), typeSolver);
+ }
+ if (parentNode == null) {
+ throw new IllegalStateException("The AST node does not appear to be inserted in a propert AST, therefore we cannot resolve symbols correctly");
+ }
+ return getContext(parentNode, typeSolver);
+ }
+ }
+
+ public static SymbolDeclarator getSymbolDeclarator(Node node, TypeSolver typeSolver) {
+ if (node instanceof FieldDeclaration) {
+ return new FieldSymbolDeclarator((FieldDeclaration) node, typeSolver);
+ } else if (node instanceof Parameter) {
+ return new ParameterSymbolDeclarator((Parameter) node, typeSolver);
+ } else if (node instanceof ExpressionStmt) {
+ ExpressionStmt expressionStmt = (ExpressionStmt) node;
+ if (expressionStmt.getExpression() instanceof VariableDeclarationExpr) {
+ return new VariableSymbolDeclarator((VariableDeclarationExpr) (expressionStmt.getExpression()), typeSolver);
+ } else {
+ return new NoSymbolDeclarator<>(expressionStmt, typeSolver);
+ }
+ } else if (node instanceof IfStmt) {
+ return new NoSymbolDeclarator<>((IfStmt) node, typeSolver);
+ } else if (node instanceof ForeachStmt) {
+ ForeachStmt foreachStmt = (ForeachStmt) node;
+ return new VariableSymbolDeclarator(foreachStmt.getVariable(), typeSolver);
+ } else {
+ return new NoSymbolDeclarator<>(node, typeSolver);
+ }
+ }
+
+ public static ResolvedReferenceTypeDeclaration toTypeDeclaration(Node node, TypeSolver typeSolver) {
+ if (node instanceof ClassOrInterfaceDeclaration) {
+ if (((ClassOrInterfaceDeclaration) node).isInterface()) {
+ return new JavaParserInterfaceDeclaration((ClassOrInterfaceDeclaration) node, typeSolver);
+ } else {
+ return new JavaParserClassDeclaration((ClassOrInterfaceDeclaration) node, typeSolver);
+ }
+ } else if (node instanceof TypeParameter) {
+ return new JavaParserTypeParameter((TypeParameter) node, typeSolver);
+ } else if (node instanceof EnumDeclaration) {
+ return new JavaParserEnumDeclaration((EnumDeclaration) node, typeSolver);
+ } else if (node instanceof AnnotationDeclaration) {
+ return new JavaParserAnnotationDeclaration((AnnotationDeclaration) node, typeSolver);
+ } else {
+ throw new IllegalArgumentException(node.getClass().getCanonicalName());
+ }
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/LambdaArgumentTypePlaceholder.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/LambdaArgumentTypePlaceholder.java
new file mode 100644
index 000000000..c0e35bf43
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/LambdaArgumentTypePlaceholder.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel;
+
+import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+
+/**
+ * Placeholder used to represent a lambda argument type while it is being
+ * calculated.
+ *
+ * @author Federico Tomassetti
+ */
+public class LambdaArgumentTypePlaceholder implements ResolvedType {
+
+ private int pos;
+ private SymbolReference<? extends ResolvedMethodLikeDeclaration> method;
+
+ public LambdaArgumentTypePlaceholder(int pos) {
+ this.pos = pos;
+ }
+
+ @Override
+ public boolean isArray() {
+ return false;
+ }
+
+ @Override
+ public boolean isPrimitive() {
+ return false;
+ }
+
+ @Override
+ public boolean isReferenceType() {
+ return false;
+ }
+
+ @Override
+ public String describe() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isTypeVariable() {
+ return false;
+ }
+
+ public void setMethod(SymbolReference<? extends ResolvedMethodLikeDeclaration> method) {
+ this.method = method;
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedType other) {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/TypeExtractor.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/TypeExtractor.java
new file mode 100644
index 000000000..44c8d2715
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/TypeExtractor.java
@@ -0,0 +1,517 @@
+package com.github.javaparser.symbolsolver.javaparsermodel;
+
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
+import com.github.javaparser.ast.body.FieldDeclaration;
+import com.github.javaparser.ast.body.Parameter;
+import com.github.javaparser.ast.body.VariableDeclarator;
+import com.github.javaparser.ast.expr.*;
+import com.github.javaparser.ast.stmt.BlockStmt;
+import com.github.javaparser.ast.stmt.ExpressionStmt;
+import com.github.javaparser.ast.stmt.ReturnStmt;
+import com.github.javaparser.ast.type.UnknownType;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.UnsolvedSymbolException;
+import com.github.javaparser.resolution.declarations.ResolvedClassDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.types.ResolvedArrayType;
+import com.github.javaparser.resolution.types.ResolvedPrimitiveType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.resolution.types.ResolvedVoidType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserSymbolDeclaration;
+import com.github.javaparser.symbolsolver.logic.FunctionalInterfaceLogic;
+import com.github.javaparser.symbolsolver.logic.InferenceContext;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.resolution.Value;
+import com.github.javaparser.symbolsolver.model.typesystem.*;
+import com.github.javaparser.symbolsolver.reflectionmodel.MyObjectProvider;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionClassDeclaration;
+import com.github.javaparser.symbolsolver.resolution.SymbolSolver;
+import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode;
+import static com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade.solveGenericTypes;
+
+public class TypeExtractor extends DefaultVisitorAdapter {
+
+ private static Logger logger = Logger.getLogger(TypeExtractor.class.getCanonicalName());
+
+ static {
+ logger.setLevel(Level.INFO);
+ ConsoleHandler consoleHandler = new ConsoleHandler();
+ consoleHandler.setLevel(Level.INFO);
+ logger.addHandler(consoleHandler);
+ }
+
+ private TypeSolver typeSolver;
+ private JavaParserFacade facade;
+
+ public TypeExtractor(TypeSolver typeSolver, JavaParserFacade facade) {
+ this.typeSolver = typeSolver;
+ this.facade = facade;
+ }
+
+ @Override
+ public ResolvedType visit(VariableDeclarator node, Boolean solveLambdas) {
+ if (requireParentNode(node) instanceof FieldDeclaration) {
+ return facade.convertToUsageVariableType(node);
+ } else if (requireParentNode(node) instanceof VariableDeclarationExpr) {
+ return facade.convertToUsageVariableType(node);
+ }
+ throw new UnsupportedOperationException(requireParentNode(node).getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(Parameter node, Boolean solveLambdas) {
+ if (node.getType() instanceof UnknownType) {
+ throw new IllegalStateException("Parameter has unknown type: " + node);
+ }
+ return facade.convertToUsage(node.getType(), node);
+ }
+
+
+ @Override
+ public ResolvedType visit(ArrayAccessExpr node, Boolean solveLambdas) {
+ ResolvedType arrayUsageType = node.getName().accept(this, solveLambdas);
+ if (arrayUsageType.isArray()) {
+ return ((ResolvedArrayType) arrayUsageType).getComponentType();
+ }
+ return arrayUsageType;
+ }
+
+ @Override
+ public ResolvedType visit(ArrayCreationExpr node, Boolean solveLambdas) {
+ ResolvedType res = facade.convertToUsage(node.getElementType(), JavaParserFactory.getContext(node, typeSolver));
+ for (int i = 0; i < node.getLevels().size(); i++) {
+ res = new ResolvedArrayType(res);
+ }
+ return res;
+ }
+
+ @Override
+ public ResolvedType visit(ArrayInitializerExpr node, Boolean solveLambdas) {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedType visit(AssignExpr node, Boolean solveLambdas) {
+ return node.getTarget().accept(this, solveLambdas);
+ }
+
+ @Override
+ public ResolvedType visit(BinaryExpr node, Boolean solveLambdas) {
+ switch (node.getOperator()) {
+ case PLUS:
+ case MINUS:
+ case DIVIDE:
+ case MULTIPLY:
+ return facade.getBinaryTypeConcrete(node.getLeft(), node.getRight(), solveLambdas);
+ case LESS_EQUALS:
+ case LESS:
+ case GREATER:
+ case GREATER_EQUALS:
+ case EQUALS:
+ case NOT_EQUALS:
+ case OR:
+ case AND:
+ return ResolvedPrimitiveType.BOOLEAN;
+ case BINARY_AND:
+ case BINARY_OR:
+ case SIGNED_RIGHT_SHIFT:
+ case UNSIGNED_RIGHT_SHIFT:
+ case LEFT_SHIFT:
+ case REMAINDER:
+ case XOR:
+ return node.getLeft().accept(this, solveLambdas);
+ default:
+ throw new UnsupportedOperationException("Operator " + node.getOperator().name());
+ }
+ }
+
+ @Override
+ public ResolvedType visit(CastExpr node, Boolean solveLambdas) {
+ return facade.convertToUsage(node.getType(), JavaParserFactory.getContext(node, typeSolver));
+ }
+
+ @Override
+ public ResolvedType visit(ClassExpr node, Boolean solveLambdas) {
+ // This implementation does not regard the actual type argument of the ClassExpr.
+ com.github.javaparser.ast.type.Type astType = node.getType();
+ ResolvedType jssType = facade.convertToUsage(astType, node.getType());
+ return new ReferenceTypeImpl(new ReflectionClassDeclaration(Class.class, typeSolver), ImmutableList.of(jssType), typeSolver);
+ }
+
+ @Override
+ public ResolvedType visit(ConditionalExpr node, Boolean solveLambdas) {
+ return node.getThenExpr().accept(this, solveLambdas);
+ }
+
+ @Override
+ public ResolvedType visit(EnclosedExpr node, Boolean solveLambdas) {
+ return node.getInner().accept(this, solveLambdas);
+ }
+
+ /**
+ * Java Parser can't differentiate between packages, internal types, and fields.
+ * All three are lumped together into FieldAccessExpr. We need to differentiate them.
+ */
+ private ResolvedType solveDotExpressionType(ResolvedReferenceTypeDeclaration parentType, FieldAccessExpr node) {
+ // Fields and internal type declarations cannot have the same name.
+ // Thus, these checks will always be mutually exclusive.
+ if (parentType.hasField(node.getName().getId())) {
+ return parentType.getField(node.getName().getId()).getType();
+ } else if (parentType.hasInternalType(node.getName().getId())) {
+ return new ReferenceTypeImpl(parentType.getInternalType(node.getName().getId()), typeSolver);
+ } else {
+ throw new UnsolvedSymbolException(node.getName().getId());
+ }
+ }
+
+ @Override
+ public ResolvedType visit(FieldAccessExpr node, Boolean solveLambdas) {
+ // We should understand if this is a static access
+ if (node.getScope() instanceof NameExpr ||
+ node.getScope() instanceof FieldAccessExpr) {
+ Expression staticValue = node.getScope();
+ SymbolReference<ResolvedTypeDeclaration> typeAccessedStatically = JavaParserFactory.getContext(node, typeSolver).solveType(staticValue.toString(), typeSolver);
+ if (typeAccessedStatically.isSolved()) {
+ // TODO here maybe we have to substitute type typeParametersValues
+ return solveDotExpressionType(
+ typeAccessedStatically.getCorrespondingDeclaration().asReferenceType(), node);
+ }
+ } else if (node.getScope() instanceof ThisExpr) {
+ // If we are accessing through a 'this' expression, first resolve the type
+ // corresponding to 'this'
+ SymbolReference<ResolvedTypeDeclaration> solve = facade.solve((ThisExpr) node.getScope());
+ // If found get it's declaration and get the field in there
+ if (solve.isSolved()) {
+ ResolvedTypeDeclaration correspondingDeclaration = solve.getCorrespondingDeclaration();
+ if (correspondingDeclaration instanceof ResolvedReferenceTypeDeclaration) {
+ return solveDotExpressionType(correspondingDeclaration.asReferenceType(), node);
+ }
+ }
+
+ } else if (node.getScope().toString().indexOf('.') > 0) {
+ // try to find fully qualified name
+ SymbolReference<ResolvedReferenceTypeDeclaration> sr = typeSolver.tryToSolveType(node.getScope().toString());
+ if (sr.isSolved()) {
+ return solveDotExpressionType(sr.getCorrespondingDeclaration(), node);
+ }
+ }
+ Optional<Value> value = Optional.empty();
+ try {
+ value = new SymbolSolver(typeSolver).solveSymbolAsValue(node.getName().getId(), node);
+ } catch (com.github.javaparser.resolution.UnsolvedSymbolException use) {
+ // This node may have a package name as part of its fully qualified name.
+ // We should solve for the type declaration inside this package.
+ SymbolReference<ResolvedReferenceTypeDeclaration> sref = typeSolver.tryToSolveType(node.toString());
+ if (sref.isSolved()) {
+ return new ReferenceTypeImpl(sref.getCorrespondingDeclaration(), typeSolver);
+ }
+ }
+ if (value.isPresent()) {
+ return value.get().getType();
+ }
+ throw new com.github.javaparser.resolution.UnsolvedSymbolException(node.getName().getId());
+ }
+
+ @Override
+ public ResolvedType visit(InstanceOfExpr node, Boolean solveLambdas) {
+ return ResolvedPrimitiveType.BOOLEAN;
+ }
+
+ @Override
+ public ResolvedType visit(StringLiteralExpr node, Boolean solveLambdas) {
+ return new ReferenceTypeImpl(new ReflectionTypeSolver().solveType(String.class.getCanonicalName()), typeSolver);
+ }
+
+ @Override
+ public ResolvedType visit(IntegerLiteralExpr node, Boolean solveLambdas) {
+ return ResolvedPrimitiveType.INT;
+ }
+
+ @Override
+ public ResolvedType visit(LongLiteralExpr node, Boolean solveLambdas) {
+ return ResolvedPrimitiveType.LONG;
+ }
+
+ @Override
+ public ResolvedType visit(CharLiteralExpr node, Boolean solveLambdas) {
+ return ResolvedPrimitiveType.CHAR;
+ }
+
+ @Override
+ public ResolvedType visit(DoubleLiteralExpr node, Boolean solveLambdas) {
+ if (node.getValue().toLowerCase().endsWith("f")) {
+ return ResolvedPrimitiveType.FLOAT;
+ }
+ return ResolvedPrimitiveType.DOUBLE;
+ }
+
+ @Override
+ public ResolvedType visit(BooleanLiteralExpr node, Boolean solveLambdas) {
+ return ResolvedPrimitiveType.BOOLEAN;
+ }
+
+ @Override
+ public ResolvedType visit(NullLiteralExpr node, Boolean solveLambdas) {
+ return NullType.INSTANCE;
+ }
+
+ @Override
+ public ResolvedType visit(MethodCallExpr node, Boolean solveLambdas) {
+ logger.finest("getType on method call " + node);
+ // first solve the method
+ MethodUsage ref = facade.solveMethodAsUsage(node);
+ logger.finest("getType on method call " + node + " resolved to " + ref);
+ logger.finest("getType on method call " + node + " return type is " + ref.returnType());
+ return ref.returnType();
+ // the type is the return type of the method
+ }
+
+ @Override
+ public ResolvedType visit(NameExpr node, Boolean solveLambdas) {
+ logger.finest("getType on name expr " + node);
+ Optional<Value> value = new SymbolSolver(typeSolver).solveSymbolAsValue(node.getName().getId(), node);
+ if (!value.isPresent()) {
+ throw new com.github.javaparser.resolution.UnsolvedSymbolException("Solving " + node, node.getName().getId());
+ } else {
+ return value.get().getType();
+ }
+ }
+
+ @Override
+ public ResolvedType visit(ObjectCreationExpr node, Boolean solveLambdas) {
+ return facade.convertToUsage(node.getType(), node);
+ }
+
+ @Override
+ public ResolvedType visit(ThisExpr node, Boolean solveLambdas) {
+ // If 'this' is prefixed by a class eg. MyClass.this
+ if (node.getClassExpr().isPresent()) {
+ // Get the class name
+ String className = node.getClassExpr().get().toString();
+ // Attempt to resolve using a typeSolver
+ SymbolReference<ResolvedReferenceTypeDeclaration> clazz = typeSolver.tryToSolveType(className);
+ if (clazz.isSolved()) {
+ return new ReferenceTypeImpl(clazz.getCorrespondingDeclaration(), typeSolver);
+ }
+ // Attempt to resolve locally in Compilation unit
+ Optional<CompilationUnit> cu = node.getAncestorOfType(CompilationUnit.class);
+ if (cu.isPresent()) {
+ Optional<ClassOrInterfaceDeclaration> classByName = cu.get().getClassByName(className);
+ if (classByName.isPresent()) {
+ return new ReferenceTypeImpl(facade.getTypeDeclaration(classByName.get()), typeSolver);
+ }
+ }
+
+ }
+ return new ReferenceTypeImpl(facade.getTypeDeclaration(facade.findContainingTypeDeclOrObjectCreationExpr(node)), typeSolver);
+ }
+
+ @Override
+ public ResolvedType visit(SuperExpr node, Boolean solveLambdas) {
+ ResolvedTypeDeclaration typeOfNode = facade.getTypeDeclaration(facade.findContainingTypeDecl(node));
+ if (typeOfNode instanceof ResolvedClassDeclaration) {
+ return ((ResolvedClassDeclaration) typeOfNode).getSuperClass();
+ } else {
+ throw new UnsupportedOperationException(node.getClass().getCanonicalName());
+ }
+ }
+
+ @Override
+ public ResolvedType visit(UnaryExpr node, Boolean solveLambdas) {
+ switch (node.getOperator()) {
+ case MINUS:
+ case PLUS:
+ return node.getExpression().accept(this, solveLambdas);
+ case LOGICAL_COMPLEMENT:
+ return ResolvedPrimitiveType.BOOLEAN;
+ case POSTFIX_DECREMENT:
+ case PREFIX_DECREMENT:
+ case POSTFIX_INCREMENT:
+ case PREFIX_INCREMENT:
+ return node.getExpression().accept(this, solveLambdas);
+ default:
+ throw new UnsupportedOperationException(node.getOperator().name());
+ }
+ }
+
+ @Override
+ public ResolvedType visit(VariableDeclarationExpr node, Boolean solveLambdas) {
+ if (node.getVariables().size() != 1) {
+ throw new UnsupportedOperationException();
+ }
+ return facade.convertToUsageVariableType(node.getVariables().get(0));
+ }
+
+
+ @Override
+ public ResolvedType visit(LambdaExpr node, Boolean solveLambdas) {
+ if (requireParentNode(node) instanceof MethodCallExpr) {
+ MethodCallExpr callExpr = (MethodCallExpr) requireParentNode(node);
+ int pos = JavaParserSymbolDeclaration.getParamPos(node);
+ SymbolReference<ResolvedMethodDeclaration> refMethod = facade.solve(callExpr);
+ if (!refMethod.isSolved()) {
+ throw new com.github.javaparser.resolution.UnsolvedSymbolException(requireParentNode(node).toString(), callExpr.getName().getId());
+ }
+ logger.finest("getType on lambda expr " + refMethod.getCorrespondingDeclaration().getName());
+ if (solveLambdas) {
+
+ // The type parameter referred here should be the java.util.stream.Stream.T
+ ResolvedType result = refMethod.getCorrespondingDeclaration().getParam(pos).getType();
+
+ if (callExpr.getScope().isPresent()) {
+ Expression scope = callExpr.getScope().get();
+
+ // If it is a static call we should not try to get the type of the scope
+ boolean staticCall = false;
+ if (scope instanceof NameExpr) {
+ NameExpr nameExpr = (NameExpr) scope;
+ try {
+ SymbolReference<ResolvedTypeDeclaration> type = JavaParserFactory.getContext(nameExpr, typeSolver).solveType(nameExpr.getName().getId(), typeSolver);
+ if (type.isSolved()) {
+ staticCall = true;
+ }
+ } catch (Exception e) {
+
+ }
+ }
+
+ if (!staticCall) {
+ ResolvedType scopeType = facade.getType(scope);
+ if (scopeType.isReferenceType()) {
+ result = scopeType.asReferenceType().useThisTypeParametersOnTheGivenType(result);
+ }
+ }
+ }
+
+ // We need to replace the type variables
+ Context ctx = JavaParserFactory.getContext(node, typeSolver);
+ result = solveGenericTypes(result, ctx, typeSolver);
+
+ //We should find out which is the functional method (e.g., apply) and replace the params of the
+ //solveLambdas with it, to derive so the values. We should also consider the value returned by the
+ //lambdas
+ Optional<MethodUsage> functionalMethod = FunctionalInterfaceLogic.getFunctionalMethod(result);
+ if (functionalMethod.isPresent()) {
+ LambdaExpr lambdaExpr = node;
+
+ InferenceContext lambdaCtx = new InferenceContext(MyObjectProvider.INSTANCE);
+ InferenceContext funcInterfaceCtx = new InferenceContext(MyObjectProvider.INSTANCE);
+
+ // At this point parameterType
+ // if Function<T=? super Stream.T, ? extends map.R>
+ // we should replace Stream.T
+ ResolvedType functionalInterfaceType = ReferenceTypeImpl.undeterminedParameters(functionalMethod.get().getDeclaration().declaringType(), typeSolver);
+
+ lambdaCtx.addPair(result, functionalInterfaceType);
+
+ ResolvedType actualType;
+
+ if (lambdaExpr.getBody() instanceof ExpressionStmt) {
+ actualType = facade.getType(((ExpressionStmt) lambdaExpr.getBody()).getExpression());
+ } else if (lambdaExpr.getBody() instanceof BlockStmt) {
+ BlockStmt blockStmt = (BlockStmt) lambdaExpr.getBody();
+
+ // Get all the return statements in the lambda block
+ List<ReturnStmt> returnStmts = blockStmt.findAll(ReturnStmt.class);
+
+ if (returnStmts.size() > 0) {
+ actualType = returnStmts.stream()
+ .map(returnStmt -> returnStmt.getExpression().map(e -> facade.getType(e)).orElse(ResolvedVoidType.INSTANCE))
+ .filter(x -> x != null && !x.isVoid() && !x.isNull())
+ .findFirst()
+ .orElse(ResolvedVoidType.INSTANCE);
+
+ } else {
+ return ResolvedVoidType.INSTANCE;
+ }
+
+
+ } else {
+ throw new UnsupportedOperationException();
+ }
+
+ ResolvedType formalType = functionalMethod.get().returnType();
+
+ // Infer the functional interfaces' return vs actual type
+ funcInterfaceCtx.addPair(formalType, actualType);
+ // Substitute to obtain a new type
+ ResolvedType functionalTypeWithReturn = funcInterfaceCtx.resolve(funcInterfaceCtx.addSingle(functionalInterfaceType));
+
+ // if the functional method returns void anyway
+ // we don't need to bother inferring types
+ if (!(formalType instanceof ResolvedVoidType)) {
+ lambdaCtx.addPair(result, functionalTypeWithReturn);
+ result = lambdaCtx.resolve(lambdaCtx.addSingle(result));
+ }
+ }
+
+ return result;
+ } else {
+ return refMethod.getCorrespondingDeclaration().getParam(pos).getType();
+ }
+ } else {
+ throw new UnsupportedOperationException("The type of a lambda expr depends on the position and its return value");
+ }
+ }
+
+ @Override
+ public ResolvedType visit(MethodReferenceExpr node, Boolean solveLambdas) {
+ if (requireParentNode(node) instanceof MethodCallExpr) {
+ MethodCallExpr callExpr = (MethodCallExpr) requireParentNode(node);
+ int pos = JavaParserSymbolDeclaration.getParamPos(node);
+ SymbolReference<ResolvedMethodDeclaration> refMethod = facade.solve(callExpr, false);
+ if (!refMethod.isSolved()) {
+ throw new com.github.javaparser.resolution.UnsolvedSymbolException(requireParentNode(node).toString(), callExpr.getName().getId());
+ }
+ logger.finest("getType on method reference expr " + refMethod.getCorrespondingDeclaration().getName());
+ //logger.finest("Method param " + refMethod.getCorrespondingDeclaration().getParam(pos));
+ if (solveLambdas) {
+ MethodUsage usage = facade.solveMethodAsUsage(callExpr);
+ ResolvedType result = usage.getParamType(pos);
+ // We need to replace the type variables
+ Context ctx = JavaParserFactory.getContext(node, typeSolver);
+ result = solveGenericTypes(result, ctx, typeSolver);
+
+ //We should find out which is the functional method (e.g., apply) and replace the params of the
+ //solveLambdas with it, to derive so the values. We should also consider the value returned by the
+ //lambdas
+ if (FunctionalInterfaceLogic.getFunctionalMethod(result).isPresent()) {
+ MethodReferenceExpr methodReferenceExpr = node;
+
+ ResolvedType actualType = facade.toMethodUsage(methodReferenceExpr).returnType();
+ ResolvedType formalType = FunctionalInterfaceLogic.getFunctionalMethod(result).get().returnType();
+
+ InferenceContext inferenceContext = new InferenceContext(MyObjectProvider.INSTANCE);
+ inferenceContext.addPair(formalType, actualType);
+ result = inferenceContext.resolve(inferenceContext.addSingle(result));
+ }
+
+ return result;
+ }
+ return refMethod.getCorrespondingDeclaration().getParam(pos).getType();
+ }
+ throw new UnsupportedOperationException("The type of a method reference expr depends on the position and its return value");
+ }
+
+ @Override
+ public ResolvedType visit(FieldDeclaration node, Boolean solveLambdas) {
+ if (node.getVariables().size() == 1) {
+ return node.getVariables().get(0).accept(this, solveLambdas);
+ }
+ throw new IllegalArgumentException("Cannot resolve the type of a field with multiple variable declarations. Pick one");
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/UnsolvedSymbolException.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/UnsolvedSymbolException.java
new file mode 100644
index 000000000..f7ab7d11e
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/UnsolvedSymbolException.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel;
+
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+/**
+ * @author Federico Tomassetti
+ *
+ * @deprecated Use {@link com.github.javaparser.resolution.UnsolvedSymbolException} instead
+ */
+// Use the one in model instead
+@Deprecated
+public class UnsolvedSymbolException extends RuntimeException {
+
+ private String context;
+ private String name;
+ private TypeSolver typeSolver;
+
+ @Deprecated
+ public UnsolvedSymbolException(String name, TypeSolver typeSolver) {
+ super("Unsolved symbol : " + name + " using typesolver " + typeSolver);
+ this.typeSolver = typeSolver;
+ this.name = name;
+ }
+
+ @Deprecated
+ public UnsolvedSymbolException(Context context, String name) {
+ super("Unsolved symbol in " + context + " : " + name);
+ this.context = context.toString();
+ this.name = name;
+ }
+
+ @Deprecated
+ public UnsolvedSymbolException(String context, String name) {
+ super("Unsolved symbol in " + context + " : " + name);
+ this.context = context;
+ this.name = name;
+ }
+
+ @Deprecated
+ public UnsolvedSymbolException(String name) {
+ super("Unsolved symbol : " + name);
+ this.context = "unknown";
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return "UnsolvedSymbolException{" +
+ "context='" + context + '\'' +
+ ", name='" + name + '\'' +
+ ", typeSolver=" + typeSolver +
+ '}';
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractJavaParserContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractJavaParserContext.java
new file mode 100644
index 000000000..cc008a527
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractJavaParserContext.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.expr.Expression;
+import com.github.javaparser.ast.expr.FieldAccessExpr;
+import com.github.javaparser.ast.expr.MethodCallExpr;
+import com.github.javaparser.ast.expr.NameExpr;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.resolution.Value;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionClassDeclaration;
+import com.github.javaparser.symbolsolver.resolution.SymbolDeclarator;
+
+import java.util.*;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentNode;
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode;
+import static java.util.Collections.*;
+
+/**
+ * @author Federico Tomassetti
+ */
+public abstract class AbstractJavaParserContext<N extends Node> implements Context {
+
+ protected N wrappedNode;
+ protected TypeSolver typeSolver;
+
+ ///
+ /// Static methods
+ ///
+
+ public static SymbolReference<ResolvedValueDeclaration> solveWith(SymbolDeclarator symbolDeclarator, String name) {
+ for (ResolvedValueDeclaration decl : symbolDeclarator.getSymbolDeclarations()) {
+ if (decl.getName().equals(name)) {
+ return SymbolReference.solved(decl);
+ }
+ }
+ return SymbolReference.unsolved(ResolvedValueDeclaration.class);
+ }
+
+ ///
+ /// Constructors
+ ///
+
+ public AbstractJavaParserContext(N wrappedNode, TypeSolver typeSolver) {
+ if (wrappedNode == null) {
+ throw new NullPointerException();
+ }
+ this.wrappedNode = wrappedNode;
+ this.typeSolver = typeSolver;
+ }
+
+ ///
+ /// Public methods
+ ///
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ AbstractJavaParserContext<?> that = (AbstractJavaParserContext<?>) o;
+
+ if (wrappedNode != null ? !wrappedNode.equals(that.wrappedNode) : that.wrappedNode != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return wrappedNode != null ? wrappedNode.hashCode() : 0;
+ }
+
+ @Override
+ public Optional<ResolvedType> solveGenericType(String name, TypeSolver typeSolver) {
+ Context parent = getParent();
+ if (parent == null) {
+ return Optional.empty();
+ } else {
+ return parent.solveGenericType(name, typeSolver);
+ }
+ }
+
+ @Override
+ public final Context getParent() {
+ Node parent = wrappedNode.getParentNode().orElse(null);
+ if (parent instanceof MethodCallExpr) {
+ MethodCallExpr parentCall = (MethodCallExpr) parent;
+ boolean found = false;
+ if (parentCall.getArguments() != null) {
+ for (Expression expression : parentCall.getArguments()) {
+ if (expression == wrappedNode) {
+ found = true;
+ }
+ }
+ }
+ if (found) {
+ Node notMethod = parent;
+ while (notMethod instanceof MethodCallExpr) {
+ notMethod = requireParentNode(notMethod);
+ }
+ return JavaParserFactory.getContext(notMethod, typeSolver);
+ }
+ }
+ Node notMethod = parent;
+ while (notMethod instanceof MethodCallExpr || notMethod instanceof FieldAccessExpr) {
+ notMethod = notMethod.getParentNode().orElse(null);
+ }
+ if (notMethod == null) {
+ return null;
+ }
+ return JavaParserFactory.getContext(notMethod, typeSolver);
+ }
+
+ ///
+ /// Protected methods
+ ///
+
+ protected Optional<Value> solveWithAsValue(SymbolDeclarator symbolDeclarator, String name, TypeSolver typeSolver) {
+ return symbolDeclarator.getSymbolDeclarations().stream()
+ .filter(d -> d.getName().equals(name))
+ .map(Value::from)
+ .findFirst();
+ }
+
+ protected Collection<ResolvedReferenceTypeDeclaration> findTypeDeclarations(Optional<Expression> optScope, TypeSolver typeSolver) {
+ if (optScope.isPresent()) {
+ Expression scope = optScope.get();
+
+ // consider static methods
+ if (scope instanceof NameExpr) {
+ NameExpr scopeAsName = (NameExpr) scope;
+ SymbolReference<ResolvedTypeDeclaration> symbolReference = this.solveType(scopeAsName.getName().getId(), typeSolver);
+ if (symbolReference.isSolved() && symbolReference.getCorrespondingDeclaration().isType()) {
+ return singletonList(symbolReference.getCorrespondingDeclaration().asReferenceType());
+ }
+ }
+
+ ResolvedType typeOfScope;
+ try {
+ typeOfScope = JavaParserFacade.get(typeSolver).getType(scope);
+ } catch (Exception e) {
+ throw new RuntimeException("Issue calculating the type of the scope of " + this, e);
+ }
+ if (typeOfScope.isWildcard()) {
+ if (typeOfScope.asWildcard().isExtends() || typeOfScope.asWildcard().isSuper()) {
+ return singletonList(typeOfScope.asWildcard().getBoundedType().asReferenceType().getTypeDeclaration());
+ } else {
+ return singletonList(new ReflectionClassDeclaration(Object.class, typeSolver).asReferenceType());
+ }
+ } else if (typeOfScope.isArray()) {
+ // method call on array are Object methods
+ return singletonList(new ReflectionClassDeclaration(Object.class, typeSolver).asReferenceType());
+ } else if (typeOfScope.isTypeVariable()) {
+ Collection<ResolvedReferenceTypeDeclaration> result = new ArrayList<>();
+ for (ResolvedTypeParameterDeclaration.Bound bound : typeOfScope.asTypeParameter().getBounds()) {
+ result.add(bound.getType().asReferenceType().getTypeDeclaration());
+ }
+ return result;
+ } else if (typeOfScope.isConstraint()) {
+ return singletonList(typeOfScope.asConstraintType().getBound().asReferenceType().getTypeDeclaration());
+ }
+ return singletonList(typeOfScope.asReferenceType().getTypeDeclaration());
+ }
+ ResolvedType typeOfScope = JavaParserFacade.get(typeSolver).getTypeOfThisIn(wrappedNode);
+ return singletonList(typeOfScope.asReferenceType().getTypeDeclaration());
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractMethodLikeDeclarationContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractMethodLikeDeclarationContext.java
new file mode 100644
index 000000000..d611d71a5
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractMethodLikeDeclarationContext.java
@@ -0,0 +1,101 @@
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.body.Parameter;
+import com.github.javaparser.ast.nodeTypes.NodeWithParameters;
+import com.github.javaparser.ast.nodeTypes.NodeWithTypeParameters;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.resolution.types.ResolvedTypeVariable;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserTypeParameter;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.resolution.Value;
+import com.github.javaparser.symbolsolver.resolution.SymbolDeclarator;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author Federico Tomassetti
+ */
+public abstract class AbstractMethodLikeDeclarationContext
+ <T extends Node & NodeWithParameters<T> & NodeWithTypeParameters<T>> extends AbstractJavaParserContext<T> {
+
+ public AbstractMethodLikeDeclarationContext(T wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+ public final SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
+ for (Parameter parameter : wrappedNode.getParameters()) {
+ SymbolDeclarator sb = JavaParserFactory.getSymbolDeclarator(parameter, typeSolver);
+ SymbolReference<? extends ResolvedValueDeclaration> symbolReference = AbstractJavaParserContext.solveWith(sb, name);
+ if (symbolReference.isSolved()) {
+ return symbolReference;
+ }
+ }
+
+ // if nothing is found we should ask the parent context
+ return getParent().solveSymbol(name, typeSolver);
+ }
+
+ @Override
+ public final Optional<ResolvedType> solveGenericType(String name, TypeSolver typeSolver) {
+ for (com.github.javaparser.ast.type.TypeParameter tp : wrappedNode.getTypeParameters()) {
+ if (tp.getName().getId().equals(name)) {
+ return Optional.of(new ResolvedTypeVariable(new JavaParserTypeParameter(tp, typeSolver)));
+ }
+ }
+ return super.solveGenericType(name, typeSolver);
+ }
+
+ @Override
+ public final Optional<Value> solveSymbolAsValue(String name, TypeSolver typeSolver) {
+ for (Parameter parameter : wrappedNode.getParameters()) {
+ SymbolDeclarator sb = JavaParserFactory.getSymbolDeclarator(parameter, typeSolver);
+ Optional<Value> symbolReference = solveWithAsValue(sb, name, typeSolver);
+ if (symbolReference.isPresent()) {
+ // Perform parameter type substitution as needed
+ return symbolReference;
+ }
+ }
+
+ // if nothing is found we should ask the parent context
+ return getParent().solveSymbolAsValue(name, typeSolver);
+ }
+
+ @Override
+ public final SymbolReference<ResolvedTypeDeclaration> solveType(String name, TypeSolver typeSolver) {
+ if (wrappedNode.getTypeParameters() != null) {
+ for (com.github.javaparser.ast.type.TypeParameter tp : wrappedNode.getTypeParameters()) {
+ if (tp.getName().getId().equals(name)) {
+ return SymbolReference.solved(new JavaParserTypeParameter(tp, typeSolver));
+ }
+ }
+ }
+
+ // Local types
+ List<com.github.javaparser.ast.body.TypeDeclaration> localTypes = wrappedNode.findAll(
+ com.github.javaparser.ast.body.TypeDeclaration.class);
+ for (com.github.javaparser.ast.body.TypeDeclaration<?> localType : localTypes) {
+ if (localType.getName().getId().equals(name)) {
+ return SymbolReference.solved(JavaParserFacade.get(typeSolver).getTypeDeclaration(localType));
+ } else if (name.startsWith(String.format("%s.", localType.getName()))) {
+ return JavaParserFactory.getContext(localType, typeSolver).solveType(
+ name.substring(localType.getName().getId().length() + 1), typeSolver);
+ }
+ }
+
+ return getParent().solveType(name, typeSolver);
+ }
+
+ @Override
+ public final SymbolReference<ResolvedMethodDeclaration> solveMethod(
+ String name, List<ResolvedType> argumentsTypes, boolean staticOnly, TypeSolver typeSolver) {
+ return getParent().solveMethod(name, argumentsTypes, false, typeSolver);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AnonymousClassDeclarationContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AnonymousClassDeclarationContext.java
new file mode 100644
index 000000000..48133596a
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AnonymousClassDeclarationContext.java
@@ -0,0 +1,186 @@
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.NodeList;
+import com.github.javaparser.ast.expr.ObjectCreationExpr;
+import com.github.javaparser.ast.nodeTypes.NodeWithTypeArguments;
+import com.github.javaparser.ast.type.TypeParameter;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations
+ .JavaParserAnonymousClassDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserTypeParameter;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionClassDeclaration;
+import com.github.javaparser.symbolsolver.resolution.MethodResolutionLogic;
+import com.google.common.base.Preconditions;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * A symbol resolution context for an object creation node.
+ */
+public class AnonymousClassDeclarationContext extends AbstractJavaParserContext<ObjectCreationExpr> {
+
+ private final JavaParserAnonymousClassDeclaration myDeclaration =
+ new JavaParserAnonymousClassDeclaration(wrappedNode, typeSolver);
+
+ public AnonymousClassDeclarationContext(ObjectCreationExpr node, TypeSolver typeSolver) {
+ super(node, typeSolver);
+ Preconditions.checkArgument(node.getAnonymousClassBody().isPresent(),
+ "An anonymous class must have a body");
+ }
+
+ @Override
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name,
+ List<ResolvedType> argumentsTypes,
+ boolean staticOnly,
+ TypeSolver typeSolver) {
+ List<ResolvedMethodDeclaration> candidateMethods =
+ myDeclaration
+ .getDeclaredMethods()
+ .stream()
+ .filter(m -> m.getName().equals(name) && (!staticOnly || m.isStatic()))
+ .collect(Collectors.toList());
+
+ if (!Object.class.getCanonicalName().equals(myDeclaration.getQualifiedName())) {
+ for (ResolvedReferenceType ancestor : myDeclaration.getAncestors()) {
+ SymbolReference<ResolvedMethodDeclaration> res =
+ MethodResolutionLogic.solveMethodInType(ancestor.getTypeDeclaration(),
+ name,
+ argumentsTypes,
+ staticOnly,
+ typeSolver);
+ // consider methods from superclasses and only default methods from interfaces :
+ // not true, we should keep abstract as a valid candidate
+ // abstract are removed in MethodResolutionLogic.isApplicable is necessary
+ if (res.isSolved()) {
+ candidateMethods.add(res.getCorrespondingDeclaration());
+ }
+ }
+ }
+
+ // We want to avoid infinite recursion when a class is using its own method
+ // see issue #75
+ if (candidateMethods.isEmpty()) {
+ SymbolReference<ResolvedMethodDeclaration> parentSolution =
+ getParent().solveMethod(name, argumentsTypes, staticOnly, typeSolver);
+ if (parentSolution.isSolved()) {
+ candidateMethods.add(parentSolution.getCorrespondingDeclaration());
+ }
+ }
+
+ // if is interface and candidate method list is empty, we should check the Object Methods
+ if (candidateMethods.isEmpty() && myDeclaration.getSuperTypeDeclaration().isInterface()) {
+ SymbolReference<ResolvedMethodDeclaration> res =
+ MethodResolutionLogic.solveMethodInType(new ReflectionClassDeclaration(Object.class,
+ typeSolver),
+ name,
+ argumentsTypes,
+ false,
+ typeSolver);
+ if (res.isSolved()) {
+ candidateMethods.add(res.getCorrespondingDeclaration());
+ }
+ }
+
+ return MethodResolutionLogic.findMostApplicable(candidateMethods,
+ name,
+ argumentsTypes,
+ typeSolver);
+ }
+
+ @Override
+ public SymbolReference<ResolvedTypeDeclaration> solveType(String name, TypeSolver typeSolver) {
+ List<com.github.javaparser.ast.body.TypeDeclaration> typeDeclarations =
+ myDeclaration
+ .findMembersOfKind(com.github.javaparser.ast.body.TypeDeclaration.class);
+
+ Optional<SymbolReference<ResolvedTypeDeclaration>> exactMatch =
+ typeDeclarations
+ .stream()
+ .filter(internalType -> internalType.getName().getId().equals(name))
+ .findFirst()
+ .map(internalType ->
+ SymbolReference.solved(
+ JavaParserFacade.get(typeSolver).getTypeDeclaration(internalType)));
+
+ if(exactMatch.isPresent()){
+ return exactMatch.get();
+ }
+
+ Optional<SymbolReference<ResolvedTypeDeclaration>> recursiveMatch =
+ typeDeclarations
+ .stream()
+ .filter(internalType -> name.startsWith(String.format("%s.", internalType.getName())))
+ .findFirst()
+ .map(internalType ->
+ JavaParserFactory
+ .getContext(internalType, typeSolver)
+ .solveType(name.substring(internalType.getName().getId().length() + 1),
+ typeSolver));
+
+ if (recursiveMatch.isPresent()) {
+ return recursiveMatch.get();
+ }
+
+ Optional<SymbolReference<ResolvedTypeDeclaration>> typeArgumentsMatch =
+ wrappedNode
+ .getTypeArguments()
+ .map(nodes ->
+ ((NodeWithTypeArguments<?>) nodes).getTypeArguments()
+ .orElse(new NodeList<>()))
+ .orElse(new NodeList<>())
+ .stream()
+ .filter(type -> type.toString().equals(name))
+ .findFirst()
+ .map(matchingType ->
+ SymbolReference.solved(
+ new JavaParserTypeParameter(new TypeParameter(matchingType.toString()),
+ typeSolver)));
+
+ if (typeArgumentsMatch.isPresent()) {
+ return typeArgumentsMatch.get();
+ }
+
+ // Look into extended classes and implemented interfaces
+ for (ResolvedReferenceType ancestor : myDeclaration.getAncestors()) {
+ // look at names of extended classes and implemented interfaces (this may not be important because they are checked in CompilationUnitContext)
+ if (ancestor.getTypeDeclaration().getName().equals(name)) {
+ return SymbolReference.solved(ancestor.getTypeDeclaration());
+ }
+ // look into internal types of extended classes and implemented interfaces
+ try {
+ for (ResolvedTypeDeclaration internalTypeDeclaration : ancestor.getTypeDeclaration().internalTypes()) {
+ if (internalTypeDeclaration.getName().equals(name)) {
+ return SymbolReference.solved(internalTypeDeclaration);
+ }
+ }
+ } catch (UnsupportedOperationException e) {
+ // just continue using the next ancestor
+ }
+ }
+
+ return getParent().solveType(name, typeSolver);
+ }
+
+ @Override
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name,
+ TypeSolver typeSolver) {
+ Preconditions.checkArgument(typeSolver != null);
+
+ if (myDeclaration.hasVisibleField(name)) {
+ return SymbolReference.solved(myDeclaration.getVisibleField(name));
+ }
+
+ return getParent().solveSymbol(name, typeSolver);
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/CatchClauseContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/CatchClauseContext.java
new file mode 100644
index 000000000..4683f272a
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/CatchClauseContext.java
@@ -0,0 +1,54 @@
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.stmt.CatchClause;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.resolution.Value;
+import com.github.javaparser.symbolsolver.resolution.SymbolDeclarator;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author Fred Lefévère-Laoide
+ */
+public class CatchClauseContext extends AbstractJavaParserContext<CatchClause> {
+
+ public CatchClauseContext(CatchClause wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+ public final SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
+ SymbolDeclarator sb = JavaParserFactory.getSymbolDeclarator(wrappedNode.getParameter(), typeSolver);
+ SymbolReference<? extends ResolvedValueDeclaration> symbolReference = AbstractJavaParserContext.solveWith(sb, name);
+ if (symbolReference.isSolved()) {
+ return symbolReference;
+ }
+
+ // if nothing is found we should ask the parent context
+ return getParent().solveSymbol(name, typeSolver);
+ }
+
+ @Override
+ public final Optional<Value> solveSymbolAsValue(String name, TypeSolver typeSolver) {
+ SymbolDeclarator sb = JavaParserFactory.getSymbolDeclarator(wrappedNode.getParameter(), typeSolver);
+ Optional<Value> symbolReference = solveWithAsValue(sb, name, typeSolver);
+ if (symbolReference.isPresent()) {
+ // Perform parameter type substitution as needed
+ return symbolReference;
+ }
+
+ // if nothing is found we should ask the parent context
+ return getParent().solveSymbolAsValue(name, typeSolver);
+ }
+
+ @Override
+ public final SymbolReference<ResolvedMethodDeclaration> solveMethod(
+ String name, List<ResolvedType> argumentsTypes, boolean staticOnly, TypeSolver typeSolver) {
+ return getParent().solveMethod(name, argumentsTypes, false, typeSolver);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ClassOrInterfaceDeclarationContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ClassOrInterfaceDeclarationContext.java
new file mode 100644
index 000000000..01f4b050c
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ClassOrInterfaceDeclarationContext.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.resolution.types.ResolvedTypeVariable;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserTypeParameter;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.resolution.Value;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ClassOrInterfaceDeclarationContext extends AbstractJavaParserContext<ClassOrInterfaceDeclaration> {
+
+ private JavaParserTypeDeclarationAdapter javaParserTypeDeclarationAdapter;
+
+ ///
+ /// Constructors
+ ///
+
+ public ClassOrInterfaceDeclarationContext(ClassOrInterfaceDeclaration wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ this.javaParserTypeDeclarationAdapter = new JavaParserTypeDeclarationAdapter(wrappedNode, typeSolver,
+ getDeclaration(), this);
+ }
+
+ ///
+ /// Public methods
+ ///
+
+ @Override
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
+ if (typeSolver == null) throw new IllegalArgumentException();
+
+ if (this.getDeclaration().hasVisibleField(name)) {
+ return SymbolReference.solved(this.getDeclaration().getVisibleField(name));
+ }
+
+ // then to parent
+ return getParent().solveSymbol(name, typeSolver);
+ }
+
+ @Override
+ public Optional<Value> solveSymbolAsValue(String name, TypeSolver typeSolver) {
+ if (typeSolver == null) throw new IllegalArgumentException();
+
+ if (this.getDeclaration().hasVisibleField(name)) {
+ return Optional.of(Value.from(this.getDeclaration().getVisibleField(name)));
+ }
+
+ // then to parent
+ return getParent().solveSymbolAsValue(name, typeSolver);
+ }
+
+ @Override
+ public Optional<ResolvedType> solveGenericType(String name, TypeSolver typeSolver) {
+ for (com.github.javaparser.ast.type.TypeParameter tp : wrappedNode.getTypeParameters()) {
+ if (tp.getName().getId().equals(name)) {
+ return Optional.of(new ResolvedTypeVariable(new JavaParserTypeParameter(tp, typeSolver)));
+ }
+ }
+ return getParent().solveGenericType(name, typeSolver);
+ }
+
+ @Override
+ public SymbolReference<ResolvedTypeDeclaration> solveType(String name, TypeSolver typeSolver) {
+ return javaParserTypeDeclarationAdapter.solveType(name, typeSolver);
+ }
+
+ @Override
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> argumentsTypes, boolean staticOnly, TypeSolver typeSolver) {
+ return javaParserTypeDeclarationAdapter.solveMethod(name, argumentsTypes, staticOnly, typeSolver);
+ }
+
+ public SymbolReference<ResolvedConstructorDeclaration> solveConstructor(List<ResolvedType> argumentsTypes, TypeSolver typeSolver) {
+ return javaParserTypeDeclarationAdapter.solveConstructor(argumentsTypes, typeSolver);
+ }
+
+ ///
+ /// Private methods
+ ///
+
+ private ResolvedReferenceTypeDeclaration getDeclaration() {
+ return JavaParserFacade.get(typeSolver).getTypeDeclaration(this.wrappedNode);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/CompilationUnitContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/CompilationUnitContext.java
new file mode 100644
index 000000000..e6a4aa0d5
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/CompilationUnitContext.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.ast.ImportDeclaration;
+import com.github.javaparser.ast.body.AnnotationDeclaration;
+import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
+import com.github.javaparser.ast.body.EnumDeclaration;
+import com.github.javaparser.ast.body.TypeDeclaration;
+import com.github.javaparser.ast.expr.MethodCallExpr;
+import com.github.javaparser.ast.expr.Name;
+import com.github.javaparser.ast.type.ClassOrInterfaceType;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserAnnotationDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserEnumDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.resolution.MethodResolutionLogic;
+import com.github.javaparser.symbolsolver.resolution.SymbolSolver;
+
+import java.util.List;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class CompilationUnitContext extends AbstractJavaParserContext<CompilationUnit> {
+
+ ///
+ /// Static methods
+ ///
+
+ private static boolean isQualifiedName(String name) {
+ return name.contains(".");
+ }
+
+ ///
+ /// Constructors
+ ///
+
+ public CompilationUnitContext(CompilationUnit wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+ ///
+ /// Public methods
+ ///
+
+ @Override
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
+
+ // solve absolute references
+ String itName = name;
+ while (itName.contains(".")) {
+ String typeName = getType(itName);
+ String memberName = getMember(itName);
+ SymbolReference<ResolvedTypeDeclaration> type = this.solveType(typeName, typeSolver);
+ if (type.isSolved()) {
+ return new SymbolSolver(typeSolver).solveSymbolInType(type.getCorrespondingDeclaration(), memberName);
+ } else {
+ itName = typeName;
+ }
+ }
+
+ // Look among statically imported values
+ if (wrappedNode.getImports() != null) {
+ for (ImportDeclaration importDecl : wrappedNode.getImports()) {
+ if(importDecl.isStatic()){
+ if(importDecl.isAsterisk()) {
+ String qName = importDecl.getNameAsString();
+ ResolvedTypeDeclaration importedType = typeSolver.solveType(qName);
+ SymbolReference<? extends ResolvedValueDeclaration> ref = new SymbolSolver(typeSolver).solveSymbolInType(importedType, name);
+ if (ref.isSolved()) {
+ return ref;
+ }
+ } else{
+ String whole = importDecl.getNameAsString();
+
+ // split in field/method name and type name
+ String memberName = getMember(whole);
+ String typeName = getType(whole);
+
+ if (memberName.equals(name)) {
+ ResolvedTypeDeclaration importedType = typeSolver.solveType(typeName);
+ return new SymbolSolver(typeSolver).solveSymbolInType(importedType, memberName);
+ }
+ }
+ }
+ }
+ }
+
+ return SymbolReference.unsolved(ResolvedValueDeclaration.class);
+ }
+
+ @Override
+ public SymbolReference<ResolvedTypeDeclaration> solveType(String name, TypeSolver typeSolver) {
+ if (wrappedNode.getTypes() != null) {
+ for (TypeDeclaration<?> type : wrappedNode.getTypes()) {
+ if (type.getName().getId().equals(name)) {
+ if (type instanceof ClassOrInterfaceDeclaration) {
+ return SymbolReference.solved(JavaParserFacade.get(typeSolver).getTypeDeclaration((ClassOrInterfaceDeclaration) type));
+ } else if (type instanceof AnnotationDeclaration) {
+ return SymbolReference.solved(new JavaParserAnnotationDeclaration((AnnotationDeclaration) type, typeSolver));
+ } else if (type instanceof EnumDeclaration) {
+ return SymbolReference.solved(new JavaParserEnumDeclaration((EnumDeclaration) type, typeSolver));
+ } else {
+ throw new UnsupportedOperationException(type.getClass().getCanonicalName());
+ }
+ }
+ }
+ }
+
+ if (wrappedNode.getImports() != null) {
+ int dotPos = name.indexOf('.');
+ String prefix = null;
+ if (dotPos > -1) {
+ prefix = name.substring(0, dotPos);
+ }
+ // look into type imports
+ for (ImportDeclaration importDecl : wrappedNode.getImports()) {
+ if (!importDecl.isAsterisk()) {
+ String qName = importDecl.getNameAsString();
+ boolean defaultPackage = !importDecl.getName().getQualifier().isPresent();
+ boolean found = !defaultPackage && importDecl.getName().getIdentifier().equals(name);
+ if (!found) {
+ if (prefix != null) {
+ found = qName.endsWith("." + prefix);
+ if (found) {
+ qName = qName + name.substring(dotPos);
+ }
+ }
+ }
+ if (found) {
+ SymbolReference<ResolvedReferenceTypeDeclaration> ref = typeSolver.tryToSolveType(qName);
+ if (ref.isSolved()) {
+ return SymbolReference.adapt(ref, ResolvedTypeDeclaration.class);
+ }
+ }
+ }
+ }
+ // look into type imports on demand
+ for (ImportDeclaration importDecl : wrappedNode.getImports()) {
+ if (importDecl.isAsterisk()) {
+ String qName = importDecl.getNameAsString() + "." + name;
+ SymbolReference<ResolvedReferenceTypeDeclaration> ref = typeSolver.tryToSolveType(qName);
+ if (ref.isSolved()) {
+ return SymbolReference.adapt(ref, ResolvedTypeDeclaration.class);
+ }
+ }
+ }
+ }
+
+ // Look in current package
+ if (this.wrappedNode.getPackageDeclaration().isPresent()) {
+ String qName = this.wrappedNode.getPackageDeclaration().get().getName().toString() + "." + name;
+ SymbolReference<ResolvedReferenceTypeDeclaration> ref = typeSolver.tryToSolveType(qName);
+ if (ref.isSolved()) {
+ return SymbolReference.adapt(ref, ResolvedTypeDeclaration.class);
+ }
+ } else {
+ // look for classes in the default package
+ String qName = name;
+ SymbolReference<ResolvedReferenceTypeDeclaration> ref = typeSolver.tryToSolveType(qName);
+ if (ref.isSolved()) {
+ return SymbolReference.adapt(ref, ResolvedTypeDeclaration.class);
+ }
+ }
+
+ // Look in the java.lang package
+ SymbolReference<ResolvedReferenceTypeDeclaration> ref = typeSolver.tryToSolveType("java.lang." + name);
+ if (ref.isSolved()) {
+ return SymbolReference.adapt(ref, ResolvedTypeDeclaration.class);
+ }
+
+ // DO NOT look for absolute name if this name is not qualified: you cannot import classes from the default package
+ if (isQualifiedName(name)) {
+ return SymbolReference.adapt(typeSolver.tryToSolveType(name), ResolvedTypeDeclaration.class);
+ } else {
+ return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class);
+ }
+ }
+
+ private String qName(ClassOrInterfaceType type) {
+ if (type.getScope().isPresent()) {
+ return qName(type.getScope().get()) + "." + type.getName().getId();
+ } else {
+ return type.getName().getId();
+ }
+ }
+
+ private String qName(Name name) {
+ if (name.getQualifier().isPresent()) {
+ return qName(name.getQualifier().get()) + "." + name.getId();
+ } else {
+ return name.getId();
+ }
+ }
+
+ private String toSimpleName(String qName) {
+ String[] parts = qName.split("\\.");
+ return parts[parts.length - 1];
+ }
+
+ private String packageName(String qName) {
+ int lastDot = qName.lastIndexOf('.');
+ if (lastDot == -1) {
+ throw new UnsupportedOperationException();
+ } else {
+ return qName.substring(0, lastDot);
+ }
+ }
+
+ @Override
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> argumentsTypes, boolean staticOnly, TypeSolver typeSolver) {
+ for (ImportDeclaration importDecl : wrappedNode.getImports()) {
+ if(importDecl.isStatic()){
+ if(importDecl.isAsterisk()){
+ String importString = importDecl.getNameAsString();
+
+ if (this.wrappedNode.getPackageDeclaration().isPresent()
+ && this.wrappedNode.getPackageDeclaration().get().getName().getIdentifier().equals(packageName(importString))
+ && this.wrappedNode.getTypes().stream().anyMatch(it -> it.getName().getIdentifier().equals(toSimpleName(importString)))) {
+ // We are using a static import on a type defined in this file. It means the value was not found at
+ // a lower level so this will fail
+ return SymbolReference.unsolved(ResolvedMethodDeclaration.class);
+ }
+
+ ResolvedTypeDeclaration ref = typeSolver.solveType(importString);
+ SymbolReference<ResolvedMethodDeclaration> method = MethodResolutionLogic.solveMethodInType(ref, name, argumentsTypes, true, typeSolver);
+
+ if (method.isSolved()) {
+ return method;
+ }
+ } else{
+ String qName = importDecl.getNameAsString();
+
+ if (qName.equals(name) || qName.endsWith("." + name)) {
+ String typeName = getType(qName);
+ ResolvedTypeDeclaration ref = typeSolver.solveType(typeName);
+ SymbolReference<ResolvedMethodDeclaration> method = MethodResolutionLogic.solveMethodInType(ref, name, argumentsTypes, true, typeSolver);
+ if (method.isSolved()) {
+ return method;
+ }
+ }
+ }
+ }
+ }
+ return SymbolReference.unsolved(ResolvedMethodDeclaration.class);
+ }
+
+ ///
+ /// Private methods
+ ///
+
+ private String getType(String qName) {
+ int index = qName.lastIndexOf('.');
+ if (index == -1) {
+ throw new UnsupportedOperationException();
+ }
+ String typeName = qName.substring(0, index);
+ return typeName;
+ }
+
+ private String getMember(String qName) {
+ int index = qName.lastIndexOf('.');
+ if (index == -1) {
+ throw new UnsupportedOperationException();
+ }
+ String memberName = qName.substring(index + 1);
+ return memberName;
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ConstructorContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ConstructorContext.java
new file mode 100644
index 000000000..bebaa7313
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ConstructorContext.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.body.ConstructorDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ConstructorContext extends AbstractMethodLikeDeclarationContext<ConstructorDeclaration> {
+
+ ///
+ /// Constructors
+ ///
+
+ public ConstructorContext(ConstructorDeclaration wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ContextHelper.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ContextHelper.java
new file mode 100644
index 000000000..e0eda24b0
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ContextHelper.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserAnonymousClassDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserClassDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserEnumDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserInterfaceDeclaration;
+import com.github.javaparser.symbolsolver.javassistmodel.JavassistClassDeclaration;
+import com.github.javaparser.symbolsolver.javassistmodel.JavassistEnumDeclaration;
+import com.github.javaparser.symbolsolver.javassistmodel.JavassistInterfaceDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionClassDeclaration;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionEnumDeclaration;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionInterfaceDeclaration;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ContextHelper {
+
+ private ContextHelper() {
+ // prevent instantiation
+ }
+
+ public static Optional<MethodUsage> solveMethodAsUsage(ResolvedTypeDeclaration typeDeclaration, String name,
+ List<ResolvedType> argumentsTypes, TypeSolver typeSolver,
+ Context invokationContext, List<ResolvedType> typeParameters) {
+ if (typeDeclaration instanceof JavassistClassDeclaration) {
+ return ((JavassistClassDeclaration) typeDeclaration).solveMethodAsUsage(name, argumentsTypes, typeSolver, invokationContext, typeParameters);
+ } else if (typeDeclaration instanceof JavassistInterfaceDeclaration) {
+ return ((JavassistInterfaceDeclaration) typeDeclaration).solveMethodAsUsage(name, argumentsTypes, typeSolver, invokationContext, typeParameters);
+ } else if (typeDeclaration instanceof JavassistEnumDeclaration) {
+ return ((JavassistEnumDeclaration) typeDeclaration).solveMethodAsUsage(name, argumentsTypes, typeSolver, invokationContext, typeParameters);
+ } else if (typeDeclaration instanceof ReflectionClassDeclaration) {
+ return ((ReflectionClassDeclaration) typeDeclaration).solveMethodAsUsage(name, argumentsTypes, typeSolver, invokationContext, typeParameters);
+ } else if (typeDeclaration instanceof ReflectionInterfaceDeclaration) {
+ return ((ReflectionInterfaceDeclaration) typeDeclaration).solveMethodAsUsage(name, argumentsTypes, typeSolver, invokationContext, typeParameters);
+ } else if (typeDeclaration instanceof ReflectionEnumDeclaration) {
+ return ((ReflectionEnumDeclaration) typeDeclaration).solveMethodAsUsage(name, argumentsTypes, typeSolver, invokationContext, typeParameters);
+ } else if (typeDeclaration instanceof JavaParserClassDeclaration) {
+ return ((JavaParserClassDeclaration) typeDeclaration).getContext().solveMethodAsUsage(name, argumentsTypes, typeSolver);
+ } else if (typeDeclaration instanceof JavaParserInterfaceDeclaration) {
+ return ((JavaParserInterfaceDeclaration) typeDeclaration).getContext().solveMethodAsUsage(name, argumentsTypes, typeSolver);
+ } else if (typeDeclaration instanceof JavaParserEnumDeclaration) {
+ return ((JavaParserEnumDeclaration) typeDeclaration).getContext().solveMethodAsUsage(name, argumentsTypes, typeSolver);
+ } else if (typeDeclaration instanceof JavaParserAnonymousClassDeclaration) {
+ return ((JavaParserAnonymousClassDeclaration) typeDeclaration).getContext().solveMethodAsUsage(name, argumentsTypes, typeSolver);
+ }
+ throw new UnsupportedOperationException(typeDeclaration.toString());
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/EnumDeclarationContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/EnumDeclarationContext.java
new file mode 100644
index 000000000..7f2365fe7
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/EnumDeclarationContext.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.body.EnumConstantDeclaration;
+import com.github.javaparser.ast.body.EnumDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserEnumConstantDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserEnumDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.List;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class EnumDeclarationContext extends AbstractJavaParserContext<EnumDeclaration> {
+
+ private JavaParserTypeDeclarationAdapter javaParserTypeDeclarationAdapter;
+
+ public EnumDeclarationContext(EnumDeclaration wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ this.javaParserTypeDeclarationAdapter = new JavaParserTypeDeclarationAdapter(wrappedNode, typeSolver,
+ getDeclaration(), this);
+ }
+
+ @Override
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
+ if (typeSolver == null) throw new IllegalArgumentException();
+
+ // among constants
+ for (EnumConstantDeclaration constant : wrappedNode.getEntries()) {
+ if (constant.getName().getId().equals(name)) {
+ return SymbolReference.solved(new JavaParserEnumConstantDeclaration(constant, typeSolver));
+ }
+ }
+
+ if (this.getDeclaration().hasField(name)) {
+ return SymbolReference.solved(this.getDeclaration().getField(name));
+ }
+
+ // then to parent
+ return getParent().solveSymbol(name, typeSolver);
+ }
+
+ @Override
+ public SymbolReference<ResolvedTypeDeclaration> solveType(String name, TypeSolver typeSolver) {
+ return javaParserTypeDeclarationAdapter.solveType(name, typeSolver);
+ }
+
+ @Override
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> argumentsTypes, boolean staticOnly, TypeSolver typeSolver) {
+ return javaParserTypeDeclarationAdapter.solveMethod(name, argumentsTypes, staticOnly, typeSolver);
+ }
+
+ ///
+ /// Private methods
+ ///
+
+ private ResolvedReferenceTypeDeclaration getDeclaration() {
+ return new JavaParserEnumDeclaration(this.wrappedNode, typeSolver);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/FieldAccessContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/FieldAccessContext.java
new file mode 100644
index 000000000..65ccedf00
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/FieldAccessContext.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.expr.Expression;
+import com.github.javaparser.ast.expr.FieldAccessExpr;
+import com.github.javaparser.ast.expr.ThisExpr;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedFieldDeclaration;
+import com.github.javaparser.resolution.types.ResolvedPrimitiveType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.resolution.Value;
+import com.github.javaparser.symbolsolver.resolution.SymbolSolver;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentNode;
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class FieldAccessContext extends AbstractJavaParserContext<FieldAccessExpr> {
+
+ private static final String ARRAY_LENGTH_FIELD_NAME = "length";
+
+ public FieldAccessContext(FieldAccessExpr wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+ @Override
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
+ if (wrappedNode.getName().toString().equals(name)) {
+ if (wrappedNode.getScope() instanceof ThisExpr) {
+ ResolvedType typeOfThis = JavaParserFacade.get(typeSolver).getTypeOfThisIn(wrappedNode);
+ return new SymbolSolver(typeSolver).solveSymbolInType(typeOfThis.asReferenceType().getTypeDeclaration(), name);
+ }
+ }
+ return JavaParserFactory.getContext(requireParentNode(wrappedNode), typeSolver).solveSymbol(name, typeSolver);
+ }
+
+ @Override
+ public SymbolReference<ResolvedTypeDeclaration> solveType(String name, TypeSolver typeSolver) {
+ return JavaParserFactory.getContext(requireParentNode(wrappedNode), typeSolver).solveType(name, typeSolver);
+ }
+
+ @Override
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> parameterTypes, boolean staticOnly, TypeSolver typeSolver) {
+ return JavaParserFactory.getContext(requireParentNode(wrappedNode), typeSolver).solveMethod(name, parameterTypes, false, typeSolver);
+ }
+
+ @Override
+ public Optional<Value> solveSymbolAsValue(String name, TypeSolver typeSolver) {
+ Expression scope = wrappedNode.getScope();
+ if (wrappedNode.getName().toString().equals(name)) {
+ ResolvedType typeOfScope = JavaParserFacade.get(typeSolver).getType(scope);
+ if (typeOfScope.isArray() && name.equals(ARRAY_LENGTH_FIELD_NAME)) {
+ return Optional.of(new Value(ResolvedPrimitiveType.INT, ARRAY_LENGTH_FIELD_NAME));
+ }
+ if (typeOfScope.isReferenceType()) {
+ Optional<ResolvedType> typeUsage = typeOfScope.asReferenceType().getFieldType(name);
+ return typeUsage.map(resolvedType -> new Value(resolvedType, name));
+ } else {
+ return Optional.empty();
+ }
+ } else {
+ return getParent().solveSymbolAsValue(name, typeSolver);
+ }
+ }
+
+ public SymbolReference<ResolvedFieldDeclaration> solveField(String name, TypeSolver typeSolver) {
+ Collection<ResolvedReferenceTypeDeclaration> rrtds = findTypeDeclarations(Optional.of(wrappedNode.getScope()), typeSolver);
+ for (ResolvedReferenceTypeDeclaration rrtd : rrtds) {
+ try {
+ return SymbolReference.solved(rrtd.getField(wrappedNode.getName().getId()));
+ } catch (Throwable t) {
+ }
+ }
+ return SymbolReference.unsolved(ResolvedFieldDeclaration.class);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ForStatementContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ForStatementContext.java
new file mode 100644
index 000000000..cf3fe3b0c
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ForStatementContext.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.body.VariableDeclarator;
+import com.github.javaparser.ast.expr.AssignExpr;
+import com.github.javaparser.ast.expr.Expression;
+import com.github.javaparser.ast.expr.MethodCallExpr;
+import com.github.javaparser.ast.expr.VariableDeclarationExpr;
+import com.github.javaparser.ast.nodeTypes.NodeWithStatements;
+import com.github.javaparser.ast.stmt.ForStmt;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserSymbolDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.List;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode;
+
+public class ForStatementContext extends AbstractJavaParserContext<ForStmt> {
+
+ public ForStatementContext(ForStmt wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+ @Override
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
+ for (Expression expression : wrappedNode.getInitialization()) {
+ if (expression instanceof VariableDeclarationExpr) {
+ VariableDeclarationExpr variableDeclarationExpr = (VariableDeclarationExpr) expression;
+ for (VariableDeclarator variableDeclarator : variableDeclarationExpr.getVariables()) {
+ if (variableDeclarator.getName().getId().equals(name)) {
+ return SymbolReference.solved(JavaParserSymbolDeclaration.localVar(variableDeclarator, typeSolver));
+ }
+ }
+ } else if (!(expression instanceof AssignExpr || expression instanceof MethodCallExpr)) {
+ throw new UnsupportedOperationException(expression.getClass().getCanonicalName());
+ }
+ }
+
+ if (requireParentNode(wrappedNode) instanceof NodeWithStatements) {
+ return StatementContext.solveInBlock(name, typeSolver, wrappedNode);
+ } else {
+ return getParent().solveSymbol(name, typeSolver);
+ }
+ }
+
+ @Override
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> argumentsTypes,
+ boolean staticOnly, TypeSolver typeSolver) {
+ return getParent().solveMethod(name, argumentsTypes, false, typeSolver);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ForechStatementContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ForechStatementContext.java
new file mode 100644
index 000000000..ba412023d
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ForechStatementContext.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.body.VariableDeclarator;
+import com.github.javaparser.ast.stmt.BlockStmt;
+import com.github.javaparser.ast.stmt.ForeachStmt;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserSymbolDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.List;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode;
+
+public class ForechStatementContext extends AbstractJavaParserContext<ForeachStmt> {
+
+ public ForechStatementContext(ForeachStmt wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+ @Override
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
+ if (wrappedNode.getVariable().getVariables().size() != 1) {
+ throw new IllegalStateException();
+ }
+ VariableDeclarator variableDeclarator = wrappedNode.getVariable().getVariables().get(0);
+ if (variableDeclarator.getName().getId().equals(name)) {
+ return SymbolReference.solved(JavaParserSymbolDeclaration.localVar(variableDeclarator, typeSolver));
+ } else {
+ if (requireParentNode(wrappedNode) instanceof BlockStmt) {
+ return StatementContext.solveInBlock(name, typeSolver, wrappedNode);
+ } else {
+ return getParent().solveSymbol(name, typeSolver);
+ }
+ }
+ }
+
+ @Override
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> argumentsTypes,
+ boolean staticOnly, TypeSolver typeSolver) {
+ return getParent().solveMethod(name, argumentsTypes, false, typeSolver);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/JavaParserTypeDeclarationAdapter.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/JavaParserTypeDeclarationAdapter.java
new file mode 100644
index 000000000..993f42d29
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/JavaParserTypeDeclarationAdapter.java
@@ -0,0 +1,133 @@
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.body.BodyDeclaration;
+import com.github.javaparser.ast.nodeTypes.NodeWithTypeParameters;
+import com.github.javaparser.ast.type.TypeParameter;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserTypeParameter;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.reflectionmodel.*;
+import com.github.javaparser.symbolsolver.resolution.ConstructorResolutionLogic;
+import com.github.javaparser.symbolsolver.resolution.MethodResolutionLogic;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavaParserTypeDeclarationAdapter {
+
+ private com.github.javaparser.ast.body.TypeDeclaration<?> wrappedNode;
+ private TypeSolver typeSolver;
+ private Context context;
+ private ResolvedReferenceTypeDeclaration typeDeclaration;
+
+ public JavaParserTypeDeclarationAdapter(com.github.javaparser.ast.body.TypeDeclaration<?> wrappedNode, TypeSolver typeSolver,
+ ResolvedReferenceTypeDeclaration typeDeclaration,
+ Context context) {
+ this.wrappedNode = wrappedNode;
+ this.typeSolver = typeSolver;
+ this.typeDeclaration = typeDeclaration;
+ this.context = context;
+ }
+
+ public SymbolReference<ResolvedTypeDeclaration> solveType(String name, TypeSolver typeSolver) {
+ if (this.wrappedNode.getName().getId().equals(name)) {
+ return SymbolReference.solved(JavaParserFacade.get(typeSolver).getTypeDeclaration(wrappedNode));
+ }
+
+ // Internal classes
+ for (BodyDeclaration<?> member : this.wrappedNode.getMembers()) {
+ if (member instanceof com.github.javaparser.ast.body.TypeDeclaration) {
+ com.github.javaparser.ast.body.TypeDeclaration<?> internalType = (com.github.javaparser.ast.body.TypeDeclaration<?>) member;
+ if (internalType.getName().getId().equals(name)) {
+ return SymbolReference.solved(JavaParserFacade.get(typeSolver).getTypeDeclaration(internalType));
+ } else if (name.startsWith(String.format("%s.%s", wrappedNode.getName(), internalType.getName()))) {
+ return JavaParserFactory.getContext(internalType, typeSolver).solveType(name.substring(wrappedNode.getName().getId().length() + 1), typeSolver);
+ } else if (name.startsWith(String.format("%s.", internalType.getName()))) {
+ return JavaParserFactory.getContext(internalType, typeSolver).solveType(name.substring(internalType.getName().getId().length() + 1), typeSolver);
+ }
+ }
+ }
+
+ if (wrappedNode instanceof NodeWithTypeParameters) {
+ NodeWithTypeParameters<?> nodeWithTypeParameters = (NodeWithTypeParameters<?>) wrappedNode;
+ for (TypeParameter astTpRaw : nodeWithTypeParameters.getTypeParameters()) {
+ TypeParameter astTp = astTpRaw;
+ if (astTp.getName().getId().equals(name)) {
+ return SymbolReference.solved(new JavaParserTypeParameter(astTp, typeSolver));
+ }
+ }
+ }
+
+ // Look into extended classes and implemented interfaces
+ for (ResolvedReferenceType ancestor : this.typeDeclaration.getAncestors()) {
+ try {
+ for (ResolvedTypeDeclaration internalTypeDeclaration : ancestor.getTypeDeclaration().internalTypes()) {
+ if (internalTypeDeclaration.getName().equals(name)) {
+ return SymbolReference.solved(internalTypeDeclaration);
+ }
+ }
+ } catch (UnsupportedOperationException e) {
+ // just continue using the next ancestor
+ }
+ }
+
+ return context.getParent().solveType(name, typeSolver);
+ }
+
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> argumentsTypes, boolean staticOnly, TypeSolver typeSolver) {
+ List<ResolvedMethodDeclaration> candidateMethods = typeDeclaration.getDeclaredMethods().stream()
+ .filter(m -> m.getName().equals(name))
+ .filter(m -> !staticOnly || (staticOnly && m.isStatic()))
+ .collect(Collectors.toList());
+ // We want to avoid infinite recursion in case of Object having Object as ancestor
+ if (!Object.class.getCanonicalName().equals(typeDeclaration.getQualifiedName())) {
+ for (ResolvedReferenceType ancestor : typeDeclaration.getAncestors()) {
+ // Avoid recursion on self
+ if (typeDeclaration != ancestor.getTypeDeclaration()) {
+ SymbolReference<ResolvedMethodDeclaration> res = MethodResolutionLogic
+ .solveMethodInType(ancestor.getTypeDeclaration(), name, argumentsTypes, staticOnly, typeSolver);
+ // consider methods from superclasses and only default methods from interfaces :
+ // not true, we should keep abstract as a valid candidate
+ // abstract are removed in MethodResolutionLogic.isApplicable is necessary
+ if (res.isSolved()) {
+ candidateMethods.add(res.getCorrespondingDeclaration());
+ }
+ }
+ }
+ }
+ // We want to avoid infinite recursion when a class is using its own method
+ // see issue #75
+ if (candidateMethods.isEmpty()) {
+ SymbolReference<ResolvedMethodDeclaration> parentSolution = context.getParent().solveMethod(name, argumentsTypes, staticOnly, typeSolver);
+ if (parentSolution.isSolved()) {
+ candidateMethods.add(parentSolution.getCorrespondingDeclaration());
+ }
+ }
+
+ // if is interface and candidate method list is empty, we should check the Object Methods
+ if (candidateMethods.isEmpty() && typeDeclaration.isInterface()) {
+ SymbolReference<ResolvedMethodDeclaration> res = MethodResolutionLogic.solveMethodInType(new ReflectionClassDeclaration(Object.class, typeSolver), name, argumentsTypes, false, typeSolver);
+ if (res.isSolved()) {
+ candidateMethods.add(res.getCorrespondingDeclaration());
+ }
+ }
+
+ return MethodResolutionLogic.findMostApplicable(candidateMethods, name, argumentsTypes, typeSolver);
+ }
+
+ public SymbolReference<ResolvedConstructorDeclaration> solveConstructor(List<ResolvedType> argumentsTypes, TypeSolver typeSolver) {
+ if (typeDeclaration instanceof ResolvedClassDeclaration) {
+ return ConstructorResolutionLogic.findMostApplicable(((ResolvedClassDeclaration) typeDeclaration).getConstructors(), argumentsTypes, typeSolver);
+ }
+ return SymbolReference.unsolved(ResolvedConstructorDeclaration.class);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/LambdaExprContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/LambdaExprContext.java
new file mode 100644
index 000000000..3c34facfe
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/LambdaExprContext.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.body.Parameter;
+import com.github.javaparser.ast.body.VariableDeclarator;
+import com.github.javaparser.ast.expr.Expression;
+import com.github.javaparser.ast.expr.LambdaExpr;
+import com.github.javaparser.ast.expr.MethodCallExpr;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.types.ResolvedLambdaConstraintType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.logic.FunctionalInterfaceLogic;
+import com.github.javaparser.symbolsolver.logic.InferenceContext;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.resolution.Value;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+import com.github.javaparser.symbolsolver.reflectionmodel.MyObjectProvider;
+import com.github.javaparser.symbolsolver.resolution.SymbolDeclarator;
+
+import java.util.*;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class LambdaExprContext extends AbstractJavaParserContext<LambdaExpr> {
+
+ public LambdaExprContext(LambdaExpr wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+ @Override
+ public Optional<Value> solveSymbolAsValue(String name, TypeSolver typeSolver) {
+ for (Parameter parameter : wrappedNode.getParameters()) {
+ SymbolDeclarator sb = JavaParserFactory.getSymbolDeclarator(parameter, typeSolver);
+ int index = 0;
+ for (ResolvedValueDeclaration decl : sb.getSymbolDeclarations()) {
+ if (decl.getName().equals(name)) {
+ if (requireParentNode(wrappedNode) instanceof MethodCallExpr) {
+ MethodCallExpr methodCallExpr = (MethodCallExpr) requireParentNode(wrappedNode);
+ MethodUsage methodUsage = JavaParserFacade.get(typeSolver).solveMethodAsUsage(methodCallExpr);
+ int i = pos(methodCallExpr, wrappedNode);
+ ResolvedType lambdaType = methodUsage.getParamTypes().get(i);
+
+ // Get the functional method in order for us to resolve it's type arguments properly
+ Optional<MethodUsage> functionalMethodOpt = FunctionalInterfaceLogic.getFunctionalMethod(lambdaType);
+ if (functionalMethodOpt.isPresent()){
+ MethodUsage functionalMethod = functionalMethodOpt.get();
+ InferenceContext inferenceContext = new InferenceContext(MyObjectProvider.INSTANCE);
+
+ // Resolve each type variable of the lambda, and use this later to infer the type of each
+ // implicit parameter
+ inferenceContext.addPair(lambdaType, new ReferenceTypeImpl(lambdaType.asReferenceType().getTypeDeclaration(), typeSolver));
+
+ // Find the position of this lambda argument
+ boolean found = false;
+ int lambdaParamIndex;
+ for (lambdaParamIndex = 0; lambdaParamIndex < wrappedNode.getParameters().size(); lambdaParamIndex++){
+ if (wrappedNode.getParameter(lambdaParamIndex).getName().getIdentifier().equals(name)){
+ found = true;
+ break;
+ }
+ }
+ if (!found) { return Optional.empty(); }
+
+ // Now resolve the argument type using the inference context
+ ResolvedType argType = inferenceContext.resolve(inferenceContext.addSingle(functionalMethod.getParamType(lambdaParamIndex)));
+
+ ResolvedLambdaConstraintType conType;
+ if (argType.isWildcard()){
+ conType = ResolvedLambdaConstraintType.bound(argType.asWildcard().getBoundedType());
+ } else {
+ conType = ResolvedLambdaConstraintType.bound(argType);
+ }
+ Value value = new Value(conType, name);
+ return Optional.of(value);
+ } else{
+ return Optional.empty();
+ }
+ } else if (requireParentNode(wrappedNode) instanceof VariableDeclarator) {
+ VariableDeclarator variableDeclarator = (VariableDeclarator) requireParentNode(wrappedNode);
+ ResolvedType t = JavaParserFacade.get(typeSolver).convertToUsageVariableType(variableDeclarator);
+ Optional<MethodUsage> functionalMethod = FunctionalInterfaceLogic.getFunctionalMethod(t);
+ if (functionalMethod.isPresent()) {
+ ResolvedType lambdaType = functionalMethod.get().getParamType(index);
+
+ // Replace parameter from declarator
+ Map<ResolvedTypeParameterDeclaration, ResolvedType> inferredTypes = new HashMap<>();
+ if (lambdaType.isReferenceType()) {
+ for (com.github.javaparser.utils.Pair<ResolvedTypeParameterDeclaration, ResolvedType> entry : lambdaType.asReferenceType().getTypeParametersMap()) {
+ if (entry.b.isTypeVariable() && entry.b.asTypeParameter().declaredOnType()) {
+ ResolvedType ot = t.asReferenceType().typeParametersMap().getValue(entry.a);
+ lambdaType = lambdaType.replaceTypeVariables(entry.a, ot, inferredTypes);
+ }
+ }
+ } else if (lambdaType.isTypeVariable() && lambdaType.asTypeParameter().declaredOnType()) {
+ lambdaType = t.asReferenceType().typeParametersMap().getValue(lambdaType.asTypeParameter());
+ }
+
+ Value value = new Value(lambdaType, name);
+ return Optional.of(value);
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+ index++;
+ }
+ }
+
+ // if nothing is found we should ask the parent context
+ return getParent().solveSymbolAsValue(name, typeSolver);
+ }
+
+ @Override
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
+ for (Parameter parameter : wrappedNode.getParameters()) {
+ SymbolDeclarator sb = JavaParserFactory.getSymbolDeclarator(parameter, typeSolver);
+ SymbolReference<ResolvedValueDeclaration> symbolReference = solveWith(sb, name);
+ if (symbolReference.isSolved()) {
+ return symbolReference;
+ }
+ }
+
+ // if nothing is found we should ask the parent context
+ return getParent().solveSymbol(name, typeSolver);
+ }
+
+ @Override
+ public SymbolReference<ResolvedTypeDeclaration> solveType(String name, TypeSolver typeSolver) {
+ return getParent().solveType(name, typeSolver);
+ }
+
+ @Override
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(
+ String name, List<ResolvedType> argumentsTypes, boolean staticOnly, TypeSolver typeSolver) {
+ return getParent().solveMethod(name, argumentsTypes, false, typeSolver);
+ }
+
+ ///
+ /// Protected methods
+ ///
+
+ protected final Optional<Value> solveWithAsValue(SymbolDeclarator symbolDeclarator, String name, TypeSolver typeSolver) {
+ for (ResolvedValueDeclaration decl : symbolDeclarator.getSymbolDeclarations()) {
+ if (decl.getName().equals(name)) {
+
+ throw new UnsupportedOperationException();
+ }
+ }
+ return Optional.empty();
+ }
+
+ ///
+ /// Private methods
+ ///
+
+ private int pos(MethodCallExpr callExpr, Expression param) {
+ int i = 0;
+ for (Expression p : callExpr.getArguments()) {
+ if (p == param) {
+ return i;
+ }
+ i++;
+ }
+ throw new IllegalArgumentException();
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/MethodCallExprContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/MethodCallExprContext.java
new file mode 100644
index 000000000..ca9f07c42
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/MethodCallExprContext.java
@@ -0,0 +1,433 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.expr.Expression;
+import com.github.javaparser.ast.expr.MethodCallExpr;
+import com.github.javaparser.ast.expr.NameExpr;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.UnsolvedSymbolException;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.*;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.resolution.Value;
+import com.github.javaparser.symbolsolver.model.typesystem.*;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionClassDeclaration;
+import com.github.javaparser.symbolsolver.resolution.MethodResolutionLogic;
+import com.github.javaparser.utils.Pair;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+public class MethodCallExprContext extends AbstractJavaParserContext<MethodCallExpr> {
+
+ ///
+ /// Constructors
+ ///
+
+ public MethodCallExprContext(MethodCallExpr wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+ ///
+ /// Public methods
+ ///
+
+ @Override
+ public Optional<ResolvedType> solveGenericType(String name, TypeSolver typeSolver) {
+ if(wrappedNode.getScope().isPresent()){
+ ResolvedType typeOfScope = JavaParserFacade.get(typeSolver).getType(wrappedNode.getScope().get());
+ Optional<ResolvedType> res = typeOfScope.asReferenceType().getGenericParameterByName(name);
+ return res;
+ } else{
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "MethodCallExprContext{wrapped=" + wrappedNode + "}";
+ }
+
+ @Override
+ public Optional<MethodUsage> solveMethodAsUsage(String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver) {
+ if (wrappedNode.getScope().isPresent()) {
+ Expression scope = wrappedNode.getScope().get();
+ // Consider static method calls
+ if (scope instanceof NameExpr) {
+ String className = ((NameExpr) scope).getName().getId();
+ SymbolReference<ResolvedTypeDeclaration> ref = solveType(className, typeSolver);
+ if (ref.isSolved()) {
+ SymbolReference<ResolvedMethodDeclaration> m = MethodResolutionLogic.solveMethodInType(ref.getCorrespondingDeclaration(), name, argumentsTypes, typeSolver);
+ if (m.isSolved()) {
+ MethodUsage methodUsage = new MethodUsage(m.getCorrespondingDeclaration());
+ methodUsage = resolveMethodTypeParametersFromExplicitList(typeSolver, methodUsage);
+ methodUsage = resolveMethodTypeParameters(methodUsage, argumentsTypes);
+ return Optional.of(methodUsage);
+ } else {
+ throw new UnsolvedSymbolException(ref.getCorrespondingDeclaration().toString(),
+ "Method '" + name + "' with parameterTypes " + argumentsTypes);
+ }
+ }
+ }
+
+ ResolvedType typeOfScope = JavaParserFacade.get(typeSolver).getType(scope);
+ // we can replace the parameter types from the scope into the typeParametersValues
+
+ Map<ResolvedTypeParameterDeclaration, ResolvedType> inferredTypes = new HashMap<>();
+ for (int i = 0; i < argumentsTypes.size(); i++) {
+ // by replacing types I can also find new equivalences
+ // for example if I replace T=U with String because I know that T=String I can derive that also U equal String
+ ResolvedType originalArgumentType = argumentsTypes.get(i);
+ ResolvedType updatedArgumentType = usingParameterTypesFromScope(typeOfScope, originalArgumentType, inferredTypes);
+ argumentsTypes.set(i, updatedArgumentType);
+ }
+ for (int i = 0; i < argumentsTypes.size(); i++) {
+ ResolvedType updatedArgumentType = applyInferredTypes(argumentsTypes.get(i), inferredTypes);
+ argumentsTypes.set(i, updatedArgumentType);
+ }
+
+ return solveMethodAsUsage(typeOfScope, name, argumentsTypes, typeSolver, this);
+ } else {
+ Context parentContext = getParent();
+ while (parentContext instanceof MethodCallExprContext) {
+ parentContext = parentContext.getParent();
+ }
+ return parentContext.solveMethodAsUsage(name, argumentsTypes, typeSolver);
+ }
+ }
+
+ private MethodUsage resolveMethodTypeParametersFromExplicitList(TypeSolver typeSolver, MethodUsage methodUsage) {
+ if (wrappedNode.getTypeArguments().isPresent()) {
+ final List<ResolvedType> typeArguments = new ArrayList<>();
+ for (com.github.javaparser.ast.type.Type ty : wrappedNode.getTypeArguments().get()) {
+ typeArguments.add(JavaParserFacade.get(typeSolver).convertToUsage(ty));
+ }
+
+ List<ResolvedTypeParameterDeclaration> tyParamDecls = methodUsage.getDeclaration().getTypeParameters();
+ if (tyParamDecls.size() == typeArguments.size()) {
+ for (int i = 0; i < tyParamDecls.size(); i++) {
+ methodUsage = methodUsage.replaceTypeParameter(tyParamDecls.get(i), typeArguments.get(i));
+ }
+ }
+ }
+
+ return methodUsage;
+ }
+
+ @Override
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
+ return getParent().solveSymbol(name, typeSolver);
+ }
+
+ @Override
+ public Optional<Value> solveSymbolAsValue(String name, TypeSolver typeSolver) {
+ Context parentContext = getParent();
+ return parentContext.solveSymbolAsValue(name, typeSolver);
+ }
+
+ @Override
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> argumentsTypes, boolean staticOnly, TypeSolver typeSolver) {
+ Collection<ResolvedReferenceTypeDeclaration> rrtds = findTypeDeclarations(wrappedNode.getScope(), typeSolver);
+ for (ResolvedReferenceTypeDeclaration rrtd : rrtds) {
+ SymbolReference<ResolvedMethodDeclaration> res = MethodResolutionLogic.solveMethodInType(rrtd, name, argumentsTypes, false, typeSolver);
+ if (res.isSolved()) {
+ return res;
+ }
+ }
+ return SymbolReference.unsolved(ResolvedMethodDeclaration.class);
+ }
+
+ ///
+ /// Private methods
+ ///
+
+ private Optional<MethodUsage> solveMethodAsUsage(ResolvedReferenceType refType, String name,
+ List<ResolvedType> argumentsTypes, TypeSolver typeSolver,
+ Context invokationContext) {
+ Optional<MethodUsage> ref = ContextHelper.solveMethodAsUsage(refType.getTypeDeclaration(), name, argumentsTypes, typeSolver, invokationContext, refType.typeParametersValues());
+ if (ref.isPresent()) {
+ MethodUsage methodUsage = ref.get();
+
+ methodUsage = resolveMethodTypeParametersFromExplicitList(typeSolver, methodUsage);
+
+ // At this stage I should derive from the context and the value some information on the type parameters
+ // for example, when calling:
+ // myStream.collect(Collectors.toList())
+ // I should be able to figure out that considering the type of the stream (e.g., Stream<String>)
+ // and considering that Stream has this method:
+ //
+ // <R,A> R collect(Collector<? super T,A,R> collector)
+ //
+ // and collector has this method:
+ //
+ // static <T> Collector<T,?,List<T>> toList()
+ //
+ // In this case collect.R has to be equal to List<toList.T>
+ // And toList.T has to be equal to ? super Stream.T
+ // Therefore R has to be equal to List<? super Stream.T>.
+ // In our example Stream.T equal to String, so the R (and the result of the call to collect) is
+ // List<? super String>
+
+ Map<ResolvedTypeParameterDeclaration, ResolvedType> derivedValues = new HashMap<>();
+ for (int i = 0; i < methodUsage.getParamTypes().size(); i++) {
+ ResolvedParameterDeclaration parameter = methodUsage.getDeclaration().getParam(i);
+ ResolvedType parameterType = parameter.getType();
+ if (parameter.isVariadic()) {
+ parameterType = parameterType.asArrayType().getComponentType();
+ }
+ inferTypes(argumentsTypes.get(i), parameterType, derivedValues);
+ }
+
+ for (Map.Entry<ResolvedTypeParameterDeclaration, ResolvedType> entry : derivedValues.entrySet()){
+ methodUsage = methodUsage.replaceTypeParameter(entry.getKey(), entry.getValue());
+ }
+
+ ResolvedType returnType = refType.useThisTypeParametersOnTheGivenType(methodUsage.returnType());
+ if (returnType != methodUsage.returnType()) {
+ methodUsage = methodUsage.replaceReturnType(returnType);
+ }
+ for (int i = 0; i < methodUsage.getParamTypes().size(); i++) {
+ ResolvedType replaced = refType.useThisTypeParametersOnTheGivenType(methodUsage.getParamTypes().get(i));
+ methodUsage = methodUsage.replaceParamType(i, replaced);
+ }
+ return Optional.of(methodUsage);
+ } else {
+ return ref;
+ }
+ }
+
+ private void inferTypes(ResolvedType source, ResolvedType target, Map<ResolvedTypeParameterDeclaration, ResolvedType> mappings) {
+ if (source.equals(target)) {
+ return;
+ }
+ if (source.isReferenceType() && target.isReferenceType()) {
+ ResolvedReferenceType sourceRefType = source.asReferenceType();
+ ResolvedReferenceType targetRefType = target.asReferenceType();
+ if (sourceRefType.getQualifiedName().equals(targetRefType.getQualifiedName())) {
+ if (!sourceRefType.isRawType() && !targetRefType.isRawType()) {
+ for (int i = 0; i < sourceRefType.typeParametersValues().size(); i++) {
+ inferTypes(sourceRefType.typeParametersValues().get(i), targetRefType.typeParametersValues().get(i), mappings);
+ }
+ }
+ }
+ return;
+ }
+ if (source.isReferenceType() && target.isWildcard()) {
+ if (target.asWildcard().isBounded()) {
+ inferTypes(source, target.asWildcard().getBoundedType(), mappings);
+ return;
+ }
+ return;
+ }
+ if (source.isWildcard() && target.isWildcard()) {
+ if (source.asWildcard().isBounded() && target.asWildcard().isBounded()){
+ inferTypes(source.asWildcard().getBoundedType(), target.asWildcard().getBoundedType(), mappings);
+ }
+ return;
+ }
+ if (source.isReferenceType() && target.isTypeVariable()) {
+ mappings.put(target.asTypeParameter(), source);
+ return;
+ }
+ if (source.isWildcard() && target.isTypeVariable()) {
+ mappings.put(target.asTypeParameter(), source);
+ return;
+ }
+ if (source.isArray() && target.isWildcard()){
+ if(target.asWildcard().isBounded()){
+ inferTypes(source, target.asWildcard().getBoundedType(), mappings);
+ return;
+ }
+ return;
+ }
+ if (source.isArray() && target.isTypeVariable()) {
+ mappings.put(target.asTypeParameter(), source);
+ return;
+ }
+
+ if (source.isWildcard() && target.isReferenceType()){
+ if (source.asWildcard().isBounded()){
+ inferTypes(source.asWildcard().getBoundedType(), target, mappings);
+ }
+ return;
+ }
+ if (source.isConstraint() && target.isReferenceType()){
+ inferTypes(source.asConstraintType().getBound(), target, mappings);
+ return;
+ }
+
+ if (source.isConstraint() && target.isTypeVariable()){
+ inferTypes(source.asConstraintType().getBound(), target, mappings);
+ return;
+ }
+ if (source.isTypeVariable() && target.isTypeVariable()) {
+ mappings.put(target.asTypeParameter(), source);
+ return;
+ }
+ if (source.isPrimitive() || target.isPrimitive()) {
+ return;
+ }
+ if (source.isNull()) {
+ return;
+ }
+ throw new RuntimeException(source.describe() + " " + target.describe());
+ }
+
+ private MethodUsage resolveMethodTypeParameters(MethodUsage methodUsage, List<ResolvedType> actualParamTypes) {
+ Map<ResolvedTypeParameterDeclaration, ResolvedType> matchedTypeParameters = new HashMap<>();
+
+ if (methodUsage.getDeclaration().hasVariadicParameter()) {
+ if (actualParamTypes.size() == methodUsage.getDeclaration().getNumberOfParams()) {
+ // the varargs parameter is an Array, so extract the inner type
+ ResolvedType expectedType =
+ methodUsage.getDeclaration().getLastParam().getType().asArrayType().getComponentType();
+ // the varargs corresponding type can be either T or Array<T>
+ ResolvedType actualType =
+ actualParamTypes.get(actualParamTypes.size() - 1).isArray() ?
+ actualParamTypes.get(actualParamTypes.size() - 1).asArrayType().getComponentType() :
+ actualParamTypes.get(actualParamTypes.size() - 1);
+ if (!expectedType.isAssignableBy(actualType)) {
+ for (ResolvedTypeParameterDeclaration tp : methodUsage.getDeclaration().getTypeParameters()) {
+ expectedType = MethodResolutionLogic.replaceTypeParam(expectedType, tp, typeSolver);
+ }
+ }
+ if (!expectedType.isAssignableBy(actualType)) {
+ // ok, then it needs to be wrapped
+ throw new UnsupportedOperationException(
+ String.format("Unable to resolve the type typeParametersValues in a MethodUsage. Expected type: %s, Actual type: %s. Method Declaration: %s. MethodUsage: %s",
+ expectedType,
+ actualType,
+ methodUsage.getDeclaration(),
+ methodUsage));
+ }
+ // match only the varargs type
+ matchTypeParameters(expectedType, actualType, matchedTypeParameters);
+ } else {
+ return methodUsage;
+ }
+ }
+
+ int until = methodUsage.getDeclaration().hasVariadicParameter() ?
+ actualParamTypes.size() - 1 :
+ actualParamTypes.size();
+
+ for (int i = 0; i < until; i++) {
+ ResolvedType expectedType = methodUsage.getParamType(i);
+ ResolvedType actualType = actualParamTypes.get(i);
+ matchTypeParameters(expectedType, actualType, matchedTypeParameters);
+ }
+ for (ResolvedTypeParameterDeclaration tp : matchedTypeParameters.keySet()) {
+ methodUsage = methodUsage.replaceTypeParameter(tp, matchedTypeParameters.get(tp));
+ }
+ return methodUsage;
+ }
+
+ private void matchTypeParameters(ResolvedType expectedType, ResolvedType actualType, Map<ResolvedTypeParameterDeclaration, ResolvedType> matchedTypeParameters) {
+ if (expectedType.isTypeVariable()) {
+ if (!actualType.isTypeVariable() && !actualType.isReferenceType()) {
+ throw new UnsupportedOperationException(actualType.getClass().getCanonicalName());
+ }
+ matchedTypeParameters.put(expectedType.asTypeParameter(), actualType);
+ } else if (expectedType.isArray()) {
+ if (!actualType.isArray()) {
+ throw new UnsupportedOperationException(actualType.getClass().getCanonicalName());
+ }
+ matchTypeParameters(
+ expectedType.asArrayType().getComponentType(),
+ actualType.asArrayType().getComponentType(),
+ matchedTypeParameters);
+ } else if (expectedType.isReferenceType()) {
+ // avoid cases where the actual type has no type parameters but the expected one has. Such as: "classX extends classY<Integer>"
+ if (actualType.isReferenceType() && actualType.asReferenceType().typeParametersValues().size() > 0) {
+ int i = 0;
+ for (ResolvedType tp : expectedType.asReferenceType().typeParametersValues()) {
+ matchTypeParameters(tp, actualType.asReferenceType().typeParametersValues().get(i), matchedTypeParameters);
+ i++;
+ }
+ }
+ } else if (expectedType.isPrimitive()) {
+ // nothing to do
+ } else if (expectedType.isWildcard()) {
+ // nothing to do
+ } else {
+ throw new UnsupportedOperationException(expectedType.getClass().getCanonicalName());
+ }
+ }
+
+ private Optional<MethodUsage> solveMethodAsUsage(ResolvedTypeVariable tp, String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver, Context invokationContext) {
+ for (ResolvedTypeParameterDeclaration.Bound bound : tp.asTypeParameter().getBounds()) {
+ Optional<MethodUsage> methodUsage = solveMethodAsUsage(bound.getType(), name, argumentsTypes, typeSolver, invokationContext);
+ if (methodUsage.isPresent()) {
+ return methodUsage;
+ }
+ }
+ return Optional.empty();
+ }
+
+ private Optional<MethodUsage> solveMethodAsUsage(ResolvedType type, String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver, Context invokationContext) {
+ if (type instanceof ResolvedReferenceType) {
+ return solveMethodAsUsage((ResolvedReferenceType) type, name, argumentsTypes, typeSolver, invokationContext);
+ } else if (type instanceof ResolvedTypeVariable) {
+ return solveMethodAsUsage((ResolvedTypeVariable) type, name, argumentsTypes, typeSolver, invokationContext);
+ } else if (type instanceof ResolvedWildcard) {
+ ResolvedWildcard wildcardUsage = (ResolvedWildcard) type;
+ if (wildcardUsage.isSuper()) {
+ return solveMethodAsUsage(wildcardUsage.getBoundedType(), name, argumentsTypes, typeSolver, invokationContext);
+ } else if (wildcardUsage.isExtends()) {
+ throw new UnsupportedOperationException("extends wildcard");
+ } else {
+ throw new UnsupportedOperationException("unbounded wildcard");
+ }
+ } else if (type instanceof ResolvedLambdaConstraintType){
+ ResolvedLambdaConstraintType constraintType = (ResolvedLambdaConstraintType) type;
+ return solveMethodAsUsage(constraintType.getBound(), name, argumentsTypes, typeSolver, invokationContext);
+ } else if (type instanceof ResolvedArrayType) {
+ // An array inherits methods from Object not from it's component type
+ return solveMethodAsUsage(new ReferenceTypeImpl(new ReflectionClassDeclaration(Object.class, typeSolver), typeSolver), name, argumentsTypes, typeSolver, invokationContext);
+ } else {
+ throw new UnsupportedOperationException("type usage: " + type.getClass().getCanonicalName());
+ }
+ }
+
+ private ResolvedType usingParameterTypesFromScope(ResolvedType scope, ResolvedType type, Map<ResolvedTypeParameterDeclaration, ResolvedType> inferredTypes) {
+ if (type.isReferenceType()) {
+ for (Pair<ResolvedTypeParameterDeclaration, ResolvedType> entry : type.asReferenceType().getTypeParametersMap()) {
+ if (entry.a.declaredOnType() && scope.asReferenceType().getGenericParameterByName(entry.a.getName()).isPresent()) {
+ type = type.replaceTypeVariables(entry.a, scope.asReferenceType().getGenericParameterByName(entry.a.getName()).get(), inferredTypes);
+ }
+ }
+ return type;
+ } else {
+ return type;
+ }
+ }
+
+ private ResolvedType applyInferredTypes(ResolvedType type, Map<ResolvedTypeParameterDeclaration, ResolvedType> inferredTypes) {
+ for (ResolvedTypeParameterDeclaration tp : inferredTypes.keySet()) {
+ type = type.replaceTypeVariables(tp, inferredTypes.get(tp), inferredTypes);
+ }
+ return type;
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/MethodContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/MethodContext.java
new file mode 100644
index 000000000..9a7530b18
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/MethodContext.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.body.MethodDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class MethodContext extends AbstractMethodLikeDeclarationContext<MethodDeclaration> {
+
+ ///
+ /// Constructors
+ ///
+
+ public MethodContext(MethodDeclaration wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/StatementContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/StatementContext.java
new file mode 100644
index 000000000..8acab7a8a
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/StatementContext.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.expr.LambdaExpr;
+import com.github.javaparser.ast.nodeTypes.NodeWithStatements;
+import com.github.javaparser.ast.stmt.IfStmt;
+import com.github.javaparser.ast.stmt.Statement;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.resolution.Value;
+import com.github.javaparser.symbolsolver.resolution.SymbolDeclarator;
+
+import java.util.List;
+import java.util.Optional;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class StatementContext<N extends Statement> extends AbstractJavaParserContext<N> {
+
+ public StatementContext(N wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+ public static SymbolReference<? extends ResolvedValueDeclaration> solveInBlock(String name, TypeSolver typeSolver, Statement stmt) {
+ if (!(requireParentNode(stmt) instanceof NodeWithStatements)) {
+ throw new IllegalArgumentException();
+ }
+ NodeWithStatements<?> blockStmt = (NodeWithStatements<?>) requireParentNode(stmt);
+ int position = -1;
+ for (int i = 0; i < blockStmt.getStatements().size(); i++) {
+ if (blockStmt.getStatements().get(i).equals(stmt)) {
+ position = i;
+ }
+ }
+ if (position == -1) {
+ throw new RuntimeException();
+ }
+ for (int i = position - 1; i >= 0; i--) {
+ SymbolDeclarator symbolDeclarator = JavaParserFactory.getSymbolDeclarator(blockStmt.getStatements().get(i), typeSolver);
+ SymbolReference<? extends ResolvedValueDeclaration> symbolReference = solveWith(symbolDeclarator, name);
+ if (symbolReference.isSolved()) {
+ return symbolReference;
+ }
+ }
+
+ // if nothing is found we should ask the parent context
+ return JavaParserFactory.getContext(requireParentNode(stmt), typeSolver).solveSymbol(name, typeSolver);
+ }
+
+ public static Optional<Value> solveInBlockAsValue(String name, TypeSolver typeSolver, Statement stmt) {
+ if (!(requireParentNode(stmt) instanceof NodeWithStatements)) {
+ throw new IllegalArgumentException();
+ }
+ NodeWithStatements<?> blockStmt = (NodeWithStatements<?>) requireParentNode(stmt);
+ int position = -1;
+ for (int i = 0; i < blockStmt.getStatements().size(); i++) {
+ if (blockStmt.getStatements().get(i).equals(stmt)) {
+ position = i;
+ }
+ }
+ if (position == -1) {
+ throw new RuntimeException();
+ }
+ for (int i = position - 1; i >= 0; i--) {
+ SymbolDeclarator symbolDeclarator = JavaParserFactory.getSymbolDeclarator(blockStmt.getStatements().get(i), typeSolver);
+ SymbolReference<? extends ResolvedValueDeclaration> symbolReference = solveWith(symbolDeclarator, name);
+ if (symbolReference.isSolved()) {
+ return Optional.of(Value.from(symbolReference.getCorrespondingDeclaration()));
+ }
+ }
+
+ // if nothing is found we should ask the parent context
+ return JavaParserFactory.getContext(requireParentNode(stmt), typeSolver).solveSymbolAsValue(name, typeSolver);
+ }
+
+ @Override
+ public Optional<Value> solveSymbolAsValue(String name, TypeSolver typeSolver) {
+
+ // if we're in a multiple Variable declaration line (for ex: double a=0, b=a;)
+ SymbolDeclarator symbolDeclarator = JavaParserFactory.getSymbolDeclarator(wrappedNode, typeSolver);
+ Optional<Value> symbolReference = solveWithAsValue(symbolDeclarator, name, typeSolver);
+ if (symbolReference.isPresent()) {
+ return symbolReference;
+ }
+
+ // we should look in all the statements preceding, treating them as SymbolDeclarators
+ if (requireParentNode(wrappedNode) instanceof com.github.javaparser.ast.body.MethodDeclaration) {
+ return getParent().solveSymbolAsValue(name, typeSolver);
+ }
+ if (requireParentNode(wrappedNode) instanceof LambdaExpr) {
+ return getParent().solveSymbolAsValue(name, typeSolver);
+ }
+ if (requireParentNode(wrappedNode) instanceof IfStmt) {
+ return getParent().solveSymbolAsValue(name, typeSolver);
+ }
+ if (!(requireParentNode(wrappedNode) instanceof NodeWithStatements)) {
+ return getParent().solveSymbolAsValue(name, typeSolver);
+ }
+ NodeWithStatements<?> nodeWithStmt = (NodeWithStatements<?>) requireParentNode(wrappedNode);
+ int position = -1;
+ for (int i = 0; i < nodeWithStmt.getStatements().size(); i++) {
+ if (nodeWithStmt.getStatements().get(i).equals(wrappedNode)) {
+ position = i;
+ }
+ }
+ if (position == -1) {
+ throw new RuntimeException();
+ }
+ for (int i = position - 1; i >= 0; i--) {
+ symbolDeclarator = JavaParserFactory.getSymbolDeclarator(nodeWithStmt.getStatements().get(i), typeSolver);
+ symbolReference = solveWithAsValue(symbolDeclarator, name, typeSolver);
+ if (symbolReference.isPresent()) {
+ return symbolReference;
+ }
+ }
+
+ // if nothing is found we should ask the parent context
+ Context parentContext = getParent();
+ return parentContext.solveSymbolAsValue(name, typeSolver);
+ }
+
+ @Override
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
+
+ // if we're in a multiple Variable declaration line (for ex: double a=0, b=a;)
+ SymbolDeclarator symbolDeclarator = JavaParserFactory.getSymbolDeclarator(wrappedNode, typeSolver);
+ SymbolReference<? extends ResolvedValueDeclaration> symbolReference = solveWith(symbolDeclarator, name);
+ if (symbolReference.isSolved()) {
+ return symbolReference;
+ }
+
+ // we should look in all the statements preceding, treating them as SymbolDeclarators
+ if (requireParentNode(wrappedNode) instanceof com.github.javaparser.ast.body.MethodDeclaration) {
+ return getParent().solveSymbol(name, typeSolver);
+ }
+ if (requireParentNode(wrappedNode) instanceof com.github.javaparser.ast.body.ConstructorDeclaration) {
+ return getParent().solveSymbol(name, typeSolver);
+ }
+ if (requireParentNode(wrappedNode) instanceof LambdaExpr) {
+ return getParent().solveSymbol(name, typeSolver);
+ }
+ if (!(requireParentNode(wrappedNode) instanceof NodeWithStatements)) {
+ return getParent().solveSymbol(name, typeSolver);
+ }
+ NodeWithStatements<?> nodeWithStmt = (NodeWithStatements<?>) requireParentNode(wrappedNode);
+ int position = -1;
+ for (int i = 0; i < nodeWithStmt.getStatements().size(); i++) {
+ if (nodeWithStmt.getStatements().get(i).equals(wrappedNode)) {
+ position = i;
+ }
+ }
+ if (position == -1) {
+ throw new RuntimeException();
+ }
+ for (int i = position - 1; i >= 0; i--) {
+ symbolDeclarator = JavaParserFactory.getSymbolDeclarator(nodeWithStmt.getStatements().get(i), typeSolver);
+ symbolReference = solveWith(symbolDeclarator, name);
+ if (symbolReference.isSolved()) {
+ return symbolReference;
+ }
+ }
+
+ // if nothing is found we should ask the parent context
+ return getParent().solveSymbol(name, typeSolver);
+ }
+
+ @Override
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> argumentsTypes, boolean staticOnly, TypeSolver typeSolver) {
+ return getParent().solveMethod(name, argumentsTypes, false, typeSolver);
+ }
+
+ @Override
+ public SymbolReference<ResolvedTypeDeclaration> solveType(String name, TypeSolver typeSolver) {
+ return getParent().solveType(name, typeSolver);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/SwitchEntryContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/SwitchEntryContext.java
new file mode 100644
index 000000000..15967295b
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/SwitchEntryContext.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.stmt.Statement;
+import com.github.javaparser.ast.stmt.SwitchEntryStmt;
+import com.github.javaparser.ast.stmt.SwitchStmt;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+import com.github.javaparser.symbolsolver.resolution.SymbolDeclarator;
+
+import java.util.List;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class SwitchEntryContext extends AbstractJavaParserContext<SwitchEntryStmt> {
+
+ public SwitchEntryContext(SwitchEntryStmt wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+ @Override
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
+ SwitchStmt switchStmt = (SwitchStmt) requireParentNode(wrappedNode);
+ ResolvedType type = JavaParserFacade.get(typeSolver).getType(switchStmt.getSelector());
+ if (type.isReferenceType() && type.asReferenceType().getTypeDeclaration().isEnum()) {
+ if (type instanceof ReferenceTypeImpl) {
+ ReferenceTypeImpl typeUsageOfTypeDeclaration = (ReferenceTypeImpl) type;
+ if (typeUsageOfTypeDeclaration.getTypeDeclaration().hasField(name)) {
+ return SymbolReference.solved(typeUsageOfTypeDeclaration.getTypeDeclaration().getField(name));
+ }
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ // look for declaration in other switch statements
+ for (SwitchEntryStmt seStmt : switchStmt.getEntries()) {
+ if (!seStmt.equals(wrappedNode)) {
+ for (Statement stmt : seStmt.getStatements()) {
+ SymbolDeclarator symbolDeclarator = JavaParserFactory.getSymbolDeclarator(stmt, typeSolver);
+ SymbolReference<? extends ResolvedValueDeclaration> symbolReference = solveWith(symbolDeclarator, name);
+ if (symbolReference.isSolved()) {
+ return symbolReference;
+ }
+ }
+ }
+ }
+
+ return getParent().solveSymbol(name, typeSolver);
+ }
+
+ @Override
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> argumentsTypes, boolean staticOnly, TypeSolver typeSolver) {
+ return getParent().solveMethod(name, argumentsTypes, false, typeSolver);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/TryWithResourceContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/TryWithResourceContext.java
new file mode 100644
index 000000000..80932e563
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/TryWithResourceContext.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.contexts;
+
+import com.github.javaparser.ast.body.VariableDeclarator;
+import com.github.javaparser.ast.expr.Expression;
+import com.github.javaparser.ast.expr.VariableDeclarationExpr;
+import com.github.javaparser.ast.stmt.BlockStmt;
+import com.github.javaparser.ast.stmt.TryStmt;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserSymbolDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.resolution.Value;
+
+import java.util.List;
+import java.util.Optional;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentNode;
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode;
+
+public class TryWithResourceContext extends AbstractJavaParserContext<TryStmt> {
+
+ public TryWithResourceContext(TryStmt wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+ @Override
+ public Optional<Value> solveSymbolAsValue(String name, TypeSolver typeSolver) {
+ for (Expression expr : wrappedNode.getResources()) {
+ if (expr instanceof VariableDeclarationExpr) {
+ for (VariableDeclarator v : ((VariableDeclarationExpr)expr).getVariables()) {
+ if (v.getName().getIdentifier().equals(name)) {
+ JavaParserSymbolDeclaration decl = JavaParserSymbolDeclaration.localVar(v, typeSolver);
+ return Optional.of(Value.from(decl));
+ }
+ }
+ }
+ }
+
+ if (requireParentNode(wrappedNode) instanceof BlockStmt) {
+ return StatementContext.solveInBlockAsValue(name, typeSolver, wrappedNode);
+ } else {
+ return getParent().solveSymbolAsValue(name, typeSolver);
+ }
+ }
+
+ @Override
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
+ for (Expression expr : wrappedNode.getResources()) {
+ if (expr instanceof VariableDeclarationExpr) {
+ for (VariableDeclarator v : ((VariableDeclarationExpr)expr).getVariables()) {
+ if (v.getName().getIdentifier().equals(name)) {
+ return SymbolReference.solved(JavaParserSymbolDeclaration.localVar(v, typeSolver));
+ }
+ }
+ }
+ }
+
+ if (requireParentNode(wrappedNode) instanceof BlockStmt) {
+ return StatementContext.solveInBlock(name, typeSolver, wrappedNode);
+ } else {
+ return getParent().solveSymbol(name, typeSolver);
+ }
+ }
+
+ @Override
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> argumentsTypes,
+ boolean staticOnly, TypeSolver typeSolver) {
+ return getParent().solveMethod(name, argumentsTypes, false, typeSolver);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/DefaultConstructorDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/DefaultConstructorDeclaration.java
new file mode 100644
index 000000000..aff7c7c85
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/DefaultConstructorDeclaration.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.declarations;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.resolution.declarations.ResolvedClassDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedConstructorDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedParameterDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This represents the default constructor added by the compiler for objects not declaring one.
+ * It takes no parameters. See JLS 8.8.9 for details.
+ *
+ * @author Federico Tomassetti
+ */
+class DefaultConstructorDeclaration implements ResolvedConstructorDeclaration {
+
+ private ResolvedClassDeclaration classDeclaration;
+
+ DefaultConstructorDeclaration(ResolvedClassDeclaration classDeclaration) {
+ this.classDeclaration = classDeclaration;
+ }
+
+ @Override
+ public ResolvedClassDeclaration declaringType() {
+ return classDeclaration;
+ }
+
+ @Override
+ public int getNumberOfParams() {
+ return 0;
+ }
+
+ @Override
+ public ResolvedParameterDeclaration getParam(int i) {
+ throw new UnsupportedOperationException("The default constructor has not parameters");
+ }
+
+ @Override
+ public String getName() {
+ return classDeclaration.getName();
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return AccessSpecifier.PUBLIC;
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public int getNumberOfSpecifiedExceptions() {
+ return 0;
+ }
+
+ @Override
+ public ResolvedType getSpecifiedException(int index) {
+ throw new UnsupportedOperationException("The default constructor does not throw exceptions");
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/Helper.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/Helper.java
new file mode 100644
index 000000000..d34e16412
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/Helper.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.declarations;
+
+import com.github.javaparser.ast.*;
+
+import java.util.EnumSet;
+import java.util.Optional;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentNode;
+
+/**
+ * @author Federico Tomassetti
+ */
+class Helper {
+
+ public static AccessSpecifier toAccessLevel(EnumSet<Modifier> modifiers) {
+ if (modifiers.contains(Modifier.PRIVATE)) {
+ return AccessSpecifier.PRIVATE;
+ } else if (modifiers.contains(Modifier.PROTECTED)) {
+ return AccessSpecifier.PROTECTED;
+ } else if (modifiers.contains(Modifier.PUBLIC)) {
+ return AccessSpecifier.PUBLIC;
+ } else {
+ return AccessSpecifier.DEFAULT;
+ }
+ }
+
+ static String containerName(Node container) {
+ String packageName = getPackageName(container);
+ String className = getClassName("", container);
+ return packageName +
+ ((!packageName.isEmpty() && !className.isEmpty()) ? "." : "") +
+ className;
+ }
+
+ static String getPackageName(Node container) {
+ if (container instanceof CompilationUnit) {
+ Optional<PackageDeclaration> p = ((CompilationUnit) container).getPackageDeclaration();
+ if (p.isPresent()) {
+ return p.get().getName().toString();
+ }
+ } else if (container != null) {
+ return getPackageName(container.getParentNode().orElse(null));
+ }
+ return "";
+ }
+
+ static String getClassName(String base, Node container) {
+ if (container instanceof com.github.javaparser.ast.body.ClassOrInterfaceDeclaration) {
+ String b = getClassName(base, container.getParentNode().orElse(null));
+ String cn = ((com.github.javaparser.ast.body.ClassOrInterfaceDeclaration) container).getName().getId();
+ if (b.isEmpty()) {
+ return cn;
+ } else {
+ return b + "." + cn;
+ }
+ } else if (container instanceof com.github.javaparser.ast.body.EnumDeclaration) {
+ String b = getClassName(base, container.getParentNode().orElse(null));
+ String cn = ((com.github.javaparser.ast.body.EnumDeclaration) container).getName().getId();
+ if (b.isEmpty()) {
+ return cn;
+ } else {
+ return b + "." + cn;
+ }
+ } else if (container != null) {
+ return getClassName(base, container.getParentNode().orElse(null));
+ }
+ return base;
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserAnnotationDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserAnnotationDeclaration.java
new file mode 100644
index 000000000..1f6c6f33f
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserAnnotationDeclaration.java
@@ -0,0 +1,103 @@
+package com.github.javaparser.symbolsolver.javaparsermodel.declarations;
+
+import com.github.javaparser.ast.body.AnnotationDeclaration;
+import com.github.javaparser.ast.body.AnnotationMemberDeclaration;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.logic.AbstractTypeDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentNode;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavaParserAnnotationDeclaration extends AbstractTypeDeclaration implements ResolvedAnnotationDeclaration {
+
+ private com.github.javaparser.ast.body.AnnotationDeclaration wrappedNode;
+ private TypeSolver typeSolver;
+
+ public JavaParserAnnotationDeclaration(AnnotationDeclaration wrappedNode, TypeSolver typeSolver) {
+ this.wrappedNode = wrappedNode;
+ this.typeSolver = typeSolver;
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getAncestors() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<ResolvedFieldDeclaration> getAllFields() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<ResolvedMethodDeclaration> getDeclaredMethods() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedType type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasDirectlyAnnotation(String qualifiedName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getPackageName() {
+ return Helper.getPackageName(wrappedNode);
+ }
+
+ @Override
+ public String getClassName() {
+ return Helper.getClassName("", wrappedNode);
+ }
+
+ @Override
+ public String getQualifiedName() {
+ String containerName = Helper.containerName(wrappedNode.getParentNode().orElse(null));
+ if (containerName.isEmpty()) {
+ return wrappedNode.getName().getId();
+ } else {
+ return containerName + "." + wrappedNode.getName();
+ }
+ }
+
+ @Override
+ public String getName() {
+ return wrappedNode.getName().getId();
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Optional<ResolvedReferenceTypeDeclaration> containerType() {
+ throw new UnsupportedOperationException("containerType is not supported for " + this.getClass().getCanonicalName());
+ }
+
+ @Override
+ public List<ResolvedAnnotationMemberDeclaration> getAnnotationMembers() {
+ return wrappedNode.getMembers().stream()
+ .filter(m -> m instanceof AnnotationMemberDeclaration)
+ .map(m -> new JavaParserAnnotationMemberDeclaration((AnnotationMemberDeclaration)m, typeSolver))
+ .collect(Collectors.toList());
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserAnnotationMemberDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserAnnotationMemberDeclaration.java
new file mode 100644
index 000000000..2a603daf8
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserAnnotationMemberDeclaration.java
@@ -0,0 +1,40 @@
+package com.github.javaparser.symbolsolver.javaparsermodel.declarations;
+
+import com.github.javaparser.ast.body.AnnotationMemberDeclaration;
+import com.github.javaparser.ast.expr.Expression;
+import com.github.javaparser.resolution.declarations.ResolvedAnnotationMemberDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavaParserAnnotationMemberDeclaration implements ResolvedAnnotationMemberDeclaration {
+
+ private com.github.javaparser.ast.body.AnnotationMemberDeclaration wrappedNode;
+ private TypeSolver typeSolver;
+
+ public AnnotationMemberDeclaration getWrappedNode() {
+ return wrappedNode;
+ }
+
+ public JavaParserAnnotationMemberDeclaration(AnnotationMemberDeclaration wrappedNode, TypeSolver typeSolver) {
+ this.wrappedNode = wrappedNode;
+ this.typeSolver = typeSolver;
+ }
+
+ @Override
+ public Expression getDefaultValue() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ResolvedType getType() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getName() {
+ return wrappedNode.getNameAsString();
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserAnonymousClassDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserAnonymousClassDeclaration.java
new file mode 100644
index 000000000..3d2cb8f69
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserAnonymousClassDeclaration.java
@@ -0,0 +1,205 @@
+package com.github.javaparser.symbolsolver.javaparsermodel.declarations;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentNode;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.expr.ObjectCreationExpr;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.logic.AbstractClassDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * An anonymous class declaration representation.
+ */
+public class JavaParserAnonymousClassDeclaration extends AbstractClassDeclaration {
+
+ private final TypeSolver typeSolver;
+ private final ObjectCreationExpr wrappedNode;
+ private final ResolvedTypeDeclaration superTypeDeclaration;
+ private final String name = "Anonymous-" + UUID.randomUUID();
+
+ public JavaParserAnonymousClassDeclaration(ObjectCreationExpr wrappedNode,
+ TypeSolver typeSolver) {
+ this.typeSolver = typeSolver;
+ this.wrappedNode = wrappedNode;
+ superTypeDeclaration =
+ JavaParserFactory.getContext(wrappedNode.getParentNode().get(), typeSolver)
+ .solveType(wrappedNode.getType().getName().getId(), typeSolver)
+ .getCorrespondingDeclaration();
+ }
+
+ public ResolvedTypeDeclaration getSuperTypeDeclaration() {
+ return superTypeDeclaration;
+ }
+
+ public <T extends Node> List<T> findMembersOfKind(final Class<T> memberClass) {
+ if (wrappedNode.getAnonymousClassBody().isPresent()) {
+ return wrappedNode
+ .getAnonymousClassBody()
+ .get()
+ .stream()
+ .filter(node -> memberClass.isAssignableFrom(node.getClass()))
+ .map(memberClass::cast)
+ .collect(Collectors.toList());
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ public Context getContext() {
+ return JavaParserFactory.getContext(wrappedNode, typeSolver);
+ }
+
+ @Override
+ protected ResolvedReferenceType object() {
+ return new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver);
+ }
+
+ @Override
+ public ResolvedReferenceType getSuperClass() {
+ return new ReferenceTypeImpl(superTypeDeclaration.asReferenceType(), typeSolver);
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getInterfaces() {
+ return
+ superTypeDeclaration
+ .asReferenceType().getAncestors()
+ .stream()
+ .filter(type -> type.getTypeDeclaration().isInterface())
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public List<ResolvedConstructorDeclaration> getConstructors() {
+ return
+ findMembersOfKind(com.github.javaparser.ast.body.ConstructorDeclaration.class)
+ .stream()
+ .map(ctor -> new JavaParserConstructorDeclaration(this, ctor, typeSolver))
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return AccessSpecifier.PRIVATE;
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getAncestors() {
+ return
+ ImmutableList.
+ <ResolvedReferenceType>builder()
+ .add(getSuperClass())
+ .addAll(superTypeDeclaration.asReferenceType().getAncestors())
+ .build();
+ }
+
+ @Override
+ public List<ResolvedFieldDeclaration> getAllFields() {
+
+ List<JavaParserFieldDeclaration> myFields =
+ findMembersOfKind(com.github.javaparser.ast.body.FieldDeclaration.class)
+ .stream()
+ .flatMap(field ->
+ field.getVariables().stream()
+ .map(variable -> new JavaParserFieldDeclaration(variable,
+ typeSolver)))
+ .collect(Collectors.toList());
+
+ List<ResolvedFieldDeclaration> superClassFields =
+ getSuperClass().getTypeDeclaration().getAllFields();
+
+ List<ResolvedFieldDeclaration> interfaceFields =
+ getInterfaces().stream()
+ .flatMap(inteface -> inteface.getTypeDeclaration().getAllFields().stream())
+ .collect(Collectors.toList());
+
+ return
+ ImmutableList
+ .<ResolvedFieldDeclaration>builder()
+ .addAll(myFields)
+ .addAll(superClassFields)
+ .addAll(interfaceFields)
+ .build();
+ }
+
+ @Override
+ public Set<ResolvedMethodDeclaration> getDeclaredMethods() {
+ return
+ findMembersOfKind(com.github.javaparser.ast.body.MethodDeclaration.class)
+ .stream()
+ .map(method -> new JavaParserMethodDeclaration(method, typeSolver))
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedType type) {
+ return false;
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) {
+ return false;
+ }
+
+ @Override
+ public boolean hasDirectlyAnnotation(String qualifiedName) {
+ return false;
+ }
+
+ @Override
+ public String getPackageName() {
+ return Helper.getPackageName(wrappedNode);
+ }
+
+ @Override
+ public String getClassName() {
+ return Helper.getClassName("", wrappedNode);
+ }
+
+ @Override
+ public String getQualifiedName() {
+ String containerName = Helper.containerName(wrappedNode.getParentNode().orElse(null));
+ if (containerName.isEmpty()) {
+ return getName();
+ } else {
+ return containerName + "." + getName();
+ }
+ }
+
+ @Override
+ public Set<ResolvedReferenceTypeDeclaration> internalTypes() {
+ return
+ findMembersOfKind(com.github.javaparser.ast.body.TypeDeclaration.class)
+ .stream()
+ .map(typeMember -> JavaParserFacade.get(typeSolver).getTypeDeclaration(typeMember))
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ return Lists.newArrayList();
+ }
+
+ @Override
+ public Optional<ResolvedReferenceTypeDeclaration> containerType() {
+ throw new UnsupportedOperationException("containerType is not supported for " + this.getClass().getCanonicalName());
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserClassDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserClassDeclaration.java
new file mode 100644
index 000000000..bb9f91cc6
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserClassDeclaration.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.declarations;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.body.*;
+import com.github.javaparser.ast.expr.AnnotationExpr;
+import com.github.javaparser.ast.type.ClassOrInterfaceType;
+import com.github.javaparser.resolution.UnsolvedSymbolException;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.logic.AbstractClassDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.LazyType;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+import com.github.javaparser.symbolsolver.resolution.SymbolSolver;
+import com.google.common.collect.ImmutableList;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavaParserClassDeclaration extends AbstractClassDeclaration {
+
+ ///
+ /// Fields
+ ///
+
+ private TypeSolver typeSolver;
+ private com.github.javaparser.ast.body.ClassOrInterfaceDeclaration wrappedNode;
+ private JavaParserTypeAdapter<ClassOrInterfaceDeclaration> javaParserTypeAdapter;
+
+ ///
+ /// Constructors
+ ///
+
+ public JavaParserClassDeclaration(com.github.javaparser.ast.body.ClassOrInterfaceDeclaration wrappedNode,
+ TypeSolver typeSolver) {
+ if (wrappedNode.isInterface()) {
+ throw new IllegalArgumentException("Interface given");
+ }
+ this.wrappedNode = wrappedNode;
+ this.typeSolver = typeSolver;
+ this.javaParserTypeAdapter = new JavaParserTypeAdapter<>(wrappedNode, typeSolver);
+ }
+
+ ///
+ /// Public methods: from Object
+ ///
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ JavaParserClassDeclaration that = (JavaParserClassDeclaration) o;
+
+ if (!wrappedNode.equals(that.wrappedNode)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return wrappedNode.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "JavaParserClassDeclaration{" +
+ "wrappedNode=" + wrappedNode +
+ '}';
+ }
+
+ ///
+ /// Public methods: fields
+ ///
+
+ @Override
+ public List<ResolvedFieldDeclaration> getAllFields() {
+ List<ResolvedFieldDeclaration> fields = javaParserTypeAdapter.getFieldsForDeclaredVariables();
+
+ getAncestors().forEach(ancestor -> ancestor.getTypeDeclaration().getAllFields().forEach(f -> {
+ fields.add(new ResolvedFieldDeclaration() {
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return f.accessSpecifier();
+ }
+
+ @Override
+ public String getName() {
+ return f.getName();
+ }
+
+ @Override
+ public ResolvedType getType() {
+ return ancestor.useThisTypeParametersOnTheGivenType(f.getType());
+ }
+
+ @Override
+ public boolean isStatic() {
+ return f.isStatic();
+ }
+
+ @Override
+ public ResolvedTypeDeclaration declaringType() {
+ return f.declaringType();
+ }
+ });
+ }));
+
+ return fields;
+ }
+
+ ///
+ /// Public methods
+ ///
+
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> parameterTypes) {
+ Context ctx = getContext();
+ return ctx.solveMethod(name, parameterTypes, false, typeSolver);
+ }
+
+ @Deprecated
+ public Context getContext() {
+ return JavaParserFactory.getContext(wrappedNode, typeSolver);
+ }
+
+ public ResolvedType getUsage(Node node) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getName() {
+ return wrappedNode.getName().getId();
+ }
+
+ @Override
+ public ResolvedReferenceType getSuperClass() {
+ if (wrappedNode.getExtendedTypes().isEmpty()) {
+ return object();
+ } else {
+ return toReferenceType(wrappedNode.getExtendedTypes().get(0));
+ }
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getInterfaces() {
+ List<ResolvedReferenceType> interfaces = new ArrayList<>();
+ if (wrappedNode.getImplementedTypes() != null) {
+ for (ClassOrInterfaceType t : wrappedNode.getImplementedTypes()) {
+ interfaces.add(toReferenceType(t));
+ }
+ }
+ return interfaces;
+ }
+
+ @Override
+ public List<ResolvedConstructorDeclaration> getConstructors() {
+ List<ResolvedConstructorDeclaration> declared = new LinkedList<>();
+ for (BodyDeclaration<?> member : wrappedNode.getMembers()) {
+ if (member instanceof com.github.javaparser.ast.body.ConstructorDeclaration) {
+ com.github.javaparser.ast.body.ConstructorDeclaration constructorDeclaration = (com.github.javaparser.ast.body.ConstructorDeclaration) member;
+ declared.add(new JavaParserConstructorDeclaration(this, constructorDeclaration, typeSolver));
+ }
+ }
+ if (declared.isEmpty()) {
+ // If there are no constructors insert the default constructor
+ return ImmutableList.of(new DefaultConstructorDeclaration(this));
+ } else {
+ return declared;
+ }
+ }
+
+ @Override
+ public boolean hasDirectlyAnnotation(String canonicalName) {
+ for (AnnotationExpr annotationExpr : wrappedNode.getAnnotations()) {
+ if (solveType(annotationExpr.getName().getId(), typeSolver).getCorrespondingDeclaration().getQualifiedName().equals(canonicalName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isInterface() {
+ return wrappedNode.isInterface();
+ }
+
+ @Override
+ public String getPackageName() {
+ return javaParserTypeAdapter.getPackageName();
+ }
+
+ @Override
+ public String getClassName() {
+ return javaParserTypeAdapter.getClassName();
+ }
+
+ @Override
+ public String getQualifiedName() {
+ return javaParserTypeAdapter.getQualifiedName();
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) {
+ return javaParserTypeAdapter.isAssignableBy(other);
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedType type) {
+ return javaParserTypeAdapter.isAssignableBy(type);
+ }
+
+ @Override
+ public boolean canBeAssignedTo(ResolvedReferenceTypeDeclaration other) {
+ // TODO consider generic types
+ if (this.getQualifiedName().equals(other.getQualifiedName())) {
+ return true;
+ }
+ ResolvedClassDeclaration superclass = (ResolvedClassDeclaration) getSuperClass().getTypeDeclaration();
+ if (superclass != null) {
+ // We want to avoid infinite recursion in case of Object having Object as ancestor
+ if (Object.class.getCanonicalName().equals(superclass.getQualifiedName())) {
+ return true;
+ }
+ if (superclass.canBeAssignedTo(other)) {
+ return true;
+ }
+ }
+
+ if (this.wrappedNode.getImplementedTypes() != null) {
+ for (ClassOrInterfaceType type : wrappedNode.getImplementedTypes()) {
+ ResolvedReferenceTypeDeclaration ancestor = (ResolvedReferenceTypeDeclaration) new SymbolSolver(typeSolver).solveType(type);
+ if (ancestor.canBeAssignedTo(other)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean isTypeParameter() {
+ return false;
+ }
+
+ @Deprecated
+ public SymbolReference<ResolvedTypeDeclaration> solveType(String name, TypeSolver typeSolver) {
+ if (this.wrappedNode.getName().getId().equals(name)) {
+ return SymbolReference.solved(this);
+ }
+ SymbolReference<ResolvedTypeDeclaration> ref = javaParserTypeAdapter.solveType(name, typeSolver);
+ if (ref.isSolved()) {
+ return ref;
+ }
+
+ String prefix = wrappedNode.getName() + ".";
+ if (name.startsWith(prefix) && name.length() > prefix.length()) {
+ return new JavaParserClassDeclaration(this.wrappedNode, typeSolver).solveType(name.substring(prefix.length()), typeSolver);
+ }
+
+ return getContext().getParent().solveType(name, typeSolver);
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getAncestors() {
+ List<ResolvedReferenceType> ancestors = new ArrayList<>();
+
+ // We want to avoid infinite recursion in case of Object having Object as ancestor
+ if (!(Object.class.getCanonicalName().equals(getQualifiedName()))) {
+ ResolvedReferenceType superclass = getSuperClass();
+ if (superclass != null) {
+ ancestors.add(superclass);
+ }
+ if (wrappedNode.getImplementedTypes() != null) {
+ for (ClassOrInterfaceType implemented : wrappedNode.getImplementedTypes()) {
+ ResolvedReferenceType ancestor = toReferenceType(implemented);
+ ancestors.add(ancestor);
+ }
+ }
+ }
+
+ return ancestors;
+ }
+
+ @Override
+ public Set<ResolvedMethodDeclaration> getDeclaredMethods() {
+ Set<ResolvedMethodDeclaration> methods = new HashSet<>();
+ for (BodyDeclaration<?> member : wrappedNode.getMembers()) {
+ if (member instanceof com.github.javaparser.ast.body.MethodDeclaration) {
+ methods.add(new JavaParserMethodDeclaration((com.github.javaparser.ast.body.MethodDeclaration) member, typeSolver));
+ }
+ }
+ return methods;
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ return this.wrappedNode.getTypeParameters().stream().map(
+ (tp) -> new JavaParserTypeParameter(tp, typeSolver)
+ ).collect(Collectors.toList());
+ }
+
+ /**
+ * Returns the JavaParser node associated with this JavaParserClassDeclaration.
+ *
+ * @return A visitable JavaParser node wrapped by this object.
+ */
+ public com.github.javaparser.ast.body.ClassOrInterfaceDeclaration getWrappedNode() {
+ return wrappedNode;
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return Helper.toAccessLevel(wrappedNode.getModifiers());
+ }
+
+ ///
+ /// Protected methods
+ ///
+
+ @Override
+ protected ResolvedReferenceType object() {
+ return new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver);
+ }
+
+ @Override
+ public Set<ResolvedReferenceTypeDeclaration> internalTypes() {
+ Set<ResolvedReferenceTypeDeclaration> res = new HashSet<>();
+ for (BodyDeclaration<?> member : this.wrappedNode.getMembers()) {
+ if (member instanceof com.github.javaparser.ast.body.TypeDeclaration) {
+ res.add(JavaParserFacade.get(typeSolver).getTypeDeclaration((com.github.javaparser.ast.body.TypeDeclaration)member));
+ }
+ }
+ return res;
+ }
+
+ @Override
+ public Optional<ResolvedReferenceTypeDeclaration> containerType() {
+ return javaParserTypeAdapter.containerType();
+ }
+
+ ///
+ /// Private methods
+ ///
+
+ private ResolvedReferenceType toReferenceType(ClassOrInterfaceType classOrInterfaceType) {
+ String className = classOrInterfaceType.getName().getId();
+ if (classOrInterfaceType.getScope().isPresent()) {
+ // look for the qualified name (for example class of type Rectangle2D.Double)
+ className = classOrInterfaceType.getScope().get().toString() + "." + className;
+ }
+ SymbolReference<ResolvedTypeDeclaration> ref = solveType(className, typeSolver);
+ if (!ref.isSolved()) {
+ Optional<ClassOrInterfaceType> localScope = classOrInterfaceType.getScope();
+ if (localScope.isPresent()) {
+ String localName = localScope.get().getName().getId() + "." + classOrInterfaceType.getName().getId();
+ ref = solveType(localName, typeSolver);
+ }
+ }
+ if (!ref.isSolved()) {
+ throw new UnsolvedSymbolException(classOrInterfaceType.getName().getId());
+ }
+ if (!classOrInterfaceType.getTypeArguments().isPresent()) {
+ return new ReferenceTypeImpl(ref.getCorrespondingDeclaration().asReferenceType(), typeSolver);
+ }
+ List<ResolvedType> superClassTypeParameters = classOrInterfaceType.getTypeArguments().get()
+ .stream().map(ta -> new LazyType(v -> JavaParserFacade.get(typeSolver).convert(ta, ta)))
+ .collect(Collectors.toList());
+ return new ReferenceTypeImpl(ref.getCorrespondingDeclaration().asReferenceType(), superClassTypeParameters, typeSolver);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserConstructorDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserConstructorDeclaration.java
new file mode 100644
index 000000000..e9500fed2
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserConstructorDeclaration.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.declarations;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.resolution.declarations.ResolvedClassDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedConstructorDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedParameterDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavaParserConstructorDeclaration implements ResolvedConstructorDeclaration {
+
+ private ResolvedClassDeclaration classDeclaration;
+ private com.github.javaparser.ast.body.ConstructorDeclaration wrappedNode;
+ private TypeSolver typeSolver;
+
+ JavaParserConstructorDeclaration(ResolvedClassDeclaration classDeclaration, com.github.javaparser.ast.body.ConstructorDeclaration wrappedNode,
+ TypeSolver typeSolver) {
+ this.classDeclaration = classDeclaration;
+ this.wrappedNode = wrappedNode;
+ this.typeSolver = typeSolver;
+ }
+
+ @Override
+ public ResolvedClassDeclaration declaringType() {
+ return classDeclaration;
+ }
+
+ @Override
+ public int getNumberOfParams() {
+ return this.wrappedNode.getParameters().size();
+ }
+
+ @Override
+ public ResolvedParameterDeclaration getParam(int i) {
+ if (i < 0 || i >= getNumberOfParams()) {
+ throw new IllegalArgumentException(String.format("No param with index %d. Number of params: %d", i, getNumberOfParams()));
+ }
+ return new JavaParserParameterDeclaration(wrappedNode.getParameters().get(i), typeSolver);
+ }
+
+ @Override
+ public String getName() {
+ return this.classDeclaration.getName();
+ }
+
+ /**
+ * Returns the JavaParser node associated with this JavaParserConstructorDeclaration.
+ *
+ * @return A visitable JavaParser node wrapped by this object.
+ */
+ public com.github.javaparser.ast.body.ConstructorDeclaration getWrappedNode() {
+ return wrappedNode;
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return Helper.toAccessLevel(wrappedNode.getModifiers());
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ return this.wrappedNode.getTypeParameters().stream().map((astTp) -> new JavaParserTypeParameter(astTp, typeSolver)).collect(Collectors.toList());
+ }
+
+ @Override
+ public int getNumberOfSpecifiedExceptions() {
+ return wrappedNode.getThrownExceptions().size();
+ }
+
+ @Override
+ public ResolvedType getSpecifiedException(int index) {
+ if (index < 0 || index >= getNumberOfSpecifiedExceptions()) {
+ throw new IllegalArgumentException(String.format("No exception with index %d. Number of exceptions: %d",
+ index, getNumberOfSpecifiedExceptions()));
+ }
+ return JavaParserFacade.get(typeSolver)
+ .convert(wrappedNode.getThrownExceptions().get(index), wrappedNode);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserEnumConstantDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserEnumConstantDeclaration.java
new file mode 100644
index 000000000..6f909582d
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserEnumConstantDeclaration.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.declarations;
+
+import com.github.javaparser.ast.body.EnumDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedEnumConstantDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavaParserEnumConstantDeclaration implements ResolvedEnumConstantDeclaration {
+
+ private TypeSolver typeSolver;
+ private com.github.javaparser.ast.body.EnumConstantDeclaration wrappedNode;
+
+ public JavaParserEnumConstantDeclaration(com.github.javaparser.ast.body.EnumConstantDeclaration wrappedNode, TypeSolver typeSolver) {
+ this.wrappedNode = wrappedNode;
+ this.typeSolver = typeSolver;
+ }
+
+ @Override
+ public ResolvedType getType() {
+ return new ReferenceTypeImpl(new JavaParserEnumDeclaration((EnumDeclaration) requireParentNode(wrappedNode), typeSolver), typeSolver);
+ }
+
+ @Override
+ public String getName() {
+ return wrappedNode.getName().getId();
+ }
+
+ /**
+ * Returns the JavaParser node associated with this JavaParserEnumConstantDeclaration.
+ *
+ * @return A visitable JavaParser node wrapped by this object.
+ */
+ public com.github.javaparser.ast.body.EnumConstantDeclaration getWrappedNode() {
+ return wrappedNode;
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserEnumDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserEnumDeclaration.java
new file mode 100644
index 000000000..818c0604c
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserEnumDeclaration.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.declarations;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.body.BodyDeclaration;
+import com.github.javaparser.ast.body.EnumConstantDeclaration;
+import com.github.javaparser.ast.type.ClassOrInterfaceType;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.UnsolvedSymbolException;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedArrayType;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.resolution.types.parametrization.ResolvedTypeParametersMap;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.logic.AbstractTypeDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionFactory;
+import com.github.javaparser.symbolsolver.resolution.SymbolSolver;
+
+import java.io.Serializable;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavaParserEnumDeclaration extends AbstractTypeDeclaration implements ResolvedEnumDeclaration {
+
+ private TypeSolver typeSolver;
+ private com.github.javaparser.ast.body.EnumDeclaration wrappedNode;
+ private JavaParserTypeAdapter<com.github.javaparser.ast.body.EnumDeclaration> javaParserTypeAdapter;
+
+ public JavaParserEnumDeclaration(com.github.javaparser.ast.body.EnumDeclaration wrappedNode, TypeSolver typeSolver) {
+ this.wrappedNode = wrappedNode;
+ this.typeSolver = typeSolver;
+ this.javaParserTypeAdapter = new JavaParserTypeAdapter<>(wrappedNode, typeSolver);
+ }
+
+ @Override
+ public String toString() {
+ return "JavaParserEnumDeclaration{" +
+ "wrappedNode=" + wrappedNode +
+ '}';
+ }
+
+ @Override
+ public Set<ResolvedMethodDeclaration> getDeclaredMethods() {
+ Set<ResolvedMethodDeclaration> methods = new HashSet<>();
+ for (BodyDeclaration<?> member : wrappedNode.getMembers()) {
+ if (member instanceof com.github.javaparser.ast.body.MethodDeclaration) {
+ methods.add(new JavaParserMethodDeclaration((com.github.javaparser.ast.body.MethodDeclaration) member, typeSolver));
+ }
+ }
+ return methods;
+ }
+
+ public Context getContext() {
+ return JavaParserFactory.getContext(wrappedNode, typeSolver);
+ }
+
+ @Override
+ public String getName() {
+ return wrappedNode.getName().getId();
+ }
+
+ @Override
+ public boolean isField() {
+ return false;
+ }
+
+ @Override
+ public boolean isParameter() {
+ return false;
+ }
+
+ @Override
+ public boolean isType() {
+ return true;
+ }
+
+ @Override
+ public boolean hasDirectlyAnnotation(String canonicalName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean canBeAssignedTo(ResolvedReferenceTypeDeclaration other) {
+ String otherName = other.getQualifiedName();
+ // Enums cannot be extended
+ if (otherName.equals(this.getQualifiedName())) {
+ return true;
+ }
+ if (otherName.equals(Enum.class.getCanonicalName())) {
+ return true;
+ }
+ // Enum implements Comparable and Serializable
+ if (otherName.equals(Comparable.class.getCanonicalName())) {
+ return true;
+ }
+ if (otherName.equals(Serializable.class.getCanonicalName())) {
+ return true;
+ }
+ if (otherName.equals(Object.class.getCanonicalName())) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isClass() {
+ return false;
+ }
+
+ @Override
+ public boolean isInterface() {
+ return false;
+ }
+
+ @Override
+ public String getPackageName() {
+ return javaParserTypeAdapter.getPackageName();
+ }
+
+ @Override
+ public String getClassName() {
+ return javaParserTypeAdapter.getClassName();
+ }
+
+ @Override
+ public String getQualifiedName() {
+ return javaParserTypeAdapter.getQualifiedName();
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) {
+ return javaParserTypeAdapter.isAssignableBy(other);
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedType type) {
+ return javaParserTypeAdapter.isAssignableBy(type);
+ }
+
+ @Override
+ public boolean isTypeParameter() {
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ JavaParserEnumDeclaration that = (JavaParserEnumDeclaration) o;
+
+ if (!wrappedNode.equals(that.wrappedNode)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return wrappedNode.hashCode();
+ }
+
+ @Deprecated
+ public Optional<MethodUsage> solveMethodAsUsage(String name, List<ResolvedType> parameterTypes,
+ TypeSolver typeSolver, Context invokationContext, List<ResolvedType> typeParameterValues) {
+ if (name.equals("values") && parameterTypes.isEmpty()) {
+ return Optional.of(new ValuesMethod(this, typeSolver).getUsage(null));
+ }
+ // TODO add methods inherited from Enum
+ return getContext().solveMethodAsUsage(name, parameterTypes, typeSolver);
+ }
+
+ @Override
+ public List<ResolvedFieldDeclaration> getAllFields() {
+ List<ResolvedFieldDeclaration> fields = javaParserTypeAdapter.getFieldsForDeclaredVariables();
+
+ if (this.wrappedNode.getEntries() != null) {
+ for (EnumConstantDeclaration member : this.wrappedNode.getEntries()) {
+ fields.add(new JavaParserFieldDeclaration(member, typeSolver));
+ }
+ }
+
+ return fields;
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getAncestors() {
+ List<ResolvedReferenceType> ancestors = new ArrayList<>();
+ ResolvedReferenceType enumClass = ReflectionFactory.typeUsageFor(Enum.class, typeSolver).asReferenceType();
+ ResolvedTypeParameterDeclaration eTypeParameter = enumClass.getTypeDeclaration().getTypeParameters().get(0);
+ enumClass = enumClass.deriveTypeParameters(new ResolvedTypeParametersMap.Builder().setValue(eTypeParameter, new ReferenceTypeImpl(this, typeSolver)).build());
+ ancestors.add(enumClass);
+ if (wrappedNode.getImplementedTypes() != null) {
+ for (ClassOrInterfaceType implementedType : wrappedNode.getImplementedTypes()) {
+ SymbolReference<ResolvedTypeDeclaration> implementedDeclRef = new SymbolSolver(typeSolver).solveTypeInType(this, implementedType.getName().getId());
+ if (!implementedDeclRef.isSolved()) {
+ throw new UnsolvedSymbolException(implementedType.getName().getId());
+ }
+ ancestors.add(new ReferenceTypeImpl((ResolvedReferenceTypeDeclaration) implementedDeclRef.getCorrespondingDeclaration(), typeSolver));
+ }
+ }
+ return ancestors;
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ return Collections.emptyList();
+ }
+
+ /**
+ * Returns the JavaParser node associated with this JavaParserEnumDeclaration.
+ *
+ * @return A visitable JavaParser node wrapped by this object.
+ */
+ public com.github.javaparser.ast.body.EnumDeclaration getWrappedNode() {
+ return wrappedNode;
+ }
+
+ @Override
+ public List<ResolvedEnumConstantDeclaration> getEnumConstants() {
+ return wrappedNode.getEntries().stream()
+ .map(entry -> new JavaParserEnumConstantDeclaration(entry, typeSolver))
+ .collect(Collectors.toList());
+ }
+
+ // Needed by ContextHelper
+ public static class ValuesMethod implements ResolvedMethodDeclaration {
+
+ private JavaParserEnumDeclaration enumDeclaration;
+ private TypeSolver typeSolver;
+
+ public ValuesMethod(JavaParserEnumDeclaration enumDeclaration, TypeSolver typeSolver) {
+ this.enumDeclaration = enumDeclaration;
+ this.typeSolver = typeSolver;
+ }
+
+ @Override
+ public ResolvedReferenceTypeDeclaration declaringType() {
+ return enumDeclaration;
+ }
+
+ @Override
+ public ResolvedType getReturnType() {
+ return new ResolvedArrayType(new ReferenceTypeImpl(enumDeclaration, typeSolver));
+ }
+
+ @Override
+ public int getNumberOfParams() {
+ return 0;
+ }
+
+ @Override
+ public ResolvedParameterDeclaration getParam(int i) {
+ throw new UnsupportedOperationException();
+ }
+
+ public MethodUsage getUsage(Node node) {
+ throw new UnsupportedOperationException();
+ }
+
+ public MethodUsage resolveTypeVariables(Context context, List<ResolvedType> parameterTypes) {
+ return new MethodUsage(this);
+ }
+
+ @Override
+ public boolean isAbstract() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isDefaultMethod() {
+ return false;
+ }
+
+ @Override
+ public boolean isStatic() {
+ return false;
+ }
+
+ @Override
+ public String getName() {
+ return "values";
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return Helper.toAccessLevel(enumDeclaration.getWrappedNode().getModifiers());
+ }
+
+ @Override
+ public int getNumberOfSpecifiedExceptions() {
+ return 0;
+ }
+
+ @Override
+ public ResolvedType getSpecifiedException(int index) {
+ throw new UnsupportedOperationException("The values method of an enum does not throw any exception");
+ }
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<ResolvedReferenceTypeDeclaration> internalTypes() {
+ Set<ResolvedReferenceTypeDeclaration> res = new HashSet<>();
+ for (BodyDeclaration<?> member : this.wrappedNode.getMembers()) {
+ if (member instanceof com.github.javaparser.ast.body.TypeDeclaration) {
+ res.add(JavaParserFacade.get(typeSolver).getTypeDeclaration((com.github.javaparser.ast.body.TypeDeclaration)member));
+ }
+ }
+ return res;
+ }
+
+ @Override
+ public Optional<ResolvedReferenceTypeDeclaration> containerType() {
+ return javaParserTypeAdapter.containerType();
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserFieldDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserFieldDeclaration.java
new file mode 100644
index 000000000..1c9e00511
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserFieldDeclaration.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.declarations;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.ast.Modifier;
+import com.github.javaparser.ast.body.EnumConstantDeclaration;
+import com.github.javaparser.ast.body.VariableDeclarator;
+import com.github.javaparser.resolution.declarations.ResolvedFieldDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparser.Navigator;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+
+import java.util.Optional;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentNode;
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavaParserFieldDeclaration implements ResolvedFieldDeclaration {
+
+ private VariableDeclarator variableDeclarator;
+ private com.github.javaparser.ast.body.FieldDeclaration wrappedNode;
+ private EnumConstantDeclaration enumConstantDeclaration;
+ private TypeSolver typeSolver;
+
+ public JavaParserFieldDeclaration(VariableDeclarator variableDeclarator, TypeSolver typeSolver) {
+ if (typeSolver == null) {
+ throw new IllegalArgumentException("typeSolver should not be null");
+ }
+ this.variableDeclarator = variableDeclarator;
+ this.typeSolver = typeSolver;
+ if (!(requireParentNode(variableDeclarator) instanceof com.github.javaparser.ast.body.FieldDeclaration)) {
+ throw new IllegalStateException(requireParentNode(variableDeclarator).getClass().getCanonicalName());
+ }
+ this.wrappedNode = (com.github.javaparser.ast.body.FieldDeclaration) requireParentNode(variableDeclarator);
+ }
+
+ public JavaParserFieldDeclaration(EnumConstantDeclaration enumConstantDeclaration, TypeSolver typeSolver) {
+ if (typeSolver == null) {
+ throw new IllegalArgumentException("typeSolver should not be null");
+ }
+ this.enumConstantDeclaration = enumConstantDeclaration;
+ this.typeSolver = typeSolver;
+ }
+
+ @Override
+ public ResolvedType getType() {
+ if (enumConstantDeclaration != null) {
+ com.github.javaparser.ast.body.EnumDeclaration enumDeclaration = (com.github.javaparser.ast.body.EnumDeclaration) requireParentNode(enumConstantDeclaration);
+ return new ReferenceTypeImpl(new JavaParserEnumDeclaration(enumDeclaration, typeSolver), typeSolver);
+ }
+ return JavaParserFacade.get(typeSolver).convert(variableDeclarator.getType(), wrappedNode);
+ }
+
+ @Override
+ public String getName() {
+ if (enumConstantDeclaration != null) {
+ return enumConstantDeclaration.getName().getId();
+ } else {
+ return variableDeclarator.getName().getId();
+ }
+ }
+
+ @Override
+ public boolean isStatic() {
+ return wrappedNode.getModifiers().contains(Modifier.STATIC);
+ }
+
+ @Override
+ public boolean isField() {
+ return true;
+ }
+
+ /**
+ * Returns the JavaParser node associated with this JavaParserFieldDeclaration.
+ *
+ * @return A visitable JavaParser node wrapped by this object.
+ */
+ public com.github.javaparser.ast.body.FieldDeclaration getWrappedNode() {
+ return wrappedNode;
+ }
+
+ @Override
+ public String toString() {
+ return "JPField{" + getName() + "}";
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return Helper.toAccessLevel(wrappedNode.getModifiers());
+ }
+
+ @Override
+ public ResolvedTypeDeclaration declaringType() {
+ Optional<com.github.javaparser.ast.body.TypeDeclaration> typeDeclaration = wrappedNode.findParent(com.github.javaparser.ast.body.TypeDeclaration.class);
+ if (typeDeclaration.isPresent()) {
+ return JavaParserFacade.get(typeSolver).getTypeDeclaration(typeDeclaration.get());
+ }
+ throw new IllegalStateException();
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserInterfaceDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserInterfaceDeclaration.java
new file mode 100644
index 000000000..34955a0c0
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserInterfaceDeclaration.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.declarations;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.body.BodyDeclaration;
+import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
+import com.github.javaparser.ast.expr.AnnotationExpr;
+import com.github.javaparser.ast.type.ClassOrInterfaceType;
+import com.github.javaparser.resolution.UnsolvedSymbolException;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.logic.AbstractTypeDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.LazyType;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+import com.github.javaparser.symbolsolver.resolution.SymbolSolver;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavaParserInterfaceDeclaration extends AbstractTypeDeclaration implements ResolvedInterfaceDeclaration {
+
+ private TypeSolver typeSolver;
+ private ClassOrInterfaceDeclaration wrappedNode;
+ private JavaParserTypeAdapter<ClassOrInterfaceDeclaration> javaParserTypeAdapter;
+
+ public JavaParserInterfaceDeclaration(ClassOrInterfaceDeclaration wrappedNode, TypeSolver typeSolver) {
+ if (!wrappedNode.isInterface()) {
+ throw new IllegalArgumentException();
+ }
+ this.wrappedNode = wrappedNode;
+ this.typeSolver = typeSolver;
+ this.javaParserTypeAdapter = new JavaParserTypeAdapter<>(wrappedNode, typeSolver);
+ }
+
+ @Override
+ public Set<ResolvedMethodDeclaration> getDeclaredMethods() {
+ Set<ResolvedMethodDeclaration> methods = new HashSet<>();
+ for (BodyDeclaration<?> member : wrappedNode.getMembers()) {
+ if (member instanceof com.github.javaparser.ast.body.MethodDeclaration) {
+ methods.add(new JavaParserMethodDeclaration((com.github.javaparser.ast.body.MethodDeclaration) member, typeSolver));
+ }
+ }
+ return methods;
+ }
+
+ public Context getContext() {
+ return JavaParserFactory.getContext(wrappedNode, typeSolver);
+ }
+
+ public ResolvedType getUsage(Node node) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ JavaParserInterfaceDeclaration that = (JavaParserInterfaceDeclaration) o;
+
+ if (!wrappedNode.equals(that.wrappedNode)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return wrappedNode.hashCode();
+ }
+
+ @Override
+ public String getName() {
+ return wrappedNode.getName().getId();
+ }
+
+ @Override
+ public ResolvedInterfaceDeclaration asInterface() {
+ return this;
+ }
+
+ @Override
+ public boolean hasDirectlyAnnotation(String canonicalName) {
+ for (AnnotationExpr annotationExpr : wrappedNode.getAnnotations()) {
+ if (solveType(annotationExpr.getName().getId(), typeSolver).getCorrespondingDeclaration().getQualifiedName().equals(canonicalName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isInterface() {
+ return true;
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getInterfacesExtended() {
+ List<ResolvedReferenceType> interfaces = new ArrayList<>();
+ if (wrappedNode.getImplementedTypes() != null) {
+ for (ClassOrInterfaceType t : wrappedNode.getImplementedTypes()) {
+ interfaces.add(new ReferenceTypeImpl(solveType(t.getName().getId(), typeSolver).getCorrespondingDeclaration().asInterface(), typeSolver));
+ }
+ }
+ return interfaces;
+ }
+
+ @Override
+ public String getPackageName() {
+ return javaParserTypeAdapter.getPackageName();
+ }
+
+ @Override
+ public String getClassName() {
+ return javaParserTypeAdapter.getClassName();
+ }
+
+ @Override
+ public String getQualifiedName() {
+ return javaParserTypeAdapter.getQualifiedName();
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) {
+ return javaParserTypeAdapter.isAssignableBy(other);
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedType type) {
+ return javaParserTypeAdapter.isAssignableBy(type);
+ }
+
+ @Override
+ public boolean canBeAssignedTo(ResolvedReferenceTypeDeclaration other) {
+ // TODO consider generic types
+ if (this.getQualifiedName().equals(other.getQualifiedName())) {
+ return true;
+ }
+ if (this.wrappedNode.getExtendedTypes() != null) {
+ for (ClassOrInterfaceType type : wrappedNode.getExtendedTypes()) {
+ ResolvedReferenceTypeDeclaration ancestor = (ResolvedReferenceTypeDeclaration) new SymbolSolver(typeSolver).solveType(type);
+ if (ancestor.canBeAssignedTo(other)) {
+ return true;
+ }
+ }
+ }
+
+ if (this.wrappedNode.getImplementedTypes() != null) {
+ for (ClassOrInterfaceType type : wrappedNode.getImplementedTypes()) {
+ ResolvedReferenceTypeDeclaration ancestor = (ResolvedReferenceTypeDeclaration) new SymbolSolver(typeSolver).solveType(type);
+ if (ancestor.canBeAssignedTo(other)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean isTypeParameter() {
+ return false;
+ }
+
+ @Override
+ public List<ResolvedFieldDeclaration> getAllFields() {
+ List<ResolvedFieldDeclaration> fields = javaParserTypeAdapter.getFieldsForDeclaredVariables();
+
+ getAncestors().forEach(ancestor -> ancestor.getTypeDeclaration().getAllFields().forEach(f -> {
+ fields.add(new ResolvedFieldDeclaration() {
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return f.accessSpecifier();
+ }
+
+ @Override
+ public String getName() {
+ return f.getName();
+ }
+
+ @Override
+ public ResolvedType getType() {
+ return ancestor.useThisTypeParametersOnTheGivenType(f.getType());
+ }
+
+ @Override
+ public boolean isStatic() {
+ return f.isStatic();
+ }
+
+ @Override
+ public ResolvedTypeDeclaration declaringType() {
+ return f.declaringType();
+ }
+ });
+ }));
+
+ return fields;
+ }
+
+
+ @Override
+ public String toString() {
+ return "JavaParserInterfaceDeclaration{" +
+ "wrappedNode=" + wrappedNode +
+ '}';
+ }
+
+ @Deprecated
+ public SymbolReference<ResolvedTypeDeclaration> solveType(String name, TypeSolver typeSolver) {
+ if (this.wrappedNode.getName().getId().equals(name)) {
+ return SymbolReference.solved(this);
+ }
+ SymbolReference<ResolvedTypeDeclaration> ref = javaParserTypeAdapter.solveType(name, typeSolver);
+ if (ref.isSolved()) {
+ return ref;
+ }
+
+ String prefix = wrappedNode.getName() + ".";
+ if (name.startsWith(prefix) && name.length() > prefix.length()) {
+ return new JavaParserInterfaceDeclaration(this.wrappedNode, typeSolver).solveType(name.substring(prefix.length()), typeSolver);
+ }
+
+ return getContext().getParent().solveType(name, typeSolver);
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getAncestors() {
+ List<ResolvedReferenceType> ancestors = new ArrayList<>();
+ if (wrappedNode.getExtendedTypes() != null) {
+ for (ClassOrInterfaceType extended : wrappedNode.getExtendedTypes()) {
+ ancestors.add(toReferenceType(extended));
+ }
+ }
+ if (wrappedNode.getImplementedTypes() != null) {
+ for (ClassOrInterfaceType implemented : wrappedNode.getImplementedTypes()) {
+ ancestors.add(toReferenceType(implemented));
+ }
+ }
+ return ancestors;
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ if (this.wrappedNode.getTypeParameters() == null) {
+ return Collections.emptyList();
+ } else {
+ return this.wrappedNode.getTypeParameters().stream().map(
+ (tp) -> new JavaParserTypeParameter(tp, typeSolver)
+ ).collect(Collectors.toList());
+ }
+ }
+
+ /**
+ * Returns the JavaParser node associated with this JavaParserInterfaceDeclaration.
+ *
+ * @return A visitable JavaParser node wrapped by this object.
+ */
+ public ClassOrInterfaceDeclaration getWrappedNode() {
+ return wrappedNode;
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return Helper.toAccessLevel(wrappedNode.getModifiers());
+ }
+
+ @Override
+ public Set<ResolvedReferenceTypeDeclaration> internalTypes() {
+ Set<ResolvedReferenceTypeDeclaration> res = new HashSet<>();
+ for (BodyDeclaration<?> member : this.wrappedNode.getMembers()) {
+ if (member instanceof com.github.javaparser.ast.body.TypeDeclaration) {
+ res.add(JavaParserFacade.get(typeSolver).getTypeDeclaration((com.github.javaparser.ast.body.TypeDeclaration)member));
+ }
+ }
+ return res;
+ }
+
+ @Override
+ public Optional<ResolvedReferenceTypeDeclaration> containerType() {
+ return javaParserTypeAdapter.containerType();
+ }
+
+ ///
+ /// Private methods
+ ///
+
+ private ResolvedReferenceType toReferenceType(ClassOrInterfaceType classOrInterfaceType) {
+ SymbolReference<? extends ResolvedTypeDeclaration> ref = null;
+ if (classOrInterfaceType.toString().indexOf('.') > -1) {
+ ref = typeSolver.tryToSolveType(classOrInterfaceType.toString());
+ }
+ if (ref == null || !ref.isSolved()) {
+ ref = solveType(classOrInterfaceType.toString(), typeSolver);
+ }
+ if (!ref.isSolved()) {
+ ref = solveType(classOrInterfaceType.getName().getId(), typeSolver);
+ }
+ if (!ref.isSolved()) {
+ throw new UnsolvedSymbolException(classOrInterfaceType.getName().getId());
+ }
+ if (!classOrInterfaceType.getTypeArguments().isPresent()) {
+ return new ReferenceTypeImpl(ref.getCorrespondingDeclaration().asReferenceType(), typeSolver);
+ }
+ List<ResolvedType> superClassTypeParameters = classOrInterfaceType.getTypeArguments().get()
+ .stream().map(ta -> new LazyType(v -> JavaParserFacade.get(typeSolver).convert(ta, ta)))
+ .collect(Collectors.toList());
+ return new ReferenceTypeImpl(ref.getCorrespondingDeclaration().asReferenceType(), superClassTypeParameters, typeSolver);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserMethodDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserMethodDeclaration.java
new file mode 100644
index 000000000..4f3cb3569
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserMethodDeclaration.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.declarations;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.expr.ObjectCreationExpr;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedParameterDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.declarations.common.MethodDeclarationCommonLogic;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavaParserMethodDeclaration implements ResolvedMethodDeclaration {
+
+ private com.github.javaparser.ast.body.MethodDeclaration wrappedNode;
+ private TypeSolver typeSolver;
+
+ public JavaParserMethodDeclaration(com.github.javaparser.ast.body.MethodDeclaration wrappedNode, TypeSolver typeSolver) {
+ this.wrappedNode = wrappedNode;
+ this.typeSolver = typeSolver;
+ }
+
+ @Override
+ public String toString() {
+ return "JavaParserMethodDeclaration{" +
+ "wrappedNode=" + wrappedNode +
+ ", typeSolver=" + typeSolver +
+ '}';
+ }
+
+ @Override
+ public ResolvedReferenceTypeDeclaration declaringType() {
+ if (requireParentNode(wrappedNode) instanceof ObjectCreationExpr) {
+ ObjectCreationExpr parentNode = (ObjectCreationExpr) requireParentNode(wrappedNode);
+ return new JavaParserAnonymousClassDeclaration(parentNode, typeSolver);
+ }
+ return JavaParserFactory.toTypeDeclaration(requireParentNode(wrappedNode), typeSolver);
+ }
+
+ @Override
+ public ResolvedType getReturnType() {
+ return JavaParserFacade.get(typeSolver).convert(wrappedNode.getType(), getContext());
+ }
+
+ @Override
+ public int getNumberOfParams() {
+ return wrappedNode.getParameters().size();
+ }
+
+ @Override
+ public ResolvedParameterDeclaration getParam(int i) {
+ if (i < 0 || i >= getNumberOfParams()) {
+ throw new IllegalArgumentException(String.format("No param with index %d. Number of params: %d", i, getNumberOfParams()));
+ }
+ return new JavaParserParameterDeclaration(wrappedNode.getParameters().get(i), typeSolver);
+ }
+
+ public MethodUsage getUsage(Node node) {
+ throw new UnsupportedOperationException();
+ }
+
+ public MethodUsage resolveTypeVariables(Context context, List<ResolvedType> parameterTypes) {
+ return new MethodDeclarationCommonLogic(this, typeSolver).resolveTypeVariables(context, parameterTypes);
+ }
+
+ private Context getContext() {
+ return JavaParserFactory.getContext(wrappedNode, typeSolver);
+ }
+
+ @Override
+ public boolean isAbstract() {
+ return !wrappedNode.getBody().isPresent();
+ }
+
+ @Override
+ public String getName() {
+ return wrappedNode.getName().getId();
+ }
+
+ @Override
+ public boolean isField() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isParameter() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isType() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ return this.wrappedNode.getTypeParameters().stream().map((astTp) -> new JavaParserTypeParameter(astTp, typeSolver)).collect(Collectors.toList());
+ }
+
+ @Override
+ public boolean isDefaultMethod() {
+ return wrappedNode.isDefault();
+ }
+
+ @Override
+ public boolean isStatic() {
+ return wrappedNode.isStatic();
+ }
+
+ /**
+ * Returns the JavaParser node associated with this JavaParserMethodDeclaration.
+ *
+ * @return A visitable JavaParser node wrapped by this object.
+ */
+ public com.github.javaparser.ast.body.MethodDeclaration getWrappedNode() {
+ return wrappedNode;
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return Helper.toAccessLevel(wrappedNode.getModifiers());
+ }
+
+ @Override
+ public int getNumberOfSpecifiedExceptions() {
+ return wrappedNode.getThrownExceptions().size();
+ }
+
+ @Override
+ public ResolvedType getSpecifiedException(int index) {
+ if (index < 0 || index >= getNumberOfSpecifiedExceptions()) {
+ throw new IllegalArgumentException(String.format("No exception with index %d. Number of exceptions: %d",
+ index, getNumberOfSpecifiedExceptions()));
+ }
+ return JavaParserFacade.get(typeSolver).convert(wrappedNode.getThrownExceptions()
+ .get(index), wrappedNode);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserParameterDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserParameterDeclaration.java
new file mode 100644
index 000000000..cba7d32b1
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserParameterDeclaration.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.declarations;
+
+import com.github.javaparser.ast.body.Parameter;
+import com.github.javaparser.ast.type.UnknownType;
+import com.github.javaparser.resolution.declarations.ResolvedParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedArrayType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.javaparsermodel.contexts.LambdaExprContext;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.resolution.Value;
+
+import java.util.Optional;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavaParserParameterDeclaration implements ResolvedParameterDeclaration {
+
+ private Parameter wrappedNode;
+ private TypeSolver typeSolver;
+
+ public JavaParserParameterDeclaration(Parameter wrappedNode, TypeSolver typeSolver) {
+ this.wrappedNode = wrappedNode;
+ this.typeSolver = typeSolver;
+ }
+
+ @Override
+ public String getName() {
+ return wrappedNode.getName().getId();
+ }
+
+ @Override
+ public boolean isField() {
+ return false;
+ }
+
+ @Override
+ public boolean isParameter() {
+ return true;
+ }
+
+ @Override
+ public boolean isVariadic() {
+ return wrappedNode.isVarArgs();
+ }
+
+ @Override
+ public boolean isType() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ResolvedType getType() {
+ if (wrappedNode.getType() instanceof UnknownType && JavaParserFactory.getContext(wrappedNode, typeSolver) instanceof LambdaExprContext) {
+ Optional<Value> value = JavaParserFactory.getContext(wrappedNode, typeSolver).solveSymbolAsValue(wrappedNode.getNameAsString(), typeSolver);
+ if (value.isPresent()) {
+ return value.get().getType();
+ }
+ }
+ ResolvedType res = JavaParserFacade.get(typeSolver).convert(wrappedNode.getType(), wrappedNode);
+ if (isVariadic()) {
+ res = new ResolvedArrayType(res);
+ }
+ return res;
+ }
+
+ @Override
+ public ResolvedParameterDeclaration asParameter() {
+ return this;
+ }
+
+ /**
+ * Returns the JavaParser node associated with this JavaParserParameterDeclaration.
+ *
+ * @return A visitable JavaParser node wrapped by this object.
+ */
+ public Parameter getWrappedNode() {
+ return wrappedNode;
+ }
+
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserSymbolDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserSymbolDeclaration.java
new file mode 100644
index 000000000..f1f1ffb69
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserSymbolDeclaration.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.declarations;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.body.FieldDeclaration;
+import com.github.javaparser.ast.body.Parameter;
+import com.github.javaparser.ast.body.VariableDeclarator;
+import com.github.javaparser.ast.expr.LambdaExpr;
+import com.github.javaparser.ast.expr.MethodCallExpr;
+import com.github.javaparser.ast.expr.VariableDeclarationExpr;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.types.ResolvedArrayType;
+import com.github.javaparser.resolution.types.ResolvedPrimitiveType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavaParserSymbolDeclaration implements ResolvedValueDeclaration {
+
+ private String name;
+ private Node wrappedNode;
+ private boolean field;
+ private boolean parameter;
+ private boolean variable;
+ private TypeSolver typeSolver;
+
+ private JavaParserSymbolDeclaration(Node wrappedNode, String name, TypeSolver typeSolver, boolean field, boolean parameter, boolean variable) {
+ this.name = name;
+ this.wrappedNode = wrappedNode;
+ this.field = field;
+ this.variable = variable;
+ this.parameter = parameter;
+ this.typeSolver = typeSolver;
+ }
+
+ public static JavaParserFieldDeclaration field(VariableDeclarator wrappedNode, TypeSolver typeSolver) {
+ return new JavaParserFieldDeclaration(wrappedNode, typeSolver);
+ }
+
+ public static JavaParserParameterDeclaration parameter(Parameter parameter, TypeSolver typeSolver) {
+ return new JavaParserParameterDeclaration(parameter, typeSolver);
+ }
+
+ public static JavaParserSymbolDeclaration localVar(VariableDeclarator variableDeclarator, TypeSolver typeSolver) {
+ return new JavaParserSymbolDeclaration(variableDeclarator, variableDeclarator.getName().getId(), typeSolver, false, false, true);
+ }
+
+ public static int getParamPos(Parameter parameter) {
+ int pos = 0;
+ for (Node node : requireParentNode(parameter).getChildNodes()) {
+ if (node == parameter) {
+ return pos;
+ } else if (node instanceof Parameter) {
+ pos++;
+ }
+ }
+ return pos;
+ }
+
+ public static int getParamPos(Node node) {
+ if (requireParentNode(node) instanceof MethodCallExpr) {
+ MethodCallExpr call = (MethodCallExpr) requireParentNode(node);
+ for (int i = 0; i < call.getArguments().size(); i++) {
+ if (call.getArguments().get(i) == node) return i;
+ }
+ throw new IllegalStateException();
+ }
+ throw new IllegalArgumentException();
+ }
+
+ @Override
+ public String toString() {
+ return "JavaParserSymbolDeclaration{" +
+ "name='" + name + '\'' +
+ ", wrappedNode=" + wrappedNode +
+ '}';
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isField() {
+ return field;
+ }
+
+ @Override
+ public boolean isParameter() {
+ return parameter;
+ }
+
+ @Override
+ public boolean isType() {
+ return false;
+ }
+
+ @Override
+ public ResolvedType getType() {
+ if (wrappedNode instanceof Parameter) {
+ Parameter parameter = (Parameter) wrappedNode;
+ if (requireParentNode(wrappedNode) instanceof LambdaExpr) {
+ int pos = getParamPos(parameter);
+ ResolvedType lambdaType = JavaParserFacade.get(typeSolver).getType(requireParentNode(wrappedNode));
+
+ // TODO understand from the context to which method this corresponds
+ //MethodDeclaration methodDeclaration = JavaParserFacade.get(typeSolver).getMethodCalled
+ //MethodDeclaration methodCalled = JavaParserFacade.get(typeSolver).solve()
+ throw new UnsupportedOperationException(wrappedNode.getClass().getCanonicalName());
+ } else {
+ final ResolvedType rawType;
+ if (parameter.getType() instanceof com.github.javaparser.ast.type.PrimitiveType) {
+ rawType = ResolvedPrimitiveType.byName(((com.github.javaparser.ast.type.PrimitiveType) parameter.getType()).getType().name());
+ } else {
+ rawType = JavaParserFacade.get(typeSolver).convertToUsage(parameter.getType(), wrappedNode);
+ }
+ if (parameter.isVarArgs()) {
+ return new ResolvedArrayType(rawType);
+ }
+ return rawType;
+ }
+ } else if (wrappedNode instanceof VariableDeclarator) {
+ VariableDeclarator variableDeclarator = (VariableDeclarator) wrappedNode;
+ if (requireParentNode(wrappedNode) instanceof VariableDeclarationExpr) {
+ return JavaParserFacade.get(typeSolver).convert(variableDeclarator.getType(), JavaParserFactory.getContext(wrappedNode, typeSolver));
+ } else if (requireParentNode(wrappedNode) instanceof FieldDeclaration) {
+ return JavaParserFacade.get(typeSolver).convert(variableDeclarator.getType(), JavaParserFactory.getContext(wrappedNode, typeSolver));
+ }
+ }
+ throw new UnsupportedOperationException(wrappedNode.getClass().getCanonicalName());
+ }
+
+ @Override
+ public ResolvedTypeDeclaration asType() {
+ throw new UnsupportedOperationException(this.getClass().getCanonicalName() + ": wrapping " + this.getWrappedNode().getClass().getCanonicalName());
+ }
+
+ /**
+ * Returns the JavaParser node associated with this JavaParserSymbolDeclaration.
+ *
+ * @return A visitable JavaParser node wrapped by this object.
+ */
+ public Node getWrappedNode() {
+ return wrappedNode;
+ }
+
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeAdapter.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeAdapter.java
new file mode 100644
index 000000000..198083f7a
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeAdapter.java
@@ -0,0 +1,140 @@
+package com.github.javaparser.symbolsolver.javaparsermodel.declarations;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.NodeList;
+import com.github.javaparser.ast.body.BodyDeclaration;
+import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
+import com.github.javaparser.ast.body.EnumDeclaration;
+import com.github.javaparser.ast.body.VariableDeclarator;
+import com.github.javaparser.ast.nodeTypes.NodeWithMembers;
+import com.github.javaparser.ast.nodeTypes.NodeWithSimpleName;
+import com.github.javaparser.ast.nodeTypes.NodeWithTypeParameters;
+import com.github.javaparser.ast.type.TypeParameter;
+import com.github.javaparser.resolution.declarations.ResolvedFieldDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+import com.github.javaparser.symbolsolver.resolution.SymbolSolver;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentNode;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavaParserTypeAdapter<T extends Node & NodeWithSimpleName<T> & NodeWithMembers<T>> {
+
+ private T wrappedNode;
+ private TypeSolver typeSolver;
+
+ public JavaParserTypeAdapter(T wrappedNode, TypeSolver typeSolver) {
+ this.wrappedNode = wrappedNode;
+ this.typeSolver = typeSolver;
+ }
+
+ public String getPackageName() {
+ return Helper.getPackageName(wrappedNode);
+ }
+
+ public String getClassName() {
+ return Helper.getClassName("", wrappedNode);
+ }
+
+ public String getQualifiedName() {
+ String containerName = Helper.containerName(getParentNode(wrappedNode));
+ if (containerName.isEmpty()) {
+ return wrappedNode.getName().getId();
+ } else {
+ return containerName + "." + wrappedNode.getName().getId();
+ }
+ }
+
+ public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) {
+ List<ResolvedReferenceType> ancestorsOfOther = other.getAllAncestors();
+ ancestorsOfOther.add(new ReferenceTypeImpl(other, typeSolver));
+ for (ResolvedReferenceType ancestorOfOther : ancestorsOfOther) {
+ if (ancestorOfOther.getQualifiedName().equals(this.getQualifiedName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean isAssignableBy(ResolvedType type) {
+ if (type.isNull()) {
+ return true;
+ }
+ if (type.isReferenceType()) {
+ ResolvedReferenceTypeDeclaration other = typeSolver.solveType(type.describe());
+ return isAssignableBy(other);
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ public SymbolReference<ResolvedTypeDeclaration> solveType(String name, TypeSolver typeSolver) {
+ if(wrappedNode instanceof NodeWithTypeParameters<?>) {
+ NodeList<TypeParameter> typeParameters = ((NodeWithTypeParameters<?>) wrappedNode).getTypeParameters();
+ for (com.github.javaparser.ast.type.TypeParameter typeParameter : typeParameters) {
+ if (typeParameter.getName().getId().equals(name)) {
+ return SymbolReference.solved(new JavaParserTypeVariableDeclaration(typeParameter, typeSolver));
+ }
+ }
+ }
+
+ // Internal classes
+ for (BodyDeclaration<?> member : this.wrappedNode.getMembers()) {
+ if (member instanceof com.github.javaparser.ast.body.TypeDeclaration) {
+ com.github.javaparser.ast.body.TypeDeclaration<?> internalType = (com.github.javaparser.ast.body.TypeDeclaration<?>) member;
+ String prefix = internalType.getName() + ".";
+ if (internalType.getName().getId().equals(name)) {
+ if (internalType instanceof ClassOrInterfaceDeclaration) {
+ return SymbolReference.solved(new JavaParserClassDeclaration((com.github.javaparser.ast.body.ClassOrInterfaceDeclaration) internalType, typeSolver));
+ } else if (internalType instanceof EnumDeclaration) {
+ return SymbolReference.solved(new JavaParserEnumDeclaration((com.github.javaparser.ast.body.EnumDeclaration) internalType, typeSolver));
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ } else if (name.startsWith(prefix) && name.length() > prefix.length()) {
+ if (internalType instanceof ClassOrInterfaceDeclaration) {
+ return new JavaParserClassDeclaration((com.github.javaparser.ast.body.ClassOrInterfaceDeclaration) internalType, typeSolver).solveType(name.substring(prefix.length()), typeSolver);
+ } else if (internalType instanceof EnumDeclaration) {
+ return new SymbolSolver(typeSolver).solveTypeInType(new JavaParserEnumDeclaration((com.github.javaparser.ast.body.EnumDeclaration) internalType, typeSolver), name.substring(prefix.length()));
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+ }
+ return SymbolReference.unsolved(ResolvedTypeDeclaration.class);
+ }
+
+ public Optional<ResolvedReferenceTypeDeclaration> containerType() {
+ return wrappedNode
+ .getParentNode()
+ .map(node -> JavaParserFactory.toTypeDeclaration(node, typeSolver));
+ }
+
+ public List<ResolvedFieldDeclaration> getFieldsForDeclaredVariables() {
+ List<ResolvedFieldDeclaration> fields = new ArrayList<>();
+ if (wrappedNode.getMembers() != null) {
+ for (BodyDeclaration<?> member : this.wrappedNode.getMembers()) {
+ if (member instanceof com.github.javaparser.ast.body.FieldDeclaration) {
+ com.github.javaparser.ast.body.FieldDeclaration field = (com.github.javaparser.ast.body.FieldDeclaration) member;
+ for (VariableDeclarator vd : field.getVariables()) {
+ fields.add(new JavaParserFieldDeclaration(vd, typeSolver));
+ }
+ }
+ }
+ }
+ return fields;
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeParameter.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeParameter.java
new file mode 100644
index 000000000..0d1f42e68
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeParameter.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.declarations;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
+import com.github.javaparser.ast.type.ClassOrInterfaceType;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.logic.AbstractTypeDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavaParserTypeParameter extends AbstractTypeDeclaration implements ResolvedTypeParameterDeclaration {
+
+ private com.github.javaparser.ast.type.TypeParameter wrappedNode;
+ private TypeSolver typeSolver;
+
+ public JavaParserTypeParameter(com.github.javaparser.ast.type.TypeParameter wrappedNode, TypeSolver typeSolver) {
+ this.wrappedNode = wrappedNode;
+ this.typeSolver = typeSolver;
+ }
+
+ @Override
+ public Set<ResolvedMethodDeclaration> getDeclaredMethods() {
+ return Collections.emptySet();
+ }
+
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> parameterTypes) {
+ return getContext().solveMethod(name, parameterTypes, false, typeSolver);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof JavaParserTypeParameter)) return false;
+
+ JavaParserTypeParameter that = (JavaParserTypeParameter) o;
+
+ if (wrappedNode != null ? !wrappedNode.equals(that.wrappedNode) : that.wrappedNode != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = wrappedNode != null ? wrappedNode.hashCode() : 0;
+ result = 31 * result + (typeSolver != null ? typeSolver.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String getName() {
+ return wrappedNode.getName().getId();
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) {
+ return isAssignableBy(new ReferenceTypeImpl(other, typeSolver));
+ }
+
+ @Override
+ public String getContainerQualifiedName() {
+ ResolvedTypeParametrizable container = getContainer();
+ if (container instanceof ResolvedReferenceTypeDeclaration) {
+ return ((ResolvedReferenceTypeDeclaration) container).getQualifiedName();
+ } else if (container instanceof JavaParserConstructorDeclaration) {
+ return ((JavaParserConstructorDeclaration) container).getQualifiedSignature();
+ } else {
+ return ((JavaParserMethodDeclaration) container).getQualifiedSignature();
+ }
+ }
+
+ @Override
+ public String getContainerId() {
+ ResolvedTypeParametrizable container = getContainer();
+ if (container instanceof ResolvedReferenceTypeDeclaration) {
+ return ((ResolvedReferenceTypeDeclaration) container).getId();
+ } else if (container instanceof JavaParserConstructorDeclaration) {
+ return ((JavaParserConstructorDeclaration) container).getQualifiedSignature();
+ } else {
+ return ((JavaParserMethodDeclaration) container).getQualifiedSignature();
+ }
+ }
+
+ @Override
+ public ResolvedTypeParametrizable getContainer() {
+ Node parentNode = requireParentNode(wrappedNode);
+ if (parentNode instanceof com.github.javaparser.ast.body.ClassOrInterfaceDeclaration) {
+ com.github.javaparser.ast.body.ClassOrInterfaceDeclaration jpTypeDeclaration = (com.github.javaparser.ast.body.ClassOrInterfaceDeclaration) parentNode;
+ return JavaParserFacade.get(typeSolver).getTypeDeclaration(jpTypeDeclaration);
+ } else if (parentNode instanceof com.github.javaparser.ast.body.ConstructorDeclaration){
+ com.github.javaparser.ast.body.ConstructorDeclaration jpConstructorDeclaration = (com.github.javaparser.ast.body.ConstructorDeclaration) parentNode;
+ Optional<ClassOrInterfaceDeclaration> jpTypeDeclaration = jpConstructorDeclaration.getAncestorOfType(com.github.javaparser.ast.body.ClassOrInterfaceDeclaration.class);
+ if (jpTypeDeclaration.isPresent()) {
+ ResolvedReferenceTypeDeclaration typeDeclaration = JavaParserFacade.get(typeSolver).getTypeDeclaration(jpTypeDeclaration.get());
+ if (typeDeclaration.isClass()) {
+ return new JavaParserConstructorDeclaration(typeDeclaration.asClass(), jpConstructorDeclaration, typeSolver);
+ }
+ }
+ } else {
+ com.github.javaparser.ast.body.MethodDeclaration jpMethodDeclaration = (com.github.javaparser.ast.body.MethodDeclaration) parentNode;
+ return new JavaParserMethodDeclaration(jpMethodDeclaration, typeSolver);
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getQualifiedName() {
+ return String.format("%s.%s", getContainerQualifiedName(), getName());
+ }
+
+ @Override
+ public List<Bound> getBounds() {
+ return wrappedNode.getTypeBound().stream().map((astB) -> toBound(astB, typeSolver)).collect(Collectors.toList());
+ }
+
+ private Bound toBound(ClassOrInterfaceType classOrInterfaceType, TypeSolver typeSolver) {
+ ResolvedType type = JavaParserFacade.get(typeSolver).convertToUsage(classOrInterfaceType, classOrInterfaceType);
+ return Bound.extendsBound(type);
+ }
+
+ public Context getContext() {
+ throw new UnsupportedOperationException();
+ }
+
+ public ResolvedType getUsage(Node node) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedType type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ResolvedFieldDeclaration getField(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasField(String name) {
+ return false;
+ }
+
+ @Override
+ public List<ResolvedFieldDeclaration> getAllFields() {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getAncestors() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isTypeParameter() {
+ return true;
+ }
+
+ @Override
+ public boolean hasDirectlyAnnotation(String canonicalName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ return Collections.emptyList();
+ }
+
+ /**
+ * Returns the JavaParser node associated with this JavaParserTypeParameter.
+ *
+ * @return A visitable JavaParser node wrapped by this object.
+ */
+ public com.github.javaparser.ast.type.TypeParameter getWrappedNode() {
+ return wrappedNode;
+ }
+
+ @Override
+ public String toString() {
+ return "JPTypeParameter(" + wrappedNode.getName() + ", bounds=" + wrappedNode.getTypeBound() + ")";
+ }
+
+ @Override
+ public Optional<ResolvedReferenceTypeDeclaration> containerType() {
+ ResolvedTypeParametrizable container = getContainer();
+ if (container instanceof ResolvedReferenceTypeDeclaration) {
+ return Optional.of((ResolvedReferenceTypeDeclaration) container);
+ }
+ return Optional.empty();
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeVariableDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeVariableDeclaration.java
new file mode 100644
index 000000000..a602ebf19
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeVariableDeclaration.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.declarations;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.type.TypeParameter;
+import com.github.javaparser.resolution.declarations.ResolvedFieldDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.logic.AbstractTypeDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavaParserTypeVariableDeclaration extends AbstractTypeDeclaration {
+
+ private TypeParameter wrappedNode;
+ private TypeSolver typeSolver;
+
+ public JavaParserTypeVariableDeclaration(TypeParameter wrappedNode, TypeSolver typeSolver) {
+ this.wrappedNode = wrappedNode;
+ this.typeSolver = typeSolver;
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) {
+ return isAssignableBy(new ReferenceTypeImpl(other, typeSolver));
+ }
+
+ @Override
+ public String getPackageName() {
+ return Helper.getPackageName(wrappedNode);
+ }
+
+ @Override
+ public String getClassName() {
+ return Helper.getClassName("", wrappedNode);
+ }
+
+ @Override
+ public String getQualifiedName() {
+ return getName();
+ }
+
+ public Context getContext() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String toString() {
+ return "JavaParserTypeVariableDeclaration{" +
+ wrappedNode.getName() +
+ '}';
+ }
+
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> parameterTypes) {
+ throw new UnsupportedOperationException();
+ }
+
+ public ResolvedType getUsage(Node node) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedType type) {
+ if (type.isTypeVariable()) {
+ throw new UnsupportedOperationException("Is this type variable declaration assignable by " + type.describe());
+ } else {
+ throw new UnsupportedOperationException("Is this type variable declaration assignable by " + type);
+ }
+ }
+
+ @Override
+ public boolean isTypeParameter() {
+ return true;
+ }
+
+ @Override
+ public ResolvedFieldDeclaration getField(String name) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasField(String name) {
+ return false;
+ }
+
+ @Override
+ public List<ResolvedFieldDeclaration> getAllFields() {
+ return new ArrayList<>();
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getAncestors() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<ResolvedMethodDeclaration> getDeclaredMethods() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public String getName() {
+ return wrappedNode.getName().getId();
+ }
+
+ @Override
+ public boolean isField() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isParameter() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isType() {
+ return true;
+ }
+
+ @Override
+ public boolean hasDirectlyAnnotation(String canonicalName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isClass() {
+ return false;
+ }
+
+ @Override
+ public boolean isInterface() {
+ return false;
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ return Collections.emptyList();
+ }
+
+ public ResolvedTypeParameterDeclaration asTypeParameter() {
+ return new JavaParserTypeParameter(this.wrappedNode, typeSolver);
+ }
+
+ /**
+ * Returns the JavaParser node associated with this JavaParserTypeVariableDeclaration.
+ *
+ * @return A visitable JavaParser node wrapped by this object.
+ */
+ public TypeParameter getWrappedNode() {
+ return wrappedNode;
+ }
+
+ @Override
+ public Optional<ResolvedReferenceTypeDeclaration> containerType() {
+ return asTypeParameter().containerType();
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/AbstractSymbolDeclarator.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/AbstractSymbolDeclarator.java
new file mode 100644
index 000000000..14fdafcd0
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/AbstractSymbolDeclarator.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.declarators;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.resolution.SymbolDeclarator;
+
+/**
+ * @author Federico Tomassetti
+ */
+public abstract class AbstractSymbolDeclarator<N extends Node> implements SymbolDeclarator {
+
+ protected N wrappedNode;
+ protected TypeSolver typeSolver;
+
+ public AbstractSymbolDeclarator(N wrappedNode, TypeSolver typeSolver) {
+ this.wrappedNode = wrappedNode;
+ this.typeSolver = typeSolver;
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/FieldSymbolDeclarator.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/FieldSymbolDeclarator.java
new file mode 100644
index 000000000..dddf7a538
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/FieldSymbolDeclarator.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.declarators;
+
+import com.github.javaparser.ast.body.FieldDeclaration;
+import com.github.javaparser.ast.body.VariableDeclarator;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserSymbolDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class FieldSymbolDeclarator extends AbstractSymbolDeclarator<FieldDeclaration> {
+
+ public FieldSymbolDeclarator(FieldDeclaration wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+ @Override
+ public List<ResolvedValueDeclaration> getSymbolDeclarations() {
+ List<ResolvedValueDeclaration> symbols = new LinkedList<>();
+ for (VariableDeclarator v : wrappedNode.getVariables()) {
+ symbols.add(JavaParserSymbolDeclaration.field(v, typeSolver));
+ }
+ return symbols;
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/NoSymbolDeclarator.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/NoSymbolDeclarator.java
new file mode 100644
index 000000000..e950b70ee
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/NoSymbolDeclarator.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.declarators;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class NoSymbolDeclarator<N extends Node> extends AbstractSymbolDeclarator<N> {
+
+ public NoSymbolDeclarator(N wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+ @Override
+ public List<ResolvedValueDeclaration> getSymbolDeclarations() {
+ return Collections.emptyList();
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/ParameterSymbolDeclarator.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/ParameterSymbolDeclarator.java
new file mode 100644
index 000000000..45d8e160f
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/ParameterSymbolDeclarator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.declarators;
+
+import com.github.javaparser.ast.body.Parameter;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserSymbolDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ParameterSymbolDeclarator extends AbstractSymbolDeclarator<Parameter> {
+
+ public ParameterSymbolDeclarator(Parameter wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ }
+
+ @Override
+ public List<ResolvedValueDeclaration> getSymbolDeclarations() {
+ List<ResolvedValueDeclaration> symbols = new LinkedList<>();
+ symbols.add(JavaParserSymbolDeclaration.parameter(wrappedNode, typeSolver));
+ return symbols;
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/VariableSymbolDeclarator.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/VariableSymbolDeclarator.java
new file mode 100644
index 000000000..88865de0d
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/VariableSymbolDeclarator.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javaparsermodel.declarators;
+
+import com.github.javaparser.ast.body.FieldDeclaration;
+import com.github.javaparser.ast.expr.VariableDeclarationExpr;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserSymbolDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentNode;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class VariableSymbolDeclarator extends AbstractSymbolDeclarator<VariableDeclarationExpr> {
+
+ public VariableSymbolDeclarator(VariableDeclarationExpr wrappedNode, TypeSolver typeSolver) {
+ super(wrappedNode, typeSolver);
+ wrappedNode.getParentNode().ifPresent(p -> {
+ if (p instanceof FieldDeclaration) {
+ throw new IllegalArgumentException();
+ }
+ });
+ }
+
+ @Override
+ public List<ResolvedValueDeclaration> getSymbolDeclarations() {
+ return wrappedNode.getVariables().stream()
+ .map(v -> JavaParserSymbolDeclaration.localVar(v, typeSolver))
+ .collect(Collectors.toCollection(LinkedList::new));
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/package-info.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/package-info.java
new file mode 100644
index 000000000..9ddf1cf01
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation of model based on JavaParser.
+ */
+package com.github.javaparser.symbolsolver.javaparsermodel; \ No newline at end of file
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistClassDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistClassDeclaration.java
new file mode 100644
index 000000000..6c516b5f5
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistClassDeclaration.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javassistmodel;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.UnsolvedSymbolException;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.LambdaArgumentTypePlaceholder;
+import com.github.javaparser.symbolsolver.logic.AbstractClassDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+import com.github.javaparser.symbolsolver.resolution.MethodResolutionLogic;
+import com.github.javaparser.symbolsolver.resolution.SymbolSolver;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.CtMethod;
+import javassist.NotFoundException;
+import javassist.bytecode.AccessFlag;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.SignatureAttribute;
+import javassist.bytecode.SyntheticAttribute;
+
+import java.lang.reflect.Modifier;
+import java.util.*;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavassistClassDeclaration extends AbstractClassDeclaration {
+
+
+
+ private CtClass ctClass;
+ private TypeSolver typeSolver;
+ private JavassistTypeDeclarationAdapter javassistTypeDeclarationAdapter;
+
+ public JavassistClassDeclaration(CtClass ctClass, TypeSolver typeSolver) {
+ if (ctClass == null) {
+ throw new IllegalArgumentException();
+ }
+ if (ctClass.isInterface() || ctClass.isAnnotation() || ctClass.isPrimitive() || ctClass.isEnum()) {
+ throw new IllegalArgumentException("Trying to instantiate a JavassistClassDeclaration with something which is not a class: " + ctClass.toString());
+ }
+ this.ctClass = ctClass;
+ this.typeSolver = typeSolver;
+ this.javassistTypeDeclarationAdapter = new JavassistTypeDeclarationAdapter(ctClass, typeSolver);
+ }
+
+ @Override
+ protected ResolvedReferenceType object() {
+ return new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver);
+ }
+
+ @Override
+ public boolean hasDirectlyAnnotation(String canonicalName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<ResolvedMethodDeclaration> getDeclaredMethods() {
+ return javassistTypeDeclarationAdapter.getDeclaredMethods();
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) {
+ return isAssignableBy(new ReferenceTypeImpl(other, typeSolver));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ JavassistClassDeclaration that = (JavassistClassDeclaration) o;
+
+ if (!ctClass.equals(that.ctClass)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return ctClass.hashCode();
+ }
+
+ @Override
+ public String getPackageName() {
+ return ctClass.getPackageName();
+ }
+
+ @Override
+ public String getClassName() {
+ String className = ctClass.getName().replace('$', '.');
+ if (getPackageName() != null) {
+ return className.substring(getPackageName().length() + 1, className.length());
+ }
+ return className;
+ }
+
+ @Override
+ public String getQualifiedName() {
+ return ctClass.getName().replace('$', '.');
+ }
+
+ public Optional<MethodUsage> solveMethodAsUsage(String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver,
+ Context invokationContext, List<ResolvedType> typeParameterValues) {
+ return JavassistUtils.getMethodUsage(ctClass, name, argumentsTypes, typeSolver, invokationContext);
+ }
+
+ @Deprecated
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
+ for (CtField field : ctClass.getDeclaredFields()) {
+ if (field.getName().equals(name)) {
+ return SymbolReference.solved(new JavassistFieldDeclaration(field, typeSolver));
+ }
+ }
+
+ final String superclassFQN = getSuperclassFQN();
+ SymbolReference<? extends ResolvedValueDeclaration> ref = solveSymbolForFQN(name, typeSolver, superclassFQN);
+ if (ref.isSolved()) {
+ return ref;
+ }
+
+ String[] interfaceFQNs = getInterfaceFQNs();
+ for (String interfaceFQN : interfaceFQNs) {
+ SymbolReference<? extends ResolvedValueDeclaration> interfaceRef = solveSymbolForFQN(name, typeSolver, interfaceFQN);
+ if (interfaceRef.isSolved()) {
+ return interfaceRef;
+ }
+ }
+
+ return SymbolReference.unsolved(ResolvedValueDeclaration.class);
+ }
+
+ private SymbolReference<? extends ResolvedValueDeclaration> solveSymbolForFQN(String symbolName, TypeSolver typeSolver, String fqn) {
+ if (fqn == null) {
+ return SymbolReference.unsolved(ResolvedValueDeclaration.class);
+ }
+
+ ResolvedReferenceTypeDeclaration fqnTypeDeclaration = typeSolver.solveType(fqn);
+ return new SymbolSolver(typeSolver).solveSymbolInType(fqnTypeDeclaration, symbolName);
+ }
+
+ private String[] getInterfaceFQNs() {
+ return ctClass.getClassFile().getInterfaces();
+ }
+
+ private String getSuperclassFQN() {
+ return ctClass.getClassFile().getSuperclass();
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getAncestors() {
+ List<ResolvedReferenceType> ancestors = new LinkedList<>();
+ if (getSuperClass() != null) {
+ ancestors.add(getSuperClass());
+ }
+ ancestors.addAll(getInterfaces());
+ return ancestors;
+ }
+
+ @Deprecated
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> argumentsTypes, boolean staticOnly) {
+ List<ResolvedMethodDeclaration> candidates = new ArrayList<>();
+ Predicate<CtMethod> staticOnlyCheck = m -> !staticOnly || (staticOnly && Modifier.isStatic(m.getModifiers()));
+ for (CtMethod method : ctClass.getDeclaredMethods()) {
+ boolean isSynthetic = method.getMethodInfo().getAttribute(SyntheticAttribute.tag) != null;
+ boolean isNotBridge = (method.getMethodInfo().getAccessFlags() & AccessFlag.BRIDGE) == 0;
+ if (method.getName().equals(name) && !isSynthetic && isNotBridge && staticOnlyCheck.test(method)) {
+ candidates.add(new JavassistMethodDeclaration(method, typeSolver));
+ }
+ }
+
+ try {
+ CtClass superClass = ctClass.getSuperclass();
+ if (superClass != null) {
+ SymbolReference<ResolvedMethodDeclaration> ref = new JavassistClassDeclaration(superClass, typeSolver).solveMethod(name, argumentsTypes, staticOnly);
+ if (ref.isSolved()) {
+ candidates.add(ref.getCorrespondingDeclaration());
+ }
+ }
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ try {
+ for (CtClass interfaze : ctClass.getInterfaces()) {
+ SymbolReference<ResolvedMethodDeclaration> ref = new JavassistInterfaceDeclaration(interfaze, typeSolver).solveMethod(name, argumentsTypes, staticOnly);
+ if (ref.isSolved()) {
+ candidates.add(ref.getCorrespondingDeclaration());
+ }
+ }
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ return MethodResolutionLogic.findMostApplicable(candidates, name, argumentsTypes, typeSolver);
+ }
+
+ public ResolvedType getUsage(Node node) {
+ return new ReferenceTypeImpl(this, typeSolver);
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedType type) {
+ if (type.isNull()) {
+ return true;
+ }
+
+ if (type instanceof LambdaArgumentTypePlaceholder) {
+ return isFunctionalInterface();
+ }
+
+ // TODO look into generics
+ if (type.describe().equals(this.getQualifiedName())) {
+ return true;
+ }
+ try {
+ if (this.ctClass.getSuperclass() != null
+ && new JavassistClassDeclaration(this.ctClass.getSuperclass(), typeSolver).isAssignableBy(type)) {
+ return true;
+ }
+ for (CtClass interfaze : ctClass.getInterfaces()) {
+ if (new JavassistInterfaceDeclaration(interfaze, typeSolver).isAssignableBy(type)) {
+ return true;
+ }
+ }
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isTypeParameter() {
+ return false;
+ }
+
+ @Override
+ public List<ResolvedFieldDeclaration> getAllFields() {
+ return javassistTypeDeclarationAdapter.getDeclaredFields();
+ }
+
+ @Override
+ public String getName() {
+ String[] nameElements = ctClass.getSimpleName().replace('$', '.').split("\\.");
+ return nameElements[nameElements.length - 1];
+ }
+
+ @Override
+ public boolean isField() {
+ return false;
+ }
+
+ @Override
+ public boolean isParameter() {
+ return false;
+ }
+
+ @Override
+ public boolean isType() {
+ return true;
+ }
+
+ @Override
+ public boolean isClass() {
+ return !ctClass.isInterface();
+ }
+
+ @Override
+ public ResolvedReferenceType getSuperClass() {
+ try {
+ if (ctClass.getSuperclass() == null) {
+ return new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver);
+ }
+ if (ctClass.getGenericSignature() == null) {
+ return new ReferenceTypeImpl(new JavassistClassDeclaration(ctClass.getSuperclass(), typeSolver), typeSolver);
+ }
+
+ SignatureAttribute.ClassSignature classSignature = SignatureAttribute.toClassSignature(ctClass.getGenericSignature());
+ return JavassistUtils.signatureTypeToType(classSignature.getSuperClass(), typeSolver, this).asReferenceType();
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ } catch (BadBytecode e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getInterfaces() {
+ try {
+ if (ctClass.getGenericSignature() == null) {
+ return Arrays.stream(ctClass.getInterfaces())
+ .map(i -> new JavassistInterfaceDeclaration(i, typeSolver))
+ .map(i -> new ReferenceTypeImpl(i, typeSolver))
+ .collect(Collectors.toList());
+ } else {
+ SignatureAttribute.ClassSignature classSignature = SignatureAttribute.toClassSignature(ctClass.getGenericSignature());
+ return Arrays.stream(classSignature.getInterfaces())
+ .map(i -> JavassistUtils.signatureTypeToType(i, typeSolver, this).asReferenceType())
+ .collect(Collectors.toList());
+ }
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ } catch (BadBytecode e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public boolean isInterface() {
+ return ctClass.isInterface();
+ }
+
+ @Override
+ public String toString() {
+ return "JavassistClassDeclaration {" + ctClass.getName() + '}';
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ return javassistTypeDeclarationAdapter.getTypeParameters();
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return JavassistFactory.modifiersToAccessLevel(ctClass.getModifiers());
+ }
+
+ @Override
+ public List<ResolvedConstructorDeclaration> getConstructors() {
+ return javassistTypeDeclarationAdapter.getConstructors();
+ }
+
+ @Override
+ public Optional<ResolvedReferenceTypeDeclaration> containerType() {
+ return javassistTypeDeclarationAdapter.containerType();
+ }
+
+ @Override
+ public Set<ResolvedReferenceTypeDeclaration> internalTypes() {
+ try {
+ /*
+ Get all internal types of the current class and get their corresponding ReferenceTypeDeclaration.
+ Finally, return them in a Set.
+ */
+ return Arrays.stream(ctClass.getDeclaredClasses()).map(itype -> JavassistFactory.toTypeDeclaration(itype, typeSolver)).collect(Collectors.toSet());
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public ResolvedReferenceTypeDeclaration getInternalType(String name) {
+ /*
+ The name of the ReferenceTypeDeclaration could be composed of the internal class and the outer class, e.g. A$B. That's why we search the internal type in the ending part.
+ In case the name is composed of the internal type only, i.e. f.getName() returns B, it will also works.
+ */
+ Optional<ResolvedReferenceTypeDeclaration> type =
+ this.internalTypes().stream().filter(f -> f.getName().endsWith(name)).findFirst();
+ return type.orElseThrow(() ->
+ new UnsolvedSymbolException("Internal type not found: " + name));
+ }
+
+ @Override
+ public boolean hasInternalType(String name) {
+ /*
+ The name of the ReferenceTypeDeclaration could be composed of the internal class and the outer class, e.g. A$B. That's why we search the internal type in the ending part.
+ In case the name is composed of the internal type only, i.e. f.getName() returns B, it will also works.
+ */
+ return this.internalTypes().stream().anyMatch(f -> f.getName().endsWith(name));
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistConstructorDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistConstructorDeclaration.java
new file mode 100644
index 000000000..0fd010ae7
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistConstructorDeclaration.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javassistmodel;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import javassist.CtConstructor;
+import javassist.NotFoundException;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.SignatureAttribute;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author Fred Lefévère-Laoide
+ */
+public class JavassistConstructorDeclaration implements ResolvedConstructorDeclaration {
+ private CtConstructor ctConstructor;
+ private TypeSolver typeSolver;
+
+ public JavassistConstructorDeclaration(CtConstructor ctConstructor, TypeSolver typeSolver) {
+ this.ctConstructor = ctConstructor;
+ this.typeSolver = typeSolver;
+ }
+
+ @Override
+ public String toString() {
+ return "JavassistMethodDeclaration{" +
+ "CtConstructor=" + ctConstructor +
+ '}';
+ }
+
+ @Override
+ public String getName() {
+ return ctConstructor.getName();
+ }
+
+ @Override
+ public boolean isField() {
+ return false;
+ }
+
+ @Override
+ public boolean isParameter() {
+ return false;
+ }
+
+ @Override
+ public boolean isType() {
+ return false;
+ }
+
+ @Override
+ public ResolvedClassDeclaration declaringType() {
+ return new JavassistClassDeclaration(ctConstructor.getDeclaringClass(), typeSolver);
+ }
+
+ @Override
+ public int getNumberOfParams() {
+ try {
+ return ctConstructor.getParameterTypes().length;
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public ResolvedParameterDeclaration getParam(int i) {
+ try {
+ boolean variadic = false;
+ if ((ctConstructor.getModifiers() & javassist.Modifier.VARARGS) > 0) {
+ variadic = i == (ctConstructor.getParameterTypes().length - 1);
+ }
+ if (ctConstructor.getGenericSignature() != null) {
+ SignatureAttribute.MethodSignature methodSignature = SignatureAttribute.toMethodSignature(ctConstructor.getGenericSignature());
+ SignatureAttribute.Type signatureType = methodSignature.getParameterTypes()[i];
+ return new JavassistParameterDeclaration(JavassistUtils.signatureTypeToType(signatureType, typeSolver, this), typeSolver, variadic);
+ } else {
+ return new JavassistParameterDeclaration(ctConstructor.getParameterTypes()[i], typeSolver, variadic);
+ }
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ } catch (BadBytecode badBytecode) {
+ throw new RuntimeException(badBytecode);
+ }
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ try {
+ if (ctConstructor.getGenericSignature() == null) {
+ return Collections.emptyList();
+ }
+ SignatureAttribute.MethodSignature methodSignature = SignatureAttribute.toMethodSignature(ctConstructor.getGenericSignature());
+ return Arrays.stream(methodSignature.getTypeParameters()).map((jasTp) -> new JavassistTypeParameter(jasTp, this, typeSolver)).collect(Collectors.toList());
+ } catch (BadBytecode badBytecode) {
+ throw new RuntimeException(badBytecode);
+ }
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return JavassistFactory.modifiersToAccessLevel(ctConstructor.getModifiers());
+ }
+
+ @Override
+ public int getNumberOfSpecifiedExceptions() {
+ try {
+ return ctConstructor.getExceptionTypes().length;
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public ResolvedType getSpecifiedException(int index) {
+ if (index < 0 || index >= getNumberOfSpecifiedExceptions()) {
+ throw new IllegalArgumentException(String.format("No exception with index %d. Number of exceptions: %d",
+ index, getNumberOfSpecifiedExceptions()));
+ }
+ try {
+ return JavassistFactory.typeUsageFor(ctConstructor.getExceptionTypes()[index], typeSolver);
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistEnumConstantDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistEnumConstantDeclaration.java
new file mode 100644
index 000000000..178c74b79
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistEnumConstantDeclaration.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javassistmodel;
+
+import com.github.javaparser.resolution.declarations.ResolvedEnumConstantDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import javassist.CtField;
+import javassist.bytecode.AccessFlag;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavassistEnumConstantDeclaration implements ResolvedEnumConstantDeclaration {
+
+ private CtField ctField;
+ private TypeSolver typeSolver;
+
+ public JavassistEnumConstantDeclaration(CtField ctField, TypeSolver typeSolver) {
+ if (ctField == null) {
+ throw new IllegalArgumentException();
+ }
+ if ((ctField.getFieldInfo2().getAccessFlags() & AccessFlag.ENUM) != 0) {
+ throw new IllegalArgumentException("Trying to instantiate a JavassistEnumConstantDeclaration with something which is not an enum field: " + ctField.toString());
+ }
+ this.ctField = ctField;
+ this.typeSolver = typeSolver;
+ }
+
+
+ @Override
+ public String getName() {
+ return ctField.getName();
+ }
+
+ @Override
+ public ResolvedType getType() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistEnumDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistEnumDeclaration.java
new file mode 100644
index 000000000..9a54a09e0
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistEnumDeclaration.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javassistmodel;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.ast.expr.MethodCallExpr;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.UnsolvedSymbolException;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.logic.AbstractTypeDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.resolution.MethodResolutionLogic;
+import com.github.javaparser.symbolsolver.resolution.SymbolSolver;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.CtMethod;
+import javassist.NotFoundException;
+import javassist.bytecode.AccessFlag;
+import javassist.bytecode.SyntheticAttribute;
+
+import java.lang.reflect.Modifier;
+import java.util.*;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavassistEnumDeclaration extends AbstractTypeDeclaration implements ResolvedEnumDeclaration {
+
+ private CtClass ctClass;
+ private TypeSolver typeSolver;
+ private JavassistTypeDeclarationAdapter javassistTypeDeclarationAdapter;
+
+ public JavassistEnumDeclaration(CtClass ctClass, TypeSolver typeSolver) {
+ if (ctClass == null) {
+ throw new IllegalArgumentException();
+ }
+ if (!ctClass.isEnum()) {
+ throw new IllegalArgumentException("Trying to instantiate a JavassistEnumDeclaration with something which is not an enum: " + ctClass.toString());
+ }
+ this.ctClass = ctClass;
+ this.typeSolver = typeSolver;
+ this.javassistTypeDeclarationAdapter = new JavassistTypeDeclarationAdapter(ctClass, typeSolver);
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return JavassistFactory.modifiersToAccessLevel(ctClass.getModifiers());
+ }
+
+ @Override
+ public String getPackageName() {
+ return ctClass.getPackageName();
+ }
+
+ @Override
+ public String getClassName() {
+ String name = ctClass.getName().replace('$', '.');
+ if (getPackageName() != null) {
+ return name.substring(getPackageName().length() + 1, name.length());
+ }
+ return name;
+ }
+
+ @Override
+ public String getQualifiedName() {
+ return ctClass.getName().replace('$', '.');
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getAncestors() {
+ // Direct ancestors of an enum are java.lang.Enum and interfaces
+ List<ResolvedReferenceType> ancestors = new LinkedList<>();
+
+ try {
+ CtClass superClass = ctClass.getSuperclass();
+
+ if (superClass != null) {
+ ResolvedType superClassTypeUsage = JavassistFactory.typeUsageFor(superClass, typeSolver);
+
+ if (superClassTypeUsage.isReferenceType()) {
+ ancestors.add(superClassTypeUsage.asReferenceType());
+ }
+ }
+
+ for (CtClass interfaze : ctClass.getInterfaces()) {
+ ResolvedType interfazeTypeUsage = JavassistFactory.typeUsageFor(interfaze, typeSolver);
+
+ if (interfazeTypeUsage.isReferenceType()) {
+ ancestors.add(interfazeTypeUsage.asReferenceType());
+ }
+ }
+ } catch (NotFoundException e) {
+ throw new RuntimeException("Ancestor not found for " + ctClass.getName() + ".", e);
+ }
+
+ return ancestors;
+ }
+
+ @Override
+ public ResolvedFieldDeclaration getField(String name) {
+ Optional<ResolvedFieldDeclaration> field = javassistTypeDeclarationAdapter.getDeclaredFields().stream().filter(f -> f.getName().equals(name)).findFirst();
+
+ return field.orElseThrow(() -> new RuntimeException("Field " + name + " does not exist in " + ctClass.getName() + "."));
+ }
+
+ @Override
+ public boolean hasField(String name) {
+ return javassistTypeDeclarationAdapter.getDeclaredFields().stream().anyMatch(f -> f.getName().equals(name));
+ }
+
+ @Override
+ public List<ResolvedFieldDeclaration> getAllFields() {
+ return javassistTypeDeclarationAdapter.getDeclaredFields();
+ }
+
+ @Override
+ public Set<ResolvedMethodDeclaration> getDeclaredMethods() {
+ return javassistTypeDeclarationAdapter.getDeclaredMethods();
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedType type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasDirectlyAnnotation(String canonicalName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getName() {
+ String[] nameElements = ctClass.getSimpleName().replace('$', '.').split("\\.");
+ return nameElements[nameElements.length - 1];
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ return javassistTypeDeclarationAdapter.getTypeParameters();
+ }
+
+ @Override
+ public Optional<ResolvedReferenceTypeDeclaration> containerType() {
+ return javassistTypeDeclarationAdapter.containerType();
+ }
+
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> argumentsTypes, boolean staticOnly) {
+ List<ResolvedMethodDeclaration> candidates = new ArrayList<>();
+ Predicate<CtMethod> staticOnlyCheck = m -> !staticOnly || (staticOnly && Modifier.isStatic(m.getModifiers()));
+ for (CtMethod method : ctClass.getDeclaredMethods()) {
+ boolean isSynthetic = method.getMethodInfo().getAttribute(SyntheticAttribute.tag) != null;
+ boolean isNotBridge = (method.getMethodInfo().getAccessFlags() & AccessFlag.BRIDGE) == 0;
+ if (method.getName().equals(name) && !isSynthetic && isNotBridge && staticOnlyCheck.test(method)) {
+ candidates.add(new JavassistMethodDeclaration(method, typeSolver));
+ }
+ }
+
+ try {
+ CtClass superClass = ctClass.getSuperclass();
+ if (superClass != null) {
+ SymbolReference<ResolvedMethodDeclaration> ref = new JavassistClassDeclaration(superClass, typeSolver).solveMethod(name, argumentsTypes, staticOnly);
+ if (ref.isSolved()) {
+ candidates.add(ref.getCorrespondingDeclaration());
+ }
+ }
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ return MethodResolutionLogic.findMostApplicable(candidates, name, argumentsTypes, typeSolver);
+ }
+
+ public Optional<MethodUsage> solveMethodAsUsage(String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver,
+ Context invokationContext, List<ResolvedType> typeParameterValues) {
+ return JavassistUtils.getMethodUsage(ctClass, name, argumentsTypes, typeSolver, invokationContext);
+ }
+
+ @Override
+ public Set<ResolvedReferenceTypeDeclaration> internalTypes() {
+ try {
+ /*
+ Get all internal types of the current class and get their corresponding ReferenceTypeDeclaration.
+ Finally, return them in a Set.
+ */
+ return Arrays.stream(ctClass.getDeclaredClasses()).map(itype -> JavassistFactory.toTypeDeclaration(itype, typeSolver)).collect(Collectors.toSet());
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public ResolvedReferenceTypeDeclaration getInternalType(String name) {
+ /*
+ The name of the ReferenceTypeDeclaration could be composed on the internal class and the outer class, e.g. A$B. That's why we search the internal type in the ending part.
+ In case the name is composed of the internal type only, i.e. f.getName() returns B, it will also works.
+ */
+ Optional<ResolvedReferenceTypeDeclaration> type =
+ this.internalTypes().stream().filter(f -> f.getName().endsWith(name)).findFirst();
+ return type.orElseThrow(() ->
+ new UnsolvedSymbolException("Internal type not found: " + name));
+ }
+
+ @Override
+ public boolean hasInternalType(String name) {
+ /*
+ The name of the ReferenceTypeDeclaration could be composed on the internal class and the outer class, e.g. A$B. That's why we search the internal type in the ending part.
+ In case the name is composed of the internal type only, i.e. f.getName() returns B, it will also works.
+ */
+ return this.internalTypes().stream().anyMatch(f -> f.getName().endsWith(name));
+ }
+
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
+ for (CtField field : ctClass.getDeclaredFields()) {
+ if (field.getName().equals(name)) {
+ return SymbolReference.solved(new JavassistFieldDeclaration(field, typeSolver));
+ }
+ }
+
+ String[] interfaceFQNs = getInterfaceFQNs();
+ for (String interfaceFQN : interfaceFQNs) {
+ SymbolReference<? extends ResolvedValueDeclaration> interfaceRef = solveSymbolForFQN(name, typeSolver, interfaceFQN);
+ if (interfaceRef.isSolved()) {
+ return interfaceRef;
+ }
+ }
+
+ return SymbolReference.unsolved(ResolvedValueDeclaration.class);
+ }
+
+ private SymbolReference<? extends ResolvedValueDeclaration> solveSymbolForFQN(String symbolName, TypeSolver typeSolver, String fqn) {
+ if (fqn == null) {
+ return SymbolReference.unsolved(ResolvedValueDeclaration.class);
+ }
+
+ ResolvedReferenceTypeDeclaration fqnTypeDeclaration = typeSolver.solveType(fqn);
+ return new SymbolSolver(typeSolver).solveSymbolInType(fqnTypeDeclaration, symbolName);
+ }
+
+ private String[] getInterfaceFQNs() {
+ return ctClass.getClassFile().getInterfaces();
+ }
+
+ @Override
+ public List<ResolvedEnumConstantDeclaration> getEnumConstants() {
+ return Arrays.stream(ctClass.getFields())
+ .filter(f -> (f.getFieldInfo2().getAccessFlags() & AccessFlag.ENUM) != 0)
+ .map(f -> new JavassistEnumConstantDeclaration(f, typeSolver))
+ .collect(Collectors.toList());
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistFactory.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistFactory.java
new file mode 100644
index 000000000..1188b9b17
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistFactory.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.github.javaparser.symbolsolver.javassistmodel;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.types.*;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.*;
+import javassist.CtClass;
+import javassist.NotFoundException;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavassistFactory {
+
+ public static ResolvedType typeUsageFor(CtClass ctClazz, TypeSolver typeSolver) {
+ try {
+ if (ctClazz.isArray()) {
+ return new ResolvedArrayType(typeUsageFor(ctClazz.getComponentType(), typeSolver));
+ } else if (ctClazz.isPrimitive()) {
+ if (ctClazz.getName().equals("void")) {
+ return ResolvedVoidType.INSTANCE;
+ } else {
+ return ResolvedPrimitiveType.byName(ctClazz.getName());
+ }
+ } else {
+ if (ctClazz.isInterface()) {
+ return new ReferenceTypeImpl(new JavassistInterfaceDeclaration(ctClazz, typeSolver),
+ typeSolver);
+ } else if (ctClazz.isEnum()) {
+ return new ReferenceTypeImpl(new JavassistEnumDeclaration(ctClazz, typeSolver),
+ typeSolver);
+ } else {
+ return new ReferenceTypeImpl(new JavassistClassDeclaration(ctClazz, typeSolver),
+ typeSolver);
+ }
+ }
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static ResolvedReferenceTypeDeclaration toTypeDeclaration(CtClass ctClazz, TypeSolver typeSolver) {
+ if (ctClazz.isInterface()) {
+ return new JavassistInterfaceDeclaration(ctClazz, typeSolver);
+ } else if (ctClazz.isEnum()) {
+ return new JavassistEnumDeclaration(ctClazz, typeSolver);
+ } else if (ctClazz.isAnnotation()) {
+ throw new UnsupportedOperationException("CtClass of annotation not yet supported");
+ } else if (ctClazz.isArray()) {
+ throw new IllegalArgumentException("This method should not be called passing an array");
+ } else {
+ return new JavassistClassDeclaration(ctClazz, typeSolver);
+ }
+ }
+
+ static AccessSpecifier modifiersToAccessLevel(final int modifiers) {
+ if (Modifier.isPublic(modifiers)) {
+ return AccessSpecifier.PUBLIC;
+ } else if (Modifier.isProtected(modifiers)) {
+ return AccessSpecifier.PROTECTED;
+ } else if (Modifier.isPrivate(modifiers)) {
+ return AccessSpecifier.PRIVATE;
+ } else {
+ return AccessSpecifier.DEFAULT;
+ }
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistFieldDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistFieldDeclaration.java
new file mode 100644
index 000000000..85958ca91
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistFieldDeclaration.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javassistmodel;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.resolution.declarations.ResolvedFieldDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParametrizable;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import javassist.CtField;
+import javassist.NotFoundException;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.SignatureAttribute;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavassistFieldDeclaration implements ResolvedFieldDeclaration {
+ private CtField ctField;
+ private TypeSolver typeSolver;
+
+ public JavassistFieldDeclaration(CtField ctField, TypeSolver typeSolver) {
+ this.ctField = ctField;
+ this.typeSolver = typeSolver;
+ }
+
+ @Override
+ public ResolvedType getType() {
+ try {
+ if (ctField.getGenericSignature() != null && declaringType() instanceof ResolvedTypeParametrizable) {
+ javassist.bytecode.SignatureAttribute.Type genericSignatureType = SignatureAttribute.toFieldSignature(ctField.getGenericSignature());
+ return JavassistUtils.signatureTypeToType(genericSignatureType, typeSolver, (ResolvedTypeParametrizable) declaringType());
+ } else {
+ return JavassistFactory.typeUsageFor(ctField.getType(), typeSolver);
+ }
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ } catch (BadBytecode e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public boolean isStatic() {
+ return Modifier.isStatic(ctField.getModifiers());
+ }
+
+ @Override
+ public String getName() {
+ return ctField.getName();
+ }
+
+ @Override
+ public boolean isField() {
+ return true;
+ }
+
+ @Override
+ public boolean isParameter() {
+ return false;
+ }
+
+ @Override
+ public boolean isType() {
+ return false;
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return JavassistFactory.modifiersToAccessLevel(ctField.getModifiers());
+ }
+
+ @Override
+ public ResolvedTypeDeclaration declaringType() {
+ return JavassistFactory.toTypeDeclaration(ctField.getDeclaringClass(), typeSolver);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistInterfaceDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistInterfaceDeclaration.java
new file mode 100644
index 000000000..9637535e1
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistInterfaceDeclaration.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javassistmodel;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.UnsolvedSymbolException;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.logic.AbstractTypeDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+import com.github.javaparser.symbolsolver.resolution.MethodResolutionLogic;
+import com.github.javaparser.symbolsolver.resolution.SymbolSolver;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.CtMethod;
+import javassist.NotFoundException;
+import javassist.bytecode.AccessFlag;
+import javassist.bytecode.SyntheticAttribute;
+
+import java.lang.reflect.Modifier;
+import java.util.*;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavassistInterfaceDeclaration extends AbstractTypeDeclaration implements ResolvedInterfaceDeclaration {
+
+ private CtClass ctClass;
+ private TypeSolver typeSolver;
+ private JavassistTypeDeclarationAdapter javassistTypeDeclarationAdapter;
+
+ @Override
+ public String toString() {
+ return "JavassistInterfaceDeclaration{" +
+ "ctClass=" + ctClass.getName() +
+ ", typeSolver=" + typeSolver +
+ '}';
+ }
+
+ public JavassistInterfaceDeclaration(CtClass ctClass, TypeSolver typeSolver) {
+ if (!ctClass.isInterface()) {
+ throw new IllegalArgumentException("Not an interface: " + ctClass.getName());
+ }
+ this.ctClass = ctClass;
+ this.typeSolver = typeSolver;
+ this.javassistTypeDeclarationAdapter = new JavassistTypeDeclarationAdapter(ctClass, typeSolver);
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getInterfacesExtended() {
+ try {
+ return Arrays.stream(ctClass.getInterfaces()).map(i -> new JavassistInterfaceDeclaration(i, typeSolver))
+ .map(i -> new ReferenceTypeImpl(i, typeSolver)).collect(Collectors.toList());
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public String getPackageName() {
+ return ctClass.getPackageName();
+ }
+
+ @Override
+ public String getClassName() {
+ String className = ctClass.getName().replace('$', '.');
+ if (getPackageName() != null) {
+ return className.substring(getPackageName().length() + 1, className.length());
+ }
+ return className;
+ }
+
+ @Override
+ public String getQualifiedName() {
+ return ctClass.getName().replace('$', '.');
+ }
+
+ @Deprecated
+ public Optional<MethodUsage> solveMethodAsUsage(String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver,
+ Context invokationContext, List<ResolvedType> typeParameterValues) {
+
+ return JavassistUtils.getMethodUsage(ctClass, name, argumentsTypes, typeSolver, invokationContext);
+ }
+
+ @Deprecated
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> argumentsTypes, boolean staticOnly) {
+ List<ResolvedMethodDeclaration> candidates = new ArrayList<>();
+ Predicate<CtMethod> staticOnlyCheck = m -> !staticOnly || (staticOnly && Modifier.isStatic(m.getModifiers()));
+ for (CtMethod method : ctClass.getDeclaredMethods()) {
+ boolean isSynthetic = method.getMethodInfo().getAttribute(SyntheticAttribute.tag) != null;
+ boolean isNotBridge = (method.getMethodInfo().getAccessFlags() & AccessFlag.BRIDGE) == 0;
+ if (method.getName().equals(name) && !isSynthetic && isNotBridge && staticOnlyCheck.test(method)) {
+ candidates.add(new JavassistMethodDeclaration(method, typeSolver));
+ }
+ }
+
+ try {
+ CtClass superClass = ctClass.getSuperclass();
+ if (superClass != null) {
+ SymbolReference<ResolvedMethodDeclaration> ref = new JavassistClassDeclaration(superClass, typeSolver).solveMethod(name, argumentsTypes, staticOnly);
+ if (ref.isSolved()) {
+ candidates.add(ref.getCorrespondingDeclaration());
+ }
+ }
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ try {
+ for (CtClass interfaze : ctClass.getInterfaces()) {
+ SymbolReference<ResolvedMethodDeclaration> ref = new JavassistInterfaceDeclaration(interfaze, typeSolver).solveMethod(name, argumentsTypes, staticOnly);
+ if (ref.isSolved()) {
+ candidates.add(ref.getCorrespondingDeclaration());
+ }
+ }
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ return MethodResolutionLogic.findMostApplicable(candidates, name, argumentsTypes, typeSolver);
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedType type) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<ResolvedFieldDeclaration> getAllFields() {
+ return javassistTypeDeclarationAdapter.getDeclaredFields();
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getAncestors() {
+ List<ResolvedReferenceType> ancestors = new ArrayList<>();
+ try {
+ for (CtClass interfaze : ctClass.getInterfaces()) {
+ ResolvedReferenceType superInterfaze = JavassistFactory.typeUsageFor(interfaze, typeSolver).asReferenceType();
+ ancestors.add(superInterfaze);
+ }
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ ancestors = ancestors.stream().filter(a -> a.getQualifiedName() != Object.class.getCanonicalName())
+ .collect(Collectors.toList());
+ ancestors.add(new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver));
+ return ancestors;
+ }
+
+ @Override
+ public Set<ResolvedMethodDeclaration> getDeclaredMethods() {
+ return Arrays.stream(ctClass.getDeclaredMethods())
+ .map(m -> new JavassistMethodDeclaration(m, typeSolver))
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public boolean hasDirectlyAnnotation(String canonicalName) {
+ try {
+ for (Object annotationRaw : ctClass.getAnnotations()) {
+ if (annotationRaw.getClass().getCanonicalName().equals(canonicalName)) {
+ return true;
+ }
+ if (Arrays.stream(annotationRaw.getClass().getInterfaces()).anyMatch(it -> it.getCanonicalName().equals(canonicalName))) {
+ return true;
+ }
+ }
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ return false;
+ }
+
+ @Override
+ public String getName() {
+ String[] nameElements = ctClass.getSimpleName().replace('$', '.').split("\\.");
+ return nameElements[nameElements.length - 1];
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ return javassistTypeDeclarationAdapter.getTypeParameters();
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return JavassistFactory.modifiersToAccessLevel(ctClass.getModifiers());
+ }
+
+ @Override
+ public ResolvedInterfaceDeclaration asInterface() {
+ return this;
+ }
+
+
+ @Deprecated
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
+ for (CtField field : ctClass.getDeclaredFields()) {
+ if (field.getName().equals(name)) {
+ return SymbolReference.solved(new JavassistFieldDeclaration(field, typeSolver));
+ }
+ }
+
+ String[] interfaceFQNs = getInterfaceFQNs();
+ for (String interfaceFQN : interfaceFQNs) {
+ SymbolReference<? extends ResolvedValueDeclaration> interfaceRef = solveSymbolForFQN(name, typeSolver, interfaceFQN);
+ if (interfaceRef.isSolved()) {
+ return interfaceRef;
+ }
+ }
+
+ return SymbolReference.unsolved(ResolvedValueDeclaration.class);
+ }
+
+ private SymbolReference<? extends ResolvedValueDeclaration> solveSymbolForFQN(String symbolName, TypeSolver typeSolver, String fqn) {
+ if (fqn == null) {
+ return SymbolReference.unsolved(ResolvedValueDeclaration.class);
+ }
+
+ ResolvedReferenceTypeDeclaration fqnTypeDeclaration = typeSolver.solveType(fqn);
+ return new SymbolSolver(typeSolver).solveSymbolInType(fqnTypeDeclaration, symbolName);
+ }
+
+ private String[] getInterfaceFQNs() {
+ return ctClass.getClassFile().getInterfaces();
+ }
+
+ @Override
+ public Optional<ResolvedReferenceTypeDeclaration> containerType() {
+ return javassistTypeDeclarationAdapter.containerType();
+ }
+
+ @Override
+ public Set<ResolvedReferenceTypeDeclaration> internalTypes() {
+ try {
+ /*
+ Get all internal types of the current class and get their corresponding ReferenceTypeDeclaration.
+ Finally, return them in a Set.
+ */
+ return Arrays.stream(ctClass.getDeclaredClasses()).map(itype -> JavassistFactory.toTypeDeclaration(itype, typeSolver)).collect(Collectors.toSet());
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public ResolvedReferenceTypeDeclaration getInternalType(String name) {
+ /*
+ The name of the ReferenceTypeDeclaration could be composed on the internal class and the outer class, e.g. A$B. That's why we search the internal type in the ending part.
+ In case the name is composed of the internal type only, i.e. f.getName() returns B, it will also works.
+ */
+ Optional<ResolvedReferenceTypeDeclaration> type =
+ this.internalTypes().stream().filter(f -> f.getName().endsWith(name)).findFirst();
+ return type.orElseThrow(() ->
+ new UnsolvedSymbolException("Internal type not found: " + name));
+ }
+
+ @Override
+ public boolean hasInternalType(String name) {
+ /*
+ The name of the ReferenceTypeDeclaration could be composed on the internal class and the outer class, e.g. A$B. That's why we search the internal type in the ending part.
+ In case the name is composed of the internal type only, i.e. f.getName() returns B, it will also works.
+ */
+ return this.internalTypes().stream().anyMatch(f -> f.getName().endsWith(name));
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistMethodDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistMethodDeclaration.java
new file mode 100644
index 000000000..c474d501d
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistMethodDeclaration.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javassistmodel;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedParameterDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.declarations.common.MethodDeclarationCommonLogic;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import javassist.CtMethod;
+import javassist.NotFoundException;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.SignatureAttribute;
+
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavassistMethodDeclaration implements ResolvedMethodDeclaration {
+ private CtMethod ctMethod;
+ private TypeSolver typeSolver;
+
+ public JavassistMethodDeclaration(CtMethod ctMethod, TypeSolver typeSolver) {
+ this.ctMethod = ctMethod;
+ this.typeSolver = typeSolver;
+ }
+
+ @Override
+ public boolean isDefaultMethod() {
+ return ctMethod.getDeclaringClass().isInterface() && !isAbstract();
+ }
+
+ @Override
+ public boolean isStatic() {
+ return Modifier.isStatic(ctMethod.getModifiers());
+ }
+
+ @Override
+ public String toString() {
+ return "JavassistMethodDeclaration{" +
+ "ctMethod=" + ctMethod +
+ '}';
+ }
+
+ @Override
+ public String getName() {
+ return ctMethod.getName();
+ }
+
+ @Override
+ public boolean isField() {
+ return false;
+ }
+
+ @Override
+ public boolean isParameter() {
+ return false;
+ }
+
+ @Override
+ public boolean isType() {
+ return false;
+ }
+
+ @Override
+ public ResolvedReferenceTypeDeclaration declaringType() {
+ if (ctMethod.getDeclaringClass().isInterface()) {
+ return new JavassistInterfaceDeclaration(ctMethod.getDeclaringClass(), typeSolver);
+ } else {
+ return new JavassistClassDeclaration(ctMethod.getDeclaringClass(), typeSolver);
+ }
+ }
+
+ @Override
+ public ResolvedType getReturnType() {
+ try {
+ if (ctMethod.getGenericSignature() != null) {
+ javassist.bytecode.SignatureAttribute.Type genericSignatureType = SignatureAttribute.toMethodSignature(ctMethod.getGenericSignature()).getReturnType();
+ return JavassistUtils.signatureTypeToType(genericSignatureType, typeSolver, this);
+ } else {
+ return JavassistFactory.typeUsageFor(ctMethod.getReturnType(), typeSolver);
+ }
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ } catch (BadBytecode e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ @Override
+ public int getNumberOfParams() {
+ try {
+ return ctMethod.getParameterTypes().length;
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public ResolvedParameterDeclaration getParam(int i) {
+ try {
+ boolean variadic = false;
+ if ((ctMethod.getModifiers() & javassist.Modifier.VARARGS) > 0) {
+ variadic = i == (ctMethod.getParameterTypes().length - 1);
+ }
+ if (ctMethod.getGenericSignature() != null) {
+ SignatureAttribute.MethodSignature methodSignature = SignatureAttribute.toMethodSignature(ctMethod.getGenericSignature());
+ SignatureAttribute.Type signatureType = methodSignature.getParameterTypes()[i];
+ return new JavassistParameterDeclaration(JavassistUtils.signatureTypeToType(signatureType, typeSolver, this), typeSolver, variadic);
+ } else {
+ return new JavassistParameterDeclaration(ctMethod.getParameterTypes()[i], typeSolver, variadic);
+ }
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ } catch (BadBytecode badBytecode) {
+ throw new RuntimeException(badBytecode);
+ }
+ }
+
+ public MethodUsage getUsage(Node node) {
+ throw new UnsupportedOperationException();
+ }
+
+ public MethodUsage resolveTypeVariables(Context context, List<ResolvedType> parameterTypes) {
+ return new MethodDeclarationCommonLogic(this, typeSolver).resolveTypeVariables(context, parameterTypes);
+ }
+
+ @Override
+ public boolean isAbstract() {
+ return Modifier.isAbstract(ctMethod.getModifiers());
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ try {
+ if (ctMethod.getGenericSignature() == null) {
+ return Collections.emptyList();
+ }
+ SignatureAttribute.MethodSignature methodSignature = SignatureAttribute.toMethodSignature(ctMethod.getGenericSignature());
+ return Arrays.stream(methodSignature.getTypeParameters()).map((jasTp) -> new JavassistTypeParameter(jasTp, this, typeSolver)).collect(Collectors.toList());
+ } catch (BadBytecode badBytecode) {
+ throw new RuntimeException(badBytecode);
+ }
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int getNumberOfSpecifiedExceptions() {
+ try {
+ return ctMethod.getExceptionTypes().length;
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public ResolvedType getSpecifiedException(int index) {
+ if (index < 0 || index >= getNumberOfSpecifiedExceptions()) {
+ throw new IllegalArgumentException(String.format("No exception with index %d. Number of exceptions: %d",
+ index, getNumberOfSpecifiedExceptions()));
+ }
+ try {
+ return JavassistFactory.typeUsageFor(ctMethod.getExceptionTypes()[index], typeSolver);
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistParameterDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistParameterDeclaration.java
new file mode 100644
index 000000000..4b3e14e37
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistParameterDeclaration.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javassistmodel;
+
+import com.github.javaparser.resolution.declarations.ResolvedParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import javassist.CtClass;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavassistParameterDeclaration implements ResolvedParameterDeclaration {
+ private ResolvedType type;
+ private TypeSolver typeSolver;
+ private boolean variadic;
+
+ public JavassistParameterDeclaration(CtClass type, TypeSolver typeSolver, boolean variadic) {
+ this(JavassistFactory.typeUsageFor(type, typeSolver), typeSolver, variadic);
+ }
+
+ public JavassistParameterDeclaration(ResolvedType type, TypeSolver typeSolver, boolean variadic) {
+ this.type = type;
+ this.typeSolver = typeSolver;
+ this.variadic = variadic;
+ }
+
+ @Override
+ public String toString() {
+ return "JavassistParameterDeclaration{" +
+ "type=" + type +
+ ", typeSolver=" + typeSolver +
+ ", variadic=" + variadic +
+ '}';
+ }
+
+ @Override
+ public String getName() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isField() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isParameter() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isVariadic() {
+ return variadic;
+ }
+
+ @Override
+ public boolean isType() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ResolvedType getType() {
+ return type;
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistTypeDeclarationAdapter.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistTypeDeclarationAdapter.java
new file mode 100644
index 000000000..ae56434c5
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistTypeDeclarationAdapter.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.github.javaparser.symbolsolver.javassistmodel;
+
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import javassist.CtClass;
+import javassist.NotFoundException;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.SignatureAttribute;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavassistTypeDeclarationAdapter {
+
+ private CtClass ctClass;
+ private TypeSolver typeSolver;
+
+ public JavassistTypeDeclarationAdapter(CtClass ctClass, TypeSolver typeSolver) {
+ this.ctClass = ctClass;
+ this.typeSolver = typeSolver;
+ }
+
+ public Set<ResolvedMethodDeclaration> getDeclaredMethods() {
+ return Arrays.stream(ctClass.getDeclaredMethods())
+ .map(m -> new JavassistMethodDeclaration(m, typeSolver)).collect(Collectors.toSet());
+ }
+
+ public List<ResolvedConstructorDeclaration> getConstructors() {
+ return Arrays.stream(ctClass.getConstructors())
+ .map(m -> new JavassistConstructorDeclaration(m, typeSolver)).collect(Collectors.toList());
+ }
+
+ public List<ResolvedFieldDeclaration> getDeclaredFields() {
+ List<ResolvedFieldDeclaration> fieldDecls = new ArrayList<>();
+ collectDeclaredFields(ctClass, fieldDecls);
+ return fieldDecls;
+ }
+
+ private void collectDeclaredFields(CtClass ctClass, List<ResolvedFieldDeclaration> fieldDecls) {
+ if (ctClass != null) {
+ Arrays.stream(ctClass.getDeclaredFields())
+ .forEach(f -> fieldDecls.add(new JavassistFieldDeclaration(f, typeSolver)));
+ try {
+ collectDeclaredFields(ctClass.getSuperclass(), fieldDecls);
+ } catch (NotFoundException e) {
+ // We'll stop here
+ }
+ }
+ }
+
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ if (null == ctClass.getGenericSignature()) {
+ return Collections.emptyList();
+ } else {
+ try {
+ SignatureAttribute.ClassSignature classSignature =
+ SignatureAttribute.toClassSignature(ctClass.getGenericSignature());
+ return Arrays.<SignatureAttribute.TypeParameter>stream(classSignature.getParameters())
+ .map((tp) -> new JavassistTypeParameter(tp, JavassistFactory.toTypeDeclaration(ctClass, typeSolver), typeSolver))
+ .collect(Collectors.toList());
+ } catch (BadBytecode badBytecode) {
+ throw new RuntimeException(badBytecode);
+ }
+ }
+ }
+
+ public Optional<ResolvedReferenceTypeDeclaration> containerType() {
+ try {
+ return ctClass.getDeclaringClass() == null ?
+ Optional.empty() :
+ Optional.of(JavassistFactory.toTypeDeclaration(ctClass.getDeclaringClass(), typeSolver));
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistTypeParameter.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistTypeParameter.java
new file mode 100644
index 000000000..bd992171a
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistTypeParameter.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javassistmodel;
+
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import javassist.bytecode.SignatureAttribute;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavassistTypeParameter implements ResolvedTypeParameterDeclaration {
+
+ private SignatureAttribute.TypeParameter wrapped;
+ private TypeSolver typeSolver;
+ private ResolvedTypeParametrizable container;
+
+ public JavassistTypeParameter(SignatureAttribute.TypeParameter wrapped, ResolvedTypeParametrizable container, TypeSolver typeSolver) {
+ this.wrapped = wrapped;
+ this.typeSolver = typeSolver;
+ this.container = container;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ResolvedTypeParameterDeclaration)) return false;
+
+ ResolvedTypeParameterDeclaration that = (ResolvedTypeParameterDeclaration) o;
+
+ if (!getQualifiedName().equals(that.getQualifiedName())) {
+ return false;
+ }
+ if (declaredOnType() != that.declaredOnType()) {
+ return false;
+ }
+ if (declaredOnMethod() != that.declaredOnMethod()) {
+ return false;
+ }
+ // TODO check bounds
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "JavassistTypeParameter{" +
+ wrapped.getName()
+ + '}';
+ }
+
+ @Override
+ public String getName() {
+ return wrapped.getName();
+ }
+
+ @Override
+ public String getContainerQualifiedName() {
+ if (this.container instanceof ResolvedReferenceTypeDeclaration) {
+ return ((ResolvedReferenceTypeDeclaration) this.container).getQualifiedName();
+ } else if (this.container instanceof ResolvedMethodLikeDeclaration) {
+ return ((ResolvedMethodLikeDeclaration) this.container).getQualifiedName();
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getContainerId() {
+ return getContainerQualifiedName();
+ }
+
+ @Override
+ public ResolvedTypeParametrizable getContainer() {
+ return this.container;
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration.Bound> getBounds() {
+ List<Bound> bounds = new ArrayList<>();
+ if (wrapped.getClassBound() != null && !wrapped.getClassBound().toString().equals(Object.class.getCanonicalName())) {
+ throw new UnsupportedOperationException(wrapped.getClassBound().toString());
+ }
+ for (SignatureAttribute.ObjectType ot : wrapped.getInterfaceBound()) {
+ throw new UnsupportedOperationException(ot.toString());
+ }
+ return bounds;
+ }
+
+ @Override
+ public Optional<ResolvedReferenceTypeDeclaration> containerType() {
+ if (container instanceof ResolvedReferenceTypeDeclaration) {
+ return Optional.of((ResolvedReferenceTypeDeclaration) container);
+ }
+ return Optional.empty();
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistUtils.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistUtils.java
new file mode 100644
index 000000000..5b3d68e8e
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistUtils.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.javassistmodel;
+
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.UnsolvedSymbolException;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParametrizable;
+import com.github.javaparser.resolution.types.*;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.*;
+import com.github.javaparser.symbolsolver.resolution.SymbolSolver;
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.NotFoundException;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.SignatureAttribute;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+class JavassistUtils {
+
+ static Optional<MethodUsage> getMethodUsage(CtClass ctClass, String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver, Context invokationContext) {
+ // TODO avoid bridge and synthetic methods
+ for (CtMethod method : ctClass.getDeclaredMethods()) {
+ if (method.getName().equals(name)) {
+ // TODO check typeParametersValues
+ MethodUsage methodUsage = new MethodUsage(new JavassistMethodDeclaration(method, typeSolver));
+ if (argumentsTypes.size() < methodUsage.getNoParams()) {
+ // this method cannot be a good candidate (except if variadic ?)
+ continue;
+ }
+ try {
+ if (method.getGenericSignature() != null) {
+ SignatureAttribute.MethodSignature methodSignature = SignatureAttribute.toMethodSignature(method.getGenericSignature());
+ List<ResolvedType> parametersOfReturnType = parseTypeParameters(methodSignature.getReturnType().toString(), typeSolver, invokationContext);
+ ResolvedType newReturnType = methodUsage.returnType();
+ // consume one parametersOfReturnType at the time
+ if (newReturnType.isReferenceType() && parametersOfReturnType.size() > 0) {
+ newReturnType = newReturnType.asReferenceType().transformTypeParameters(tp -> parametersOfReturnType.remove(0));
+ }
+ methodUsage = methodUsage.replaceReturnType(newReturnType);
+ }
+ return Optional.of(methodUsage);
+ } catch (BadBytecode e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ try {
+ CtClass superClass = ctClass.getSuperclass();
+ if (superClass != null) {
+ Optional<MethodUsage> ref = new JavassistClassDeclaration(superClass, typeSolver).solveMethodAsUsage(name, argumentsTypes, typeSolver, invokationContext, null);
+ if (ref.isPresent()) {
+ return ref;
+ }
+ }
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ try {
+ for (CtClass interfaze : ctClass.getInterfaces()) {
+ Optional<MethodUsage> ref = new JavassistInterfaceDeclaration(interfaze, typeSolver).solveMethodAsUsage(name, argumentsTypes, typeSolver, invokationContext, null);
+ if (ref.isPresent()) {
+ return ref;
+ }
+ }
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ return Optional.empty();
+ }
+
+ private static List<ResolvedType> parseTypeParameters(String signature, TypeSolver typeSolver, Context invokationContext) {
+ String originalSignature = signature;
+ if (signature.contains("<")) {
+ signature = signature.substring(signature.indexOf('<') + 1);
+ if (!signature.endsWith(">")) {
+ throw new IllegalArgumentException();
+ }
+ signature = signature.substring(0, signature.length() - 1);
+ if (signature.contains(",")) {
+ throw new UnsupportedOperationException();
+ }
+ if (signature.startsWith("?")) {
+ // TODO: check bounds
+ List<ResolvedType> types = new ArrayList<>();
+ types.add(ResolvedWildcard.UNBOUNDED);
+ return types;
+ }
+ List<ResolvedType> typeParameters = parseTypeParameters(signature, typeSolver, invokationContext);
+ if (signature.contains("<")) {
+ signature = signature.substring(0, signature.indexOf('<'));
+ }
+ if (signature.contains(">")) {
+ throw new UnsupportedOperationException();
+ }
+
+ ResolvedType type = new SymbolSolver(typeSolver).solveTypeUsage(signature, invokationContext);
+
+ if (type.isReferenceType() && typeParameters.size() > 0) {
+ type = type.asReferenceType().transformTypeParameters(tp -> typeParameters.remove(0));
+ }
+ List<ResolvedType> types = new ArrayList<>();
+ types.add(type);
+ return types;
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ static ResolvedType signatureTypeToType(SignatureAttribute.Type signatureType, TypeSolver typeSolver, ResolvedTypeParametrizable typeParametrizable) {
+ if (signatureType instanceof SignatureAttribute.ClassType) {
+ SignatureAttribute.ClassType classType = (SignatureAttribute.ClassType) signatureType;
+ List<ResolvedType> typeArguments = classType.getTypeArguments() == null ? Collections.emptyList() : Arrays.stream(classType.getTypeArguments()).map(ta -> typeArgumentToType(ta, typeSolver, typeParametrizable)).collect(Collectors.toList());
+ final String typeName =
+ classType.getDeclaringClass() != null ?
+ classType.getDeclaringClass().getName() + "." + classType.getName() :
+ classType.getName();
+ ResolvedReferenceTypeDeclaration typeDeclaration = typeSolver.solveType(
+ removeTypeArguments(internalNameToCanonicalName(typeName)));
+ return new ReferenceTypeImpl(typeDeclaration, typeArguments, typeSolver);
+ } else if (signatureType instanceof SignatureAttribute.TypeVariable) {
+ SignatureAttribute.TypeVariable typeVariableSignature = (SignatureAttribute.TypeVariable)signatureType;
+ Optional<ResolvedTypeParameterDeclaration> typeParameterDeclarationOpt = typeParametrizable.findTypeParameter(typeVariableSignature.getName());
+ if (!typeParameterDeclarationOpt.isPresent()) {
+ throw new UnsolvedSymbolException("Unable to solve TypeVariable " + typeVariableSignature);
+ }
+ ResolvedTypeParameterDeclaration typeParameterDeclaration = typeParameterDeclarationOpt.get();
+ return new ResolvedTypeVariable(typeParameterDeclaration);
+ } else if (signatureType instanceof SignatureAttribute.ArrayType) {
+ SignatureAttribute.ArrayType arrayType = (SignatureAttribute.ArrayType) signatureType;
+ return new ResolvedArrayType(signatureTypeToType(arrayType.getComponentType(), typeSolver, typeParametrizable));
+ } else if (signatureType instanceof SignatureAttribute.BaseType) {
+ SignatureAttribute.BaseType baseType = (SignatureAttribute.BaseType) signatureType;
+ if (baseType.toString().equals("void")) {
+ return ResolvedVoidType.INSTANCE;
+ } else {
+ return ResolvedPrimitiveType.byName(baseType.toString());
+ }
+ } else {
+ throw new RuntimeException(signatureType.getClass().getCanonicalName());
+ }
+ }
+
+ private static String removeTypeArguments(String typeName) {
+ if (typeName.contains("<")) {
+ return typeName.substring(0, typeName.indexOf('<'));
+ } else {
+ return typeName;
+ }
+ }
+
+ private static String internalNameToCanonicalName(String typeName) {
+ return typeName.replaceAll("\\$", ".");
+ }
+
+ private static ResolvedType objectTypeArgumentToType(SignatureAttribute.ObjectType typeArgument, TypeSolver typeSolver, ResolvedTypeParametrizable typeParametrizable) {
+ String typeName = typeArgument.jvmTypeName();
+ Optional<ResolvedType> type = getGenericParameterByName(typeName, typeParametrizable);
+ return type.orElseGet(() -> new ReferenceTypeImpl(
+ typeSolver.solveType(removeTypeArguments(internalNameToCanonicalName(typeName))),
+ typeSolver));
+ }
+
+ private static Optional<ResolvedType> getGenericParameterByName(String typeName, ResolvedTypeParametrizable typeParametrizable) {
+ Optional<ResolvedTypeParameterDeclaration> tp = typeParametrizable.findTypeParameter(typeName);
+ return tp.map(ResolvedTypeVariable::new);
+ }
+
+ private static ResolvedType typeArgumentToType(SignatureAttribute.TypeArgument typeArgument, TypeSolver typeSolver, ResolvedTypeParametrizable typeParametrizable) {
+ if (typeArgument.isWildcard()) {
+ if (typeArgument.getType() == null) {
+ return ResolvedWildcard.UNBOUNDED;
+ } else if (typeArgument.getKind() == '+') {
+ return ResolvedWildcard.extendsBound(objectTypeArgumentToType(typeArgument.getType(), typeSolver, typeParametrizable));
+ } else if (typeArgument.getKind() == '-') {
+ return ResolvedWildcard.superBound(objectTypeArgumentToType(typeArgument.getType(), typeSolver, typeParametrizable));
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ } else {
+ return objectTypeArgumentToType(typeArgument.getType(), typeSolver, typeParametrizable);
+ }
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/package-info.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/package-info.java
new file mode 100644
index 000000000..3512f530b
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation of model based on Javassist.
+ */
+package com.github.javaparser.symbolsolver.javassistmodel; \ No newline at end of file
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/model/typesystem/LazyType.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/model/typesystem/LazyType.java
new file mode 100644
index 000000000..44dbdec1c
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/model/typesystem/LazyType.java
@@ -0,0 +1,120 @@
+package com.github.javaparser.symbolsolver.model.typesystem;
+
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.*;
+import com.github.javaparser.resolution.types.ResolvedType;
+
+import java.util.Map;
+import java.util.function.Function;
+
+public class LazyType implements ResolvedType {
+ private ResolvedType concrete;
+ private Function<Void, ResolvedType> provider;
+
+ public LazyType(Function<Void, ResolvedType> provider) {
+ this.provider = provider;
+ }
+
+ private ResolvedType getType() {
+ if (concrete == null) {
+ concrete = provider.apply(null);
+ }
+ return concrete;
+ }
+
+ @Override
+ public boolean isArray() {
+ return getType().isArray();
+ }
+
+ @Override
+ public int arrayLevel() {
+ return getType().arrayLevel();
+ }
+
+ @Override
+ public boolean isPrimitive() {
+ return getType().isPrimitive();
+ }
+
+ @Override
+ public boolean isNull() {
+ return getType().isNull();
+ }
+
+ @Override
+ public boolean isReference() {
+ return getType().isReference();
+ }
+
+ @Override
+ public boolean isReferenceType() {
+ return getType().isReferenceType();
+ }
+
+ @Override
+ public boolean isVoid() {
+ return getType().isVoid();
+ }
+
+ @Override
+ public boolean isTypeVariable() {
+ return getType().isTypeVariable();
+ }
+
+ @Override
+ public boolean isWildcard() {
+ return getType().isWildcard();
+ }
+
+ @Override
+ public ResolvedArrayType asArrayType() {
+ return getType().asArrayType();
+ }
+
+ @Override
+ public ResolvedReferenceType asReferenceType() {
+ return getType().asReferenceType();
+ }
+
+ @Override
+ public ResolvedTypeParameterDeclaration asTypeParameter() {
+ return getType().asTypeParameter();
+ }
+
+ @Override
+ public ResolvedTypeVariable asTypeVariable() {
+ return getType().asTypeVariable();
+ }
+
+ @Override
+ public ResolvedPrimitiveType asPrimitive() {
+ return getType().asPrimitive();
+ }
+
+ @Override
+ public ResolvedWildcard asWildcard() {
+ return getType().asWildcard();
+ }
+
+ @Override
+ public String describe() {
+ return getType().describe();
+ }
+
+ @Override
+ public ResolvedType replaceTypeVariables(ResolvedTypeParameterDeclaration tp, ResolvedType replaced,
+ Map<ResolvedTypeParameterDeclaration, ResolvedType> inferredTypes) {
+ return getType().replaceTypeVariables(tp, replaced, inferredTypes);
+ }
+
+ @Override
+ public ResolvedType replaceTypeVariables(ResolvedTypeParameterDeclaration tp, ResolvedType replaced) {
+ return getType().replaceTypeVariables(tp, replaced);
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedType other) {
+ return getType().isAssignableBy(other);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/model/typesystem/ReferenceTypeImpl.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/model/typesystem/ReferenceTypeImpl.java
new file mode 100644
index 000000000..927253e24
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/model/typesystem/ReferenceTypeImpl.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.model.typesystem;
+
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.resolution.types.ResolvedTypeTransformer;
+import com.github.javaparser.resolution.types.ResolvedTypeVariable;
+import com.github.javaparser.resolution.types.parametrization.ResolvedTypeParametersMap;
+import com.github.javaparser.symbolsolver.javaparsermodel.LambdaArgumentTypePlaceholder;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserTypeVariableDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+// TODO Remove references to typeSolver: it is needed to instantiate other instances of ReferenceTypeUsage
+// and to get the Object type declaration
+public class ReferenceTypeImpl extends ResolvedReferenceType {
+
+ private TypeSolver typeSolver;
+
+ public static ResolvedReferenceType undeterminedParameters(ResolvedReferenceTypeDeclaration typeDeclaration, TypeSolver typeSolver) {
+ return new ReferenceTypeImpl(typeDeclaration, typeDeclaration.getTypeParameters().stream().map(
+ ResolvedTypeVariable::new
+ ).collect(Collectors.toList()), typeSolver);
+ }
+
+ @Override
+ protected ResolvedReferenceType create(ResolvedReferenceTypeDeclaration typeDeclaration, List<ResolvedType> typeParametersCorrected) {
+ return new ReferenceTypeImpl(typeDeclaration, typeParametersCorrected, typeSolver);
+ }
+
+ @Override
+ protected ResolvedReferenceType create(ResolvedReferenceTypeDeclaration typeDeclaration) {
+ return new ReferenceTypeImpl(typeDeclaration, typeSolver);
+ }
+
+ public ReferenceTypeImpl(ResolvedReferenceTypeDeclaration typeDeclaration, TypeSolver typeSolver) {
+ super(typeDeclaration);
+ this.typeSolver = typeSolver;
+ }
+
+ public ReferenceTypeImpl(ResolvedReferenceTypeDeclaration typeDeclaration, List<ResolvedType> typeArguments, TypeSolver typeSolver) {
+ super(typeDeclaration, typeArguments);
+ this.typeSolver = typeSolver;
+ }
+
+ @Override
+ public ResolvedTypeParameterDeclaration asTypeParameter() {
+ if (this.typeDeclaration instanceof JavaParserTypeVariableDeclaration) {
+ JavaParserTypeVariableDeclaration javaParserTypeVariableDeclaration = (JavaParserTypeVariableDeclaration) this.typeDeclaration;
+ return javaParserTypeVariableDeclaration.asTypeParameter();
+ }
+ throw new UnsupportedOperationException(this.typeDeclaration.getClass().getCanonicalName());
+ }
+
+ /**
+ * This method checks if ThisType t = new OtherType() would compile.
+ */
+ @Override
+ public boolean isAssignableBy(ResolvedType other) {
+ if (other instanceof NullType) {
+ return !this.isPrimitive();
+ }
+ // everything is assignable to Object except void
+ if (!other.isVoid() && this.getQualifiedName().equals(Object.class.getCanonicalName())) {
+ return true;
+ }
+ // consider boxing
+ if (other.isPrimitive()) {
+ if (this.getQualifiedName().equals(Object.class.getCanonicalName())) {
+ return true;
+ } else {
+ // Check if 'other' can be boxed to match this type
+ if (isCorrespondingBoxingType(other.describe())) return true;
+
+ // Resolve the boxed type and check if it can be assigned via widening reference conversion
+ SymbolReference<ResolvedReferenceTypeDeclaration> type = typeSolver.tryToSolveType(other.asPrimitive().getBoxTypeQName());
+ return type.getCorrespondingDeclaration().canBeAssignedTo(super.typeDeclaration);
+ }
+ }
+ if (other instanceof LambdaArgumentTypePlaceholder) {
+ return this.getTypeDeclaration().hasAnnotation(FunctionalInterface.class.getCanonicalName());
+ } else if (other instanceof ReferenceTypeImpl) {
+ ReferenceTypeImpl otherRef = (ReferenceTypeImpl) other;
+ if (compareConsideringTypeParameters(otherRef)) {
+ return true;
+ }
+ for (ResolvedReferenceType otherAncestor : otherRef.getAllAncestors()) {
+ if (compareConsideringTypeParameters(otherAncestor)) {
+ return true;
+ }
+ }
+ return false;
+ } else if (other.isTypeVariable()) {
+ for (ResolvedTypeParameterDeclaration.Bound bound : other.asTypeVariable().asTypeParameter().getBounds()) {
+ if (bound.isExtends()) {
+ if (this.isAssignableBy(bound.getType())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ } else if (other.isConstraint()){
+ return isAssignableBy(other.asConstraintType().getBound());
+ } else if (other.isWildcard()) {
+ if (this.getQualifiedName().equals(Object.class.getCanonicalName())) {
+ return true;
+ } else if (other.asWildcard().isExtends()) {
+ return isAssignableBy(other.asWildcard().getBoundedType());
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public Set<MethodUsage> getDeclaredMethods() {
+ // TODO replace variables
+ Set<MethodUsage> methods = new HashSet<>();
+ for (ResolvedMethodDeclaration methodDeclaration : getTypeDeclaration().getDeclaredMethods()) {
+ MethodUsage methodUsage = new MethodUsage(methodDeclaration);
+ methods.add(methodUsage);
+ }
+ return methods;
+ }
+
+ @Override
+ public ResolvedType toRawType() {
+ if (this.isRawType()) {
+ return this;
+ } else {
+ return new ReferenceTypeImpl(typeDeclaration, typeSolver);
+ }
+ }
+
+ @Override
+ public boolean mention(List<ResolvedTypeParameterDeclaration> typeParameters) {
+ return typeParametersValues().stream().anyMatch(tp -> tp.mention(typeParameters));
+ }
+
+ /**
+ * Execute a transformation on all the type parameters of this element.
+ */
+ @Override
+ public ResolvedType transformTypeParameters(ResolvedTypeTransformer transformer) {
+ ResolvedType result = this;
+ int i = 0;
+ for (ResolvedType tp : this.typeParametersValues()) {
+ ResolvedType transformedTp = transformer.transform(tp);
+ // Identity comparison on purpose
+ if (transformedTp != tp) {
+ List<ResolvedType> typeParametersCorrected = result.asReferenceType().typeParametersValues();
+ typeParametersCorrected.set(i, transformedTp);
+ result = create(typeDeclaration, typeParametersCorrected);
+ }
+ i++;
+ }
+ return result;
+ }
+
+ public List<ResolvedReferenceType> getAllAncestors() {
+ // We need to go through the inheritance line and propagate the type parametes
+
+ List<ResolvedReferenceType> ancestors = typeDeclaration.getAllAncestors();
+
+ ancestors = ancestors.stream()
+ .map(a -> typeParametersMap().replaceAll(a).asReferenceType())
+ .collect(Collectors.toList());
+
+ // Avoid repetitions of Object
+ ancestors.removeIf(a -> a.getQualifiedName().equals(Object.class.getCanonicalName()));
+ ResolvedReferenceTypeDeclaration objectType = typeSolver.solveType(Object.class.getCanonicalName());
+ ResolvedReferenceType objectRef = create(objectType);
+ ancestors.add(objectRef);
+ return ancestors;
+ }
+
+ public ResolvedReferenceType deriveTypeParameters(ResolvedTypeParametersMap typeParametersMap) {
+ return create(typeDeclaration, typeParametersMap);
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/MyObjectProvider.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/MyObjectProvider.java
new file mode 100644
index 000000000..a9cbea796
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/MyObjectProvider.java
@@ -0,0 +1,36 @@
+package com.github.javaparser.symbolsolver.reflectionmodel;
+
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.symbolsolver.logic.ObjectProvider;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class MyObjectProvider implements ObjectProvider {
+
+ public static final MyObjectProvider INSTANCE = new MyObjectProvider();
+
+ private MyObjectProvider() {
+ // prevent instantiation
+ }
+
+ @Override
+ public ResolvedReferenceType object() {
+ return new ReferenceTypeImpl(new ReflectionClassDeclaration(Object.class, new ReflectionTypeSolver()), new ReflectionTypeSolver());
+ }
+
+ @Override
+ public ResolvedReferenceType byName(String qualifiedName) {
+ TypeSolver typeSolver = new ReflectionTypeSolver();
+ ResolvedReferenceTypeDeclaration typeDeclaration = typeSolver.solveType(qualifiedName);
+ if (!typeDeclaration.getTypeParameters().isEmpty()) {
+ throw new UnsupportedOperationException();
+ }
+ return new ReferenceTypeImpl(typeDeclaration, typeSolver);
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionClassAdapter.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionClassAdapter.java
new file mode 100644
index 000000000..5be7bece9
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionClassAdapter.java
@@ -0,0 +1,190 @@
+package com.github.javaparser.symbolsolver.reflectionmodel;
+
+import com.github.javaparser.resolution.UnsolvedSymbolException;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.LambdaArgumentTypePlaceholder;
+import com.github.javaparser.symbolsolver.logic.FunctionalInterfaceLogic;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.NullType;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.TypeVariable;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+class ReflectionClassAdapter {
+
+ private Class<?> clazz;
+ private TypeSolver typeSolver;
+ private ResolvedReferenceTypeDeclaration typeDeclaration;
+
+ public ReflectionClassAdapter(Class<?> clazz, TypeSolver typeSolver, ResolvedReferenceTypeDeclaration typeDeclaration) {
+ this.clazz = clazz;
+ this.typeSolver = typeSolver;
+ this.typeDeclaration = typeDeclaration;
+ }
+
+ public ReferenceTypeImpl getSuperClass() {
+ if (clazz.getGenericSuperclass() == null) {
+ return null;
+ }
+ java.lang.reflect.Type superType = clazz.getGenericSuperclass();
+ if (superType instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) superType;
+ List<ResolvedType> typeParameters = Arrays.stream(parameterizedType.getActualTypeArguments())
+ .map((t) -> ReflectionFactory.typeUsageFor(t, typeSolver))
+ .collect(Collectors.toList());
+ return new ReferenceTypeImpl(new ReflectionClassDeclaration(clazz.getSuperclass(), typeSolver), typeParameters, typeSolver);
+ }
+ return new ReferenceTypeImpl(new ReflectionClassDeclaration(clazz.getSuperclass(), typeSolver), typeSolver);
+ }
+
+ public List<ResolvedReferenceType> getInterfaces() {
+ List<ResolvedReferenceType> interfaces = new ArrayList<>();
+ for (java.lang.reflect.Type superInterface : clazz.getGenericInterfaces()) {
+ if (superInterface instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) superInterface;
+ List<ResolvedType> typeParameters = Arrays.stream(parameterizedType.getActualTypeArguments())
+ .map((t) -> ReflectionFactory.typeUsageFor(t, typeSolver))
+ .collect(Collectors.toList());
+ interfaces.add(new ReferenceTypeImpl(new ReflectionInterfaceDeclaration((Class<?>) ((ParameterizedType) superInterface).getRawType(), typeSolver), typeParameters, typeSolver));
+ } else {
+ interfaces.add(new ReferenceTypeImpl(new ReflectionInterfaceDeclaration((Class<?>) superInterface, typeSolver), typeSolver));
+ }
+ }
+ return interfaces;
+ }
+
+ public List<ResolvedReferenceType> getAncestors() {
+ List<ResolvedReferenceType> ancestors = new LinkedList<>();
+ if (getSuperClass() != null) {
+ ReferenceTypeImpl superClass = getSuperClass();
+ ancestors.add(superClass);
+ } else {
+ ReferenceTypeImpl object = new ReferenceTypeImpl(new ReflectionClassDeclaration(Object.class, typeSolver), typeSolver);
+ ancestors.add(object);
+ }
+ ancestors.addAll(getInterfaces());
+ for (int i = 0; i < ancestors.size(); i++) {
+ ResolvedReferenceType ancestor = ancestors.get(i);
+ if (ancestor.hasName() && ancestor.getQualifiedName().equals(Object.class.getCanonicalName())) {
+ ancestors.remove(i);
+ i--;
+ }
+ }
+ return ancestors;
+ }
+
+ public ResolvedFieldDeclaration getField(String name) {
+ for (Field field : clazz.getDeclaredFields()) {
+ if (field.getName().equals(name)) {
+ return new ReflectionFieldDeclaration(field, typeSolver);
+ }
+ }
+ for (ResolvedReferenceType ancestor : typeDeclaration.getAllAncestors()) {
+ if (ancestor.getTypeDeclaration().hasField(name)) {
+ ReflectionFieldDeclaration reflectionFieldDeclaration = (ReflectionFieldDeclaration) ancestor.getTypeDeclaration().getField(name);
+ return reflectionFieldDeclaration.replaceType(ancestor.getFieldType(name).get());
+ }
+ }
+ throw new UnsolvedSymbolException(name, "Field in " + this);
+ }
+
+ public boolean hasField(String name) {
+ for (Field field : clazz.getDeclaredFields()) {
+ if (field.getName().equals(name)) {
+ return true;
+ }
+ }
+ ReferenceTypeImpl superclass = getSuperClass();
+ if (superclass == null) {
+ return false;
+ } else {
+ return superclass.getTypeDeclaration().hasField(name);
+ }
+ }
+
+ public List<ResolvedFieldDeclaration> getAllFields() {
+ ArrayList<ResolvedFieldDeclaration> fields = new ArrayList<>();
+ for (Field field : clazz.getDeclaredFields()) {
+ fields.add(new ReflectionFieldDeclaration(field, typeSolver));
+ }
+ for (ResolvedReferenceType ancestor : typeDeclaration.getAllAncestors()) {
+ fields.addAll(ancestor.getTypeDeclaration().getAllFields());
+ }
+ return fields;
+ }
+
+ public Set<ResolvedMethodDeclaration> getDeclaredMethods() {
+ return Arrays.stream(clazz.getDeclaredMethods())
+ .filter(m -> !m.isSynthetic() && !m.isBridge())
+ .map(m -> new ReflectionMethodDeclaration(m, typeSolver))
+ .collect(Collectors.toSet());
+ }
+
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ List<ResolvedTypeParameterDeclaration> params = new ArrayList<>();
+ for (TypeVariable<?> tv : this.clazz.getTypeParameters()) {
+ params.add(new ReflectionTypeParameter(tv, true, typeSolver));
+ }
+ return params;
+ }
+
+ public boolean isAssignableBy(ResolvedType type) {
+ if (type instanceof NullType) {
+ return true;
+ }
+ if (type instanceof LambdaArgumentTypePlaceholder) {
+ return isFunctionalInterface();
+ }
+ if (type.isArray()) {
+ return false;
+ }
+ if (type.isPrimitive()) {
+ return false;
+ }
+ if (type.describe().equals(typeDeclaration.getQualifiedName())) {
+ return true;
+ }
+ if (type instanceof ReferenceTypeImpl) {
+ ReferenceTypeImpl otherTypeDeclaration = (ReferenceTypeImpl) type;
+ return otherTypeDeclaration.getTypeDeclaration().canBeAssignedTo(typeDeclaration);
+ }
+
+ return false;
+ }
+
+ public boolean hasDirectlyAnnotation(String canonicalName) {
+ for (Annotation a : clazz.getDeclaredAnnotations()) {
+ if (a.annotationType().getCanonicalName().equals(canonicalName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private final boolean isFunctionalInterface() {
+ return FunctionalInterfaceLogic.getFunctionalMethod(typeDeclaration).isPresent();
+ }
+
+ public List<ResolvedConstructorDeclaration> getConstructors() {
+ return Arrays.stream(clazz.getConstructors())
+ .map(m -> new ReflectionConstructorDeclaration(m, typeSolver))
+ .collect(Collectors.toList());
+ }
+
+ public Optional<ResolvedReferenceTypeDeclaration> containerType() {
+ Class<?> declaringClass = clazz.getDeclaringClass();
+ return declaringClass == null ?
+ Optional.empty() :
+ Optional.of(ReflectionFactory.typeDeclarationFor(declaringClass, typeSolver));
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionClassDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionClassDeclaration.java
new file mode 100644
index 000000000..a93353c94
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionClassDeclaration.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.reflectionmodel;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.LambdaArgumentTypePlaceholder;
+import com.github.javaparser.symbolsolver.javaparsermodel.contexts.ContextHelper;
+import com.github.javaparser.symbolsolver.logic.AbstractClassDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+import com.github.javaparser.symbolsolver.reflectionmodel.comparators.MethodComparator;
+import com.github.javaparser.symbolsolver.resolution.MethodResolutionLogic;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ReflectionClassDeclaration extends AbstractClassDeclaration {
+
+ ///
+ /// Fields
+ ///
+
+ private Class<?> clazz;
+ private TypeSolver typeSolver;
+ private ReflectionClassAdapter reflectionClassAdapter;
+
+ ///
+ /// Constructors
+ ///
+
+ public ReflectionClassDeclaration(Class<?> clazz, TypeSolver typeSolver) {
+ if (clazz == null) {
+ throw new IllegalArgumentException("Class should not be null");
+ }
+ if (clazz.isInterface()) {
+ throw new IllegalArgumentException("Class should not be an interface");
+ }
+ if (clazz.isPrimitive()) {
+ throw new IllegalArgumentException("Class should not represent a primitive class");
+ }
+ if (clazz.isArray()) {
+ throw new IllegalArgumentException("Class should not be an array");
+ }
+ if (clazz.isEnum()) {
+ throw new IllegalArgumentException("Class should not be an enum");
+ }
+ this.clazz = clazz;
+ this.typeSolver = typeSolver;
+ this.reflectionClassAdapter = new ReflectionClassAdapter(clazz, typeSolver, this);
+ }
+
+ ///
+ /// Public methods
+ ///
+
+ @Override
+ public Set<ResolvedMethodDeclaration> getDeclaredMethods() {
+ return reflectionClassAdapter.getDeclaredMethods();
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getAncestors() {
+ return reflectionClassAdapter.getAncestors();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ReflectionClassDeclaration that = (ReflectionClassDeclaration) o;
+
+ if (!clazz.getCanonicalName().equals(that.clazz.getCanonicalName())) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return clazz.hashCode();
+ }
+
+
+ @Override
+ public String getPackageName() {
+ if (clazz.getPackage() != null) {
+ return clazz.getPackage().getName();
+ }
+ return null;
+ }
+
+ @Override
+ public String getClassName() {
+ String canonicalName = clazz.getCanonicalName();
+ if (canonicalName != null && getPackageName() != null) {
+ return canonicalName.substring(getPackageName().length() + 1, canonicalName.length());
+ }
+ return null;
+ }
+
+ @Override
+ public String getQualifiedName() {
+ return clazz.getCanonicalName();
+ }
+
+ @Deprecated
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> argumentsTypes, boolean staticOnly) {
+ List<ResolvedMethodDeclaration> methods = new ArrayList<>();
+ Predicate<Method> staticFilter = m -> !staticOnly || (staticOnly && Modifier.isStatic(m.getModifiers()));
+ for (Method method : Arrays.stream(clazz.getDeclaredMethods()).filter((m) -> m.getName().equals(name)).filter(staticFilter)
+ .sorted(new MethodComparator()).collect(Collectors.toList())) {
+ if (method.isBridge() || method.isSynthetic()) continue;
+ ResolvedMethodDeclaration methodDeclaration = new ReflectionMethodDeclaration(method, typeSolver);
+ methods.add(methodDeclaration);
+ }
+ if (getSuperClass() != null) {
+ ResolvedClassDeclaration superClass = (ResolvedClassDeclaration) getSuperClass().getTypeDeclaration();
+ SymbolReference<ResolvedMethodDeclaration> ref = MethodResolutionLogic.solveMethodInType(superClass, name, argumentsTypes, staticOnly, typeSolver);
+ if (ref.isSolved()) {
+ methods.add(ref.getCorrespondingDeclaration());
+ }
+ }
+ for (ResolvedReferenceType interfaceDeclaration : getInterfaces()) {
+ SymbolReference<ResolvedMethodDeclaration> ref = MethodResolutionLogic.solveMethodInType(interfaceDeclaration.getTypeDeclaration(), name, argumentsTypes, staticOnly, typeSolver);
+ if (ref.isSolved()) {
+ methods.add(ref.getCorrespondingDeclaration());
+ }
+ }
+ return MethodResolutionLogic.findMostApplicable(methods, name, argumentsTypes, typeSolver);
+ }
+
+ @Override
+ public String toString() {
+ return "ReflectionClassDeclaration{" +
+ "clazz=" + getId() +
+ '}';
+ }
+
+ public ResolvedType getUsage(Node node) {
+
+ return new ReferenceTypeImpl(this, typeSolver);
+ }
+
+ public Optional<MethodUsage> solveMethodAsUsage(String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver, Context invokationContext, List<ResolvedType> typeParameterValues) {
+ List<MethodUsage> methods = new ArrayList<>();
+ for (Method method : Arrays.stream(clazz.getDeclaredMethods()).filter((m) -> m.getName().equals(name)).sorted(new MethodComparator()).collect(Collectors.toList())) {
+ if (method.isBridge() || method.isSynthetic()) continue;
+ ResolvedMethodDeclaration methodDeclaration = new ReflectionMethodDeclaration(method, typeSolver);
+ MethodUsage methodUsage = new MethodUsage(methodDeclaration);
+ for (int i = 0; i < getTypeParameters().size() && i < typeParameterValues.size(); i++) {
+ ResolvedTypeParameterDeclaration tpToReplace = getTypeParameters().get(i);
+ ResolvedType newValue = typeParameterValues.get(i);
+ methodUsage = methodUsage.replaceTypeParameter(tpToReplace, newValue);
+ }
+ methods.add(methodUsage);
+ }
+ if (getSuperClass() != null) {
+ ResolvedClassDeclaration superClass = (ResolvedClassDeclaration) getSuperClass().getTypeDeclaration();
+ Optional<MethodUsage> ref = ContextHelper.solveMethodAsUsage(superClass, name, argumentsTypes, typeSolver, invokationContext, typeParameterValues);
+ if (ref.isPresent()) {
+ methods.add(ref.get());
+ }
+ }
+ for (ResolvedReferenceType interfaceDeclaration : getInterfaces()) {
+ Optional<MethodUsage> ref = ContextHelper.solveMethodAsUsage(interfaceDeclaration.getTypeDeclaration(), name, argumentsTypes, typeSolver, invokationContext, typeParameterValues);
+ if (ref.isPresent()) {
+ methods.add(ref.get());
+ }
+ }
+ Optional<MethodUsage> ref = MethodResolutionLogic.findMostApplicableUsage(methods, name, argumentsTypes, typeSolver);
+ return ref;
+ }
+
+ @Override
+ public boolean canBeAssignedTo(ResolvedReferenceTypeDeclaration other) {
+ if (other instanceof LambdaArgumentTypePlaceholder) {
+ return isFunctionalInterface();
+ }
+ if (other.getQualifiedName().equals(getQualifiedName())) {
+ return true;
+ }
+ if (this.clazz.getSuperclass() != null
+ && new ReflectionClassDeclaration(clazz.getSuperclass(), typeSolver).canBeAssignedTo(other)) {
+ return true;
+ }
+ for (Class<?> interfaze : clazz.getInterfaces()) {
+ if (new ReflectionInterfaceDeclaration(interfaze, typeSolver).canBeAssignedTo(other)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedType type) {
+ return reflectionClassAdapter.isAssignableBy(type);
+ }
+
+ @Override
+ public boolean isTypeParameter() {
+ return false;
+ }
+
+ @Override
+ public ResolvedFieldDeclaration getField(String name) {
+ return reflectionClassAdapter.getField(name);
+ }
+
+ @Override
+ public List<ResolvedFieldDeclaration> getAllFields() {
+ return reflectionClassAdapter.getAllFields();
+ }
+
+ @Deprecated
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
+ for (Field field : clazz.getFields()) {
+ if (field.getName().equals(name)) {
+ return SymbolReference.solved(new ReflectionFieldDeclaration(field, typeSolver));
+ }
+ }
+ return SymbolReference.unsolved(ResolvedValueDeclaration.class);
+ }
+
+ @Override
+ public boolean hasDirectlyAnnotation(String canonicalName) {
+ return reflectionClassAdapter.hasDirectlyAnnotation(canonicalName);
+ }
+
+ @Override
+ public boolean hasField(String name) {
+ return reflectionClassAdapter.hasField(name);
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) {
+ return isAssignableBy(new ReferenceTypeImpl(other, typeSolver));
+ }
+
+ @Override
+ public String getName() {
+ return clazz.getSimpleName();
+ }
+
+ @Override
+ public boolean isField() {
+ return false;
+ }
+
+ @Override
+ public boolean isParameter() {
+ return false;
+ }
+
+ @Override
+ public boolean isType() {
+ return true;
+ }
+
+ @Override
+ public boolean isClass() {
+ return !clazz.isInterface();
+ }
+
+ @Override
+ public ReferenceTypeImpl getSuperClass() {
+ return reflectionClassAdapter.getSuperClass();
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getInterfaces() {
+ return reflectionClassAdapter.getInterfaces();
+ }
+
+ @Override
+ public boolean isInterface() {
+ return clazz.isInterface();
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ return reflectionClassAdapter.getTypeParameters();
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return ReflectionFactory.modifiersToAccessLevel(this.clazz.getModifiers());
+ }
+
+ @Override
+ public List<ResolvedConstructorDeclaration> getConstructors() {
+ return reflectionClassAdapter.getConstructors();
+ }
+
+ @Override
+ public Optional<ResolvedReferenceTypeDeclaration> containerType() {
+ return reflectionClassAdapter.containerType();
+ }
+
+ @Override
+ public Set<ResolvedReferenceTypeDeclaration> internalTypes() {
+ return Arrays.stream(this.clazz.getDeclaredClasses())
+ .map(ic -> ReflectionFactory.typeDeclarationFor(ic, typeSolver))
+ .collect(Collectors.toSet());
+ }
+
+ ///
+ /// Protected methods
+ ///
+
+ @Override
+ protected ResolvedReferenceType object() {
+ return new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver);
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionConstructorDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionConstructorDeclaration.java
new file mode 100644
index 000000000..beb0c4347
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionConstructorDeclaration.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.reflectionmodel;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.resolution.declarations.ResolvedClassDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedConstructorDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedParameterDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.lang.reflect.Constructor;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author Fred Lefévère-Laoide
+ */
+public class ReflectionConstructorDeclaration implements ResolvedConstructorDeclaration {
+
+ private Constructor<?> constructor;
+ private TypeSolver typeSolver;
+
+ public ReflectionConstructorDeclaration(Constructor<?> constructor,
+ TypeSolver typeSolver) {
+ this.constructor = constructor;
+ this.typeSolver = typeSolver;
+ }
+
+ @Override
+ public ResolvedClassDeclaration declaringType() {
+ return new ReflectionClassDeclaration(constructor.getDeclaringClass(), typeSolver);
+ }
+
+ @Override
+ public int getNumberOfParams() {
+ return constructor.getParameterCount();
+ }
+
+ @Override
+ public ResolvedParameterDeclaration getParam(int i) {
+ if (i < 0 || i >= getNumberOfParams()) {
+ throw new IllegalArgumentException(String.format("No param with index %d. Number of params: %d", i, getNumberOfParams()));
+ }
+ boolean variadic = false;
+ if (constructor.isVarArgs()) {
+ variadic = i == (constructor.getParameterCount() - 1);
+ }
+ return new ReflectionParameterDeclaration(constructor.getParameterTypes()[i], constructor.getGenericParameterTypes()[i], typeSolver, variadic);
+ }
+
+ @Override
+ public String getName() {
+ return constructor.getName();
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return ReflectionFactory.modifiersToAccessLevel(constructor.getModifiers());
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ return Arrays.stream(constructor.getTypeParameters()).map((refTp) -> new ReflectionTypeParameter(refTp, false, typeSolver)).collect(Collectors.toList());
+ }
+
+ @Override
+ public int getNumberOfSpecifiedExceptions() {
+ return this.constructor.getExceptionTypes().length;
+ }
+
+ @Override
+ public ResolvedType getSpecifiedException(int index) {
+ if (index < 0 || index >= getNumberOfSpecifiedExceptions()) {
+ throw new IllegalArgumentException();
+ }
+ return ReflectionFactory.typeUsageFor(this.constructor.getExceptionTypes()[index], typeSolver);
+ }
+} \ No newline at end of file
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionEnumConstantDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionEnumConstantDeclaration.java
new file mode 100644
index 000000000..07799e829
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionEnumConstantDeclaration.java
@@ -0,0 +1,31 @@
+package com.github.javaparser.symbolsolver.reflectionmodel;
+
+import com.github.javaparser.resolution.declarations.ResolvedEnumConstantDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.lang.reflect.Field;
+
+public class ReflectionEnumConstantDeclaration implements ResolvedEnumConstantDeclaration {
+
+ private Field enumConstant;
+ private TypeSolver typeSolver;
+
+ public ReflectionEnumConstantDeclaration(Field enumConstant, TypeSolver typeSolver) {
+ if (!enumConstant.isEnumConstant()) {
+ throw new IllegalArgumentException("The given field does not represent an enum constant");
+ }
+ this.enumConstant = enumConstant;
+ this.typeSolver = typeSolver;
+ }
+
+ @Override
+ public String getName() {
+ return enumConstant.getName();
+ }
+
+ @Override
+ public ResolvedType getType() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionEnumDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionEnumDeclaration.java
new file mode 100644
index 000000000..a0b4bc3ba
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionEnumDeclaration.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.github.javaparser.symbolsolver.reflectionmodel;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.logic.AbstractTypeDeclaration;
+import com.github.javaparser.symbolsolver.logic.ConfilictingGenericTypesException;
+import com.github.javaparser.symbolsolver.logic.InferenceContext;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ReflectionEnumDeclaration extends AbstractTypeDeclaration implements ResolvedEnumDeclaration {
+
+ ///
+ /// Fields
+ ///
+
+ private Class<?> clazz;
+ private TypeSolver typeSolver;
+ private ReflectionClassAdapter reflectionClassAdapter;
+
+ ///
+ /// Constructors
+ ///
+
+ public ReflectionEnumDeclaration(Class<?> clazz, TypeSolver typeSolver) {
+ if (clazz == null) {
+ throw new IllegalArgumentException("Class should not be null");
+ }
+ if (clazz.isInterface()) {
+ throw new IllegalArgumentException("Class should not be an interface");
+ }
+ if (clazz.isPrimitive()) {
+ throw new IllegalArgumentException("Class should not represent a primitive class");
+ }
+ if (clazz.isArray()) {
+ throw new IllegalArgumentException("Class should not be an array");
+ }
+ if (!clazz.isEnum()) {
+ throw new IllegalArgumentException("Class should be an enum");
+ }
+ this.clazz = clazz;
+ this.typeSolver = typeSolver;
+ this.reflectionClassAdapter = new ReflectionClassAdapter(clazz, typeSolver, this);
+ }
+
+ ///
+ /// Public methods
+ ///
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return ReflectionFactory.modifiersToAccessLevel(this.clazz.getModifiers());
+ }
+
+ @Override
+ public Optional<ResolvedReferenceTypeDeclaration> containerType() {
+ return reflectionClassAdapter.containerType();
+ }
+
+ @Override
+ public String getPackageName() {
+ if (clazz.getPackage() != null) {
+ return clazz.getPackage().getName();
+ }
+ return null;
+ }
+
+ @Override
+ public String getClassName() {
+ String canonicalName = clazz.getCanonicalName();
+ if (canonicalName != null && getPackageName() != null) {
+ return canonicalName.substring(getPackageName().length() + 1, canonicalName.length());
+ }
+ return null;
+ }
+
+ @Override
+ public String getQualifiedName() {
+ return clazz.getCanonicalName();
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getAncestors() {
+ return reflectionClassAdapter.getAncestors();
+ }
+
+ @Override
+ public ResolvedFieldDeclaration getField(String name) {
+ return reflectionClassAdapter.getField(name);
+ }
+
+ @Override
+ public boolean hasField(String name) {
+ return reflectionClassAdapter.hasField(name);
+ }
+
+ @Override
+ public List<ResolvedFieldDeclaration> getAllFields() {
+ return reflectionClassAdapter.getAllFields();
+ }
+
+ @Override
+ public Set<ResolvedMethodDeclaration> getDeclaredMethods() {
+ return reflectionClassAdapter.getDeclaredMethods();
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedType type) {
+ return reflectionClassAdapter.isAssignableBy(type);
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) {
+ return isAssignableBy(new ReferenceTypeImpl(other, typeSolver));
+ }
+
+ @Override
+ public boolean hasDirectlyAnnotation(String qualifiedName) {
+ return reflectionClassAdapter.hasDirectlyAnnotation(qualifiedName);
+ }
+
+ @Override
+ public String getName() {
+ return clazz.getSimpleName();
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ return reflectionClassAdapter.getTypeParameters();
+ }
+
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> parameterTypes, boolean staticOnly) {
+ return ReflectionMethodResolutionLogic.solveMethod(name, parameterTypes, staticOnly,
+ typeSolver,this, clazz);
+ }
+
+ public Optional<MethodUsage> solveMethodAsUsage(String name, List<ResolvedType> parameterTypes, TypeSolver typeSolver,
+ Context invokationContext, List<ResolvedType> typeParameterValues) {
+ Optional<MethodUsage> res = ReflectionMethodResolutionLogic.solveMethodAsUsage(name, parameterTypes, typeSolver, invokationContext,
+ typeParameterValues, this, clazz);
+ if (res.isPresent()) {
+ // We have to replace method type typeParametersValues here
+ InferenceContext inferenceContext = new InferenceContext(MyObjectProvider.INSTANCE);
+ MethodUsage methodUsage = res.get();
+ int i = 0;
+ List<ResolvedType> parameters = new LinkedList<>();
+ for (ResolvedType actualType : parameterTypes) {
+ ResolvedType formalType = methodUsage.getParamType(i);
+ // We need to replace the class type typeParametersValues (while we derive the method ones)
+
+ parameters.add(inferenceContext.addPair(formalType, actualType));
+ i++;
+ }
+ try {
+ ResolvedType returnType = inferenceContext.addSingle(methodUsage.returnType());
+ for (int j=0;j<parameters.size();j++) {
+ methodUsage = methodUsage.replaceParamType(j, inferenceContext.resolve(parameters.get(j)));
+ }
+ methodUsage = methodUsage.replaceReturnType(inferenceContext.resolve(returnType));
+ return Optional.of(methodUsage);
+ } catch (ConfilictingGenericTypesException e) {
+ return Optional.empty();
+ }
+ } else {
+ return res;
+ }
+}
+
+ @Override
+ public List<ResolvedEnumConstantDeclaration> getEnumConstants() {
+ return Arrays.stream(clazz.getFields())
+ .filter(f -> f.isEnumConstant())
+ .map(c -> new ReflectionEnumConstantDeclaration(c, typeSolver))
+ .collect(Collectors.toList());
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionFactory.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionFactory.java
new file mode 100644
index 000000000..fe0fc3450
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionFactory.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.reflectionmodel;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.*;
+import com.github.javaparser.resolution.types.ResolvedTypeVariable;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.*;
+
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.WildcardType;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ReflectionFactory {
+
+ public static ResolvedReferenceTypeDeclaration typeDeclarationFor(Class<?> clazz, TypeSolver typeSolver) {
+ if (clazz.isArray()) {
+ throw new IllegalArgumentException("No type declaration available for an Array");
+ } else if (clazz.isPrimitive()) {
+ throw new IllegalArgumentException();
+ } else if (clazz.isInterface()) {
+ return new ReflectionInterfaceDeclaration(clazz, typeSolver);
+ } else if (clazz.isEnum()) {
+ return new ReflectionEnumDeclaration(clazz, typeSolver);
+ } else {
+ return new ReflectionClassDeclaration(clazz, typeSolver);
+ }
+ }
+
+ public static ResolvedType typeUsageFor(java.lang.reflect.Type type, TypeSolver typeSolver) {
+ if (type instanceof java.lang.reflect.TypeVariable) {
+ java.lang.reflect.TypeVariable<?> tv = (java.lang.reflect.TypeVariable<?>) type;
+ boolean declaredOnClass = tv.getGenericDeclaration() instanceof java.lang.reflect.Type;
+ ResolvedTypeParameterDeclaration typeParameter = new ReflectionTypeParameter(tv, declaredOnClass, typeSolver);
+ return new ResolvedTypeVariable(typeParameter);
+ } else if (type instanceof ParameterizedType) {
+ ParameterizedType pt = (ParameterizedType) type;
+ ResolvedReferenceType rawType = typeUsageFor(pt.getRawType(), typeSolver).asReferenceType();
+ List<java.lang.reflect.Type> actualTypes = new ArrayList<>();
+ actualTypes.addAll(Arrays.asList(pt.getActualTypeArguments()));
+ // we consume the actual types
+ rawType = rawType.transformTypeParameters(tp -> typeUsageFor(actualTypes.remove(0), typeSolver)).asReferenceType();
+ return rawType;
+ } else if (type instanceof Class) {
+ Class<?> c = (Class<?>) type;
+ if (c.isPrimitive()) {
+ if (c.getName().equals(Void.TYPE.getName())) {
+ return ResolvedVoidType.INSTANCE;
+ } else {
+ return ResolvedPrimitiveType.byName(c.getName());
+ }
+ } else if (c.isArray()) {
+ return new ResolvedArrayType(typeUsageFor(c.getComponentType(), typeSolver));
+ } else {
+ return new ReferenceTypeImpl(typeDeclarationFor(c, typeSolver), typeSolver);
+ }
+ } else if (type instanceof GenericArrayType) {
+ GenericArrayType genericArrayType = (GenericArrayType) type;
+ return new ResolvedArrayType(typeUsageFor(genericArrayType.getGenericComponentType(), typeSolver));
+ } else if (type instanceof WildcardType) {
+ WildcardType wildcardType = (WildcardType) type;
+ if (wildcardType.getLowerBounds().length > 0 && wildcardType.getUpperBounds().length > 0) {
+ if (wildcardType.getUpperBounds().length == 1 && wildcardType.getUpperBounds()[0].getTypeName().equals("java.lang.Object")) {
+ // ok, it does not matter
+ }
+ }
+ if (wildcardType.getLowerBounds().length > 0) {
+ if (wildcardType.getLowerBounds().length > 1) {
+ throw new UnsupportedOperationException();
+ }
+ return ResolvedWildcard.superBound(typeUsageFor(wildcardType.getLowerBounds()[0], typeSolver));
+ }
+ if (wildcardType.getUpperBounds().length > 0) {
+ if (wildcardType.getUpperBounds().length > 1) {
+ throw new UnsupportedOperationException();
+ }
+ return ResolvedWildcard.extendsBound(typeUsageFor(wildcardType.getUpperBounds()[0], typeSolver));
+ }
+ return ResolvedWildcard.UNBOUNDED;
+ } else {
+ throw new UnsupportedOperationException(type.getClass().getCanonicalName() + " " + type);
+ }
+ }
+
+ static AccessSpecifier modifiersToAccessLevel(final int modifiers) {
+ if (Modifier.isPublic(modifiers)) {
+ return AccessSpecifier.PUBLIC;
+ } else if (Modifier.isProtected(modifiers)) {
+ return AccessSpecifier.PROTECTED;
+ } else if (Modifier.isPrivate(modifiers)) {
+ return AccessSpecifier.PRIVATE;
+ } else {
+ return AccessSpecifier.DEFAULT;
+ }
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionFieldDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionFieldDeclaration.java
new file mode 100644
index 000000000..198a3e0e9
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionFieldDeclaration.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.reflectionmodel;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.resolution.declarations.ResolvedFieldDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ReflectionFieldDeclaration implements ResolvedFieldDeclaration {
+
+ private Field field;
+ private TypeSolver typeSolver;
+ private ResolvedType type;
+
+ public ReflectionFieldDeclaration(Field field, TypeSolver typeSolver) {
+ this.field = field;
+ this.typeSolver = typeSolver;
+ this.type = calcType();
+ }
+
+ private ReflectionFieldDeclaration(Field field, TypeSolver typeSolver, ResolvedType type) {
+ this.field = field;
+ this.typeSolver = typeSolver;
+ this.type = type;
+ }
+
+ @Override
+ public ResolvedType getType() {
+ return type;
+ }
+
+ private ResolvedType calcType() {
+ // TODO consider interfaces, enums, primitive types, arrays
+ return ReflectionFactory.typeUsageFor(field.getGenericType(), typeSolver);
+ }
+
+ @Override
+ public String getName() {
+ return field.getName();
+ }
+
+ @Override
+ public boolean isStatic() {
+ return Modifier.isStatic(field.getModifiers());
+ }
+
+ @Override
+ public boolean isField() {
+ return true;
+ }
+
+ @Override
+ public ResolvedTypeDeclaration declaringType() {
+ return ReflectionFactory.typeDeclarationFor(field.getDeclaringClass(), typeSolver);
+ }
+
+ public ResolvedFieldDeclaration replaceType(ResolvedType fieldType) {
+ return new ReflectionFieldDeclaration(field, typeSolver, fieldType);
+ }
+
+ @Override
+ public boolean isParameter() {
+ return false;
+ }
+
+ @Override
+ public boolean isType() {
+ return false;
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return ReflectionFactory.modifiersToAccessLevel(field.getModifiers());
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionInterfaceDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionInterfaceDeclaration.java
new file mode 100644
index 000000000..7453e23a6
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionInterfaceDeclaration.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.reflectionmodel;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.LambdaArgumentTypePlaceholder;
+import com.github.javaparser.symbolsolver.logic.AbstractTypeDeclaration;
+import com.github.javaparser.symbolsolver.logic.ConfilictingGenericTypesException;
+import com.github.javaparser.symbolsolver.logic.InferenceContext;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.NullType;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+
+import java.lang.reflect.Field;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ReflectionInterfaceDeclaration extends AbstractTypeDeclaration implements ResolvedInterfaceDeclaration {
+
+ ///
+ /// Fields
+ ///
+
+ private Class<?> clazz;
+ private TypeSolver typeSolver;
+ private ReflectionClassAdapter reflectionClassAdapter;
+
+ ///
+ /// Constructor
+ ///
+
+ public ReflectionInterfaceDeclaration(Class<?> clazz, TypeSolver typeSolver) {
+ if (!clazz.isInterface()) {
+ throw new IllegalArgumentException();
+ }
+
+ this.clazz = clazz;
+ this.typeSolver = typeSolver;
+ this.reflectionClassAdapter = new ReflectionClassAdapter(clazz, typeSolver, this);
+ }
+
+ ///
+ /// Public methods
+ ///
+
+ @Override
+ public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) {
+ return isAssignableBy(new ReferenceTypeImpl(other, typeSolver));
+ }
+
+ @Override
+ public String getPackageName() {
+ if (clazz.getPackage() != null) {
+ return clazz.getPackage().getName();
+ }
+ return null;
+ }
+
+ @Override
+ public String getClassName() {
+ String canonicalName = clazz.getCanonicalName();
+ if (canonicalName != null && getPackageName() != null) {
+ return canonicalName.substring(getPackageName().length() + 1, canonicalName.length());
+ }
+ return null;
+ }
+
+ @Override
+ public String getQualifiedName() {
+ return clazz.getCanonicalName();
+ }
+
+ @Deprecated
+ public SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> parameterTypes, boolean staticOnly) {
+ return ReflectionMethodResolutionLogic.solveMethod(name, parameterTypes, staticOnly,
+ typeSolver,this, clazz);
+ }
+
+ @Override
+ public String toString() {
+ return "ReflectionInterfaceDeclaration{" +
+ "clazz=" + clazz.getCanonicalName() +
+ '}';
+ }
+
+ public ResolvedType getUsage(Node node) {
+ return new ReferenceTypeImpl(this, typeSolver);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ReflectionInterfaceDeclaration)) return false;
+
+ ReflectionInterfaceDeclaration that = (ReflectionInterfaceDeclaration) o;
+
+ if (!clazz.getCanonicalName().equals(that.clazz.getCanonicalName())) return false;
+
+ if (!getTypeParameters().equals(that.getTypeParameters())) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return clazz.hashCode();
+ }
+
+ public Optional<MethodUsage> solveMethodAsUsage(String name, List<ResolvedType> parameterTypes, TypeSolver typeSolver,
+ Context invokationContext, List<ResolvedType> typeParameterValues) {
+ Optional<MethodUsage> res = ReflectionMethodResolutionLogic.solveMethodAsUsage(name, parameterTypes, typeSolver, invokationContext,
+ typeParameterValues, this, clazz);
+ if (res.isPresent()) {
+ // We have to replace method type typeParametersValues here
+ InferenceContext inferenceContext = new InferenceContext(MyObjectProvider.INSTANCE);
+ MethodUsage methodUsage = res.get();
+ int i = 0;
+ List<ResolvedType> parameters = new LinkedList<>();
+ for (ResolvedType actualType : parameterTypes) {
+ ResolvedType formalType = methodUsage.getParamType(i);
+ // We need to replace the class type typeParametersValues (while we derive the method ones)
+
+ parameters.add(inferenceContext.addPair(formalType, actualType));
+ i++;
+ }
+ try {
+ ResolvedType returnType = inferenceContext.addSingle(methodUsage.returnType());
+ for (int j=0;j<parameters.size();j++) {
+ methodUsage = methodUsage.replaceParamType(j, inferenceContext.resolve(parameters.get(j)));
+ }
+ methodUsage = methodUsage.replaceReturnType(inferenceContext.resolve(returnType));
+ return Optional.of(methodUsage);
+ } catch (ConfilictingGenericTypesException e) {
+ return Optional.empty();
+ }
+ } else {
+ return res;
+ }
+ }
+
+ @Override
+ public boolean canBeAssignedTo(ResolvedReferenceTypeDeclaration other) {
+ if (other instanceof LambdaArgumentTypePlaceholder) {
+ return isFunctionalInterface();
+ }
+ if (other.getQualifiedName().equals(getQualifiedName())) {
+ return true;
+ }
+ if (this.clazz.getSuperclass() != null
+ && new ReflectionInterfaceDeclaration(clazz.getSuperclass(), typeSolver).canBeAssignedTo(other)) {
+ return true;
+ }
+ for (Class interfaze : clazz.getInterfaces()) {
+ if (new ReflectionInterfaceDeclaration(interfaze, typeSolver).canBeAssignedTo(other)) {
+ return true;
+ }
+ }
+
+ if (other.getQualifiedName().equals(Object.class.getCanonicalName())) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedType type) {
+ if (type instanceof NullType) {
+ return true;
+ }
+ if (type instanceof LambdaArgumentTypePlaceholder) {
+ return isFunctionalInterface();
+ }
+ if (type.isArray()) {
+ return false;
+ }
+ if (type.isPrimitive()) {
+ return false;
+ }
+ if (type.describe().equals(getQualifiedName())) {
+ return true;
+ }
+ if (type instanceof ReferenceTypeImpl) {
+ ReferenceTypeImpl otherTypeDeclaration = (ReferenceTypeImpl) type;
+ return otherTypeDeclaration.getTypeDeclaration().canBeAssignedTo(this);
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean isTypeParameter() {
+ return false;
+ }
+
+ @Override
+ public ResolvedFieldDeclaration getField(String name) {
+ return reflectionClassAdapter.getField(name);
+ }
+
+ @Override
+ public List<ResolvedFieldDeclaration> getAllFields() {
+ return reflectionClassAdapter.getAllFields();
+ }
+
+ @Deprecated
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
+ for (Field field : clazz.getFields()) {
+ if (field.getName().equals(name)) {
+ return SymbolReference.solved(new ReflectionFieldDeclaration(field, typeSolver));
+ }
+ }
+ return SymbolReference.unsolved(ResolvedValueDeclaration.class);
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getAncestors() {
+ return reflectionClassAdapter.getAncestors();
+ }
+
+ @Override
+ public Set<ResolvedMethodDeclaration> getDeclaredMethods() {
+ return reflectionClassAdapter.getDeclaredMethods();
+ }
+
+ @Override
+ public boolean hasField(String name) {
+ return reflectionClassAdapter.hasField(name);
+ }
+
+ @Override
+ public String getName() {
+ return clazz.getSimpleName();
+ }
+
+ @Override
+ public boolean isInterface() {
+ return true;
+ }
+
+ @Override
+ public List<ResolvedReferenceType> getInterfacesExtended() {
+ List<ResolvedReferenceType> res = new ArrayList<>();
+ for (Class i : clazz.getInterfaces()) {
+ res.add(new ReferenceTypeImpl(new ReflectionInterfaceDeclaration(i, typeSolver), typeSolver));
+ }
+ return res;
+ }
+
+ @Override
+ public Optional<ResolvedReferenceTypeDeclaration> containerType() {
+ return reflectionClassAdapter.containerType();
+ }
+
+ @Override
+ public Set<ResolvedReferenceTypeDeclaration> internalTypes() {
+ return Arrays.stream(this.clazz.getDeclaredClasses())
+ .map(ic -> ReflectionFactory.typeDeclarationFor(ic, typeSolver))
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public ResolvedInterfaceDeclaration asInterface() {
+ return this;
+ }
+
+ @Override
+ public boolean hasDirectlyAnnotation(String canonicalName) {
+ return reflectionClassAdapter.hasDirectlyAnnotation(canonicalName);
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ return reflectionClassAdapter.getTypeParameters();
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return ReflectionFactory.modifiersToAccessLevel(this.clazz.getModifiers());
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionMethodDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionMethodDeclaration.java
new file mode 100644
index 000000000..04047c630
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionMethodDeclaration.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.reflectionmodel;
+
+import com.github.javaparser.ast.AccessSpecifier;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedParameterDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.declarations.common.MethodDeclarationCommonLogic;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ReflectionMethodDeclaration implements ResolvedMethodDeclaration {
+
+ private Method method;
+ private TypeSolver typeSolver;
+
+ public ReflectionMethodDeclaration(Method method, TypeSolver typeSolver) {
+ this.method = method;
+ if (method.isSynthetic() || method.isBridge()) {
+ throw new IllegalArgumentException();
+ }
+ this.typeSolver = typeSolver;
+ }
+
+ @Override
+ public String getName() {
+ return method.getName();
+ }
+
+ @Override
+ public boolean isField() {
+ return false;
+ }
+
+ @Override
+ public boolean isParameter() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "ReflectionMethodDeclaration{" +
+ "method=" + method +
+ '}';
+ }
+
+ @Override
+ public boolean isType() {
+ return false;
+ }
+
+ @Override
+ public ResolvedReferenceTypeDeclaration declaringType() {
+ if (method.getDeclaringClass().isInterface()) {
+ return new ReflectionInterfaceDeclaration(method.getDeclaringClass(), typeSolver);
+ }
+ if (method.getDeclaringClass().isEnum()) {
+ return new ReflectionEnumDeclaration(method.getDeclaringClass(), typeSolver);
+ } else {
+ return new ReflectionClassDeclaration(method.getDeclaringClass(), typeSolver);
+ }
+ }
+
+ @Override
+ public ResolvedType getReturnType() {
+ return ReflectionFactory.typeUsageFor(method.getGenericReturnType(), typeSolver);
+ }
+
+ @Override
+ public int getNumberOfParams() {
+ return method.getParameterTypes().length;
+ }
+
+ @Override
+ public ResolvedParameterDeclaration getParam(int i) {
+ boolean variadic = false;
+ if (method.isVarArgs()) {
+ variadic = i == (method.getParameterCount() - 1);
+ }
+ return new ReflectionParameterDeclaration(method.getParameterTypes()[i], method.getGenericParameterTypes()[i], typeSolver, variadic);
+ }
+
+ @Override
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ return Arrays.stream(method.getTypeParameters()).map((refTp) -> new ReflectionTypeParameter(refTp, false, typeSolver)).collect(Collectors.toList());
+ }
+
+ public MethodUsage resolveTypeVariables(Context context, List<ResolvedType> parameterTypes) {
+ return new MethodDeclarationCommonLogic(this, typeSolver).resolveTypeVariables(context, parameterTypes);
+ }
+
+ @Override
+ public boolean isAbstract() {
+ return Modifier.isAbstract(method.getModifiers());
+ }
+
+ @Override
+ public boolean isDefaultMethod() {
+ return method.isDefault();
+ }
+
+ @Override
+ public boolean isStatic() {
+ return Modifier.isStatic(method.getModifiers());
+ }
+
+ @Override
+ public AccessSpecifier accessSpecifier() {
+ return ReflectionFactory.modifiersToAccessLevel(this.method.getModifiers());
+ }
+
+ @Override
+ public int getNumberOfSpecifiedExceptions() {
+ return this.method.getExceptionTypes().length;
+ }
+
+ @Override
+ public ResolvedType getSpecifiedException(int index) {
+ if (index < 0 || index >= getNumberOfSpecifiedExceptions()) {
+ throw new IllegalArgumentException();
+ }
+ return ReflectionFactory.typeUsageFor(this.method.getExceptionTypes()[index], typeSolver);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionMethodResolutionLogic.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionMethodResolutionLogic.java
new file mode 100644
index 000000000..acf39c23b
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionMethodResolutionLogic.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.reflectionmodel;
+
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.resolution.types.ResolvedTypeVariable;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+import com.github.javaparser.symbolsolver.resolution.MethodResolutionLogic;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+class ReflectionMethodResolutionLogic {
+
+ static SymbolReference<ResolvedMethodDeclaration> solveMethod(String name, List<ResolvedType> parameterTypes, boolean staticOnly,
+ TypeSolver typeSolver, ResolvedReferenceTypeDeclaration scopeType,
+ Class clazz){
+ List<ResolvedMethodDeclaration> methods = new ArrayList<>();
+ Predicate<Method> staticOnlyCheck = m -> !staticOnly || (staticOnly && Modifier.isStatic(m.getModifiers()));
+ for (Method method : clazz.getMethods()) {
+ if (method.isBridge() || method.isSynthetic() || !method.getName().equals(name)|| !staticOnlyCheck.test(method)) continue;
+ ResolvedMethodDeclaration methodDeclaration = new ReflectionMethodDeclaration(method, typeSolver);
+ methods.add(methodDeclaration);
+ }
+
+ for (ResolvedReferenceType ancestor : scopeType.getAncestors()) {
+ SymbolReference<ResolvedMethodDeclaration> ref = MethodResolutionLogic.solveMethodInType(ancestor.getTypeDeclaration(), name, parameterTypes, staticOnly, typeSolver);
+ if (ref.isSolved()) {
+ methods.add(ref.getCorrespondingDeclaration());
+ }
+ }
+
+ if (scopeType.getAncestors().isEmpty()){
+ ReferenceTypeImpl objectClass = new ReferenceTypeImpl(new ReflectionClassDeclaration(Object.class, typeSolver), typeSolver);
+ SymbolReference<ResolvedMethodDeclaration> ref = MethodResolutionLogic.solveMethodInType(objectClass.getTypeDeclaration(), name, parameterTypes, staticOnly, typeSolver);
+ if (ref.isSolved()) {
+ methods.add(ref.getCorrespondingDeclaration());
+ }
+ }
+ return MethodResolutionLogic.findMostApplicable(methods, name, parameterTypes, typeSolver);
+ }
+
+ static Optional<MethodUsage> solveMethodAsUsage(String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver,
+ Context invokationContext, List<ResolvedType> typeParameterValues,
+ ResolvedReferenceTypeDeclaration scopeType, Class clazz) {
+ if (typeParameterValues.size() != scopeType.getTypeParameters().size()) {
+ // if it is zero we are going to ignore them
+ if (!scopeType.getTypeParameters().isEmpty()) {
+ // Parameters not specified, so default to Object
+ typeParameterValues = new ArrayList<>();
+ for (int i = 0; i < scopeType.getTypeParameters().size(); i++) {
+ typeParameterValues.add(new ReferenceTypeImpl(new ReflectionClassDeclaration(Object.class, typeSolver), typeSolver));
+ }
+ }
+ }
+ List<MethodUsage> methods = new ArrayList<>();
+ for (Method method : clazz.getMethods()) {
+ if (method.getName().equals(name) && !method.isBridge() && !method.isSynthetic()) {
+ ResolvedMethodDeclaration methodDeclaration = new ReflectionMethodDeclaration(method, typeSolver);
+ MethodUsage methodUsage = replaceParams(typeParameterValues, scopeType, methodDeclaration);
+ methods.add(methodUsage);
+ }
+
+ }
+
+ for(ResolvedReferenceType ancestor : scopeType.getAncestors()){
+ SymbolReference<ResolvedMethodDeclaration> ref = MethodResolutionLogic.solveMethodInType(ancestor.getTypeDeclaration(), name, argumentsTypes, typeSolver);
+ if (ref.isSolved()){
+ ResolvedMethodDeclaration correspondingDeclaration = ref.getCorrespondingDeclaration();
+ MethodUsage methodUsage = replaceParams(typeParameterValues, ancestor.getTypeDeclaration(), correspondingDeclaration);
+ methods.add(methodUsage);
+ }
+ }
+
+ if (scopeType.getAncestors().isEmpty()){
+ ReferenceTypeImpl objectClass = new ReferenceTypeImpl(new ReflectionClassDeclaration(Object.class, typeSolver), typeSolver);
+ SymbolReference<ResolvedMethodDeclaration> ref = MethodResolutionLogic.solveMethodInType(objectClass.getTypeDeclaration(), name, argumentsTypes, typeSolver);
+ if (ref.isSolved()) {
+ MethodUsage usage = replaceParams(typeParameterValues, objectClass.getTypeDeclaration(), ref.getCorrespondingDeclaration());
+ methods.add(usage);
+ }
+ }
+
+ final List<ResolvedType> finalTypeParameterValues = typeParameterValues;
+ argumentsTypes = argumentsTypes.stream().map((pt) -> {
+ int i = 0;
+ for (ResolvedTypeParameterDeclaration tp : scopeType.getTypeParameters()) {
+ pt = pt.replaceTypeVariables(tp, finalTypeParameterValues.get(i));
+ i++;
+ }
+ return pt;
+ }).collect(Collectors.toList());
+ return MethodResolutionLogic.findMostApplicableUsage(methods, name, argumentsTypes, typeSolver);
+ }
+
+ private static MethodUsage replaceParams(List<ResolvedType> typeParameterValues, ResolvedReferenceTypeDeclaration typeParametrizable, ResolvedMethodDeclaration methodDeclaration) {
+ MethodUsage methodUsage = new MethodUsage(methodDeclaration);
+ int i = 0;
+
+ // Only replace if we have enough values provided
+ if (typeParameterValues.size() == typeParametrizable.getTypeParameters().size()){
+ for (ResolvedTypeParameterDeclaration tp : typeParametrizable.getTypeParameters()) {
+ methodUsage = methodUsage.replaceTypeParameter(tp, typeParameterValues.get(i));
+ i++;
+ }
+ }
+
+ for (ResolvedTypeParameterDeclaration methodTypeParameter : methodDeclaration.getTypeParameters()) {
+ methodUsage = methodUsage.replaceTypeParameter(methodTypeParameter, new ResolvedTypeVariable(methodTypeParameter));
+ }
+
+ return methodUsage;
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionParameterDeclaration.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionParameterDeclaration.java
new file mode 100644
index 000000000..be5e9df37
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionParameterDeclaration.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.reflectionmodel;
+
+import com.github.javaparser.resolution.declarations.ResolvedParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ReflectionParameterDeclaration implements ResolvedParameterDeclaration {
+ private Class<?> type;
+ private java.lang.reflect.Type genericType;
+ private TypeSolver typeSolver;
+ private boolean variadic;
+
+ public ReflectionParameterDeclaration(Class<?> type, java.lang.reflect.Type genericType, TypeSolver typeSolver, boolean variadic) {
+ this.type = type;
+ this.genericType = genericType;
+ this.typeSolver = typeSolver;
+ this.variadic = variadic;
+ }
+
+ @Override
+ public String getName() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String toString() {
+ return "ReflectionParameterDeclaration{" +
+ "type=" + type +
+ '}';
+ }
+
+ @Override
+ public boolean isField() {
+ return false;
+ }
+
+ @Override
+ public boolean isParameter() {
+ return true;
+ }
+
+ @Override
+ public boolean isVariadic() {
+ return variadic;
+ }
+
+ @Override
+ public boolean isType() {
+ return false;
+ }
+
+ @Override
+ public ResolvedType getType() {
+ return ReflectionFactory.typeUsageFor(genericType, typeSolver);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionTypeParameter.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionTypeParameter.java
new file mode 100644
index 000000000..0532ed6c7
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionTypeParameter.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.reflectionmodel;
+
+import com.github.javaparser.resolution.declarations.ResolvedMethodLikeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParametrizable;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.Method;
+import java.lang.reflect.TypeVariable;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ReflectionTypeParameter implements ResolvedTypeParameterDeclaration {
+
+ private TypeVariable typeVariable;
+ private TypeSolver typeSolver;
+ private ResolvedTypeParametrizable container;
+
+ public ReflectionTypeParameter(TypeVariable typeVariable, boolean declaredOnClass, TypeSolver typeSolver) {
+ GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
+ if (genericDeclaration instanceof Class) {
+ container = ReflectionFactory.typeDeclarationFor((Class) genericDeclaration, typeSolver);
+ } else if (genericDeclaration instanceof Method) {
+ container = new ReflectionMethodDeclaration((Method) genericDeclaration, typeSolver);
+ } else if (genericDeclaration instanceof Constructor) {
+ container = new ReflectionConstructorDeclaration((Constructor) genericDeclaration, typeSolver);
+ }
+ this.typeVariable = typeVariable;
+ this.typeSolver = typeSolver;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ResolvedTypeParameterDeclaration)) return false;
+
+ ResolvedTypeParameterDeclaration that = (ResolvedTypeParameterDeclaration) o;
+
+ if (!getQualifiedName().equals(that.getQualifiedName())) {
+ return false;
+ }
+ if (declaredOnType() != that.declaredOnType()) {
+ return false;
+ }
+ if (declaredOnMethod() != that.declaredOnMethod()) {
+ return false;
+ }
+ // TODO check bounds
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = typeVariable.hashCode();
+ result = 31 * result + container.hashCode();
+ return result;
+ }
+
+ @Override
+ public String getName() {
+ return typeVariable.getName();
+ }
+
+ @Override
+ public String getContainerQualifiedName() {
+ if (container instanceof ResolvedReferenceTypeDeclaration) {
+ return ((ResolvedReferenceTypeDeclaration) container).getQualifiedName();
+ } else {
+ return ((ResolvedMethodLikeDeclaration) container).getQualifiedSignature();
+ }
+ }
+
+ @Override
+ public String getContainerId() {
+ if (container instanceof ResolvedReferenceTypeDeclaration) {
+ return ((ResolvedReferenceTypeDeclaration) container).getId();
+ } else {
+ return ((ResolvedMethodLikeDeclaration) container).getQualifiedSignature();
+ }
+ }
+
+ @Override
+ public ResolvedTypeParametrizable getContainer() {
+ return this.container;
+ }
+
+ @Override
+ public List<Bound> getBounds() {
+ return Arrays.stream(typeVariable.getBounds()).map((refB) -> Bound.extendsBound(ReflectionFactory.typeUsageFor(refB, typeSolver))).collect(Collectors.toList());
+ }
+
+ @Override
+ public String toString() {
+ return "ReflectionTypeParameter{" +
+ "typeVariable=" + typeVariable +
+ '}';
+ }
+
+ @Override
+ public Optional<ResolvedReferenceTypeDeclaration> containerType() {
+ if (container instanceof ResolvedReferenceTypeDeclaration) {
+ return Optional.of((ResolvedReferenceTypeDeclaration) container);
+ }
+ return Optional.empty();
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/comparators/ClassComparator.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/comparators/ClassComparator.java
new file mode 100644
index 000000000..e22c2049c
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/comparators/ClassComparator.java
@@ -0,0 +1,25 @@
+package com.github.javaparser.symbolsolver.reflectionmodel.comparators;
+
+import java.util.Comparator;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ClassComparator implements Comparator<Class<?>> {
+
+ @Override
+ public int compare(Class<?> o1, Class<?> o2) {
+ int subCompare;
+ subCompare = o1.getCanonicalName().compareTo(o2.getCanonicalName());
+ if (subCompare != 0) return subCompare;
+ subCompare = Boolean.compare(o1.isAnnotation(), o2.isAnnotation());
+ if (subCompare != 0) return subCompare;
+ subCompare = Boolean.compare(o1.isArray(), o2.isArray());
+ if (subCompare != 0) return subCompare;
+ subCompare = Boolean.compare(o1.isEnum(), o2.isEnum());
+ if (subCompare != 0) return subCompare;
+ subCompare = Boolean.compare(o1.isInterface(), o2.isInterface());
+ if (subCompare != 0) return subCompare;
+ return 0;
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/comparators/MethodComparator.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/comparators/MethodComparator.java
new file mode 100644
index 000000000..9f2fcb101
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/comparators/MethodComparator.java
@@ -0,0 +1,25 @@
+package com.github.javaparser.symbolsolver.reflectionmodel.comparators;
+
+import java.lang.reflect.Method;
+import java.util.Comparator;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class MethodComparator implements Comparator<Method> {
+
+ @Override
+ public int compare(Method o1, Method o2) {
+ int compareName = o1.getName().compareTo(o2.getName());
+ if (compareName != 0) return compareName;
+ int compareNParams = o1.getParameterCount() - o2.getParameterCount();
+ if (compareNParams != 0) return compareNParams;
+ for (int i = 0; i < o1.getParameterCount(); i++) {
+ int compareParam = new ParameterComparator().compare(o1.getParameters()[i], o2.getParameters()[i]);
+ if (compareParam != 0) return compareParam;
+ }
+ int compareResult = new ClassComparator().compare(o1.getReturnType(), o2.getReturnType());
+ if (compareResult != 0) return compareResult;
+ return 0;
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/comparators/ParameterComparator.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/comparators/ParameterComparator.java
new file mode 100644
index 000000000..42ed6b87a
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/comparators/ParameterComparator.java
@@ -0,0 +1,19 @@
+package com.github.javaparser.symbolsolver.reflectionmodel.comparators;
+
+import java.lang.reflect.Parameter;
+import java.util.Comparator;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ParameterComparator implements Comparator<Parameter> {
+
+ @Override
+ public int compare(Parameter o1, Parameter o2) {
+ int compareName = o1.getName().compareTo(o2.getName());
+ if (compareName != 0) return compareName;
+ int compareType = new ClassComparator().compare(o1.getType(), o2.getType());
+ if (compareType != 0) return compareType;
+ return 0;
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/package-info.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/package-info.java
new file mode 100644
index 000000000..9e58cbd0e
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation of model based on reflection.
+ */
+package com.github.javaparser.symbolsolver.reflectionmodel; \ No newline at end of file
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/ConstructorResolutionLogic.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/ConstructorResolutionLogic.java
new file mode 100644
index 000000000..dfd50ed48
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/ConstructorResolutionLogic.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.github.javaparser.symbolsolver.resolution;
+
+import com.github.javaparser.resolution.MethodAmbiguityException;
+import com.github.javaparser.resolution.declarations.ResolvedConstructorDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedArrayType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author Fred Lefévère-Laoide
+ */
+public class ConstructorResolutionLogic {
+
+ private static List<ResolvedType> groupVariadicParamValues(List<ResolvedType> argumentsTypes, int startVariadic,
+ ResolvedType variadicType) {
+ List<ResolvedType> res = new ArrayList<>(argumentsTypes.subList(0, startVariadic));
+ List<ResolvedType> variadicValues = argumentsTypes.subList(startVariadic, argumentsTypes.size());
+ if (variadicValues.isEmpty()) {
+ // TODO if there are no variadic values we should default to the bound of the formal type
+ res.add(variadicType);
+ } else {
+ ResolvedType componentType = findCommonType(variadicValues);
+ res.add(new ResolvedArrayType(componentType));
+ }
+ return res;
+ }
+
+ private static ResolvedType findCommonType(List<ResolvedType> variadicValues) {
+ if (variadicValues.isEmpty()) {
+ throw new IllegalArgumentException();
+ }
+ // TODO implement this decently
+ return variadicValues.get(0);
+ }
+
+ public static boolean isApplicable(ResolvedConstructorDeclaration constructor, List<ResolvedType> argumentsTypes,
+ TypeSolver typeSolver) {
+ return isApplicable(constructor, argumentsTypes, typeSolver, false);
+ }
+
+ private static boolean isApplicable(ResolvedConstructorDeclaration constructor, List<ResolvedType> argumentsTypes,
+ TypeSolver typeSolver, boolean withWildcardTolerance) {
+ if (constructor.hasVariadicParameter()) {
+ int pos = constructor.getNumberOfParams() - 1;
+ if (constructor.getNumberOfParams() == argumentsTypes.size()) {
+ // check if the last value is directly assignable as an array
+ ResolvedType expectedType = constructor.getLastParam().getType();
+ ResolvedType actualType = argumentsTypes.get(pos);
+ if (!expectedType.isAssignableBy(actualType)) {
+ for (ResolvedTypeParameterDeclaration tp : constructor.getTypeParameters()) {
+ expectedType = MethodResolutionLogic.replaceTypeParam(expectedType, tp, typeSolver);
+ }
+ if (!expectedType.isAssignableBy(actualType)) {
+ if (actualType.isArray()
+ && expectedType.isAssignableBy(actualType.asArrayType().getComponentType())) {
+ argumentsTypes.set(pos, actualType.asArrayType().getComponentType());
+ } else {
+ argumentsTypes = groupVariadicParamValues(argumentsTypes, pos,
+ constructor.getLastParam().getType());
+ }
+ }
+ } // else it is already assignable, nothing to do
+ } else {
+ if (pos > argumentsTypes.size()) {
+ return false;
+ }
+ argumentsTypes =
+ groupVariadicParamValues(argumentsTypes, pos, constructor.getLastParam().getType());
+ }
+ }
+
+ if (constructor.getNumberOfParams() != argumentsTypes.size()) {
+ return false;
+ }
+ Map<String, ResolvedType> matchedParameters = new HashMap<>();
+ boolean needForWildCardTolerance = false;
+ for (int i = 0; i < constructor.getNumberOfParams(); i++) {
+ ResolvedType expectedType = constructor.getParam(i).getType();
+ ResolvedType actualType = argumentsTypes.get(i);
+ if ((expectedType.isTypeVariable() && !(expectedType.isWildcard()))
+ && expectedType.asTypeParameter().declaredOnMethod()) {
+ matchedParameters.put(expectedType.asTypeParameter().getName(), actualType);
+ continue;
+ }
+ boolean isAssignableWithoutSubstitution =
+ expectedType.isAssignableBy(actualType) || (constructor.getParam(i).isVariadic()
+ && new ResolvedArrayType(expectedType).isAssignableBy(actualType));
+ if (!isAssignableWithoutSubstitution && expectedType.isReferenceType()
+ && actualType.isReferenceType()) {
+ isAssignableWithoutSubstitution = MethodResolutionLogic.isAssignableMatchTypeParameters(
+ expectedType.asReferenceType(), actualType.asReferenceType(), matchedParameters);
+ }
+ if (!isAssignableWithoutSubstitution) {
+ List<ResolvedTypeParameterDeclaration> typeParameters = constructor.getTypeParameters();
+ typeParameters.addAll(constructor.declaringType().getTypeParameters());
+ for (ResolvedTypeParameterDeclaration tp : typeParameters) {
+ expectedType = MethodResolutionLogic.replaceTypeParam(expectedType, tp, typeSolver);
+ }
+
+ if (!expectedType.isAssignableBy(actualType)) {
+ if (actualType.isWildcard() && withWildcardTolerance && !expectedType.isPrimitive()) {
+ needForWildCardTolerance = true;
+ continue;
+ }
+ if (constructor.hasVariadicParameter() && i == constructor.getNumberOfParams() - 1) {
+ if (new ResolvedArrayType(expectedType).isAssignableBy(actualType)) {
+ continue;
+ }
+ }
+ return false;
+ }
+ }
+ }
+ return !withWildcardTolerance || needForWildCardTolerance;
+ }
+
+ /**
+ * @param constructors we expect the methods to be ordered such that inherited methods are later in the list
+ * @param argumentsTypes
+ * @param typeSolver
+ * @return
+ */
+ public static SymbolReference<ResolvedConstructorDeclaration> findMostApplicable(
+ List<ResolvedConstructorDeclaration> constructors, List<ResolvedType> argumentsTypes, TypeSolver typeSolver) {
+ SymbolReference<ResolvedConstructorDeclaration> res =
+ findMostApplicable(constructors, argumentsTypes, typeSolver, false);
+ if (res.isSolved()) {
+ return res;
+ }
+ return findMostApplicable(constructors, argumentsTypes, typeSolver, true);
+ }
+
+ public static SymbolReference<ResolvedConstructorDeclaration> findMostApplicable(
+ List<ResolvedConstructorDeclaration> constructors, List<ResolvedType> argumentsTypes, TypeSolver typeSolver, boolean wildcardTolerance) {
+ List<ResolvedConstructorDeclaration> applicableConstructors = constructors.stream().filter((m) -> isApplicable(m, argumentsTypes, typeSolver, wildcardTolerance)).collect(Collectors.toList());
+ if (applicableConstructors.isEmpty()) {
+ return SymbolReference.unsolved(ResolvedConstructorDeclaration.class);
+ }
+ if (applicableConstructors.size() == 1) {
+ return SymbolReference.solved(applicableConstructors.get(0));
+ } else {
+ ResolvedConstructorDeclaration winningCandidate = applicableConstructors.get(0);
+ ResolvedConstructorDeclaration other = null;
+ boolean possibleAmbiguity = false;
+ for (int i = 1; i < applicableConstructors.size(); i++) {
+ other = applicableConstructors.get(i);
+ if (isMoreSpecific(winningCandidate, other, typeSolver)) {
+ possibleAmbiguity = false;
+ } else if (isMoreSpecific(other, winningCandidate, typeSolver)) {
+ possibleAmbiguity = false;
+ winningCandidate = other;
+ } else {
+ if (winningCandidate.declaringType().getQualifiedName()
+ .equals(other.declaringType().getQualifiedName())) {
+ possibleAmbiguity = true;
+ } else {
+ // we expect the methods to be ordered such that inherited methods are later in the list
+ }
+ }
+ }
+ if (possibleAmbiguity) {
+ // pick the first exact match if it exists
+ if (!MethodResolutionLogic.isExactMatch(winningCandidate, argumentsTypes)) {
+ if (MethodResolutionLogic.isExactMatch(other, argumentsTypes)) {
+ winningCandidate = other;
+ } else {
+ throw new MethodAmbiguityException("Ambiguous constructor call: cannot find a most applicable constructor: " + winningCandidate + ", " + other);
+ }
+ }
+ }
+ return SymbolReference.solved(winningCandidate);
+ }
+ }
+
+ private static boolean isMoreSpecific(ResolvedConstructorDeclaration constructorA,
+ ResolvedConstructorDeclaration constructorB, TypeSolver typeSolver) {
+ boolean oneMoreSpecificFound = false;
+ if (constructorA.getNumberOfParams() < constructorB.getNumberOfParams()) {
+ return true;
+ }
+ if (constructorA.getNumberOfParams() > constructorB.getNumberOfParams()) {
+ return false;
+ }
+ for (int i = 0; i < constructorA.getNumberOfParams(); i++) {
+ ResolvedType tdA = constructorA.getParam(i).getType();
+ ResolvedType tdB = constructorB.getParam(i).getType();
+ // B is more specific
+ if (tdB.isAssignableBy(tdA) && !tdA.isAssignableBy(tdB)) {
+ oneMoreSpecificFound = true;
+ }
+ // A is more specific
+ if (tdA.isAssignableBy(tdB) && !tdB.isAssignableBy(tdA)) {
+ return false;
+ }
+ // if it matches a variadic and a not variadic I pick the not variadic
+ // FIXME
+ if (i == (constructorA.getNumberOfParams() - 1) && tdA.arrayLevel() > tdB.arrayLevel()) {
+ return true;
+ }
+ }
+ return oneMoreSpecificFound;
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/MethodResolutionLogic.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/MethodResolutionLogic.java
new file mode 100644
index 000000000..01fdfaa62
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/MethodResolutionLogic.java
@@ -0,0 +1,696 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.resolution;
+
+import com.github.javaparser.resolution.MethodAmbiguityException;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.declarations.*;
+import com.github.javaparser.resolution.types.*;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserAnonymousClassDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserClassDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserEnumDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserInterfaceDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserMethodDeclaration;
+import com.github.javaparser.symbolsolver.javassistmodel.JavassistClassDeclaration;
+import com.github.javaparser.symbolsolver.javassistmodel.JavassistEnumDeclaration;
+import com.github.javaparser.symbolsolver.javassistmodel.JavassistInterfaceDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.*;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionClassDeclaration;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionEnumDeclaration;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionInterfaceDeclaration;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class MethodResolutionLogic {
+
+ private static List<ResolvedType> groupVariadicParamValues(List<ResolvedType> argumentsTypes, int startVariadic, ResolvedType variadicType) {
+ List<ResolvedType> res = new ArrayList<>(argumentsTypes.subList(0, startVariadic));
+ List<ResolvedType> variadicValues = argumentsTypes.subList(startVariadic, argumentsTypes.size());
+ if (variadicValues.isEmpty()) {
+ // TODO if there are no variadic values we should default to the bound of the formal type
+ res.add(variadicType);
+ } else {
+ ResolvedType componentType = findCommonType(variadicValues);
+ res.add(new ResolvedArrayType(componentType));
+ }
+ return res;
+ }
+
+ private static ResolvedType findCommonType(List<ResolvedType> variadicValues) {
+ if (variadicValues.isEmpty()) {
+ throw new IllegalArgumentException();
+ }
+ // TODO implement this decently
+ return variadicValues.get(0);
+ }
+
+ public static boolean isApplicable(ResolvedMethodDeclaration method, String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver) {
+ return isApplicable(method, name, argumentsTypes, typeSolver, false);
+ }
+
+ private static boolean isApplicable(ResolvedMethodDeclaration method, String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver, boolean withWildcardTolerance) {
+ if (!method.getName().equals(name)) {
+ return false;
+ }
+ if (method.hasVariadicParameter()) {
+ int pos = method.getNumberOfParams() - 1;
+ if (method.getNumberOfParams() == argumentsTypes.size()) {
+ // check if the last value is directly assignable as an array
+ ResolvedType expectedType = method.getLastParam().getType();
+ ResolvedType actualType = argumentsTypes.get(pos);
+ if (!expectedType.isAssignableBy(actualType)) {
+ for (ResolvedTypeParameterDeclaration tp : method.getTypeParameters()) {
+ expectedType = replaceTypeParam(expectedType, tp, typeSolver);
+ }
+ if (!expectedType.isAssignableBy(actualType)) {
+ if (actualType.isArray() && expectedType.isAssignableBy(actualType.asArrayType().getComponentType())) {
+ argumentsTypes.set(pos, actualType.asArrayType().getComponentType());
+ } else {
+ argumentsTypes = groupVariadicParamValues(argumentsTypes, pos, method.getLastParam().getType());
+ }
+ }
+ } // else it is already assignable, nothing to do
+ } else {
+ if (pos > argumentsTypes.size()) {
+ return false;
+ }
+ argumentsTypes = groupVariadicParamValues(argumentsTypes, pos, method.getLastParam().getType());
+ }
+ }
+
+ if (method.getNumberOfParams() != argumentsTypes.size()) {
+ return false;
+ }
+ Map<String, ResolvedType> matchedParameters = new HashMap<>();
+ boolean needForWildCardTolerance = false;
+ for (int i = 0; i < method.getNumberOfParams(); i++) {
+ ResolvedType expectedType = method.getParam(i).getType();
+ ResolvedType actualType = argumentsTypes.get(i);
+ if ((expectedType.isTypeVariable() && !(expectedType.isWildcard())) && expectedType.asTypeParameter().declaredOnMethod()) {
+ matchedParameters.put(expectedType.asTypeParameter().getName(), actualType);
+ continue;
+ }
+ boolean isAssignableWithoutSubstitution = expectedType.isAssignableBy(actualType) ||
+ (method.getParam(i).isVariadic() && new ResolvedArrayType(expectedType).isAssignableBy(actualType));
+ if (!isAssignableWithoutSubstitution && expectedType.isReferenceType() && actualType.isReferenceType()) {
+ isAssignableWithoutSubstitution = isAssignableMatchTypeParameters(
+ expectedType.asReferenceType(),
+ actualType.asReferenceType(),
+ matchedParameters);
+ }
+ if (!isAssignableWithoutSubstitution) {
+ List<ResolvedTypeParameterDeclaration> typeParameters = method.getTypeParameters();
+ typeParameters.addAll(method.declaringType().getTypeParameters());
+ for (ResolvedTypeParameterDeclaration tp : typeParameters) {
+ expectedType = replaceTypeParam(expectedType, tp, typeSolver);
+ }
+
+ if (!expectedType.isAssignableBy(actualType)) {
+ if (actualType.isWildcard() && withWildcardTolerance && !expectedType.isPrimitive()) {
+ needForWildCardTolerance = true;
+ continue;
+ }
+ if (method.hasVariadicParameter() && i == method.getNumberOfParams() - 1) {
+ if (new ResolvedArrayType(expectedType).isAssignableBy(actualType)) {
+ continue;
+ }
+ }
+ return false;
+ }
+ }
+ }
+ return !withWildcardTolerance || needForWildCardTolerance;
+ }
+
+ public static boolean isAssignableMatchTypeParameters(ResolvedType expected, ResolvedType actual,
+ Map<String, ResolvedType> matchedParameters) {
+ if (expected.isReferenceType() && actual.isReferenceType()) {
+ return isAssignableMatchTypeParameters(expected.asReferenceType(), actual.asReferenceType(), matchedParameters);
+ } else if (expected.isTypeVariable()) {
+ matchedParameters.put(expected.asTypeParameter().getName(), actual);
+ return true;
+ } else {
+ throw new UnsupportedOperationException(expected.getClass().getCanonicalName() + " " + actual.getClass().getCanonicalName());
+ }
+ }
+
+ public static boolean isAssignableMatchTypeParameters(ResolvedReferenceType expected, ResolvedReferenceType actual,
+ Map<String, ResolvedType> matchedParameters) {
+ if (actual.getQualifiedName().equals(expected.getQualifiedName())) {
+ return isAssignableMatchTypeParametersMatchingQName(expected, actual, matchedParameters);
+ } else {
+ List<ResolvedReferenceType> ancestors = actual.getAllAncestors();
+ for (ResolvedReferenceType ancestor : ancestors) {
+ if (isAssignableMatchTypeParametersMatchingQName(expected, ancestor, matchedParameters)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static boolean isAssignableMatchTypeParametersMatchingQName(ResolvedReferenceType expected, ResolvedReferenceType actual,
+ Map<String, ResolvedType> matchedParameters) {
+
+ if (!expected.getQualifiedName().equals(actual.getQualifiedName())) {
+ return false;
+ }
+ if (expected.typeParametersValues().size() != actual.typeParametersValues().size()) {
+ throw new UnsupportedOperationException();
+ //return true;
+ }
+ for (int i = 0; i < expected.typeParametersValues().size(); i++) {
+ ResolvedType expectedParam = expected.typeParametersValues().get(i);
+ ResolvedType actualParam = actual.typeParametersValues().get(i);
+
+ // In the case of nested parameterizations eg. List<R> <-> List<Integer>
+ // we should peel off one layer and ensure R <-> Integer
+ if (expectedParam.isReferenceType() && actualParam.isReferenceType()){
+ ResolvedReferenceType r1 = expectedParam.asReferenceType();
+ ResolvedReferenceType r2 = actualParam.asReferenceType();
+
+ return isAssignableMatchTypeParametersMatchingQName(r1, r2, matchedParameters);
+ }
+
+ if (expectedParam.isTypeVariable()) {
+ String expectedParamName = expectedParam.asTypeParameter().getName();
+ if (!actualParam.isTypeVariable() || !actualParam.asTypeParameter().getName().equals(expectedParamName)) {
+ return matchTypeVariable(expectedParam.asTypeVariable(), actualParam, matchedParameters);
+ }
+ } else if (expectedParam.isReferenceType()) {
+ if (actualParam.isTypeVariable()) {
+ return matchTypeVariable(actualParam.asTypeVariable(), expectedParam, matchedParameters);
+ } else if (!expectedParam.equals(actualParam)) {
+ return false;
+ }
+ } else if (expectedParam.isWildcard()) {
+ if (expectedParam.asWildcard().isExtends()) {
+ return isAssignableMatchTypeParameters(expectedParam.asWildcard().getBoundedType(), actual, matchedParameters);
+ }
+ // TODO verify super bound
+ return true;
+ } else {
+ throw new UnsupportedOperationException(expectedParam.describe());
+ }
+ }
+ return true;
+ }
+
+ private static boolean matchTypeVariable(ResolvedTypeVariable typeVariable, ResolvedType type, Map<String, ResolvedType> matchedParameters) {
+ String typeParameterName = typeVariable.asTypeParameter().getName();
+ if (matchedParameters.containsKey(typeParameterName)) {
+ ResolvedType matchedParameter = matchedParameters.get(typeParameterName);
+ if (matchedParameter.isAssignableBy(type)) {
+ return true;
+ } else if (type.isAssignableBy(matchedParameter)) {
+ // update matchedParameters to contain the more general type
+ matchedParameters.put(typeParameterName, type);
+ return true;
+ }
+ return false;
+ } else {
+ matchedParameters.put(typeParameterName, type);
+ }
+ return true;
+ }
+
+ public static ResolvedType replaceTypeParam(ResolvedType type, ResolvedTypeParameterDeclaration tp, TypeSolver typeSolver) {
+ if (type.isTypeVariable()) {
+ if (type.describe().equals(tp.getName())) {
+ List<ResolvedTypeParameterDeclaration.Bound> bounds = tp.getBounds();
+ if (bounds.size() > 1) {
+ throw new UnsupportedOperationException();
+ } else if (bounds.size() == 1) {
+ return bounds.get(0).getType();
+ } else {
+ return new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver);
+ }
+ }
+ return type;
+ } else if (type.isPrimitive()) {
+ return type;
+ } else if (type.isArray()) {
+ return new ResolvedArrayType(replaceTypeParam(type.asArrayType().getComponentType(), tp, typeSolver));
+ } else if (type.isReferenceType()) {
+ ResolvedReferenceType result = type.asReferenceType();
+ result = result.transformTypeParameters(typeParam -> replaceTypeParam(typeParam, tp, typeSolver)).asReferenceType();
+ return result;
+ } else if (type.isWildcard()) {
+ if (type.describe().equals(tp.getName())) {
+ List<ResolvedTypeParameterDeclaration.Bound> bounds = tp.getBounds();
+ if (bounds.size() > 1) {
+ throw new UnsupportedOperationException();
+ } else if (bounds.size() == 1) {
+ return bounds.get(0).getType();
+ } else {
+ return new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver);
+ }
+ }
+ return type;
+ } else {
+ throw new UnsupportedOperationException("Replacing " + type + ", param " + tp + " with " + type.getClass().getCanonicalName());
+ }
+ }
+
+ public static boolean isApplicable(MethodUsage method, String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver) {
+ if (!method.getName().equals(name)) {
+ return false;
+ }
+ // TODO Consider varargs
+ if (method.getNoParams() != argumentsTypes.size()) {
+ return false;
+ }
+ for (int i = 0; i < method.getNoParams(); i++) {
+ ResolvedType expectedType = method.getParamType(i);
+ ResolvedType expectedTypeWithoutSubstitutions = expectedType;
+ ResolvedType expectedTypeWithInference = method.getParamType(i);
+ ResolvedType actualType = argumentsTypes.get(i);
+
+ List<ResolvedTypeParameterDeclaration> typeParameters = method.getDeclaration().getTypeParameters();
+ typeParameters.addAll(method.declaringType().getTypeParameters());
+
+ if (expectedType.describe().equals(actualType.describe())){
+ return true;
+ }
+
+ Map<ResolvedTypeParameterDeclaration, ResolvedType> derivedValues = new HashMap<>();
+ for (int j = 0; j < method.getParamTypes().size(); j++) {
+ ResolvedParameterDeclaration parameter = method.getDeclaration().getParam(i);
+ ResolvedType parameterType = parameter.getType();
+ if (parameter.isVariadic()) {
+ parameterType = parameterType.asArrayType().getComponentType();
+ }
+ inferTypes(argumentsTypes.get(j), parameterType, derivedValues);
+ }
+
+ for (Map.Entry<ResolvedTypeParameterDeclaration, ResolvedType> entry : derivedValues.entrySet()){
+ ResolvedTypeParameterDeclaration tp = entry.getKey();
+ expectedTypeWithInference = expectedTypeWithInference.replaceTypeVariables(tp, entry.getValue());
+ }
+
+ for (ResolvedTypeParameterDeclaration tp : typeParameters) {
+ if (tp.getBounds().isEmpty()) {
+ //expectedType = expectedType.replaceTypeVariables(tp.getName(), new ReferenceTypeUsageImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver));
+ expectedType = expectedType.replaceTypeVariables(tp, ResolvedWildcard.extendsBound(new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver)));
+ } else if (tp.getBounds().size() == 1) {
+ ResolvedTypeParameterDeclaration.Bound bound = tp.getBounds().get(0);
+ if (bound.isExtends()) {
+ //expectedType = expectedType.replaceTypeVariables(tp.getName(), bound.getType());
+ expectedType = expectedType.replaceTypeVariables(tp, ResolvedWildcard.extendsBound(bound.getType()));
+ } else {
+ //expectedType = expectedType.replaceTypeVariables(tp.getName(), new ReferenceTypeUsageImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver));
+ expectedType = expectedType.replaceTypeVariables(tp, ResolvedWildcard.superBound(bound.getType()));
+ }
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+ ResolvedType expectedType2 = expectedTypeWithoutSubstitutions;
+ for (ResolvedTypeParameterDeclaration tp : typeParameters) {
+ if (tp.getBounds().isEmpty()) {
+ expectedType2 = expectedType2.replaceTypeVariables(tp, new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver));
+ } else if (tp.getBounds().size() == 1) {
+ ResolvedTypeParameterDeclaration.Bound bound = tp.getBounds().get(0);
+ if (bound.isExtends()) {
+ expectedType2 = expectedType2.replaceTypeVariables(tp, bound.getType());
+ } else {
+ expectedType2 = expectedType2.replaceTypeVariables(tp, new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver));
+ }
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+ if (!expectedType.isAssignableBy(actualType)
+ && !expectedType2.isAssignableBy(actualType)
+ && !expectedTypeWithInference.isAssignableBy(actualType)
+ && !expectedTypeWithoutSubstitutions.isAssignableBy(actualType)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static List<ResolvedMethodDeclaration> getMethodsWithoutDuplicates(List<ResolvedMethodDeclaration> methods) {
+ Set<ResolvedMethodDeclaration> s = new TreeSet<>((m1, m2) -> {
+ if (m1 instanceof JavaParserMethodDeclaration && m2 instanceof JavaParserMethodDeclaration &&
+ ((JavaParserMethodDeclaration) m1).getWrappedNode().equals(((JavaParserMethodDeclaration) m2).getWrappedNode())) {
+ return 0;
+ }
+ return 1;
+ });
+ s.addAll(methods);
+ List<ResolvedMethodDeclaration> res = new ArrayList<>();
+ Set<String> usedSignatures = new HashSet<>();
+ for (ResolvedMethodDeclaration md : methods) {
+ String signature = md.getQualifiedSignature();
+ if (!usedSignatures.contains(signature)) {
+ usedSignatures.add(signature);
+ res.add(md);
+ }
+ }
+ return res;
+ }
+
+ /**
+ * @param methods we expect the methods to be ordered such that inherited methods are later in the list
+ * @param name
+ * @param argumentsTypes
+ * @param typeSolver
+ * @return
+ */
+ public static SymbolReference<ResolvedMethodDeclaration> findMostApplicable(List<ResolvedMethodDeclaration> methods,
+ String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver) {
+ SymbolReference<ResolvedMethodDeclaration> res = findMostApplicable(methods, name, argumentsTypes, typeSolver, false);
+ if (res.isSolved()) {
+ return res;
+ }
+ return findMostApplicable(methods, name, argumentsTypes, typeSolver, true);
+ }
+
+ public static SymbolReference<ResolvedMethodDeclaration> findMostApplicable(List<ResolvedMethodDeclaration> methods,
+ String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver, boolean wildcardTolerance) {
+ List<ResolvedMethodDeclaration> applicableMethods = getMethodsWithoutDuplicates(methods).stream().filter((m) -> isApplicable(m, name, argumentsTypes, typeSolver, wildcardTolerance)).collect(Collectors.toList());
+ if (applicableMethods.isEmpty()) {
+ return SymbolReference.unsolved(ResolvedMethodDeclaration.class);
+ }
+
+ if (applicableMethods.size() > 1) {
+ List<Integer> nullParamIndexes = new ArrayList<>();
+ for (int i = 0; i < argumentsTypes.size(); i++) {
+ if (argumentsTypes.get(i).isNull()) {
+ nullParamIndexes.add(i);
+ }
+ }
+ if (!nullParamIndexes.isEmpty()) {
+ // remove method with array param if a non array exists and arg is null
+ Set<ResolvedMethodDeclaration> removeCandidates = new HashSet<>();
+ for (Integer nullParamIndex: nullParamIndexes) {
+ for (ResolvedMethodDeclaration methDecl: applicableMethods) {
+ if (methDecl.getParam(nullParamIndex.intValue()).getType().isArray()) {
+ removeCandidates.add(methDecl);
+ }
+ }
+ }
+ if (!removeCandidates.isEmpty() && removeCandidates.size() < applicableMethods.size()) {
+ applicableMethods.removeAll(removeCandidates);
+ }
+ }
+ }
+ if (applicableMethods.size() == 1) {
+ return SymbolReference.solved(applicableMethods.get(0));
+ } else {
+ ResolvedMethodDeclaration winningCandidate = applicableMethods.get(0);
+ ResolvedMethodDeclaration other = null;
+ boolean possibleAmbiguity = false;
+ for (int i = 1; i < applicableMethods.size(); i++) {
+ other = applicableMethods.get(i);
+ if (isMoreSpecific(winningCandidate, other, argumentsTypes, typeSolver)) {
+ possibleAmbiguity = false;
+ } else if (isMoreSpecific(other, winningCandidate, argumentsTypes, typeSolver)) {
+ possibleAmbiguity = false;
+ winningCandidate = other;
+ } else {
+ if (winningCandidate.declaringType().getQualifiedName().equals(other.declaringType().getQualifiedName())) {
+ possibleAmbiguity = true;
+ } else {
+ // we expect the methods to be ordered such that inherited methods are later in the list
+ }
+ }
+ }
+ if (possibleAmbiguity) {
+ // pick the first exact match if it exists
+ if (!isExactMatch(winningCandidate, argumentsTypes)) {
+ if (isExactMatch(other, argumentsTypes)) {
+ winningCandidate = other;
+ } else {
+ throw new MethodAmbiguityException("Ambiguous method call: cannot find a most applicable method: " + winningCandidate + ", " + other);
+ }
+ }
+ }
+ return SymbolReference.solved(winningCandidate);
+ }
+ }
+
+ protected static boolean isExactMatch(ResolvedMethodLikeDeclaration method, List<ResolvedType> argumentsTypes) {
+ for (int i = 0; i < method.getNumberOfParams(); i++) {
+ if (!method.getParam(i).getType().equals(argumentsTypes.get(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean isMoreSpecific(ResolvedMethodDeclaration methodA, ResolvedMethodDeclaration methodB,
+ List<ResolvedType> argumentTypes, TypeSolver typeSolver) {
+ boolean oneMoreSpecificFound = false;
+ if (methodA.getNumberOfParams() < methodB.getNumberOfParams()) {
+ return true;
+ }
+ if (methodA.getNumberOfParams() > methodB.getNumberOfParams()) {
+ return false;
+ }
+ for (int i = 0; i < methodA.getNumberOfParams(); i++) {
+ ResolvedType tdA = methodA.getParam(i).getType();
+ ResolvedType tdB = methodB.getParam(i).getType();
+ // B is more specific
+ if (tdB.isAssignableBy(tdA) && !tdA.isAssignableBy(tdB)) {
+ oneMoreSpecificFound = true;
+ }
+ // A is more specific
+ if (tdA.isAssignableBy(tdB) && !tdB.isAssignableBy(tdA)) {
+ return false;
+ }
+ }
+
+ if (!oneMoreSpecificFound) {
+ int lastIndex = argumentTypes.size() - 1;
+
+ if (methodA.hasVariadicParameter() && !methodB.hasVariadicParameter()) {
+ // if the last argument is an array then m1 is more specific
+ if (argumentTypes.get(lastIndex).isArray()) {
+ return true;
+ }
+
+ if (!argumentTypes.get(lastIndex).isArray()) {
+ return false;
+ }
+ }
+ if (!methodA.hasVariadicParameter() && methodB.hasVariadicParameter()) {
+ // if the last argument is an array and m1 is not variadic then
+ // it is not more specific
+ if (argumentTypes.get(lastIndex).isArray()) {
+ return false;
+ }
+
+ if (!argumentTypes.get(lastIndex).isArray()) {
+ return true;
+ }
+ }
+ }
+
+ return oneMoreSpecificFound;
+ }
+
+ private static boolean isMoreSpecific(MethodUsage methodA, MethodUsage methodB, TypeSolver typeSolver) {
+ boolean oneMoreSpecificFound = false;
+ for (int i = 0; i < methodA.getNoParams(); i++) {
+ ResolvedType tdA = methodA.getParamType(i);
+ ResolvedType tdB = methodB.getParamType(i);
+
+ boolean aIsAssignableByB = tdA.isAssignableBy(tdB);
+ boolean bIsAssignableByA = tdB.isAssignableBy(tdA);
+
+ // B is more specific
+ if (bIsAssignableByA && !aIsAssignableByB) {
+ oneMoreSpecificFound = true;
+ }
+ // A is more specific
+ if (aIsAssignableByB && !bIsAssignableByA) {
+ return false;
+ }
+ }
+ return oneMoreSpecificFound;
+ }
+
+ public static Optional<MethodUsage> findMostApplicableUsage(List<MethodUsage> methods, String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver) {
+ List<MethodUsage> applicableMethods = methods.stream().filter((m) -> isApplicable(m, name, argumentsTypes, typeSolver)).collect(Collectors.toList());
+
+ if (applicableMethods.isEmpty()) {
+ return Optional.empty();
+ }
+ if (applicableMethods.size() == 1) {
+ return Optional.of(applicableMethods.get(0));
+ } else {
+ MethodUsage winningCandidate = applicableMethods.get(0);
+ for (int i = 1; i < applicableMethods.size(); i++) {
+ MethodUsage other = applicableMethods.get(i);
+ if (isMoreSpecific(winningCandidate, other, typeSolver)) {
+ // nothing to do
+ } else if (isMoreSpecific(other, winningCandidate, typeSolver)) {
+ winningCandidate = other;
+ } else {
+ if (winningCandidate.declaringType().getQualifiedName().equals(other.declaringType().getQualifiedName())) {
+ if (!areOverride(winningCandidate, other)) {
+ throw new MethodAmbiguityException("Ambiguous method call: cannot find a most applicable method: " + winningCandidate + ", " + other + ". First declared in " + winningCandidate.declaringType().getQualifiedName());
+ }
+ } else {
+ // we expect the methods to be ordered such that inherited methods are later in the list
+ //throw new UnsupportedOperationException();
+ }
+ }
+ }
+ return Optional.of(winningCandidate);
+ }
+ }
+
+ private static boolean areOverride(MethodUsage winningCandidate, MethodUsage other) {
+ if (!winningCandidate.getName().equals(other.getName())) {
+ return false;
+ }
+ if (winningCandidate.getNoParams() != other.getNoParams()) {
+ return false;
+ }
+ for (int i = 0; i < winningCandidate.getNoParams(); i++) {
+ if (!winningCandidate.getParamTypes().get(i).equals(other.getParamTypes().get(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static SymbolReference<ResolvedMethodDeclaration> solveMethodInType(ResolvedTypeDeclaration typeDeclaration,
+ String name, List<ResolvedType> argumentsTypes, TypeSolver typeSolver) {
+ return solveMethodInType(typeDeclaration, name, argumentsTypes, false, typeSolver);
+ }
+
+ /**
+ * Replace TypeDeclaration.solveMethod
+ *
+ * @param typeDeclaration
+ * @param name
+ * @param argumentsTypes
+ * @param staticOnly
+ * @return
+ */
+ public static SymbolReference<ResolvedMethodDeclaration> solveMethodInType(ResolvedTypeDeclaration typeDeclaration,
+ String name, List<ResolvedType> argumentsTypes, boolean staticOnly,
+ TypeSolver typeSolver) {
+ if (typeDeclaration instanceof JavaParserClassDeclaration) {
+ Context ctx = ((JavaParserClassDeclaration) typeDeclaration).getContext();
+ return ctx.solveMethod(name, argumentsTypes, staticOnly, typeSolver);
+ }
+ if (typeDeclaration instanceof JavaParserInterfaceDeclaration) {
+ Context ctx = ((JavaParserInterfaceDeclaration) typeDeclaration).getContext();
+ return ctx.solveMethod(name, argumentsTypes, staticOnly, typeSolver);
+ }
+ if (typeDeclaration instanceof JavaParserEnumDeclaration) {
+ if (name.equals("values") && argumentsTypes.isEmpty()) {
+ return SymbolReference.solved(new JavaParserEnumDeclaration.ValuesMethod((JavaParserEnumDeclaration) typeDeclaration, typeSolver));
+ }
+ Context ctx = ((JavaParserEnumDeclaration) typeDeclaration).getContext();
+ return ctx.solveMethod(name, argumentsTypes, staticOnly, typeSolver);
+ }
+ if (typeDeclaration instanceof JavaParserAnonymousClassDeclaration) {
+ Context ctx = ((JavaParserAnonymousClassDeclaration) typeDeclaration).getContext();
+ return ctx.solveMethod(name, argumentsTypes, staticOnly, typeSolver);
+ }
+ if (typeDeclaration instanceof ReflectionClassDeclaration) {
+ return ((ReflectionClassDeclaration) typeDeclaration).solveMethod(name, argumentsTypes, staticOnly);
+ }
+ if (typeDeclaration instanceof ReflectionInterfaceDeclaration) {
+ return ((ReflectionInterfaceDeclaration) typeDeclaration).solveMethod(name, argumentsTypes, staticOnly);
+ }
+ if (typeDeclaration instanceof ReflectionEnumDeclaration) {
+ return ((ReflectionEnumDeclaration) typeDeclaration).solveMethod(name, argumentsTypes, staticOnly);
+ }
+ if (typeDeclaration instanceof JavassistInterfaceDeclaration) {
+ return ((JavassistInterfaceDeclaration) typeDeclaration).solveMethod(name, argumentsTypes, staticOnly);
+ }
+ if (typeDeclaration instanceof JavassistClassDeclaration) {
+ return ((JavassistClassDeclaration) typeDeclaration).solveMethod(name, argumentsTypes, staticOnly);
+ }
+ if (typeDeclaration instanceof JavassistEnumDeclaration) {
+ return ((JavassistEnumDeclaration) typeDeclaration).solveMethod(name, argumentsTypes, staticOnly);
+ }
+ throw new UnsupportedOperationException(typeDeclaration.getClass().getCanonicalName());
+ }
+
+ private static void inferTypes(ResolvedType source, ResolvedType target, Map<ResolvedTypeParameterDeclaration, ResolvedType> mappings) {
+
+
+ if (source.equals(target)) {
+ return;
+ }
+ if (source.isReferenceType() && target.isReferenceType()) {
+ ResolvedReferenceType sourceRefType = source.asReferenceType();
+ ResolvedReferenceType targetRefType = target.asReferenceType();
+ if (sourceRefType.getQualifiedName().equals(targetRefType.getQualifiedName())) {
+ if (!sourceRefType.isRawType() && !targetRefType.isRawType()) {
+ for (int i = 0; i < sourceRefType.typeParametersValues().size(); i++) {
+ inferTypes(sourceRefType.typeParametersValues().get(i), targetRefType.typeParametersValues().get(i), mappings);
+ }
+ }
+ }
+ return;
+ }
+ if (source.isReferenceType() && target.isWildcard()) {
+ if (target.asWildcard().isBounded()) {
+ inferTypes(source, target.asWildcard().getBoundedType(), mappings);
+ return;
+ }
+ return;
+ }
+ if (source.isWildcard() && target.isWildcard()) {
+ return;
+ }
+ if (source.isReferenceType() && target.isTypeVariable()) {
+ mappings.put(target.asTypeParameter(), source);
+ return;
+ }
+
+ if (source.isWildcard() && target.isReferenceType()){
+ if (source.asWildcard().isBounded()){
+ inferTypes(source.asWildcard().getBoundedType(), target, mappings);
+ }
+ return;
+ }
+
+ if (source.isWildcard() && target.isTypeVariable()) {
+ mappings.put(target.asTypeParameter(), source);
+ return;
+ }
+ if (source.isTypeVariable() && target.isTypeVariable()) {
+ mappings.put(target.asTypeParameter(), source);
+ return;
+ }
+ if (source.isPrimitive() || target.isPrimitive()) {
+ return;
+ }
+ if (source.isNull()) {
+ return;
+ }
+ }
+
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/SymbolDeclarator.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/SymbolDeclarator.java
new file mode 100644
index 000000000..322096f16
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/SymbolDeclarator.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.resolution;
+
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+
+import java.util.List;
+
+/**
+ * @author Federico Tomassetti
+ */
+public interface SymbolDeclarator {
+
+ List<ResolvedValueDeclaration> getSymbolDeclarations();
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/SymbolSolver.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/SymbolSolver.java
new file mode 100644
index 000000000..ee4a735e8
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/SymbolSolver.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.resolution;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.type.ClassOrInterfaceType;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.UnsolvedSymbolException;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedValueDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.core.resolution.Context;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserClassDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserEnumDeclaration;
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserInterfaceDeclaration;
+import com.github.javaparser.symbolsolver.javassistmodel.JavassistClassDeclaration;
+import com.github.javaparser.symbolsolver.javassistmodel.JavassistEnumDeclaration;
+import com.github.javaparser.symbolsolver.javassistmodel.JavassistInterfaceDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.resolution.Value;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionClassDeclaration;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionInterfaceDeclaration;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class SymbolSolver {
+
+ private TypeSolver typeSolver;
+
+ public SymbolSolver(TypeSolver typeSolver) {
+ if (typeSolver == null) throw new IllegalArgumentException();
+
+ this.typeSolver = typeSolver;
+ }
+
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, Context context) {
+ return context.solveSymbol(name, typeSolver);
+ }
+
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, Node node) {
+ return solveSymbol(name, JavaParserFactory.getContext(node, typeSolver));
+ }
+
+ public Optional<Value> solveSymbolAsValue(String name, Context context) {
+ return context.solveSymbolAsValue(name, typeSolver);
+ }
+
+ public Optional<Value> solveSymbolAsValue(String name, Node node) {
+ Context context = JavaParserFactory.getContext(node, typeSolver);
+ return solveSymbolAsValue(name, context);
+ }
+
+ public SymbolReference<? extends ResolvedTypeDeclaration> solveType(String name, Context context) {
+ return context.solveType(name, typeSolver);
+ }
+
+ public SymbolReference<? extends ResolvedTypeDeclaration> solveType(String name, Node node) {
+ return solveType(name, JavaParserFactory.getContext(node, typeSolver));
+ }
+
+ public MethodUsage solveMethod(String methodName, List<ResolvedType> argumentsTypes, Context context) {
+ SymbolReference<ResolvedMethodDeclaration> decl = context.solveMethod(methodName, argumentsTypes, false, typeSolver);
+ if (!decl.isSolved()) {
+ throw new UnsolvedSymbolException(context.toString(), methodName);
+ }
+ return new MethodUsage(decl.getCorrespondingDeclaration());
+ }
+
+ public MethodUsage solveMethod(String methodName, List<ResolvedType> argumentsTypes, Node node) {
+ return solveMethod(methodName, argumentsTypes, JavaParserFactory.getContext(node, typeSolver));
+ }
+
+ public ResolvedTypeDeclaration solveType(com.github.javaparser.ast.type.Type type) {
+ if (type instanceof ClassOrInterfaceType) {
+
+ // FIXME should call typesolver here!
+
+ String name = ((ClassOrInterfaceType) type).getName().getId();
+ SymbolReference<ResolvedTypeDeclaration> ref = JavaParserFactory.getContext(type, typeSolver).solveType(name, typeSolver);
+ if (!ref.isSolved()) {
+ throw new UnsolvedSymbolException(JavaParserFactory.getContext(type, typeSolver).toString(), name);
+ }
+ return ref.getCorrespondingDeclaration();
+ } else {
+ throw new UnsupportedOperationException(type.getClass().getCanonicalName());
+ }
+ }
+
+ public ResolvedType solveTypeUsage(String name, Context context) {
+ Optional<ResolvedType> genericType = context.solveGenericType(name, typeSolver);
+ if (genericType.isPresent()) {
+ return genericType.get();
+ }
+ ResolvedReferenceTypeDeclaration typeDeclaration = typeSolver.solveType(name);
+ return new ReferenceTypeImpl(typeDeclaration, typeSolver);
+ }
+
+ /**
+ * Solve any possible visible symbols including: fields, internal types, type variables, the type itself or its
+ * containers.
+ * <p>
+ * It should contain its own private fields but not inherited private fields.
+ */
+ public SymbolReference<? extends ResolvedValueDeclaration> solveSymbolInType(ResolvedTypeDeclaration typeDeclaration, String name) {
+ if (typeDeclaration instanceof JavaParserClassDeclaration) {
+ Context ctx = ((JavaParserClassDeclaration) typeDeclaration).getContext();
+ return ctx.solveSymbol(name, typeSolver);
+ }
+ if (typeDeclaration instanceof JavaParserInterfaceDeclaration) {
+ Context ctx = ((JavaParserInterfaceDeclaration) typeDeclaration).getContext();
+ return ctx.solveSymbol(name, typeSolver);
+ }
+ if (typeDeclaration instanceof JavaParserEnumDeclaration) {
+ Context ctx = ((JavaParserEnumDeclaration) typeDeclaration).getContext();
+ return ctx.solveSymbol(name, typeSolver);
+ }
+ if (typeDeclaration instanceof ReflectionClassDeclaration) {
+ return ((ReflectionClassDeclaration) typeDeclaration).solveSymbol(name, typeSolver);
+ }
+ if (typeDeclaration instanceof ReflectionInterfaceDeclaration) {
+ return ((ReflectionInterfaceDeclaration) typeDeclaration).solveSymbol(name, typeSolver);
+ }
+ if (typeDeclaration instanceof JavassistClassDeclaration) {
+ return ((JavassistClassDeclaration) typeDeclaration).solveSymbol(name, typeSolver);
+ }
+ if (typeDeclaration instanceof JavassistEnumDeclaration) {
+ return ((JavassistEnumDeclaration) typeDeclaration).solveSymbol(name, typeSolver);
+ }
+ if (typeDeclaration instanceof JavassistInterfaceDeclaration) {
+ return ((JavassistInterfaceDeclaration) typeDeclaration).solveSymbol(name, typeSolver);
+ }
+ return SymbolReference.unsolved(ResolvedValueDeclaration.class);
+ }
+
+ /**
+ * Try to solve a symbol just in the declaration, it does not delegate to the container.
+ */
+ @Deprecated
+ public SymbolReference<ResolvedTypeDeclaration> solveTypeInType(ResolvedTypeDeclaration typeDeclaration, String name) {
+ if (typeDeclaration instanceof JavaParserClassDeclaration) {
+ return ((JavaParserClassDeclaration) typeDeclaration).solveType(name, typeSolver);
+ }
+ if (typeDeclaration instanceof JavaParserInterfaceDeclaration) {
+ return ((JavaParserInterfaceDeclaration) typeDeclaration).solveType(name, typeSolver);
+ }
+ return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/Bound.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/Bound.java
new file mode 100644
index 000000000..79c239b68
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/Bound.java
@@ -0,0 +1,106 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference;
+
+import com.github.javaparser.symbolsolver.resolution.typeinference.bounds.FalseBound;
+
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Bounds are defined for Inference Variables.
+ *
+ * @author Federico Tomassetti
+ */
+public abstract class Bound {
+
+ ///
+ /// Creation of bounds
+ ///
+
+ static Bound falseBound() {
+ return FalseBound.getInstance();
+ }
+
+ ///
+ /// Satisfiability
+ ///
+
+ /**
+ * A bound is satisfied by an inference variable substitution if, after applying the substitution,
+ * the assertion is true.
+ */
+ public abstract boolean isSatisfied(InferenceVariableSubstitution inferenceVariableSubstitution);
+
+ ///
+ /// Classification of bounds
+ ///
+
+ /**
+ * Given a bound of the form α = T or T = α, we say T is an instantiation of α.
+ *
+ * Return empty if it is not an instantiation. Otherwise it returns the variable of which this is an
+ * instantiation.
+ */
+ public Optional<Instantiation> isAnInstantiation() {
+ return Optional.empty();
+ }
+
+ boolean isAnInstantiationFor(InferenceVariable v) {
+ return isAnInstantiation().isPresent() && isAnInstantiation().get().getInferenceVariable().equals(v);
+ }
+
+ /**
+ * Given a bound of the form α <: T, we say T is a proper upper bound of α.
+ *
+ * Return empty if it is not a proper upper bound. Otherwise it returns the variable of which this is an
+ * proper upper bound.
+ */
+ public Optional<ProperUpperBound> isProperUpperBound() {
+ return Optional.empty();
+ }
+
+ /**
+ * Given a bound of the form T <: α, we say T is a proper lower bound of α.
+ *
+ * Return empty if it is not a proper lower bound. Otherwise it returns the variable of which this is an
+ * proper lower bound.
+ */
+ public Optional<ProperLowerBound> isProperLowerBound() {
+ return Optional.empty();
+ }
+
+ Optional<ProperLowerBound> isProperLowerBoundFor(InferenceVariable inferenceVariable) {
+ Optional<ProperLowerBound> partial = isProperLowerBound();
+ if (partial.isPresent() && partial.get().getInferenceVariable().equals(inferenceVariable)) {
+ return partial;
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ Optional<ProperUpperBound> isProperUpperBoundFor(InferenceVariable inferenceVariable) {
+ Optional<ProperUpperBound> partial = isProperUpperBound();
+ if (partial.isPresent() && partial.get().getInferenceVariable().equals(inferenceVariable)) {
+ return partial;
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ /**
+ * Other bounds relate two inference variables, or an inference variable to a type that contains inference
+ * variables. Such bounds, of the form S = T or S <: T, are called dependencies.
+ */
+ public boolean isADependency() {
+ return false;
+ }
+
+ boolean isThrowsBoundOn(InferenceVariable inferenceVariable) {
+ return false;
+ }
+
+ ///
+ /// Other methods
+ ///
+
+ public abstract Set<InferenceVariable> usedInferenceVariables();
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/BoundSet.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/BoundSet.java
new file mode 100644
index 000000000..59e73b190
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/BoundSet.java
@@ -0,0 +1,804 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference;
+
+import com.github.javaparser.resolution.types.ResolvedReferenceType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+import com.github.javaparser.symbolsolver.resolution.typeinference.bounds.*;
+import com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas.TypeSameAsType;
+import com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas.TypeSubtypeOfType;
+import com.github.javaparser.utils.Pair;
+
+import java.util.*;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.*;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class BoundSet {
+
+ private List<Bound> bounds = new LinkedList<>();
+
+ private static final BoundSet EMPTY = new BoundSet();
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ BoundSet boundSet = (BoundSet) o;
+
+ return new HashSet<>(bounds).equals(new HashSet<>(boundSet.bounds));
+ }
+
+ @Override
+ public int hashCode() {
+ return bounds.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "BoundSet{" +
+ "bounds=" + bounds +
+ '}';
+ }
+
+ /**
+
+ * It is sometimes convenient to refer to an empty bound set with the symbol true; this is merely out of
+ * convenience, and the two are interchangeable.
+ */
+ public boolean isTrue() {
+ return bounds.isEmpty();
+ }
+
+ public static BoundSet empty() {
+ return EMPTY;
+ }
+
+ public BoundSet withBound(Bound bound) {
+ if (this.bounds.contains(bound)) {
+ return this;
+ }
+ BoundSet boundSet = new BoundSet();
+ boundSet.bounds.addAll(this.bounds);
+ boundSet.bounds.add(bound);
+ return boundSet;
+ }
+
+ private Optional<Pair<SameAsBound, SameAsBound>> findPairSameAs(Predicate<Pair<SameAsBound, SameAsBound>> condition) {
+ for (int i=0;i<bounds.size();i++) {
+ Bound bi = bounds.get(i);
+ if (bi instanceof SameAsBound) {
+ SameAsBound si = (SameAsBound)bi;
+ for (int j = i + 1; j < bounds.size(); j++) {
+ Bound bj = bounds.get(j);
+ if (bj instanceof SameAsBound) {
+ SameAsBound sj = (SameAsBound)bj;
+ Pair<SameAsBound, SameAsBound> pair = new Pair<SameAsBound, SameAsBound>(si, sj);
+ if (condition.test(pair)) {
+ return Optional.of(pair);
+ }
+ }
+ }
+ }
+ }
+ return Optional.empty();
+ }
+
+ public boolean isEmpty() {
+ return bounds.isEmpty();
+ }
+
+ interface Processor<B1 extends Bound, B2 extends Bound, R> {
+ R process(B1 a, B2 b, R initialValue);
+ }
+
+ private <T> T forEachPairSameAs(Processor<SameAsBound, SameAsBound, T> processor, T initialValue) {
+ T currentValue = initialValue;
+ for (int i=0;i<bounds.size();i++) {
+ Bound bi = bounds.get(i);
+ if (bi instanceof SameAsBound) {
+ SameAsBound si = (SameAsBound)bi;
+ for (int j = i + 1; j < bounds.size(); j++) {
+ Bound bj = bounds.get(j);
+ if (bj instanceof SameAsBound) {
+ SameAsBound sj = (SameAsBound)bj;
+ currentValue = processor.process(si, sj, currentValue);
+ }
+ }
+ }
+ }
+ return currentValue;
+ }
+
+ private <T> T forEachPairSameAndSubtype(Processor<SameAsBound, SubtypeOfBound, T> processor, T initialValue) {
+ T currentValue = initialValue;
+ for (int i=0;i<bounds.size();i++) {
+ Bound bi = bounds.get(i);
+ if (bi instanceof SameAsBound) {
+ SameAsBound si = (SameAsBound)bi;
+ for (int j = i + 1; j < bounds.size(); j++) {
+ Bound bj = bounds.get(j);
+ if (bj instanceof SubtypeOfBound) {
+ SubtypeOfBound sj = (SubtypeOfBound)bj;
+ currentValue = processor.process(si, sj, currentValue);
+ }
+ }
+ }
+ }
+ return currentValue;
+ }
+
+ private <T> T forEachPairSubtypeAndSubtype(Processor<SubtypeOfBound, SubtypeOfBound, T> processor, T initialValue) {
+ T currentValue = initialValue;
+ for (int i=0;i<bounds.size();i++) {
+ Bound bi = bounds.get(i);
+ if (bi instanceof SubtypeOfBound) {
+ SubtypeOfBound si = (SubtypeOfBound)bi;
+ for (int j = i + 1; j < bounds.size(); j++) {
+ Bound bj = bounds.get(j);
+ if (bj instanceof SubtypeOfBound) {
+ SubtypeOfBound sj = (SubtypeOfBound)bj;
+ currentValue = processor.process(si, sj, currentValue);
+ }
+ }
+ }
+ }
+ return currentValue;
+ }
+
+ private boolean areSameTypeInference(ResolvedType a, ResolvedType b) {
+ return isInferenceVariable(a) && isInferenceVariable(b) && a.equals(b);
+ }
+
+ private List<Pair<ResolvedReferenceType, ResolvedReferenceType>> findPairsOfCommonAncestors(ResolvedReferenceType r1, ResolvedReferenceType r2) {
+ List<ResolvedReferenceType> set1 = new LinkedList<>();
+ set1.add(r1);
+ set1.addAll(r1.getAllAncestors());
+ List<ResolvedReferenceType> set2 = new LinkedList<>();
+ set2.add(r2);
+ set2.addAll(r2.getAllAncestors());
+ List<Pair<ResolvedReferenceType, ResolvedReferenceType>> pairs = new LinkedList<>();
+ for (ResolvedReferenceType rtFrom1 : set1) {
+ for (ResolvedReferenceType rtFrom2 : set2) {
+ if (rtFrom1.getTypeDeclaration().equals(rtFrom2.getTypeDeclaration())) {
+ pairs.add(new Pair<>(rtFrom1, rtFrom2));
+ }
+ }
+ }
+ return pairs;
+ }
+
+ /**
+ * Maintains a set of inference variable bounds, ensuring that these are consistent as new bounds are added.
+ * Because the bounds on one variable can sometimes impact the possible choices for another variable, this process
+ * propagates bounds between such interdependent variables.
+ */
+ public BoundSet incorporate(BoundSet otherBounds, TypeSolver typeSolver) {
+ BoundSet newBoundSet = this;
+ for (Bound b : otherBounds.bounds) {
+ newBoundSet = newBoundSet.withBound(b);
+ }
+ return newBoundSet.deriveImpliedBounds(typeSolver);
+ }
+
+ public BoundSet deriveImpliedBounds(TypeSolver typeSolver) {
+ // As bound sets are constructed and grown during inference, it is possible that new bounds can be inferred
+ // based on the assertions of the original bounds. The process of incorporation identifies these new bounds
+ // and adds them to the bound set.
+ //
+ // Incorporation can happen in two scenarios. One scenario is that the bound set contains complementary pairs
+ // of bounds; this implies new constraint formulas, as specified in §18.3.1. The other scenario is that the
+ // bound set contains a bound involving capture conversion; this implies new bounds and may imply new
+ // constraint formulas, as specified in §18.3.2. In both scenarios, any new constraint formulas are reduced,
+ // and any new bounds are added to the bound set. This may trigger further incorporation; ultimately, the set
+ // will reach a fixed point and no further bounds can be inferred.
+ //
+ // If incorporation of a bound set has reached a fixed point, and the set does not contain the bound false,
+ // then the bound set has the following properties:
+ //
+ // - For each combination of a proper lower bound L and a proper upper bound U of an inference variable, L <: U.
+ //
+ // - If every inference variable mentioned by a bound has an instantiation, the bound is satisfied by the
+ // corresponding substitution.
+ //
+ // - Given a dependency α = β, every bound of α matches a bound of β, and vice versa.
+ //
+ // - Given a dependency α <: β, every lower bound of α is a lower bound of β, and every upper bound of β is an
+ // upper bound of α.
+
+ ConstraintFormulaSet newConstraintsSet = ConstraintFormulaSet.empty();
+
+ // SECTION Complementary Pairs of Bounds
+ // (In this section, S and T are inference variables or types, and U is a proper type. For conciseness, a bound
+ // of the form α = T may also match a bound of the form T = α.)
+ //
+ // When a bound set contains a pair of bounds that match one of the following rules, a new constraint formula
+ // is implied:
+ //
+ // - α = S and α = T imply ‹S = T›
+
+ newConstraintsSet = forEachPairSameAs((a, b, currentConstraintSet) -> {
+ if (areSameTypeInference(a.getS(), b.getS())) {
+ currentConstraintSet = currentConstraintSet.withConstraint(new TypeSameAsType(a.getT(), b.getT()));
+ }
+ if (areSameTypeInference(a.getS(), b.getT())) {
+ currentConstraintSet = currentConstraintSet.withConstraint(new TypeSameAsType(a.getS(), b.getT()));
+ }
+ if (areSameTypeInference(a.getT(), b.getS())) {
+ currentConstraintSet = currentConstraintSet.withConstraint(new TypeSameAsType(a.getT(), b.getS()));
+ }
+ if (areSameTypeInference(a.getT(), b.getT())) {
+ currentConstraintSet = currentConstraintSet.withConstraint(new TypeSameAsType(a.getS(), b.getS()));
+ }
+ return currentConstraintSet;
+ }, newConstraintsSet);
+
+ // - α = S and α <: T imply ‹S <: T›
+
+ newConstraintsSet = forEachPairSameAndSubtype((a, b, currentConstraintSet) -> {
+ if (areSameTypeInference(a.getS(), b.getS())) {
+ currentConstraintSet = currentConstraintSet.withConstraint(new TypeSubtypeOfType(typeSolver, a.getT(), b.getT()));
+ }
+ if (areSameTypeInference(a.getT(), b.getS())) {
+ currentConstraintSet = currentConstraintSet.withConstraint(new TypeSubtypeOfType(typeSolver, a.getS(), b.getT()));
+ }
+ return currentConstraintSet;
+ }, newConstraintsSet);
+
+ // - α = S and T <: α imply ‹T <: S›
+
+ newConstraintsSet = forEachPairSameAndSubtype((a, b, currentConstraintSet) -> {
+ if (areSameTypeInference(a.getS(), b.getT())) {
+ currentConstraintSet = currentConstraintSet.withConstraint(new TypeSubtypeOfType(typeSolver, b.getS(), a.getT()));
+ }
+ if (areSameTypeInference(a.getT(), b.getT())) {
+ currentConstraintSet = currentConstraintSet.withConstraint(new TypeSubtypeOfType(typeSolver, b.getS(), a.getS()));
+ }
+ return currentConstraintSet;
+ }, newConstraintsSet);
+
+ // - S <: α and α <: T imply ‹S <: T›
+
+ newConstraintsSet = forEachPairSubtypeAndSubtype((a, b, currentConstraintSet) -> {
+ if (areSameTypeInference(a.getT(), b.getS())) {
+ currentConstraintSet = currentConstraintSet.withConstraint(new TypeSubtypeOfType(typeSolver, b.getS(), a.getT()));
+ }
+ return currentConstraintSet;
+ }, newConstraintsSet);
+
+ // - α = U and S = T imply ‹S[α:=U] = T[α:=U]›
+
+ newConstraintsSet = forEachPairSameAs((a, b, currentConstraintSet) -> {
+ if (isInferenceVariable(a.getS()) && isProperType(a.getT())) {
+ InferenceVariable alpha = (InferenceVariable)a.getS();
+ ResolvedType U = a.getT();
+ ResolvedType S = b.getS();
+ ResolvedType T = b.getT();
+ Substitution sub = Substitution.empty().withPair(alpha.getTypeParameterDeclaration(), U);
+ currentConstraintSet = currentConstraintSet.withConstraint(new TypeSameAsType(sub.apply(S), sub.apply(T)));
+ }
+ if (isInferenceVariable(a.getT()) && isProperType(a.getS())) {
+ InferenceVariable alpha = (InferenceVariable)a.getT();
+ ResolvedType U = a.getS();
+ ResolvedType S = b.getS();
+ ResolvedType T = b.getT();
+ Substitution sub = Substitution.empty().withPair(alpha.getTypeParameterDeclaration(), U);
+ currentConstraintSet = currentConstraintSet.withConstraint(new TypeSameAsType(sub.apply(S), sub.apply(T)));
+ }
+ if (isInferenceVariable(b.getS()) && isProperType(b.getT())) {
+ InferenceVariable alpha = (InferenceVariable)b.getS();
+ ResolvedType U = b.getT();
+ ResolvedType S = a.getS();
+ ResolvedType T = a.getT();
+ Substitution sub = Substitution.empty().withPair(alpha.getTypeParameterDeclaration(), U);
+ currentConstraintSet = currentConstraintSet.withConstraint(new TypeSameAsType(sub.apply(S), sub.apply(T)));
+ }
+ if (isInferenceVariable(b.getT()) && isProperType(b.getS())) {
+ InferenceVariable alpha = (InferenceVariable)b.getT();
+ ResolvedType U = b.getS();
+ ResolvedType S = a.getS();
+ ResolvedType T = a.getT();
+ Substitution sub = Substitution.empty().withPair(alpha.getTypeParameterDeclaration(), U);
+ currentConstraintSet = currentConstraintSet.withConstraint(new TypeSameAsType(sub.apply(S), sub.apply(T)));
+ }
+ return currentConstraintSet;
+ }, newConstraintsSet);
+
+ // - α = U and S <: T imply ‹S[α:=U] <: T[α:=U]›
+
+ newConstraintsSet = forEachPairSameAndSubtype((a, b, currentConstraintSet) -> {
+ if (isInferenceVariable(a.getS()) && isProperType(a.getT())) {
+ InferenceVariable alpha = (InferenceVariable)a.getS();
+ ResolvedType U = a.getT();
+ ResolvedType S = b.getS();
+ ResolvedType T = b.getT();
+ Substitution sub = Substitution.empty().withPair(alpha.getTypeParameterDeclaration(), U);
+ currentConstraintSet = currentConstraintSet.withConstraint(new TypeSubtypeOfType(typeSolver, sub.apply(S), sub.apply(T)));
+ }
+ if (isInferenceVariable(a.getT()) && isProperType(a.getS())) {
+ InferenceVariable alpha = (InferenceVariable)a.getT();
+ ResolvedType U = a.getS();
+ ResolvedType S = b.getS();
+ ResolvedType T = b.getT();
+ Substitution sub = Substitution.empty().withPair(alpha.getTypeParameterDeclaration(), U);
+ currentConstraintSet = currentConstraintSet.withConstraint(new TypeSubtypeOfType(typeSolver, sub.apply(S), sub.apply(T)));
+ }
+ return currentConstraintSet;
+ }, newConstraintsSet);
+
+ // When a bound set contains a pair of bounds α <: S and α <: T, and there exists a supertype of S of the
+ // form G<S1, ..., Sn> and a supertype of T of the form G<T1, ..., Tn> (for some generic class or interface, G),
+ // then for all i (1 ≤ i ≤ n), if Si and Ti are types (not wildcards), the constraint formula ‹Si = Ti› is
+ // implied.
+
+ newConstraintsSet = forEachPairSubtypeAndSubtype((a, b, currentConstraintSet) -> {
+ if (isInferenceVariable(a.getS()) && isInferenceVariable(b.getS())) {
+ if (a.getT().isReferenceType() && b.getT().isReferenceType()) {
+ ResolvedReferenceType S = a.getT().asReferenceType();
+ ResolvedReferenceType T = b.getT().asReferenceType();
+ List<Pair<ResolvedReferenceType, ResolvedReferenceType>> pairs = findPairsOfCommonAncestors(S, T);
+ for (Pair<ResolvedReferenceType, ResolvedReferenceType> pair : pairs) {
+ for (int i=0;i<Math.min(pair.a.typeParametersValues().size(), pair.b.typeParametersValues().size()); i++) {
+ ResolvedType si = pair.a.typeParametersValues().get(i);
+ ResolvedType ti = pair.b.typeParametersValues().get(i);
+ if (!si.isWildcard() && !ti.isWildcard()) {
+ currentConstraintSet = currentConstraintSet.withConstraint(new TypeSameAsType(si, ti));
+ }
+ }
+ }
+ }
+ }
+ return currentConstraintSet;
+ }, newConstraintsSet);
+
+ // SECTION Bounds Involving Capture Conversion
+ //
+ // When a bound set contains a bound of the form G<α1, ..., αn> = capture(G<A1, ..., An>), new bounds are
+ // implied and new constraint formulas may be implied, as follows.
+
+ for (Bound b : this.bounds.stream().filter(b -> b instanceof CapturesBound).collect(Collectors.toList())) {
+ CapturesBound capturesBound = (CapturesBound)b;
+
+ throw new UnsupportedOperationException();
+
+ // Let P1, ..., Pn represent the type parameters of G and let B1, ..., Bn represent the bounds of these type
+ // parameters. Let θ represent the substitution [P1:=α1, ..., Pn:=αn]. Let R be a type that is not an inference
+ // variable (but is not necessarily a proper type).
+ //
+ // A set of bounds on α1, ..., αn is implied, constructed from the declared bounds of P1, ..., Pn as specified
+ // in §18.1.3.
+ //
+ // In addition, for all i (1 ≤ i ≤ n):
+ //
+ // - If Ai is not a wildcard, then the bound αi = Ai is implied.
+ //
+ // - If Ai is a wildcard of the form ?:
+ //
+ // - αi = R implies the bound false
+ //
+ // - αi <: R implies the constraint formula ‹Bi θ <: R›
+ //
+ // - R <: αi implies the bound false
+ //
+ // - If Ai is a wildcard of the form ? extends T:
+ //
+ // - αi = R implies the bound false
+ //
+ // - If Bi is Object, then αi <: R implies the constraint formula ‹T <: R›
+ //
+ // - If T is Object, then αi <: R implies the constraint formula ‹Bi θ <: R›
+ //
+ // - R <: αi implies the bound false
+ //
+ // - If Ai is a wildcard of the form ? super T:
+ //
+ // - αi = R implies the bound false
+ //
+ // - αi <: R implies the constraint formula ‹Bi θ <: R›
+ //
+ // - R <: αi implies the constraint formula ‹R <: T›
+ }
+
+ if (newConstraintsSet.isEmpty()) {
+ return this;
+ } else {
+ BoundSet newBounds = newConstraintsSet.reduce(typeSolver);
+ if (newBounds.isEmpty()) {
+ return this;
+ }
+ return this.incorporate(newBounds, typeSolver);
+ }
+ }
+
+ public boolean containsFalse() {
+ return bounds.stream().anyMatch(it -> it instanceof FalseBound);
+ }
+
+ private class VariableDependency {
+ private InferenceVariable depending;
+ private InferenceVariable dependedOn;
+
+ public VariableDependency(InferenceVariable depending, InferenceVariable dependedOn) {
+ this.depending = depending;
+ this.dependedOn = dependedOn;
+ }
+
+ public InferenceVariable getDepending() {
+ return depending;
+ }
+
+ public InferenceVariable getDependedOn() {
+ return dependedOn;
+ }
+
+ public boolean isReflexive() {
+ return dependedOn.equals(depending);
+ }
+ }
+
+ private Set<InferenceVariable> allInferenceVariables() {
+ Set<InferenceVariable> variables = new HashSet<>();
+ for (Bound b : bounds) {
+ variables.addAll(b.usedInferenceVariables());
+ }
+ return variables;
+ }
+
+ private boolean hasInstantiationFor(InferenceVariable v) {
+ for (Bound b : bounds) {
+ if (b.isAnInstantiationFor(v)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private Instantiation getInstantiationFor(InferenceVariable v) {
+ for (Bound b : bounds) {
+ if (b.isAnInstantiationFor(v)) {
+ return b.isAnInstantiation().get();
+ }
+ }
+ throw new IllegalArgumentException();
+ }
+
+ private boolean thereIsSomeJSuchThatβequalAlphaJ(Set<InferenceVariable> alphas, InferenceVariable beta) {
+ for (InferenceVariable alphaJ : alphas) {
+ for (Bound b : bounds) {
+ if (b instanceof SameAsBound) {
+ SameAsBound sameAsBound = (SameAsBound)b;
+ if (sameAsBound.getS().equals(alphaJ) && sameAsBound.getT().equals(beta)) {
+ return true;
+ }
+ if (sameAsBound.getT().equals(alphaJ) && sameAsBound.getS().equals(beta)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private <T> List<Set<T>> buildAllSubsetsOfSize(Set<T> allElements, int desiredSize) {
+ if (desiredSize == allElements.size()) {
+ return Arrays.asList(allElements);
+ } else {
+ List<Set<T>> res = new LinkedList<>();
+ for (T element : allElements) {
+ Set<T> subset = allButOne(allElements, element);
+ res.addAll(buildAllSubsetsOfSize(subset, desiredSize));
+ }
+ return res;
+ }
+ }
+
+ private <T> Set<T> allButOne(Set<T> elements, T element) {
+ Set<T> set = new HashSet<T>(elements);
+ set.remove(element);
+ return set;
+ }
+
+ /**
+ * there exists no non-empty proper subset of { α1, ..., αn } with this property.
+ */
+ private Optional<Set<InferenceVariable>> smallestSetWithProperty(Set<InferenceVariable> uninstantiatedVariables,
+ List<VariableDependency> dependencies) {
+ for (int i=1;i<=uninstantiatedVariables.size();i++) {
+ for (Set<InferenceVariable> aSubSet : buildAllSubsetsOfSize(uninstantiatedVariables, i)){
+ if (hasProperty(aSubSet, dependencies)) {
+ return Optional.of(aSubSet);
+ }
+ }
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * if αi depends on the resolution of a variable β, then either β has an instantiation
+ * or there is some j such that β = αj
+ * @return
+ */
+ private boolean hasProperty(Set<InferenceVariable> alphas, List<VariableDependency> dependencies) {
+ for (InferenceVariable alphaI: alphas) {
+ for (InferenceVariable beta: dependencies.stream()
+ .filter(d -> d.depending.equals(alphaI))
+ .filter(d -> !d.isReflexive())
+ .map(d -> d.dependedOn)
+ .collect(Collectors.toList())) {
+ if (!hasInstantiationFor(beta) && !thereIsSomeJSuchThatβequalAlphaJ(alphas, beta)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Examines the bounds on an inference variable and determines an instantiation that is compatible with those
+ * bounds. It also decides the order in which interdependent inference variables are to be resolved.
+ */
+ public Optional<InstantiationSet> performResolution(List<InferenceVariable> variablesToResolve, TypeSolver typeSolver) {
+
+ if (this.containsFalse()) {
+ return Optional.empty();
+ }
+
+ List<VariableDependency> dependencies = new LinkedList<>();
+
+ // Given a bound set that does not contain the bound false, a subset of the inference variables mentioned by
+ // the bound set may be resolved. This means that a satisfactory instantiation may be added to the set for each
+ // inference variable, until all the requested variables have instantiations.
+ //
+ // Dependencies in the bound set may require that the variables be resolved in a particular order, or that
+ // additional variables be resolved. Dependencies are specified as follows:
+ //
+ // - Given a bound of one of the following forms, where T is either an inference variable β or a type that
+ // mentions β:
+ //
+ // - α = T
+ //
+ // - α <: T
+ //
+ // - T = α
+ //
+ // - T <: α
+ //
+ // If α appears on the left-hand side of another bound of the form G<..., α, ...> = capture(G<...>), then β
+ // depends on the resolution of α. Otherwise, α depends on the resolution of β.
+
+ for (Bound b : bounds) {
+ if (b instanceof CapturesBound) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ // - An inference variable α appearing on the left-hand side of a bound of the form
+ // G<..., α, ...> = capture(G<...>) depends on the resolution of every other inference variable mentioned in
+ // this bound (on both sides of the = sign).
+
+ for (Bound b : bounds) {
+ if (b instanceof CapturesBound) {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ // - An inference variable α depends on the resolution of an inference variable β if there exists an inference
+ // variable γ such that α depends on the resolution of γ and γ depends on the resolution of β.
+
+ for (int i=0;i<dependencies.size();i++) {
+ VariableDependency di = dependencies.get(i);
+ for (int j=i+1;j<dependencies.size();j++) {
+ VariableDependency dj = dependencies.get(j);
+ if (di.dependedOn.equals(dj.depending)) {
+ dependencies.add(new VariableDependency(di.getDepending(), dj.getDependedOn()));
+ }
+ }
+ }
+
+ // - An inference variable α depends on the resolution of itself.
+
+ for (InferenceVariable v : allInferenceVariables()) {
+ dependencies.add(new VariableDependency(v, v));
+ }
+
+ // Given a set of inference variables to resolve, let V be the union of this set and all variables upon which
+ // the resolution of at least one variable in this set depends.
+
+ Set<InferenceVariable> V = new HashSet<>();
+ V.addAll(variablesToResolve);
+ for (VariableDependency dependency : dependencies) {
+ if (variablesToResolve.contains(dependency.depending)) {
+ V.add(dependency.dependedOn);
+ }
+ }
+
+ // If every variable in V has an instantiation, then resolution succeeds and this procedure terminates.
+
+ boolean ok = true;
+ for (InferenceVariable v : V) {
+ if (!hasInstantiationFor(v)) {
+ ok = false;
+ }
+ }
+ if (ok) {
+ InstantiationSet instantiationSet = InstantiationSet.empty();
+ for (InferenceVariable v : V) {
+ instantiationSet = instantiationSet.withInstantiation(getInstantiationFor(v));
+ }
+ return Optional.of(instantiationSet);
+ }
+
+ // Otherwise, let { α1, ..., αn } be a non-empty subset of uninstantiated variables in V such that i)
+ // for all i (1 ≤ i ≤ n), if αi depends on the resolution of a variable β, then either β has an instantiation
+ // or there is some j such that β = αj; and ii) there exists no non-empty proper subset of { α1, ..., αn }
+ // with this property.
+
+ Set<InferenceVariable> uninstantiatedPortionOfV = new HashSet<>();
+ for (InferenceVariable v : V) {
+ if (!hasInstantiationFor(v)) {
+ uninstantiatedPortionOfV.add(v);
+ }
+ }
+ for (Set<InferenceVariable> alphas: allSetsWithProperty(uninstantiatedPortionOfV, dependencies)) {
+
+ // Resolution proceeds by generating an instantiation for each of α1, ..., αn based on the
+ // bounds in the bound set:
+
+ boolean hasSomeCaptureForAlphas = alphas.stream().anyMatch(
+ alphaI -> appearInLeftPartOfCapture(alphaI)
+ );
+
+ // - If the bound set does not contain a bound of the form G<..., αi, ...> = capture(G<...>)
+ // for all i (1 ≤ i ≤ n), then a candidate instantiation Ti is defined for each αi:
+
+ if (!hasSomeCaptureForAlphas) {
+ BoundSet newBounds = BoundSet.empty();
+ for (InferenceVariable alphaI : alphas) {
+ Set<ResolvedType> properLowerBounds = bounds.stream()
+ .filter(b -> b.isProperLowerBoundFor(alphaI).isPresent())
+ .map(b -> b.isProperLowerBoundFor(alphaI).get().getProperType())
+ .collect(Collectors.toSet());
+
+ ResolvedType Ti = null;
+
+ // - If αi has one or more proper lower bounds, L1, ..., Lk, then Ti = lub(L1, ..., Lk) (§4.10.4).
+
+ if (properLowerBounds.size() > 0) {
+ Ti = leastUpperBound(properLowerBounds);
+ }
+
+ // - Otherwise, if the bound set contains throws αi, and the proper upper bounds of αi are, at most,
+ // Exception, Throwable, and Object, then Ti = RuntimeException.
+
+ boolean throwsBound = bounds.stream().anyMatch(b -> b.isThrowsBoundOn(alphaI));
+ if (Ti == null && throwsBound && properUpperBoundsAreAtMostExceptionThrowableAndObject(alphaI)) {
+ Ti = new ReferenceTypeImpl(typeSolver.solveType(RuntimeException.class.getCanonicalName()), typeSolver);
+ }
+
+ // - Otherwise, where αi has proper upper bounds U1, ..., Uk, Ti = glb(U1, ..., Uk) (§5.1.10).
+
+ if (Ti == null) {
+ Set<ResolvedType> properUpperBounds = bounds.stream()
+ .filter(b -> b.isProperUpperBoundFor(alphaI).isPresent())
+ .map(b -> b.isProperUpperBoundFor(alphaI).get().getProperType())
+ .collect(Collectors.toSet());
+ if (properUpperBounds.size() == 0) {
+ throw new IllegalStateException();
+ }
+ Ti = glb(properUpperBounds);
+ }
+
+ newBounds = newBounds.withBound(new SameAsBound(alphaI, Ti));
+ }
+
+ // The bounds α1 = T1, ..., αn = Tn are incorporated with the current bound set.
+
+ BoundSet incorporatedBoundSet = this.incorporate(newBounds, typeSolver);
+
+ // If the result does not contain the bound false, then the result becomes the new bound set, and resolution
+ // proceeds by selecting a new set of variables to instantiate (if necessary), as described above.
+
+ if (!incorporatedBoundSet.containsFalse()) {
+ return incorporatedBoundSet.performResolution(variablesToResolve, typeSolver);
+ }
+
+ // Otherwise, the result contains the bound false, so a second attempt is made to instantiate { α1, ..., αn }
+ // by performing the step below.
+
+ throw new UnsupportedOperationException();
+ }
+
+ // - If the bound set contains a bound of the form G<..., αi, ...> = capture(G<...>) for some i (1 ≤ i ≤ n), or;
+
+ else {
+
+ // If the bound set produced in the step above contains the bound false;
+ //
+ // then let Y1, ..., Yn be fresh type variables whose bounds are as follows:
+ //
+ // - For all i (1 ≤ i ≤ n), if αi has one or more proper lower bounds L1, ..., Lk, then let the lower bound
+ // of Yi be lub(L1, ..., Lk); if not, then Yi has no lower bound.
+ //
+ // - For all i (1 ≤ i ≤ n), where αi has upper bounds U1, ..., Uk, let the upper bound of Yi be
+ // glb(U1 θ, ..., Uk θ), where θ is the substitution [α1:=Y1, ..., αn:=Yn].
+ //
+ // If the type variables Y1, ..., Yn do not have well-formed bounds (that is, a lower bound is not a subtype
+ // of an upper bound, or an intersection type is inconsistent), then resolution fails.
+ //
+ // Otherwise, for all i (1 ≤ i ≤ n), all bounds of the form G<..., αi, ...> = capture(G<...>) are removed
+ // from the current bound set, and the bounds α1 = Y1, ..., αn = Yn are incorporated.
+ //
+ // If the result does not contain the bound false, then the result becomes the new bound set, and resolution
+ // proceeds by selecting a new set of variables to instantiate (if necessary), as described above.
+ //
+ // Otherwise, the result contains the bound false, and resolution fails.
+
+ throw new UnsupportedOperationException();
+ }
+ }
+ return Optional.empty();
+ }
+
+ private Set<Set<InferenceVariable>> allPossibleSetsWithProperty(Set<InferenceVariable> allElements, List<VariableDependency> dependencies) {
+ Set<Set<InferenceVariable>> result = new HashSet<>();
+ for (int i=1;i<=allElements.size();i++) {
+ for (Set<InferenceVariable> aSubSet : buildAllSubsetsOfSize(allElements, i)){
+ if (hasProperty(aSubSet, dependencies)) {
+ result.add(aSubSet);
+ }
+ }
+ }
+ return result;
+ }
+
+ private boolean thereAreProperSubsets(Set<InferenceVariable> aSet, Set<Set<InferenceVariable>> allPossibleSets) {
+ for (Set<InferenceVariable> anotherSet : allPossibleSets) {
+ if (!anotherSet.equals(aSet)) {
+ if (isTheFirstAProperSubsetOfTheSecond(anotherSet, aSet)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isTheFirstAProperSubsetOfTheSecond(Set<InferenceVariable> subset, Set<InferenceVariable> originalSet) {
+ return originalSet.containsAll(subset) && originalSet.size() > subset.size();
+ }
+
+ private Set<Set<InferenceVariable>> allSetsWithProperty(Set<InferenceVariable> allElements, List<VariableDependency> dependencies) {
+ Set<Set<InferenceVariable>> allPossibleSets = allPossibleSetsWithProperty(allElements, dependencies);
+ Set<Set<InferenceVariable>> selected = new HashSet<>();
+ for (Set<InferenceVariable> aSet : allPossibleSets) {
+ if (!thereAreProperSubsets(aSet, allPossibleSets)) {
+ selected.add(aSet);
+ }
+ }
+ return selected;
+ }
+
+ private boolean properUpperBoundsAreAtMostExceptionThrowableAndObject(InferenceVariable inferenceVariable) {
+ throw new UnsupportedOperationException();
+ }
+
+ private boolean appearInLeftPartOfCapture(InferenceVariable inferenceVariable) {
+ for (Bound b : bounds) {
+ if (b instanceof CapturesBound) {
+ CapturesBound capturesBound = (CapturesBound)b;
+ if (capturesBound.getInferenceVariables().contains(inferenceVariable)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public List<Bound> getProperUpperBoundsFor(InferenceVariable inferenceVariable) {
+ return bounds.stream().filter(b -> b.isProperUpperBoundFor(inferenceVariable).isPresent()).collect(Collectors.toList());
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ConstraintFormula.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ConstraintFormula.java
new file mode 100644
index 000000000..3efd04f62
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ConstraintFormula.java
@@ -0,0 +1,125 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Constraint formulas are assertions of compatibility or subtyping that may involve inference variables.
+ *
+ * @author Federico Tomassetti
+ */
+public abstract class ConstraintFormula {
+
+ public static class ReductionResult {
+ private BoundSet boundSet;
+ private List<ConstraintFormula> constraintFormulas;
+
+ public BoundSet getBoundSet() {
+ return boundSet;
+ }
+
+ public List<ConstraintFormula> getConstraintFormulas() {
+ return constraintFormulas;
+ }
+
+ public static ReductionResult empty() {
+ return new ReductionResult();
+ }
+
+ public ReductionResult withConstraint(ConstraintFormula constraintFormula) {
+ ReductionResult newInstance = new ReductionResult();
+ newInstance.boundSet = this.boundSet;
+ newInstance.constraintFormulas = new LinkedList<>();
+ newInstance.constraintFormulas.addAll(this.constraintFormulas);
+ newInstance.constraintFormulas.add(constraintFormula);
+ return newInstance;
+ }
+
+ public ReductionResult withBound(Bound bound) {
+ ReductionResult newInstance = new ReductionResult();
+ newInstance.boundSet = this.boundSet.withBound(bound);
+ newInstance.constraintFormulas = this.constraintFormulas;
+ return newInstance;
+ }
+
+ private ReductionResult() {
+ this.boundSet = BoundSet.empty();
+ this.constraintFormulas = new LinkedList<>();
+ }
+
+ public static ReductionResult trueResult() {
+ return empty();
+ }
+
+ public static ReductionResult falseResult() {
+ return empty().withBound(Bound.falseBound());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ReductionResult that = (ReductionResult) o;
+
+ if (!boundSet.equals(that.boundSet)) return false;
+ return constraintFormulas.equals(that.constraintFormulas);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = boundSet.hashCode();
+ result = 31 * result + constraintFormulas.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "ReductionResult{" +
+ "boundSet=" + boundSet +
+ ", constraintFormulas=" + constraintFormulas +
+ '}';
+ }
+
+ public ConstraintFormula getConstraint(int index) {
+ if (constraintFormulas.size() <= index) {
+ throw new IllegalArgumentException("Constraint with index " + index + " is not available as there are " + constraintFormulas.size() + " constraints");
+ }
+ return constraintFormulas.get(index);
+ }
+
+ public static ReductionResult oneConstraint(ConstraintFormula constraintFormula) {
+ return empty().withConstraint(constraintFormula);
+ }
+
+ public static ReductionResult withConstraints(ConstraintFormula... constraints) {
+ return withConstraints(Arrays.asList(constraints));
+ }
+
+ public static ReductionResult oneBound(Bound bound) {
+ return empty().withBound(bound);
+ }
+
+ public static ReductionResult withConstraints(List<ConstraintFormula> constraints) {
+ ReductionResult reductionResult = new ReductionResult();
+ reductionResult.constraintFormulas.addAll(constraints);
+ return reductionResult;
+ }
+
+ public static ReductionResult bounds(BoundSet bounds) {
+ ReductionResult reductionResult = new ReductionResult();
+ reductionResult.boundSet = bounds;
+ return reductionResult;
+ }
+ }
+
+ /**
+ * A formula is reduced to one or both of:
+ * i) A bound or bound set, which is to be incorporated with the "current" bound set. Initially, the current bound
+ * set is empty.
+ * ii) Further constraint formulas, which are to be reduced recursively.
+ */
+ public abstract ReductionResult reduce(BoundSet currentBoundSet);
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ConstraintFormulaSet.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ConstraintFormulaSet.java
new file mode 100644
index 000000000..bca6e12e4
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ConstraintFormulaSet.java
@@ -0,0 +1,53 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference;
+
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ConstraintFormulaSet {
+ private List<ConstraintFormula> constraintFormulas;
+
+ public ConstraintFormulaSet withConstraint(ConstraintFormula constraintFormula) {
+ ConstraintFormulaSet newInstance = new ConstraintFormulaSet();
+ newInstance.constraintFormulas.addAll(this.constraintFormulas);
+ newInstance.constraintFormulas.add(constraintFormula);
+ return newInstance;
+ }
+
+ private static final ConstraintFormulaSet EMPTY = new ConstraintFormulaSet();
+
+ public static ConstraintFormulaSet empty() {
+ return EMPTY;
+ }
+
+ private ConstraintFormulaSet() {
+ constraintFormulas = new LinkedList<>();
+ }
+
+ /**
+ * Takes a compatibility assertion about an expression or type, called a constraint formula, and reduces it to a
+ * set of bounds on inference variables. Often, a constraint formula reduces to other constraint formulas,
+ * which must be recursively reduced. A procedure is followed to identify these additional constraint formulas and,
+ * ultimately, to express via a bound set the conditions under which the choices for inferred types would render
+ * each constraint formula true.
+ */
+ public BoundSet reduce(TypeSolver typeSolver) {
+ List<ConstraintFormula> constraints = new LinkedList<>(constraintFormulas);
+ BoundSet boundSet = BoundSet.empty();
+ while (constraints.size() > 0) {
+ ConstraintFormula constraintFormula = constraints.remove(0);
+ ConstraintFormula.ReductionResult reductionResult = constraintFormula.reduce(boundSet);
+ constraints.addAll(reductionResult.getConstraintFormulas());
+ boundSet.incorporate(reductionResult.getBoundSet(), typeSolver);
+ }
+ return boundSet;
+ }
+
+ public boolean isEmpty() {
+ return constraintFormulas.isEmpty();
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ControlFlowLogic.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ControlFlowLogic.java
new file mode 100644
index 000000000..56b42e5e8
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ControlFlowLogic.java
@@ -0,0 +1,290 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.body.ConstructorDeclaration;
+import com.github.javaparser.ast.body.InitializerDeclaration;
+import com.github.javaparser.ast.body.MethodDeclaration;
+import com.github.javaparser.ast.expr.VariableDeclarationExpr;
+import com.github.javaparser.ast.stmt.*;
+import com.github.javaparser.ast.visitor.GenericVisitor;
+import com.github.javaparser.ast.visitor.GenericVisitorAdapter;
+
+import java.util.List;
+
+/**
+ * Consider Control Flow to determine which statements are reachable.
+ *
+ * Except for the special treatment of while, do, and for statements whose condition expression has the constant value
+ * true, the values of expressions are not taken into account in the flow analysis.
+ *
+ * See JLS 14.21
+ *
+ * @author Federico Tomassetti
+ */
+public class ControlFlowLogic {
+
+ private static ControlFlowLogic instance = new ControlFlowLogic();
+
+ public static ControlFlowLogic getInstance() {
+ return instance;
+ }
+
+ private ControlFlowLogic() {
+
+ }
+
+ /**
+ * A break statement with no label attempts to transfer control to the innermost enclosing switch, while, do, or
+ * for statement of the immediately enclosing method or initializer; this statement, which is called the break
+ * target, then immediately completes normally.
+ *
+ *
+ * A break statement with label Identifier attempts to transfer control to the enclosing labeled statement (§14.7)
+ * that has the same Identifier as its label; this statement, which is called the break target, then immediately
+ * completes normally. In this case, the break target need not be a switch, while, do, or for statement.
+ */
+ public Statement breakTarget(BreakStmt breakStmt) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * A reachable break statement exits a statement if, within the break target, either there are no try statements
+ * whose try blocks contain the break statement, or there are try statements whose try blocks contain the break
+ * statement and all finally clauses of those try statements can complete normally.
+ */
+ public boolean exitTheStatement(BreakStmt breakStmt) {
+ if (!isReachable(breakStmt)) {
+ return false;
+ }
+ Statement breakTarget = breakTarget(breakStmt);
+ for (TryStmt tryStmt : containedTryStmts(breakTarget)) {
+ if (contains(tryStmt.getTryBlock(), breakStmt)) {
+ if (!tryStmt.getFinallyBlock().isPresent() && !canCompleteNormally(tryStmt.getFinallyBlock().get())) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public boolean continueADoStatement(ContinueStmt continueStmt, DoStmt doStmt) {
+ for (TryStmt tryStmt : containedTryStmts(continueStmt)) {
+ if (contains(tryStmt.getTryBlock(), continueStmt)) {
+ if (!tryStmt.getFinallyBlock().isPresent() && !canCompleteNormally(tryStmt.getFinallyBlock().get())) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private boolean contains(Statement container, Statement contained) {
+ throw new UnsupportedOperationException();
+ }
+
+ private List<TryStmt> containedTryStmts(Statement statement) {
+ throw new UnsupportedOperationException();
+ }
+
+ private <P extends Node> boolean parentIs(Node node, Class<P> parentClass) {
+ if (node.getParentNode().isPresent()) {
+ return parentClass.isInstance(node.getParentNode().get());
+ } else {
+ return false;
+ }
+ }
+
+ // See JLS 14.21
+ public boolean canCompleteNormally(Statement statement) {
+ if (!isReachable(statement)) {
+ return false;
+ }
+ GenericVisitor<Boolean, Void> visitor = new GenericVisitorAdapter<Boolean, Void>(){
+ @Override
+ public Boolean visit(BlockStmt n, Void arg) {
+ // An empty block that is not a switch block can complete normally iff it is reachable
+ if (n.isEmpty() && !parentIs(statement, SwitchStmt.class)) {
+ return isReachable(statement);
+ }
+ // A non-empty block that is not a switch block can complete normally iff the last statement in
+ // it can complete normally.
+ if (!n.isEmpty() && !parentIs(statement, SwitchStmt.class)) {
+ return canCompleteNormally(n.getStatement(n.getStatements().size() - 1));
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Boolean visit(LabeledStmt n, Void arg) {
+ // A labeled statement can complete normally if at least one of the following is true:
+ // – The contained statement can complete normally.
+ // – There is a reachable break statement that exits the labeled statement.
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Boolean visit(EmptyStmt n, Void arg) {
+ // An empty statement can complete normally iff it is reachable.
+ return isReachable(n);
+ }
+
+ @Override
+ public Boolean visit(LocalClassDeclarationStmt n, Void arg) {
+ // A local class declaration statement can complete normally iff it is reachable.
+ return isReachable(n);
+ }
+
+ @Override
+ public Boolean visit(IfStmt n, Void arg) {
+ if (n.getElseStmt().isPresent()) {
+ // An if-then-else statement can complete normally iff the then-statement can
+ // complete normally or the else-statement can complete normally.
+ return canCompleteNormally(n.getThenStmt()) || canCompleteNormally(n.getElseStmt().get());
+ } else {
+ // An if-then statement can complete normally iff it is reachable.
+ return isReachable(n);
+ }
+ }
+
+ @Override
+ public Boolean visit(AssertStmt n, Void arg) {
+ // An assert statement can complete normally iff it is reachable.
+ return isReachable(n);
+ }
+
+ @Override
+ public Boolean visit(ExpressionStmt n, Void arg) {
+ // A local variable declaration statement can complete normally iff it is reachable.
+ if (n.getExpression() instanceof VariableDeclarationExpr) {
+ VariableDeclarationExpr expr = (VariableDeclarationExpr) n.getExpression();
+ return isReachable(n);
+ }
+ // An expression statement can complete normally iff it is reachable.
+ return isReachable(n);
+ }
+ };
+ return statement.accept(visitor, null);
+ }
+
+ private boolean isReachableBecauseOfPosition(Statement statement) {
+ // The first statement in a non-empty block that is not a switch block is reachable iff the block is reachable.
+
+ // Every other statement S in a non-empty block that is not a switch block is reachable iff the statement
+ // preceding S can complete normally.
+
+ // The contained statement of a Labelled Statement is reachable iff the labeled statement is reachable.
+
+ // The then-statement of an if-then statement is reachable iff the if-then statement is reachable.
+
+ // The then-statement of an if-then-else statement is reachable iff the if-then-else statement is reachable.
+ // The else-statement is reachable iff the if-then-else statement is reachable.
+
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isReachable(Statement statement) {
+
+ GenericVisitor<Boolean, Void> visitor = new GenericVisitorAdapter<Boolean, Void>(){
+ @Override
+ public Boolean visit(BlockStmt n, Void arg) {
+ // The block that is the body of a constructor, method, instance initializer, or static initializer is
+ // reachable
+ if (statement.getParentNode().isPresent()) {
+ if (statement.getParentNode().get() instanceof ConstructorDeclaration) {
+ return true;
+ }
+ if (statement.getParentNode().get() instanceof MethodDeclaration) {
+ return true;
+ }
+ if (statement.getParentNode().get() instanceof InitializerDeclaration) {
+ return true;
+ }
+ }
+ return isReachableBecauseOfPosition(statement);
+ }
+
+ @Override
+ public Boolean visit(LocalClassDeclarationStmt n, Void arg) {
+ return super.visit(n, arg);
+ }
+ };
+ return statement.accept(visitor, null);
+
+ //
+ //
+ //
+ // A switch statement can complete normally iff at least one of the following is
+ //true:
+ //– The switch block is empty or contains only switch labels.
+ //– The last statement in the switch block can complete normally.
+ //– There is at least one switch label after the last switch block statement group. – The switch block does not contain a default label.
+ //– There is a reachable break statement that exits the switch statement.
+ // BLOCKS AND STATEMENTS Unreachable Statements 14.21
+ //
+ //A switch block is reachable iff its switch statement is reachable.
+ //
+ // A statement in a switch block is reachable iff its switch statement is reachable
+ //and at least one of the following is true:
+ //– It bears a case or default label.
+ //– There is a statement preceding it in the switch block and that preceding statement can complete normally.
+ //
+ //A while statement can complete normally iff at least one of the following is true:
+ //– Thewhilestatementisreachableandtheconditionexpressionisnotaconstant
+ //expression (§15.28) with value true.
+ //– There is a reachable break statement that exits the while statement.
+ // The contained statement is reachable iff the while statement is reachable and the condition expression is not a constant expression whose value is false.
+ //
+ // A do statement can complete normally iff at least one of the following is true:
+ //– The contained statement can complete normally and the condition expression
+ //is not a constant expression (§15.28) with value true.
+ //– The do statement contains a reachable continue statement with no label, and the do statement is the innermost while, do, or for statement that contains that continue statement, and the continue statement continues that do statement, and the condition expression is not a constant expression with value true.
+ //– The do statement contains a reachable continue statement with a label L, and the do statement has label L, and the continue statement continues that do statement, and the condition expression is not a constant expression with value true.
+ //– There is a reachable break statement that exits the do statement.
+ // The contained statement is reachable iff the do statement is reachable.
+ //
+ // A basic for statement can complete normally iff at least one of the following
+ //is true:
+ //– The for statement is reachable, there is a condition expression, and the
+ //condition expression is not a constant expression (§15.28) with value true.
+ //– There is a reachable break statement that exits the for statement.
+ // The contained statement is reachable iff the for statement is reachable and the condition expression is not a constant expression whose value is false.
+ //
+ //• An enhanced for statement can complete normally iff it is reachable.
+ //
+ //• A break, continue, return, or throw statement cannot complete normally.
+ //
+ //• A synchronized statement can complete normally iff the contained statement can complete normally.
+ // The contained statement is reachable iff the synchronized statement is reachable.
+ //
+ //• A try statement can complete normally iff both of the following are true:
+ //– The try block can complete normally or any catch block can complete
+ //normally.
+ //– Ifthetrystatementhasafinallyblock,thenthefinallyblockcancomplete normally.
+ //
+ //• The try block is reachable iff the try statement is reachable.
+ //
+ //• A catch block C is reachable iff both of the following are true:
+ //– Either the type of C's parameter is an unchecked exception type or Exception or a superclass of Exception, or some expression or throw statement in the try block is reachable and can throw a checked exception whose type is assignable to the type of C's parameter. (An expression is reachable iff the innermost statement containing it is reachable.)
+ //See §15.6 for normal and abrupt completion of expressions.
+ //– There is no earlier catch block A in the try statement such that the type of C's
+ //parameter is the same as or a subclass of the type of A's parameter.
+ //• The Block of a catch block is reachable iff the catch block is reachable.
+ //• If a finally block is present, it is reachable iff the try statement is reachable.
+ // One might expect the if statement to be handled in the following manner:
+ //• An if-then statement can complete normally iff at least one of the following is true:
+ //– The if-then statement is reachable and the condition expression is not a constant expression whose value is true.
+ //– The then-statement can complete normally.
+ // The then-statement is reachable iff the if-then statement is reachable and the
+ //condition expression is not a constant expression whose value is false.
+ //• An if-then-else statement can complete normally iff the then-statement can complete normally or the else-statement can complete normally.
+ // BLOCKS AND STATEMENTS Unreachable Statements 14.21
+ //The then-statement is reachable iff the if-then-else statement is reachable and the condition expression is not a constant expression whose value is false.
+ // The else-statement is reachable iff the if-then-else statement is reachable and the condition expression is not a constant expression whose value is true.
+ // This approach would be consistent with the treatment of other control structures. However, in order to allow the if statement to be used conveniently for "conditional compilation" purposes, the actual rules differ.
+
+ // FIXME
+ //throw new UnsupportedOperationException();
+ }
+
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ExpressionHelper.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ExpressionHelper.java
new file mode 100644
index 000000000..8562ece5e
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ExpressionHelper.java
@@ -0,0 +1,137 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.expr.*;
+import com.github.javaparser.ast.stmt.BlockStmt;
+import com.github.javaparser.ast.stmt.ExpressionStmt;
+import com.github.javaparser.ast.stmt.ReturnStmt;
+import com.github.javaparser.ast.type.UnknownType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.List;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ExpressionHelper {
+
+ /**
+ * See https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.2
+ * @return
+ */
+ public static boolean isStandaloneExpression(Expression expression) {
+ return !isPolyExpression(expression);
+ }
+
+ /**
+ * See https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.2
+ * @return
+ */
+ public static boolean isPolyExpression(Expression expression) {
+ if (expression instanceof EnclosedExpr) {
+ throw new UnsupportedOperationException(expression.toString());
+ }
+ if (expression instanceof ObjectCreationExpr) {
+ // A class instance creation expression is a poly expression (§15.2) if it uses the diamond form for type
+ // arguments to the class, and it appears in an assignment context or an invocation context (§5.2, §5.3).
+ // Otherwise, it is a standalone expression.
+ ObjectCreationExpr objectCreationExpr = (ObjectCreationExpr)expression;
+ if (objectCreationExpr.isUsingDiamondOperator()) {
+ throw new UnsupportedOperationException(expression.toString());
+ } else {
+ return false;
+ }
+ }
+ if (expression instanceof MethodCallExpr) {
+ MethodCallExpr methodCallExpr = (MethodCallExpr)expression;
+
+ // A method invocation expression is a poly expression if all of the following are true:
+ //
+ // 1. The invocation appears in an assignment context or an invocation context (§5.2, §5.3).
+
+ if (!appearsInAssignmentContext(expression) || appearsInInvocationContext(expression)) {
+ return false;
+ }
+
+ // 2. If the invocation is qualified (that is, any form of MethodInvocation except for the first), then
+ // the invocation elides TypeArguments to the left of the Identifier.
+
+ if (isQualified(methodCallExpr) && !elidesTypeArguments(methodCallExpr)) {
+ return false;
+ }
+
+ // 3. The method to be invoked, as determined by the following subsections, is generic (§8.4.4) and has a
+ // return type that mentions at least one of the method's type parameters.
+
+ //boolean condition3 =;
+ throw new UnsupportedOperationException(expression.toString());
+
+ // Otherwise, the method invocation expression is a standalone expression.
+ //return true;
+ }
+ if (expression instanceof MethodReferenceExpr) {
+ throw new UnsupportedOperationException(expression.toString());
+ }
+ if (expression instanceof ConditionalExpr) {
+ throw new UnsupportedOperationException(expression.toString());
+ }
+ if (expression instanceof LambdaExpr) {
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean elidesTypeArguments(MethodCallExpr methodCall) {
+ throw new UnsupportedOperationException();
+ }
+
+ private static boolean isQualified(MethodCallExpr methodCall) {
+ throw new UnsupportedOperationException();
+ }
+
+ // Not sure if should look if the parent is an assignment context
+ private static boolean appearsInAssignmentContext(Expression expression) {
+ if (expression.getParentNode().isPresent()) {
+ Node parent = expression.getParentNode().get();
+ if (parent instanceof ExpressionStmt) {
+ return false;
+ }
+ if (parent instanceof MethodCallExpr) {
+ return false;
+ }
+ if (parent instanceof ReturnStmt) {
+ return false;
+ }
+ throw new UnsupportedOperationException(parent.getClass().getCanonicalName());
+ }
+ return false;
+ }
+
+ private static boolean appearsInInvocationContext(Expression expression) {
+ if (expression.getParentNode().isPresent()) {
+ Node parent = expression.getParentNode().get();
+ if (parent instanceof ExpressionStmt) {
+ return false;
+ }
+ if (parent instanceof MethodCallExpr) {
+ return true;
+ }
+ throw new UnsupportedOperationException(parent.getClass().getCanonicalName());
+ }
+ return false;
+ }
+
+ public static boolean isExplicitlyTyped(LambdaExpr lambdaExpr) {
+ return lambdaExpr.getParameters().stream().allMatch(p -> !(p.getType() instanceof UnknownType));
+ }
+
+ public static List<Expression> getResultExpressions(BlockStmt blockStmt) {
+ throw new UnsupportedOperationException();
+ }
+
+ public static boolean isCompatibleInAssignmentContext(Expression expression, ResolvedType type, TypeSolver typeSolver) {
+ return type.isAssignableBy(JavaParserFacade.get(typeSolver).getType(expression, false));
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/InferenceVariable.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/InferenceVariable.java
new file mode 100644
index 000000000..18b8d742c
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/InferenceVariable.java
@@ -0,0 +1,93 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference;
+
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Are meta-variables for types - that is, they are special names that allow abstract reasoning about types.
+ * To distinguish them from type variables, inference variables are represented with Greek letters, principally α.
+ *
+ * See JLS 18
+ *
+ * @author Federico Tomassetti
+ */
+public class InferenceVariable implements ResolvedType {
+ private static int unnamedInstantiated = 0;
+
+ private String name;
+ private ResolvedTypeParameterDeclaration typeParameterDeclaration;
+
+ public static List<InferenceVariable> instantiate(List<ResolvedTypeParameterDeclaration> typeParameterDeclarations) {
+ List<InferenceVariable> inferenceVariables = new LinkedList<>();
+ for (ResolvedTypeParameterDeclaration tp : typeParameterDeclarations) {
+ inferenceVariables.add(InferenceVariable.unnamed(tp));
+ }
+ return inferenceVariables;
+ }
+
+ public static InferenceVariable unnamed(ResolvedTypeParameterDeclaration typeParameterDeclaration) {
+ return new InferenceVariable("__unnamed__" + (unnamedInstantiated++), typeParameterDeclaration);
+ }
+
+ public InferenceVariable(String name, ResolvedTypeParameterDeclaration typeParameterDeclaration) {
+ this.name = name;
+ this.typeParameterDeclaration = typeParameterDeclaration;
+ }
+
+ @Override
+ public String describe() {
+ return name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ InferenceVariable that = (InferenceVariable) o;
+
+ if (!name.equals(that.name)) return false;
+ return typeParameterDeclaration != null ? typeParameterDeclaration.equals(that.typeParameterDeclaration)
+ : that.typeParameterDeclaration == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = name.hashCode();
+ result = 31 * result + (typeParameterDeclaration != null ? typeParameterDeclaration.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public boolean isAssignableBy(ResolvedType other) {
+ if (other.equals(this)) {
+ return true;
+ }
+ throw new UnsupportedOperationException(
+ "We are unable to determine the assignability of an inference variable without knowing the bounds and"
+ + " constraints");
+ }
+
+ public ResolvedTypeParameterDeclaration getTypeParameterDeclaration() {
+ if (typeParameterDeclaration == null) {
+ throw new IllegalStateException("The type parameter declaration was not specified");
+ }
+ return typeParameterDeclaration;
+ }
+
+ @Override
+ public String toString() {
+ return "InferenceVariable{" +
+ "name='" + name + '\'' +
+ ", typeParameterDeclaration=" + typeParameterDeclaration +
+ '}';
+ }
+
+ @Override
+ public boolean mention(List<ResolvedTypeParameterDeclaration> typeParameters) {
+ return false;
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/InferenceVariableSubstitution.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/InferenceVariableSubstitution.java
new file mode 100644
index 000000000..437358b45
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/InferenceVariableSubstitution.java
@@ -0,0 +1,36 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference;
+
+import com.github.javaparser.resolution.types.ResolvedType;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class InferenceVariableSubstitution {
+
+ private final static InferenceVariableSubstitution EMPTY = new InferenceVariableSubstitution();
+
+ private List<InferenceVariable> inferenceVariables;
+ private List<ResolvedType> types;
+
+ public static InferenceVariableSubstitution empty() {
+ return EMPTY;
+ }
+
+ private InferenceVariableSubstitution() {
+ this.inferenceVariables = new LinkedList<>();
+ this.types = new LinkedList<>();
+ }
+
+ public InferenceVariableSubstitution withPair(InferenceVariable inferenceVariable, ResolvedType type) {
+ InferenceVariableSubstitution newInstance = new InferenceVariableSubstitution();
+ newInstance.inferenceVariables.addAll(this.inferenceVariables);
+ newInstance.types.addAll(this.types);
+ newInstance.inferenceVariables.add(inferenceVariable);
+ newInstance.types.add(type);
+ return newInstance;
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/Instantiation.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/Instantiation.java
new file mode 100644
index 000000000..c4b59e5bd
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/Instantiation.java
@@ -0,0 +1,51 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference;
+
+
+import com.github.javaparser.resolution.types.ResolvedType;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class Instantiation {
+ private InferenceVariable inferenceVariable;
+ private ResolvedType properType;
+
+ public Instantiation(InferenceVariable inferenceVariable, ResolvedType properType) {
+ this.inferenceVariable = inferenceVariable;
+ this.properType = properType;
+ }
+
+ public InferenceVariable getInferenceVariable() {
+ return inferenceVariable;
+ }
+
+ public ResolvedType getProperType() {
+ return properType;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Instantiation that = (Instantiation) o;
+
+ if (!inferenceVariable.equals(that.inferenceVariable)) return false;
+ return properType.equals(that.properType);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = inferenceVariable.hashCode();
+ result = 31 * result + properType.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "Instantiation{" +
+ "inferenceVariable=" + inferenceVariable +
+ ", properType=" + properType +
+ '}';
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/InstantiationSet.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/InstantiationSet.java
new file mode 100644
index 000000000..e0bfe3e88
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/InstantiationSet.java
@@ -0,0 +1,68 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference;
+
+import com.github.javaparser.resolution.types.ResolvedType;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class InstantiationSet {
+
+ private List<Instantiation> instantiations;
+
+ public boolean allInferenceVariablesAreResolved(BoundSet boundSet) {
+ throw new UnsupportedOperationException();
+ }
+
+ public static InstantiationSet empty() {
+ return EMPTY;
+ }
+
+ private static final InstantiationSet EMPTY = new InstantiationSet();
+
+ private InstantiationSet() {
+ instantiations = new LinkedList<>();
+ }
+
+ public InstantiationSet withInstantiation(Instantiation instantiation) {
+ InstantiationSet newInstance = new InstantiationSet();
+ newInstance.instantiations.addAll(this.instantiations);
+ newInstance.instantiations.add(instantiation);
+ return newInstance;
+ }
+
+ public boolean isEmpty() {
+ return instantiations.isEmpty();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ InstantiationSet that = (InstantiationSet) o;
+
+ return instantiations.equals(that.instantiations);
+ }
+
+ @Override
+ public int hashCode() {
+ return instantiations.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "InstantiationSet{" +
+ "instantiations=" + instantiations +
+ '}';
+ }
+
+ public ResolvedType apply(ResolvedType type) {
+ for (Instantiation instantiation : instantiations) {
+ type = type.replaceTypeVariables(instantiation.getInferenceVariable().getTypeParameterDeclaration(), instantiation.getProperType());
+ }
+ return type;
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/MethodType.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/MethodType.java
new file mode 100644
index 000000000..87494d47a
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/MethodType.java
@@ -0,0 +1,55 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference;
+
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+
+import java.util.List;
+
+/**
+ * A MethodType is an ordered 4-tuple consisting of:
+ * 1. type parameters: the declarations of any type parameters of the method member.
+ * 2. argument types: a list of the types of the arguments to the method member.
+ * 3. return type: the return type of the method member.
+ * 4. throws clause: exception types declared in the throws clause of the method member.
+ *
+ * See JLS 8.2
+ *
+ * @author Federico Tomassetti
+ */
+public class MethodType {
+ private List<ResolvedTypeParameterDeclaration> typeParameters;
+ private List<ResolvedType> formalArgumentTypes;
+ private ResolvedType returnType;
+ private List<ResolvedType> exceptionTypes;
+
+ public static MethodType fromMethodUsage(MethodUsage methodUsage) {
+ return new MethodType(methodUsage.getDeclaration().getTypeParameters(), methodUsage.getParamTypes(),
+ methodUsage.returnType(), methodUsage.exceptionTypes());
+ }
+
+ public MethodType(List<ResolvedTypeParameterDeclaration> typeParameters, List<ResolvedType> formalArgumentTypes,
+ ResolvedType returnType,
+ List<ResolvedType> exceptionTypes) {
+ this.typeParameters = typeParameters;
+ this.formalArgumentTypes = formalArgumentTypes;
+ this.returnType = returnType;
+ this.exceptionTypes = exceptionTypes;
+ }
+
+ public List<ResolvedTypeParameterDeclaration> getTypeParameters() {
+ return typeParameters;
+ }
+
+ public List<ResolvedType> getFormalArgumentTypes() {
+ return formalArgumentTypes;
+ }
+
+ public ResolvedType getReturnType() {
+ return returnType;
+ }
+
+ public List<ResolvedType> getExceptionTypes() {
+ return exceptionTypes;
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ProperLowerBound.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ProperLowerBound.java
new file mode 100644
index 000000000..03568a71f
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ProperLowerBound.java
@@ -0,0 +1,50 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference;
+
+import com.github.javaparser.resolution.types.ResolvedType;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ProperLowerBound {
+ private InferenceVariable inferenceVariable;
+ private ResolvedType properType;
+
+ public ProperLowerBound(InferenceVariable inferenceVariable, ResolvedType properType) {
+ this.inferenceVariable = inferenceVariable;
+ this.properType = properType;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ProperLowerBound that = (ProperLowerBound) o;
+
+ if (!inferenceVariable.equals(that.inferenceVariable)) return false;
+ return properType.equals(that.properType);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = inferenceVariable.hashCode();
+ result = 31 * result + properType.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "ProperLowerBound{" +
+ "inferenceVariable=" + inferenceVariable +
+ ", properType=" + properType +
+ '}';
+ }
+
+ public InferenceVariable getInferenceVariable() {
+ return inferenceVariable;
+ }
+
+ public ResolvedType getProperType() {
+ return properType;
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ProperUpperBound.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ProperUpperBound.java
new file mode 100644
index 000000000..07d8cdb3c
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ProperUpperBound.java
@@ -0,0 +1,50 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference;
+
+import com.github.javaparser.resolution.types.ResolvedType;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ProperUpperBound {
+ private InferenceVariable inferenceVariable;
+ private ResolvedType properType;
+
+ public ProperUpperBound(InferenceVariable inferenceVariable, ResolvedType properType) {
+ this.inferenceVariable = inferenceVariable;
+ this.properType = properType;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ProperUpperBound that = (ProperUpperBound) o;
+
+ if (!inferenceVariable.equals(that.inferenceVariable)) return false;
+ return properType.equals(that.properType);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = inferenceVariable.hashCode();
+ result = 31 * result + properType.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "ProperUpperBound{" +
+ "inferenceVariable=" + inferenceVariable +
+ ", properType=" + properType +
+ '}';
+ }
+
+ public InferenceVariable getInferenceVariable() {
+ return inferenceVariable;
+ }
+
+ public ResolvedType getProperType() {
+ return properType;
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/Substitution.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/Substitution.java
new file mode 100644
index 000000000..36325420b
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/Substitution.java
@@ -0,0 +1,45 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference;
+
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class Substitution {
+
+ private List<ResolvedTypeParameterDeclaration> typeParameterDeclarations;
+ private List<ResolvedType> types;
+
+ private final static Substitution EMPTY = new Substitution();
+
+ public static Substitution empty() {
+ return EMPTY;
+ }
+
+ public Substitution withPair(ResolvedTypeParameterDeclaration typeParameterDeclaration, ResolvedType type) {
+ Substitution newInstance = new Substitution();
+ newInstance.typeParameterDeclarations.addAll(this.typeParameterDeclarations);
+ newInstance.types.addAll(this.types);
+ newInstance.typeParameterDeclarations.add(typeParameterDeclaration);
+ newInstance.types.add(type);
+ return newInstance;
+
+ }
+
+ private Substitution() {
+ this.typeParameterDeclarations = new LinkedList<>();
+ this.types = new LinkedList<>();
+ }
+
+ public ResolvedType apply(ResolvedType originalType) {
+ ResolvedType result = originalType;
+ for (int i=0;i<typeParameterDeclarations.size();i++) {
+ result = result.replaceTypeVariables(typeParameterDeclarations.get(i), types.get(i));
+ }
+ return result;
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/TypeHelper.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/TypeHelper.java
new file mode 100644
index 000000000..01d041d11
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/TypeHelper.java
@@ -0,0 +1,405 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference;
+
+import com.github.javaparser.ast.expr.Expression;
+import com.github.javaparser.ast.expr.LambdaExpr;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.types.*;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.logic.FunctionalInterfaceLogic;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.*;
+import com.github.javaparser.utils.Pair;
+
+import java.util.*;
+
+/**
+ * The term "type" is used loosely in this chapter to include type-like syntax that contains inference variables.
+ *
+ * Assertions that involve inference
+ * variables are assertions about every proper type that can be produced by replacing each inference variable with
+ * a proper type.
+ *
+ * @author Federico Tomassetti
+ */
+public class TypeHelper {
+
+ /**
+ * The term proper type excludes such "types" that mention inference variables.
+ */
+ public static boolean isProperType(ResolvedType type) {
+ if (type instanceof InferenceVariable) {
+ return false;
+ }
+ if (type instanceof ResolvedReferenceType) {
+ ResolvedReferenceType referenceType = (ResolvedReferenceType) type;
+ return referenceType.typeParametersValues().stream().allMatch(it -> isProperType(it));
+ }
+ if (type instanceof ResolvedWildcard) {
+ ResolvedWildcard wildcard = (ResolvedWildcard)type;
+ if (wildcard.isBounded()) {
+ return isProperType(wildcard.getBoundedType());
+ } else {
+ return true;
+ }
+ }
+ if (type.isPrimitive()) {
+ return true;
+ }
+ if (type.isTypeVariable()) {
+ // FIXME I am not sure...
+ return false;
+ }
+ if (type.isArray()) {
+ return isProperType(type.asArrayType().getComponentType());
+ }
+ throw new UnsupportedOperationException(type.toString());
+ }
+
+ /**
+ * see https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.3
+ * @param expression
+ * @param t
+ * @return
+ */
+ public static boolean isCompatibleInAStrictInvocationContext(Expression expression, ResolvedType t) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * see https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.3
+ * @param expression
+ * @param t
+ * @return
+ */
+ public static boolean isCompatibleInALooseInvocationContext(TypeSolver typeSolver, Expression expression, ResolvedType t) {
+ //throw new UnsupportedOperationException("Unable to determine if " + expression + " is compatible in a loose invocation context with type " + t);
+ return isCompatibleInALooseInvocationContext(JavaParserFacade.get(typeSolver).getType(expression), t);
+ }
+
+ /**
+ * see https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.3
+ * @param s
+ * @param t
+ * @return
+ */
+ public static boolean isCompatibleInALooseInvocationContext(ResolvedType s, ResolvedType t) {
+ // Loose invocation contexts allow a more permissive set of conversions, because they are only used for a
+ // particular invocation if no applicable declaration can be found using strict invocation contexts. Loose
+ // invocation contexts allow the use of one of the following:
+ //
+ // - an identity conversion (§5.1.1)
+
+ if (s.equals(t)) {
+ return true;
+ }
+
+ // - a widening primitive conversion (§5.1.2)
+
+ if (s.isPrimitive() && t.isPrimitive() && areCompatibleThroughWideningPrimitiveConversion(s, t)) {
+ return true;
+ }
+
+ // - a widening reference conversion (§5.1.5)
+
+ if (s.isReferenceType() && t.isReferenceType() && areCompatibleThroughWideningReferenceConversion(s, t)) {
+ return true;
+ }
+
+ // - a boxing conversion (§5.1.7) optionally followed by widening reference conversion
+
+ if (s.isPrimitive() && t.isReferenceType() &&
+ areCompatibleThroughWideningReferenceConversion(toBoxedType(s.asPrimitive()), t)) {
+ return true;
+ }
+
+ // - an unboxing conversion (§5.1.8) optionally followed by a widening primitive conversion
+
+ if (isUnboxable(s) && s.isReferenceType() && t.isPrimitive() &&
+ areCompatibleThroughWideningPrimitiveConversion(toUnboxedType(s.asReferenceType()), t)) {
+ return true;
+ }
+
+ // If, after the conversions listed for an invocation context have been applied, the resulting type is a raw
+ // type (§4.8), an unchecked conversion (§5.1.9) may then be applied.
+ //
+ // A value of the null type (the null reference is the only such value) may be assigned to any reference type
+ if (s.isNull() && t.isReferenceType()) {
+ return true;
+ }
+
+ //throw new UnsupportedOperationException("isCompatibleInALooseInvocationContext unable to decide on s=" + s + ", t=" + t);
+ // TODO FIXME
+ return t.isAssignableBy(s);
+ }
+
+ private static boolean isUnboxable(ResolvedType referenceType) {
+ if (!referenceType.isReferenceType()) {
+ return false;
+ }
+ return Arrays.stream(ResolvedPrimitiveType.values()).anyMatch(pt -> referenceType.asReferenceType().getQualifiedName().equals(pt.getBoxTypeQName()));
+ }
+
+ private static ResolvedType toUnboxedType(ResolvedReferenceType referenceType) {
+ throw new UnsupportedOperationException(referenceType.toString());
+ }
+
+ private static ResolvedType toBoxedType(ResolvedPrimitiveType primitiveType) {
+ throw new UnsupportedOperationException();
+ }
+
+ private static boolean areCompatibleThroughWideningReferenceConversion(ResolvedType s, ResolvedType t) {
+ Optional<ResolvedPrimitiveType> correspondingPrimitiveTypeForS = Arrays.stream(ResolvedPrimitiveType.values()).filter(pt -> pt.getBoxTypeQName().equals(s.asReferenceType().getQualifiedName())).findFirst();
+ if (!correspondingPrimitiveTypeForS.isPresent()) {
+ return false;
+ }
+ throw new UnsupportedOperationException("areCompatibleThroughWideningReferenceConversion s="+s+", t=" + t);
+ }
+
+ private static boolean areCompatibleThroughWideningPrimitiveConversion(ResolvedType s, ResolvedType t) {
+ if (s.isPrimitive() && t.isPrimitive()) {
+ return s.isAssignableBy(t);
+ } else {
+ return false;
+ }
+ }
+
+ public static boolean isInferenceVariable(ResolvedType type) {
+ return type instanceof InferenceVariable;
+ }
+
+ public static Set<InferenceVariable> usedInferenceVariables(ResolvedType type) {
+ if (isInferenceVariable(type)) {
+ return new HashSet<>(Arrays.asList((InferenceVariable)type));
+ }
+ if (type.isReferenceType()) {
+ Set<InferenceVariable> res = new HashSet<>();
+ for (ResolvedType tp : type.asReferenceType().typeParametersValues()) {
+ res.addAll(usedInferenceVariables(tp));
+ }
+ return res;
+ }
+ throw new UnsupportedOperationException(type.toString());
+ }
+
+ /**
+ * See JLS 4.10.4. Least Upper Bound.
+ */
+ public static ResolvedType leastUpperBound(Set<ResolvedType> types) {
+ if (types.size() == 0) {
+ throw new IllegalArgumentException();
+ }
+
+ // The least upper bound, or "lub", of a set of reference types is a shared supertype that is more specific than
+ // any other shared supertype (that is, no other shared supertype is a subtype of the least upper bound).
+ // This type, lub(U1, ..., Uk), is determined as follows.
+ //
+ // If k = 1, then the lub is the type itself: lub(U) = U.
+
+ if (types.size() == 1) {
+ return types.stream().findFirst().get();
+ }
+
+ //
+ //Otherwise:
+ //
+ //For each Ui (1 ≤ i ≤ k):
+ //
+ //Let ST(Ui) be the set of supertypes of Ui.
+ //
+ //Let EST(Ui), the set of erased supertypes of Ui, be:
+ //
+ //EST(Ui) = { |W| | W in ST(Ui) } where |W| is the erasure of W.
+ //
+ //The reason for computing the set of erased supertypes is to deal with situations where the set of types includes several distinct parameterizations of a generic type.
+ //
+ //For example, given List<String> and List<Object>, simply intersecting the sets ST(List<String>) = { List<String>, Collection<String>, Object } and ST(List<Object>) = { List<Object>, Collection<Object>, Object } would yield a set { Object }, and we would have lost track of the fact that the upper bound can safely be assumed to be a List.
+ //
+ //In contrast, intersecting EST(List<String>) = { List, Collection, Object } and EST(List<Object>) = { List, Collection, Object } yields { List, Collection, Object }, which will eventually enable us to produce List<?>.
+ //
+ //Let EC, the erased candidate set for U1 ... Uk, be the intersection of all the sets EST(Ui) (1 ≤ i ≤ k).
+ //
+ //Let MEC, the minimal erased candidate set for U1 ... Uk, be:
+ //
+ //MEC = { V | V in EC, and for all W ≠ V in EC, it is not the case that W <: V }
+ //
+ //Because we are seeking to infer more precise types, we wish to filter out any candidates that are supertypes of other candidates. This is what computing MEC accomplishes. In our running example, we had EC = { List, Collection, Object }, so MEC = { List }. The next step is to recover type arguments for the erased types in MEC.
+ //
+ //For any element G of MEC that is a generic type:
+ //
+ //Let the "relevant" parameterizations of G, Relevant(G), be:
+ //
+ //Relevant(G) = { V | 1 ≤ i ≤ k: V in ST(Ui) and V = G<...> }
+ //
+ //In our running example, the only generic element of MEC is List, and Relevant(List) = { List<String>, List<Object> }. We will now seek to find a type argument for List that contains (§4.5.1) both String and Object.
+ //
+ //This is done by means of the least containing parameterization (lcp) operation defined below. The first line defines lcp() on a set, such as Relevant(List), as an operation on a list of the elements of the set. The next line defines the operation on such lists, as a pairwise reduction on the elements of the list. The third line is the definition of lcp() on pairs of parameterized types, which in turn relies on the notion of least containing type argument (lcta). lcta() is defined for all possible cases.
+ //
+ //Let the "candidate" parameterization of G, Candidate(G), be the most specific parameterization of the generic type G that contains all the relevant parameterizations of G:
+ //
+ //Candidate(G) = lcp(Relevant(G))
+ //
+ //where lcp(), the least containing invocation, is:
+ //
+ //lcp(S) = lcp(e1, ..., en) where ei (1 ≤ i ≤ n) in S
+ //
+ //lcp(e1, ..., en) = lcp(lcp(e1, e2), e3, ..., en)
+ //
+ //lcp(G<X1, ..., Xn>, G<Y1, ..., Yn>) = G<lcta(X1, Y1), ..., lcta(Xn, Yn)>
+ //
+ //lcp(G<X1, ..., Xn>) = G<lcta(X1), ..., lcta(Xn)>
+ //
+ //and where lcta(), the least containing type argument, is: (assuming U and V are types)
+ //
+ //lcta(U, V) = U if U = V, otherwise ? extends lub(U, V)
+ //
+ //lcta(U, ? extends V) = ? extends lub(U, V)
+ //
+ //lcta(U, ? super V) = ? super glb(U, V)
+ //
+ //lcta(? extends U, ? extends V) = ? extends lub(U, V)
+ //
+ //lcta(? extends U, ? super V) = U if U = V, otherwise ?
+ //
+ //lcta(? super U, ? super V) = ? super glb(U, V)
+ //
+ //lcta(U) = ? if U's upper bound is Object, otherwise ? extends lub(U,Object)
+ //
+ //and where glb() is as defined in §5.1.10.
+ //
+ //Let lub(U1 ... Uk) be:
+ //
+ //Best(W1) & ... & Best(Wr)
+ //
+ //where Wi (1 ≤ i ≤ r) are the elements of MEC, the minimal erased candidate set of U1 ... Uk;
+ //
+ //and where, if any of these elements are generic, we use the candidate parameterization (so as to recover type arguments):
+ //
+ //Best(X) = Candidate(X) if X is generic; X otherwise.
+ //
+ //Strictly speaking, this lub() function only approximates a least upper bound. Formally, there may exist some other type T such that all of U1 ... Uk are subtypes of T and T is a subtype of lub(U1, ..., Uk). However, a compiler for the Java programming language must implement lub() as specified above.
+ //
+ //It is possible that the lub() function yields an infinite type. This is permissible, and a compiler for the Java programming language must recognize such situations and represent them appropriately using cyclic data structures.
+ //
+ //The possibility of an infinite type stems from the recursive calls to lub(). Readers familiar with recursive types should note that an infinite type is not the same as a recursive type
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * See JLS 15.27.3. Type of a Lambda Expression
+ * @return
+ */
+ public static Pair<ResolvedType, Boolean> groundTargetTypeOfLambda(LambdaExpr lambdaExpr, ResolvedType T, TypeSolver typeSolver) {
+ // The ground target type is derived from T as follows:
+ //
+ boolean used18_5_3 = false;
+
+ boolean wildcardParameterized = T.asReferenceType().typeParametersValues().stream()
+ .anyMatch(tp -> tp.isWildcard());
+ if (wildcardParameterized) {
+ // - If T is a wildcard-parameterized functional interface type and the lambda expression is explicitly typed,
+ // then the ground target type is inferred as described in §18.5.3.
+
+ if (ExpressionHelper.isExplicitlyTyped(lambdaExpr)) {
+ used18_5_3 = true;
+ throw new UnsupportedOperationException();
+ }
+
+ // - If T is a wildcard-parameterized functional interface type and the lambda expression is implicitly typed,
+ // then the ground target type is the non-wildcard parameterization (§9.9) of T.
+
+ else {
+ return new Pair<>(nonWildcardParameterizationOf(T.asReferenceType(), typeSolver), used18_5_3);
+ }
+ }
+
+ // - Otherwise, the ground target type is T.
+ return new Pair<>(T, used18_5_3);
+ }
+
+ /**
+ * See JLS 9.9
+ */
+ private static ResolvedReferenceType nonWildcardParameterizationOf(ResolvedReferenceType originalType, TypeSolver typeSolver) {
+ List<ResolvedType> TIs = new LinkedList<>();
+ List<ResolvedType> AIs = originalType.typeParametersValues();
+ List<ResolvedTypeParameterDeclaration> TPs = originalType.getTypeDeclaration().getTypeParameters();
+
+ // Let P1...Pn be the type parameters of I with corresponding bounds B1...Bn. For all i (1 ≤ i ≤ n),
+ // Ti is derived according to the form of Ai:
+
+ ResolvedReferenceType object = new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver);
+
+ for (int i=0;i<AIs.size();i++) {
+ ResolvedType Ai = AIs.get(i);
+ ResolvedType Ti = null;
+
+ // - If Ai is a type, then Ti = Ai.
+
+ if (!Ai.isWildcard()) {
+ Ti = Ai;
+ }
+
+ // - If Ai is a wildcard, and the corresponding type parameter's bound, Bi, mentions one of P1...Pn, then
+ // Ti is undefined and there is no function type.
+
+ if (Ti == null && Ai.isWildcard() && Ai.asWildcard().mention(originalType.getTypeDeclaration().getTypeParameters())) {
+ throw new IllegalArgumentException();
+ }
+
+ // - Otherwise:
+
+ if (Ti == null) {
+
+ ResolvedType Bi = TPs.get(i).hasLowerBound() ? TPs.get(i).getLowerBound() : object;
+
+ // - If Ai is an unbound wildcard ?, then Ti = Bi.
+
+ if (Ai.isWildcard() && !Ai.asWildcard().isBounded()) {
+ Ti = Bi;
+ }
+
+ // - If Ai is a upper-bounded wildcard ? extends Ui, then Ti = glb(Ui, Bi) (§5.1.10).
+
+ else if (Ai.isWildcard() && Ai.asWildcard().isUpperBounded()) {
+ ResolvedType Ui = Ai.asWildcard().getBoundedType();
+ Ti = glb(new HashSet<>(Arrays.asList(Ui, Bi)));
+ }
+
+ // - If Ai is a lower-bounded wildcard ? super Li, then Ti = Li.
+
+ else if (Ai.isWildcard() && Ai.asWildcard().isLowerBounded()) {
+ Ti = Ai.asWildcard().getBoundedType();
+ }
+
+ else throw new RuntimeException("This should not happen");
+ }
+
+ TIs.add(Ti);
+ }
+
+ return new ReferenceTypeImpl(originalType.getTypeDeclaration(), TIs, typeSolver);
+ }
+
+ public static MethodType getFunctionType(ResolvedType type) {
+ Optional<MethodUsage> mu = FunctionalInterfaceLogic.getFunctionalMethod(type);
+ if (mu.isPresent()) {
+ return MethodType.fromMethodUsage(mu.get());
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /**
+ * See JLS 5.1.10. Capture Conversion.
+ */
+ public static ResolvedType glb(Set<ResolvedType> types) {
+ if (types.size() == 0) {
+ throw new IllegalArgumentException();
+ }
+ if (types.size() == 1) {
+ return types.iterator().next();
+ }
+ return new ResolvedIntersectionType(types);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/TypeInference.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/TypeInference.java
new file mode 100644
index 000000000..b68c157fb
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/TypeInference.java
@@ -0,0 +1,719 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference;
+
+import com.github.javaparser.ast.expr.*;
+import com.github.javaparser.ast.type.UnknownType;
+import com.github.javaparser.resolution.MethodUsage;
+import com.github.javaparser.resolution.declarations.ResolvedInterfaceDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedMethodDeclaration;
+import com.github.javaparser.resolution.declarations.ResolvedTypeParameterDeclaration;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+import com.github.javaparser.symbolsolver.resolution.typeinference.bounds.SubtypeOfBound;
+import com.github.javaparser.symbolsolver.resolution.typeinference.bounds.ThrowsBound;
+import com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas.ExpressionCompatibleWithType;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+
+import static com.github.javaparser.symbolsolver.resolution.typeinference.ExpressionHelper.isStandaloneExpression;
+
+/**
+ * The API exposed by the TypeInference subsystem.
+ *
+ * @author Federico Tomassetti
+ */
+public class TypeInference {
+
+ private final ResolvedType object;
+ private TypeSolver typeSolver;
+
+ public TypeInference(TypeSolver typeSolver) {
+ if (typeSolver == null) {
+ throw new NullPointerException();
+ }
+ this.typeSolver = typeSolver;
+ this.object = new ReferenceTypeImpl(typeSolver.solveType(Object.class.getCanonicalName()), typeSolver);
+ }
+
+ ///
+ /// Public static methods
+ ///
+
+ public static MethodUsage toMethodUsage(MethodCallExpr call, ResolvedMethodDeclaration methodDeclaration, TypeSolver typeSolver) {
+ TypeInference typeInference = new TypeInference(typeSolver);
+ Optional<InstantiationSet> instantiationSetOpt = typeInference.instantiationInference(call, methodDeclaration);
+ if (instantiationSetOpt.isPresent()) {
+ return instantiationSetToMethodUsage(methodDeclaration, instantiationSetOpt.get());
+ } else {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ ///
+ /// Public instance methods
+ ///
+
+ public Optional<InstantiationSet> instantiationInference(MethodCallExpr methodCallExpr, ResolvedMethodDeclaration methodDeclaration) {
+ return instantiationInference(methodCallExpr.getArguments(), methodDeclaration);
+ }
+
+ public Optional<InstantiationSet> instantiationInference(List<Expression> argumentExpressions, ResolvedMethodDeclaration methodDeclaration) {
+// if (methodCallExpr.getTypeArguments().isPresent()) {
+// throw new IllegalArgumentException("Type inference unnecessary as type arguments have been specified");
+// }
+
+ // Given a method invocation that provides no explicit type arguments, the process to determine whether a
+ // potentially applicable generic method m is applicable is as follows:
+
+ // - Where P1, ..., Pp (p ≥ 1) are the type parameters of m, let α1, ..., αp be inference variables, and
+ // let θ be the substitution [P1:=α1, ..., Pp:=αp].
+
+ List<ResolvedTypeParameterDeclaration> Ps = methodDeclaration.getTypeParameters();
+ List<InferenceVariable> alphas = InferenceVariable.instantiate(Ps);
+ Substitution theta = Substitution.empty();
+ for (int i=0;i<Ps.size();i++) {
+ theta = theta.withPair(Ps.get(0), alphas.get(0));
+ }
+
+ // - An initial bound set, B0, is constructed from the declared bounds of P1, ..., Pp, as described in §18.1.3.
+
+ BoundSet B0 = boundSetup(Ps, alphas);
+
+ // - For all i (1 ≤ i ≤ p), if Pi appears in the throws clause of m, then the bound throws αi is implied.
+ // These bounds, if any, are incorporated with B0 to produce a new bound set, B1.
+
+ BoundSet B1 = B0;
+ for (int i=0;i<Ps.size();i++) {
+ ResolvedTypeParameterDeclaration Pi = Ps.get(i);
+ if (appearInThrowsClause(Pi, methodDeclaration)) {
+ B1 = B1.withBound(new ThrowsBound(alphas.get(i)));
+ }
+ }
+
+ // - A set of constraint formulas, C, is constructed as follows.
+ //
+ // Let F1, ..., Fn be the formal parameter types of m, and let e1, ..., ek be the actual argument expressions
+ // of the invocation. Then:
+
+ List<ResolvedType> Fs = formalParameterTypes(methodDeclaration);
+ List<Expression> es = argumentExpressions;
+
+ Optional<ConstraintFormulaSet> C = Optional.empty();
+
+ // - To test for applicability by strict invocation:
+
+ if (!C.isPresent()) {
+ C = testForApplicabilityByStrictInvocation(Fs, es, theta);
+ }
+
+ // - To test for applicability by loose invocation:
+
+ if (!C.isPresent()) {
+ C = testForApplicabilityByLooseInvocation(Fs, es, theta);
+ }
+
+ // - To test for applicability by variable arity invocation:
+
+ if (!C.isPresent()) {
+ C = testForApplicabilityByVariableArityInvocation(Fs, es, theta);
+ }
+
+ if (!C.isPresent()) {
+ return Optional.empty();
+ }
+
+ // - C is reduced (§18.2) and the resulting bounds are incorporated with B1 to produce a new bound set, B2.
+
+ BoundSet resultingBounds = C.get().reduce(typeSolver);
+ BoundSet B2 = B1.incorporate(resultingBounds, typeSolver);
+
+ // - Finally, the method m is applicable if B2 does not contain the bound false and resolution of all the
+ // inference variables in B2 succeeds (§18.4).
+
+ if (B2.containsFalse()) {
+ return Optional.empty();
+ }
+
+ Optional<InstantiationSet> instantiation = B2.performResolution(alphas, typeSolver);
+ return instantiation;
+ }
+
+ /**
+ * Determine whether a potentially applicable generic method m is applicable for a method invocation that
+ * provides no explicit type arguments.
+ */
+ public boolean invocationApplicabilityInference(MethodCallExpr methodCallExpr, ResolvedMethodDeclaration methodDeclaration) {
+ if (!methodCallExpr.getNameAsString().equals(methodDeclaration.getName())) {
+ throw new IllegalArgumentException();
+ }
+ Optional<InstantiationSet> partial = instantiationInference(methodCallExpr, methodDeclaration);
+ if (!partial.isPresent()) {
+ return false;
+ }
+ int nActualParams = methodCallExpr.getArguments().size();
+ int nFormalParams = methodDeclaration.getNumberOfParams();
+ if (nActualParams != nFormalParams) {
+ if (methodDeclaration.hasVariadicParameter()) {
+ if (nActualParams < (nFormalParams - 1)) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ //MethodUsage methodUsage = instantiationSetToMethodUsage(methodDeclaration, partial.get());
+// for (int i=0;i<nActualParams;i++) {
+// int formalIndex = i >= nFormalParams ? nFormalParams - 1 : i;
+// Type formalType = methodDeclaration.getParam(formalIndex).getType();
+// Type actualType = JavaParserFacade.get(typeSolver).getType(methodCallExpr.getArgument(i));
+// //if (!formalType.isAssignableBy(actualType)) {
+// // return false;
+// //}
+// }
+ return true;
+ }
+
+ public BoundSet invocationTypeInferenceBoundsSetB3() {
+ // Given a method invocation that provides no explicit type arguments, and a corresponding most specific
+ // applicable generic method m, the process to infer the invocation type (§15.12.2.6) of the chosen method is
+ // as follows:
+ //
+ // - Let θ be the substitution [P1:=α1, ..., Pp:=αp] defined in §18.5.1 to replace the type parameters of m with inference variables.
+ //
+ // - Let B2 be the bound set produced by reduction in order to demonstrate that m is applicable in §18.5.1. (While it was necessary in §18.5.1 to demonstrate that the inference variables in B2 could be resolved, in order to establish applicability, the instantiations produced by this resolution step are not considered part of B2.)
+ //
+ // - If the invocation is not a poly expression, let the bound set B3 be the same as B2.
+ //
+ // If the invocation is a poly expression, let the bound set B3 be derived from B2 as follows. Let R be the
+ // return type of m, let T be the invocation's target type, and then:
+ //
+ // - If unchecked conversion was necessary for the method to be applicable during constraint set reduction
+ // in §18.5.1, the constraint formula ‹|R| → T› is reduced and incorporated with B2.
+ //
+ // - Otherwise, if R θ is a parameterized type, G<A1, ..., An>, and one of A1, ..., An is a wildcard, then,
+ // for fresh inference variables β1, ..., βn, the constraint formula ‹G<β1, ..., βn> → T› is reduced and
+ // incorporated, along with the bound G<β1, ..., βn> = capture(G<A1, ..., An>), with B2.
+ //
+ // - Otherwise, if R θ is an inference variable α, and one of the following is true:
+ //
+ // - T is a reference type, but is not a wildcard-parameterized type, and either i) B2 contains a bound of
+ // one of the forms α = S or S <: α, where S is a wildcard-parameterized type, or ii) B2 contains two
+ // bounds of the forms S1 <: α and S2 <: α, where S1 and S2 have supertypes that are two different
+ // parameterizations of the same generic class or interface.
+ //
+ // - T is a parameterization of a generic class or interface, G, and B2 contains a bound of one of the
+ // forms α = S or S <: α, where there exists no type of the form G<...> that is a supertype of S, but the
+ // raw type |G<...>| is a supertype of S.
+ //
+ // - T is a primitive type, and one of the primitive wrapper classes mentioned in §5.1.7 is an
+ // instantiation, upper bound, or lower bound for α in B2.
+ //
+ // then α is resolved in B2, and where the capture of the resulting instantiation of α is U, the constraint
+ // formula ‹U → T› is reduced and incorporated with B2.
+ //
+ // - Otherwise, the constraint formula ‹R θ → T› is reduced and incorporated with B2.
+ throw new UnsupportedOperationException();
+ }
+
+ public void invocationTypeInference() {
+ BoundSet B3 = invocationTypeInferenceBoundsSetB3();
+ //
+ //A set of constraint formulas, C, is constructed as follows.
+ //
+ // Let e1, ..., ek be the actual argument expressions of the invocation. If m is applicable by strict or loose invocation, let F1, ..., Fk be the formal parameter types of m; if m is applicable by variable arity invocation, let F1, ..., Fk the first k variable arity parameter types of m (§15.12.2.4). Then:
+ //
+ //For all i (1 ≤ i ≤ k), if ei is not pertinent to applicability, C contains ‹ei → Fi θ›.
+ //
+ //For all i (1 ≤ i ≤ k), additional constraints may be included, depending on the form of ei:
+ //
+ //If ei is a LambdaExpression, C contains ‹LambdaExpression →throws Fi θ›.
+ //
+ //In addition, the lambda body is searched for additional constraints:
+ //
+ //For a block lambda body, the search is applied recursively to each result expression.
+ //
+ //For a poly class instance creation expression (§15.9) or a poly method invocation expression (§15.12), C contains all the constraint formulas that would appear in the set C generated by §18.5.2 when inferring the poly expression's invocation type.
+ //
+ //For a parenthesized expression, the search is applied recursively to the contained expression.
+ //
+ //For a conditional expression, the search is applied recursively to the second and third operands.
+ //
+ //For a lambda expression, the search is applied recursively to the lambda body.
+ //
+ //If ei is a MethodReference, C contains ‹MethodReference →throws Fi θ›.
+ //
+ //If ei is a poly class instance creation expression (§15.9) or a poly method invocation expression (§15.12), C contains all the constraint formulas that would appear in the set C generated by §18.5.2 when inferring the poly expression's invocation type.
+ //
+ //If ei is a parenthesized expression, these rules are applied recursively to the contained expression.
+ //
+ //If ei is a conditional expression, these rules are applied recursively to the second and third operands.
+ //
+ //While C is not empty, the following process is repeated, starting with the bound set B3 and accumulating new bounds into a "current" bound set, ultimately producing a new bound set, B4:
+ //
+ //A subset of constraints is selected in C, satisfying the property that, for each constraint, no input variable can influence an output variable of another constraint in C. The terms input variable and output variable are defined below. An inference variable α can influence an inference variable β if α depends on the resolution of β (§18.4), or vice versa; or if there exists a third inference variable γ such that α can influence γ and γ can influence β.
+ //
+ //If this subset is empty, then there is a cycle (or cycles) in the graph of dependencies between constraints. In this case, all constraints are considered that participate in a dependency cycle (or cycles) and do not depend on any constraints outside of the cycle (or cycles). A single constraint is selected from the considered constraints, as follows:
+ //
+ //If any of the considered constraints have the form ‹Expression → T›, then the selected constraint is the considered constraint of this form that contains the expression to the left (§3.5) of the expression of every other considered constraint of this form.
+ //
+ // If no considered constraint has the form ‹Expression → T›, then the selected constraint is the considered constraint that contains the expression to the left of the expression of every other considered constraint.
+ //
+ // The selected constraint(s) are removed from C.
+ //
+ // The input variables α1, ..., αm of all the selected constraint(s) are resolved.
+ //
+ // Where T1, ..., Tm are the instantiations of α1, ..., αm, the substitution [α1:=T1, ..., αm:=Tm] is applied to every constraint.
+ //
+ // The constraint(s) resulting from substitution are reduced and incorporated with the current bound set.
+ //
+ //Finally, if B4 does not contain the bound false, the inference variables in B4 are resolved.
+ //
+ //If resolution succeeds with instantiations T1, ..., Tp for inference variables α1, ..., αp, let θ' be the substitution [P1:=T1, ..., Pp:=Tp]. Then:
+ //
+ //If unchecked conversion was necessary for the method to be applicable during constraint set reduction in §18.5.1, then the parameter types of the invocation type of m are obtained by applying θ' to the parameter types of m's type, and the return type and thrown types of the invocation type of m are given by the erasure of the return type and thrown types of m's type.
+ //
+ //If unchecked conversion was not necessary for the method to be applicable, then the invocation type of m is obtained by applying θ' to the type of m.
+ //
+ //If B4 contains the bound false, or if resolution fails, then a compile-time error occurs.
+ //
+ //Invocation type inference may require carefully sequencing the reduction of constraint formulas of the forms ‹Expression → T›, ‹LambdaExpression →throws T›, and ‹MethodReference →throws T›. To facilitate this sequencing, the input variables of these constraints are defined as follows:
+ //
+ //For ‹LambdaExpression → T›:
+ //
+ //If T is an inference variable, it is the (only) input variable.
+ //
+ // If T is a functional interface type, and a function type can be derived from T (§15.27.3), then the input variables include i) if the lambda expression is implicitly typed, the inference variables mentioned by the function type's parameter types; and ii) if the function type's return type, R, is not void, then for each result expression e in the lambda body (or for the body itself if it is an expression), the input variables of ‹e → R›.
+ //
+ //Otherwise, there are no input variables.
+ //
+ //For ‹LambdaExpression →throws T›:
+ //
+ //If T is an inference variable, it is the (only) input variable.
+ //
+ // If T is a functional interface type, and a function type can be derived, as described in §15.27.3, the input variables include i) if the lambda expression is implicitly typed, the inference variables mentioned by the function type's parameter types; and ii) the inference variables mentioned by the function type's return type.
+ //
+ // Otherwise, there are no input variables.
+ //
+ // For ‹MethodReference → T›:
+ //
+ //If T is an inference variable, it is the (only) input variable.
+ //
+ // If T is a functional interface type with a function type, and if the method reference is inexact (§15.13.1), the input variables are the inference variables mentioned by the function type's parameter types.
+ //
+ //Otherwise, there are no input variables.
+ //
+ //For ‹MethodReference →throws T›:
+ //
+ //If T is an inference variable, it is the (only) input variable.
+ //
+ // If T is a functional interface type with a function type, and if the method reference is inexact (§15.13.1), the input variables are the inference variables mentioned by the function type's parameter types and the function type's return type.
+ //
+ // Otherwise, there are no input variables.
+ //
+ // For ‹Expression → T›, if Expression is a parenthesized expression:
+ //
+ //Where the contained expression of Expression is Expression', the input variables are the input variables of ‹Expression' → T›.
+ //
+ //For ‹ConditionalExpression → T›:
+ //
+ //Where the conditional expression has the form e1 ? e2 : e3, the input variables are the input variables of ‹e2 → T› and ‹e3 → T›.
+ //
+ //For all other constraint formulas, there are no input variables.
+ //
+ //The output variables of these constraints are all inference variables mentioned by the type on the right-hand side of the constraint, T, that are not input variables.
+
+ throw new UnsupportedOperationException();
+ }
+
+ public void functionalInterfaceParameterizationInference(LambdaExpr lambdaExpr,
+ ResolvedInterfaceDeclaration interfaceDeclaration) {
+ // Where a lambda expression with explicit parameter types P1, ..., Pn targets a functional interface
+ // type F<A1, ..., Am> with at least one wildcard type argument, then a parameterization of F may be derived
+ // as the ground target type of the lambda expression as follows.
+
+ int n = lambdaExpr.getParameters().size();
+
+ if (interfaceDeclaration.getTypeParameters().isEmpty()) {
+ throw new IllegalArgumentException("Functional Interface without type arguments");
+ }
+
+ // Let Q1, ..., Qk be the parameter types of the function type of the type F<α1, ..., αm>,
+ // where α1, ..., αm are fresh inference variables.
+
+ int k = interfaceDeclaration.getTypeParameters().size();
+ List<InferenceVariable> alphas = InferenceVariable.instantiate(interfaceDeclaration.getTypeParameters());
+
+ TypeInferenceCache.recordInferenceVariables(typeSolver, lambdaExpr, alphas);
+
+ // If n ≠ k, no valid parameterization exists.
+
+ if (n != k) {
+ throw new IllegalArgumentException("No valida parameterization can exist has n=" + " and k=" + k);
+ }
+
+ // Otherwise, a set of constraint formulas is formed with, for
+ // all i (1 ≤ i ≤ n), ‹Pi = Qi›. This constraint formula set is reduced to form the bound set B.
+
+ ConstraintFormulaSet constraintFormulaSet = ConstraintFormulaSet.empty();
+ for (int i=0; i<n; i++) {
+ throw new UnsupportedOperationException();
+ //Type pi = JavaParserFacade.get(typeSolver).convertToUsage(lambdaExpr.getParameters().get(i).getType(), lambdaExpr);
+ //Type qi = JavaParserFacade.get(typeSolver).convertToUsage(interfaceDeclaration.getm.get(i).getType(), lambdaExpr);
+ //constraintFormulaSet = constraintFormulaSet.withConstraint(new TypeSameAsType(pi, qi));
+ }
+ BoundSet B = constraintFormulaSet.reduce(typeSolver);
+
+ // If B contains the bound false, no valid parameterization exists. Otherwise, a new parameterization of the
+ // functional interface type, F<A'1, ..., A'm>, is constructed as follows, for 1 ≤ i ≤ m:
+ //
+ // - If B contains an instantiation (§18.1.3) for αi, T, then A'i = T.
+ //
+ // - Otherwise, A'i = Ai.
+ //
+ // If F<A'1, ..., A'm> is not a well-formed type (that is, the type arguments are not within their bounds), or if F<A'1, ..., A'm> is not a subtype of F<A1, ..., Am>, no valid parameterization exists. Otherwise, the inferred parameterization is either F<A'1, ..., A'm>, if all the type arguments are types, or the non-wildcard parameterization (§9.9) of F<A'1, ..., A'm>, if one or more type arguments are still wildcards.
+
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Return if m2 is more specific than m1
+ * @param methodCall
+ * @param m1
+ * @param m2
+ */
+ public boolean moreSpecificMethodInference(MethodCallExpr methodCall, ResolvedMethodDeclaration m1, ResolvedMethodDeclaration m2) {
+ // When testing that one applicable method is more specific than another (§15.12.2.5), where the second method
+ // is generic, it is necessary to test whether some instantiation of the second method's type parameters can be
+ // inferred to make the first method more specific than the second.
+
+ if (!m2.isGeneric()) {
+ throw new IllegalArgumentException("M2 is not generic (m2: " + m2 + ")");
+ }
+
+ // Let m1 be the first method and m2 be the second method. Where m2 has type parameters P1, ..., Pp,
+ // let α1, ..., αp be inference variables, and let θ be the substitution [P1:=α1, ..., Pp:=αp].
+ //
+ // Let e1, ..., ek be the argument expressions of the corresponding invocation. Then:
+ //
+ // - If m1 and m2 are applicable by strict or loose invocation (§15.12.2.2, §15.12.2.3), then let S1, ..., Sk be the formal parameter types of m1, and let T1, ..., Tk be the result of θ applied to the formal parameter types of m2.
+ //
+ // - If m1 and m2 are applicable by variable arity invocation (§15.12.2.4), then let S1, ..., Sk be the first k variable arity parameter types of m1, and let T1, ..., Tk be the result of θ applied to the first k variable arity parameter types of m2.
+ //
+ // Note that no substitution is applied to S1, ..., Sk; even if m1 is generic, the type parameters of m1 are treated as type variables, not inference variables.
+ //
+ // The process to determine if m1 is more specific than m2 is as follows:
+ //
+ // - First, an initial bound set, B, is constructed from the declared bounds of P1, ..., Pp, as specified in §18.1.3.
+ //
+ // - Second, for all i (1 ≤ i ≤ k), a set of constraint formulas or bounds is generated.
+ //
+ // If Ti is a proper type, the result is true if Si is more specific than Ti for ei (§15.12.2.5), and false otherwise. (Note that Si is always a proper type.)
+ //
+ // Otherwise, if Ti is not a functional interface type, the constraint formula ‹Si <: Ti› is generated.
+ //
+ // Otherwise, Ti is a parameterization of a functional interface, I. It must be determined whether Si satisfies the following five conditions:
+ //
+ // 1. Si is a functional interface type.
+ //
+ // 2. Si is not a superinterface of I, nor a parameterization of a superinterface of I.
+ //
+ // 3. Si is not a subinterface of I, nor a parameterization of a subinterface of I.
+ //
+ // 4. If Si is an intersection type, at least one element of the intersection is not a superinterface of I, nor a parameterization of a superinterface of I.
+ //
+ // 5. If Si is an intersection type, no element of the intersection is a subinterface of I, nor a parameterization of a subinterface of I.
+ //
+ // If all five conditions are true, then the following constraint formulas or bounds are generated (where U1 ... Uk and R1 are the parameter types and return type of the function type of the capture of Si, and V1 ... Vk and R2 are the parameter types and return type of the function type of Ti):
+ //
+ // - If ei is an explicitly typed lambda expression:
+ //
+ // - For all j (1 ≤ j ≤ k), ‹Uj = Vj›.
+ //
+ // - If R2 is void, true.
+ //
+ // - Otherwise, if R1 and R2 are functional interface types, and neither interface is a subinterface of the other, and ei has at least one result expression, then these rules are applied recursively to R1 and R2, for each result expression in ei.
+ //
+ // - Otherwise, if R1 is a primitive type and R2 is not, and ei has at least one result expression, and each result expression of ei is a standalone expression (§15.2) of a primitive type, true.
+ //
+ // - Otherwise, if R2 is a primitive type and R1 is not, and ei has at least one result expression, and each result expression of ei is either a standalone expression of a reference type or a poly expression, true.
+ //
+ // - Otherwise, ‹R1 <: R2›.
+ //
+ // - If ei is an exact method reference:
+ //
+ // - For all j (1 ≤ j ≤ k), ‹Uj = Vj›.
+ //
+ // - If R2 is void, true.
+ //
+ // - Otherwise, if R1 is a primitive type and R2 is not, and the compile-time declaration for ei has a primitive return type, true.
+ //
+ // - Otherwise if R2 is a primitive type and R1 is not, and the compile-time declaration for ei has a reference return type, true.
+ //
+ // - Otherwise, ‹R1 <: R2›.
+ //
+ // - If ei is a parenthesized expression, these rules are applied recursively to the contained expression.
+ //
+ // - If ei is a conditional expression, these rules are applied recursively to each of the second and third operands.
+ //
+ // - Otherwise, false.
+ //
+ // If the five constraints on Si are not satisfied, the constraint formula ‹Si <: Ti› is generated instead.
+ //
+ // - Third, if m2 is applicable by variable arity invocation and has k+1 parameters, then where Sk+1 is the k+1'th variable arity parameter type of m1 and Tk+1 is the result of θ applied to the k+1'th variable arity parameter type of m2, the constraint ‹Sk+1 <: Tk+1› is generated.
+ //
+ // - Fourth, the generated bounds and constraint formulas are reduced and incorporated with B to produce a bound set B'.
+ //
+ // If B' does not contain the bound false, and resolution of all the inference variables in B' succeeds, then m1 is more specific than m2.
+ //
+ // Otherwise, m1 is not more specific than m2.
+
+ throw new UnsupportedOperationException();
+ }
+
+
+ ///
+ /// Private static methods
+ ///
+
+ private static MethodUsage instantiationSetToMethodUsage(ResolvedMethodDeclaration methodDeclaration, InstantiationSet instantiationSet) {
+ if (instantiationSet.isEmpty()) {
+ return new MethodUsage(methodDeclaration);
+ }
+ List<ResolvedType> paramTypes = new LinkedList<>();
+ for (int i=0;i<methodDeclaration.getNumberOfParams();i++) {
+ paramTypes.add(instantiationSet.apply(methodDeclaration.getParam(i).getType()));
+ }
+ ResolvedType returnType = instantiationSet.apply(methodDeclaration.getReturnType());
+ return new MethodUsage(methodDeclaration, paramTypes, returnType);
+ }
+
+ ///
+ /// Private instance methods
+ ///
+
+ /**
+ * When inference begins, a bound set is typically generated from a list of type parameter declarations P1, ..., Pp
+ * and associated inference variables α1, ..., αp
+ *
+ * @param typeParameterDeclarations
+ * @param inferenceVariables
+ * @return
+ */
+ private BoundSet boundSetup(List<ResolvedTypeParameterDeclaration> typeParameterDeclarations, List<InferenceVariable> inferenceVariables) {
+ if (typeParameterDeclarations.size() != inferenceVariables.size()) {
+ throw new IllegalArgumentException();
+ }
+
+ // When inference begins, a bound set is typically generated from a list of
+ // type parameter declarations P1, ..., Pp and associated inference variables α1, ..., αp.
+ // Such a bound set is constructed as follows. For each l (1 ≤ l ≤ p):
+
+ BoundSet boundSet = BoundSet.empty();
+
+ for (int l=0;l<typeParameterDeclarations.size();l++) {
+ ResolvedTypeParameterDeclaration Pl = typeParameterDeclarations.get(l);
+ InferenceVariable alphaL = inferenceVariables.get(l);
+
+ // - If Pl has no TypeBound, the bound αl <: Object appears in the set.
+
+ if (Pl.getBounds().isEmpty()) {
+ boundSet = boundSet.withBound(new SubtypeOfBound(alphaL, object));
+ } else {
+
+ // - Otherwise, for each type T delimited by & in the TypeBound, the bound αl <: T[P1:=α1, ..., Pp:=αp] appears
+ // in the set; if this results in no proper upper bounds for αl (only dependencies), then the
+ // bound αl <: Object also appears in the set.
+
+ for (ResolvedTypeParameterDeclaration.Bound bound : Pl.getBounds()) {
+ ResolvedType T = bound.getType();
+ Substitution substitution = Substitution.empty();
+ for (int j=0;j<typeParameterDeclarations.size();j++) {
+ substitution = substitution.withPair(typeParameterDeclarations.get(j), inferenceVariables.get(j));
+ }
+ ResolvedType TWithSubstitutions = substitution.apply(T);
+
+ boundSet = boundSet.withBound(new SubtypeOfBound(alphaL, TWithSubstitutions));
+
+ if (boundSet.getProperUpperBoundsFor(alphaL).isEmpty()) {
+ boundSet = boundSet.withBound(new SubtypeOfBound(alphaL, object));
+ }
+ }
+ }
+ }
+
+ return boundSet;
+ }
+
+ private boolean appearInThrowsClause(ResolvedTypeParameterDeclaration p, ResolvedMethodDeclaration methodDeclaration) {
+ for (int j=0;j<methodDeclaration.getNumberOfSpecifiedExceptions();j++) {
+ ResolvedType thrownType = methodDeclaration.getSpecifiedException(j);
+ if (thrownType.isTypeVariable() && thrownType.asTypeVariable().asTypeParameter().equals(p)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private List<ResolvedType> formalParameterTypes(ResolvedMethodDeclaration methodDeclaration) {
+ List<ResolvedType> types = new LinkedList<>();
+ for (int i=0;i<methodDeclaration.getNumberOfParams();i++) {
+ types.add(methodDeclaration.getParam(i).getType());
+ }
+ return types;
+ }
+
+ private boolean isImplicitlyTyped(LambdaExpr lambdaExpr) {
+ return lambdaExpr.getParameters().stream().anyMatch(p -> p.getType() instanceof UnknownType);
+ }
+
+ private boolean isInexact(MethodReferenceExpr methodReferenceExpr) {
+ throw new UnsupportedOperationException();
+ }
+
+ private boolean isPertinentToApplicability(Expression argument) {
+ // An argument expression is considered pertinent to applicability for a potentially applicable method m
+ // unless it has one of the following forms:
+ //
+ // - An implicitly typed lambda expression (§15.27.1).
+
+ if (argument instanceof LambdaExpr) {
+ LambdaExpr lambdaExpr = (LambdaExpr)argument;
+ if (isImplicitlyTyped(lambdaExpr)) {
+ return false;
+ }
+ }
+
+ // - An inexact method reference expression (§15.13.1).
+
+ if (argument instanceof MethodReferenceExpr) {
+ MethodReferenceExpr methodReferenceExpr = (MethodReferenceExpr)argument;
+ if (isInexact(methodReferenceExpr)) {
+ return false;
+ }
+ }
+
+ // - If m is a generic method and the method invocation does not provide explicit type arguments, an
+ // explicitly typed lambda expression or an exact method reference expression for which the
+ // corresponding target type (as derived from the signature of m) is a type parameter of m.
+
+ if (argument instanceof LambdaExpr) {
+ throw new UnsupportedOperationException();
+ }
+
+ if (argument instanceof MethodReferenceExpr) {
+ throw new UnsupportedOperationException();
+ }
+
+ // - An explicitly typed lambda expression whose body is an expression that is not pertinent to applicability.
+
+ if (argument instanceof LambdaExpr) {
+ throw new UnsupportedOperationException();
+ }
+
+ // - An explicitly typed lambda expression whose body is a block, where at least one result expression is not
+ // pertinent to applicability.
+
+ if (argument instanceof LambdaExpr) {
+ throw new UnsupportedOperationException();
+ }
+
+ // - A parenthesized expression (§15.8.5) whose contained expression is not pertinent to applicability.
+
+ if (argument instanceof EnclosedExpr) {
+ EnclosedExpr enclosedExpr = (EnclosedExpr)argument;
+ return isPertinentToApplicability(enclosedExpr.getInner());
+ }
+
+ // - A conditional expression (§15.25) whose second or third operand is not pertinent to applicability.
+
+ if (argument instanceof ConditionalExpr) {
+ ConditionalExpr conditionalExpr = (ConditionalExpr)argument;
+ return isPertinentToApplicability(conditionalExpr.getThenExpr()) &&
+ isPertinentToApplicability(conditionalExpr.getElseExpr());
+ }
+
+ return true;
+ }
+
+ private Optional<ConstraintFormulaSet> testForApplicabilityByStrictInvocation(List<ResolvedType> Fs, List<Expression> es,
+ Substitution theta) {
+ int n = Fs.size();
+ int k = es.size();
+
+ // If k ≠ n, or if there exists an i (1 ≤ i ≤ n) such that ei is pertinent to applicability (§15.12.2.2)
+ // and either:
+ // i) ei is a standalone expression of a primitive type but Fi is a reference type, or
+ // ii) Fi is a primitive type but ei is not a standalone expression of a primitive type;
+ if (k != n) {
+ return Optional.empty();
+ }
+ for (int i=0;i<n;i++) {
+ Expression ei = es.get(i);
+ ResolvedType fi = Fs.get(i);
+ if (isPertinentToApplicability(ei)) {
+ if (isStandaloneExpression(ei) && JavaParserFacade.get(typeSolver).getType(ei).isPrimitive()
+ && fi.isReferenceType()) {
+ return Optional.empty();
+ }
+ if (fi.isPrimitive() && (!isStandaloneExpression(ei) || !JavaParserFacade.get(typeSolver).getType(ei).isPrimitive())) {
+ return Optional.empty();
+ }
+ }
+ }
+ // then the method is not applicable and there is no need to proceed with inference.
+ //
+ // Otherwise, C includes, for all i (1 ≤ i ≤ k) where ei is pertinent to applicability, ‹ei → Fi θ›.
+
+ return Optional.of(constraintSetFromArgumentsSubstitution(Fs, es, theta, k));
+ }
+
+ private ResolvedType typeWithSubstitution(ResolvedType originalType, Substitution substitution) {
+ return substitution.apply(originalType);
+ }
+
+ private Optional<ConstraintFormulaSet> testForApplicabilityByLooseInvocation(List<ResolvedType> Fs, List<Expression> es,
+ Substitution theta) {
+ int n = Fs.size();
+ int k = es.size();
+
+ // If k ≠ n, the method is not applicable and there is no need to proceed with inference.
+
+ if (k != n) {
+ return Optional.empty();
+ }
+
+ // Otherwise, C includes, for all i (1 ≤ i ≤ k) where ei is pertinent to applicability, ‹ei → Fi θ›.
+ return Optional.of(constraintSetFromArgumentsSubstitution(Fs, es, theta, k));
+ }
+
+ private ConstraintFormulaSet constraintSetFromArgumentsSubstitution(List<ResolvedType> Fs, List<Expression> es, Substitution theta, int k) {
+ ConstraintFormulaSet constraintFormulaSet = ConstraintFormulaSet.empty();
+ for (int i=0;i<k;i++) {
+ Expression ei = es.get(i);
+ ResolvedType fi = Fs.get(i);
+ ResolvedType fiTheta = typeWithSubstitution(fi, theta);
+ constraintFormulaSet = constraintFormulaSet.withConstraint(
+ new ExpressionCompatibleWithType(typeSolver, ei, fiTheta));
+ }
+ return constraintFormulaSet;
+ }
+
+ private Optional<ConstraintFormulaSet> testForApplicabilityByVariableArityInvocation(List<ResolvedType> Fs, List<Expression> es,
+ Substitution theta) {
+ int k = es.size();
+
+ // Let F'1, ..., F'k be the first k variable arity parameter types of m (§15.12.2.4). C includes,
+ // for all i (1 ≤ i ≤ k) where ei is pertinent to applicability, ‹ei → F'i θ›.
+
+ List<ResolvedType> FsFirst = new LinkedList<>();
+ for (int i=0;i<k;i++) {
+ ResolvedType FFirstI = i < Fs.size() ? Fs.get(i) : Fs.get(Fs.size() - 1);
+ FsFirst.add(FFirstI);
+ }
+
+ return Optional.of(constraintSetFromArgumentsSubstitution(FsFirst, es, theta, k));
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/TypeInferenceCache.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/TypeInferenceCache.java
new file mode 100644
index 000000000..496aa0001
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/TypeInferenceCache.java
@@ -0,0 +1,56 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference;
+
+import com.github.javaparser.ast.expr.LambdaExpr;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.*;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class TypeInferenceCache {
+
+ private static Map<TypeSolver, IdentityHashMap<LambdaExpr, Map<String, ResolvedType>>> typeForLambdaParameters = new HashMap<>();
+ private static Map<TypeSolver, IdentityHashMap<LambdaExpr, List<InferenceVariable>>> inferenceVariables = new HashMap<>();
+
+ public static void record(TypeSolver typeSolver, LambdaExpr lambdaExpr, String paramName, ResolvedType type) {
+ if (!typeForLambdaParameters.containsKey(typeSolver)) {
+ typeForLambdaParameters.put(typeSolver, new IdentityHashMap<>());
+ }
+ if (!typeForLambdaParameters.get(typeSolver).containsKey(lambdaExpr)) {
+ typeForLambdaParameters.get(typeSolver).put(lambdaExpr, new HashMap<>());
+ }
+ typeForLambdaParameters.get(typeSolver).get(lambdaExpr).put(paramName, type);
+ }
+
+ public static Optional<ResolvedType> retrieve(TypeSolver typeSolver, LambdaExpr lambdaExpr, String paramName) {
+ if (!typeForLambdaParameters.containsKey(typeSolver)) {
+ return Optional.empty();
+ }
+ if (!typeForLambdaParameters.get(typeSolver).containsKey(lambdaExpr)) {
+ return Optional.empty();
+ }
+ if (!typeForLambdaParameters.get(typeSolver).get(lambdaExpr).containsKey(paramName)) {
+ return Optional.empty();
+ }
+ return Optional.of(typeForLambdaParameters.get(typeSolver).get(lambdaExpr).get(paramName));
+ }
+
+ public static void recordInferenceVariables(TypeSolver typeSolver, LambdaExpr lambdaExpr, List<InferenceVariable> _inferenceVariables) {
+ if (!inferenceVariables.containsKey(typeSolver)) {
+ inferenceVariables.put(typeSolver, new IdentityHashMap<>());
+ }
+ inferenceVariables.get(typeSolver).put(lambdaExpr, _inferenceVariables);
+ }
+
+ public static Optional<List<InferenceVariable>> retrieveInferenceVariables(TypeSolver typeSolver, LambdaExpr lambdaExpr) {
+ if (!inferenceVariables.containsKey(typeSolver)) {
+ return Optional.empty();
+ }
+ if (!inferenceVariables.get(typeSolver).containsKey(lambdaExpr)) {
+ return Optional.empty();
+ }
+ return Optional.of(inferenceVariables.get(typeSolver).get(lambdaExpr));
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/CapturesBound.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/CapturesBound.java
new file mode 100644
index 000000000..563bb29e7
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/CapturesBound.java
@@ -0,0 +1,69 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference.bounds;
+
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.resolution.typeinference.Bound;
+import com.github.javaparser.symbolsolver.resolution.typeinference.InferenceVariable;
+import com.github.javaparser.symbolsolver.resolution.typeinference.InferenceVariableSubstitution;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Capture(G<A1, ..., An>): The variables α1, ..., αn represent the result of capture conversion (§5.1.10)
+ * applied to G<A1, ..., An> (where A1, ..., An may be types or wildcards and may mention inference variables).
+ *
+ * @author Federico Tomassetti
+ */
+public class CapturesBound extends Bound {
+ private List<InferenceVariable> inferenceVariables;
+ private List<ResolvedType> typesOrWildcards;
+
+ public CapturesBound(List<InferenceVariable> inferenceVariables, List<ResolvedType> typesOrWildcards) {
+ this.inferenceVariables = inferenceVariables;
+ this.typesOrWildcards = typesOrWildcards;
+ }
+
+ @Override
+ public boolean isSatisfied(InferenceVariableSubstitution inferenceVariableSubstitution) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<InferenceVariable> usedInferenceVariables() {
+ throw new UnsupportedOperationException();
+ }
+
+ public List<InferenceVariable> getInferenceVariables() {
+ return inferenceVariables;
+ }
+
+ public List<ResolvedType> getTypesOrWildcards() {
+ return typesOrWildcards;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ CapturesBound that = (CapturesBound) o;
+
+ if (!inferenceVariables.equals(that.inferenceVariables)) return false;
+ return typesOrWildcards.equals(that.typesOrWildcards);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = inferenceVariables.hashCode();
+ result = 31 * result + typesOrWildcards.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "CapturesBound{" +
+ "inferenceVariables=" + inferenceVariables +
+ ", typesOrWildcards=" + typesOrWildcards +
+ '}';
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/FalseBound.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/FalseBound.java
new file mode 100644
index 000000000..b1554db3d
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/FalseBound.java
@@ -0,0 +1,42 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference.bounds;
+
+import com.github.javaparser.symbolsolver.resolution.typeinference.Bound;
+import com.github.javaparser.symbolsolver.resolution.typeinference.InferenceVariable;
+import com.github.javaparser.symbolsolver.resolution.typeinference.InferenceVariableSubstitution;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * No valid choice of inference variables exists.
+ *
+ * @author Federico Tomassetti
+ */
+public class FalseBound extends Bound {
+
+ private static FalseBound INSTANCE = new FalseBound();
+
+ private FalseBound() {
+
+ }
+
+ public static FalseBound getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public String toString() {
+ return "FalseBound{}";
+ }
+
+ @Override
+ public boolean isSatisfied(InferenceVariableSubstitution inferenceVariableSubstitution) {
+ return false;
+ }
+
+ @Override
+ public Set<InferenceVariable> usedInferenceVariables() {
+ return Collections.emptySet();
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/SameAsBound.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/SameAsBound.java
new file mode 100644
index 000000000..4d0756bac
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/SameAsBound.java
@@ -0,0 +1,96 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference.bounds;
+
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.resolution.typeinference.Bound;
+import com.github.javaparser.symbolsolver.resolution.typeinference.InferenceVariable;
+import com.github.javaparser.symbolsolver.resolution.typeinference.InferenceVariableSubstitution;
+import com.github.javaparser.symbolsolver.resolution.typeinference.Instantiation;
+import com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper;
+
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isInferenceVariable;
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isProperType;
+
+/**
+ * S = T, where at least one of S or T is an inference variable: S is the same as T.
+ *
+ * @author Federico Tomassetti
+ */
+public class SameAsBound extends Bound {
+ private ResolvedType s;
+ private ResolvedType t;
+
+ public SameAsBound(ResolvedType s, ResolvedType t) {
+ if (!isInferenceVariable(s) && !isInferenceVariable(t)) {
+ throw new IllegalArgumentException("One of S or T should be an inference variable");
+ }
+ this.s = s;
+ this.t = t;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ SameAsBound that = (SameAsBound) o;
+
+ if (!s.equals(that.s)) return false;
+ return t.equals(that.t);
+ }
+
+ @Override
+ public String toString() {
+ return "SameAsBound{" +
+ "s=" + s +
+ ", t=" + t +
+ '}';
+ }
+
+ @Override
+ public int hashCode() {
+ int result = s.hashCode();
+ result = 31 * result + t.hashCode();
+ return result;
+ }
+
+ @Override
+ public Set<InferenceVariable> usedInferenceVariables() {
+ Set<InferenceVariable> variables = new HashSet<>();
+ variables.addAll(TypeHelper.usedInferenceVariables(s));
+ variables.addAll(TypeHelper.usedInferenceVariables(t));
+ return variables;
+ }
+
+ public ResolvedType getS() {
+ return s;
+ }
+
+ public ResolvedType getT() {
+ return t;
+ }
+
+ @Override
+ public boolean isADependency() {
+ return !isAnInstantiation().isPresent();
+ }
+
+ @Override
+ public Optional<Instantiation> isAnInstantiation() {
+ if (isInferenceVariable(s) && isProperType(t)) {
+ return Optional.of(new Instantiation((InferenceVariable) s, t));
+ }
+ if (isProperType(s) && isInferenceVariable(t)) {
+ return Optional.of(new Instantiation((InferenceVariable) t, s));
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public boolean isSatisfied(InferenceVariableSubstitution inferenceVariableSubstitution) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/SubtypeOfBound.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/SubtypeOfBound.java
new file mode 100644
index 000000000..357e7a000
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/SubtypeOfBound.java
@@ -0,0 +1,98 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference.bounds;
+
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.resolution.typeinference.*;
+import com.github.javaparser.utils.Pair;
+
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isInferenceVariable;
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isProperType;
+
+/**
+ * S <: T, where at least one of S or T is an inference variable: S is a subtype of T
+ *
+ * @author Federico Tomassetti
+ */
+public class SubtypeOfBound extends Bound {
+ private ResolvedType s;
+ private ResolvedType t;
+
+ public SubtypeOfBound(ResolvedType s, ResolvedType t) {
+ if (!isInferenceVariable(s) && !isInferenceVariable(t)) {
+ throw new IllegalArgumentException("One of S or T should be an inference variable");
+ }
+ this.s = s;
+ this.t = t;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ SubtypeOfBound that = (SubtypeOfBound) o;
+
+ if (!s.equals(that.s)) return false;
+ return t.equals(that.t);
+ }
+
+ @Override
+ public String toString() {
+ return "SubtypeOfBound{" +
+ "s=" + s +
+ ", t=" + t +
+ '}';
+ }
+
+ @Override
+ public int hashCode() {
+ int result = s.hashCode();
+ result = 31 * result + t.hashCode();
+ return result;
+ }
+
+ public ResolvedType getS() {
+ return s;
+ }
+
+ @Override
+ public Set<InferenceVariable> usedInferenceVariables() {
+ Set<InferenceVariable> variables = new HashSet<>();
+ variables.addAll(TypeHelper.usedInferenceVariables(s));
+ variables.addAll(TypeHelper.usedInferenceVariables(t));
+ return variables;
+ }
+
+ public ResolvedType getT() {
+ return t;
+ }
+
+ @Override
+ public Optional<ProperUpperBound> isProperUpperBound() {
+ if (isInferenceVariable(s) && isProperType(t)) {
+ return Optional.of(new ProperUpperBound((InferenceVariable) s, t));
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional<ProperLowerBound> isProperLowerBound() {
+ if (isProperType(s) && isInferenceVariable(t)) {
+ return Optional.of(new ProperLowerBound((InferenceVariable) t, s));
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public boolean isADependency() {
+ return !isProperLowerBound().isPresent() && !isProperUpperBound().isPresent();
+ }
+
+ @Override
+ public boolean isSatisfied(InferenceVariableSubstitution inferenceVariableSubstitution) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/ThrowsBound.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/ThrowsBound.java
new file mode 100644
index 000000000..acea3bae8
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/ThrowsBound.java
@@ -0,0 +1,62 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference.bounds;
+
+import com.github.javaparser.symbolsolver.resolution.typeinference.Bound;
+import com.github.javaparser.symbolsolver.resolution.typeinference.InferenceVariable;
+import com.github.javaparser.symbolsolver.resolution.typeinference.InferenceVariableSubstitution;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * The inference variable α appears in a throws clause.
+ *
+ * A bound of the form throws α is purely informational: it directs resolution to optimize the instantiation of α so
+ * that, if possible, it is not a checked exception type.
+ *
+ * @author Federico Tomassetti
+ */
+public class ThrowsBound extends Bound {
+ private InferenceVariable inferenceVariable;
+
+ public ThrowsBound(InferenceVariable inferenceVariable) {
+ this.inferenceVariable = inferenceVariable;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ThrowsBound that = (ThrowsBound) o;
+
+ return inferenceVariable.equals(that.inferenceVariable);
+ }
+
+ @Override
+ public String toString() {
+ return "ThrowsBound{" +
+ "inferenceVariable=" + inferenceVariable +
+ '}';
+ }
+
+ @Override
+ public int hashCode() {
+ return inferenceVariable.hashCode();
+ }
+
+ @Override
+ public Set<InferenceVariable> usedInferenceVariables() {
+ Set<InferenceVariable> variables = new HashSet<>();
+ variables.add(inferenceVariable);
+ return variables;
+ }
+
+ @Override
+ public boolean isSatisfied(InferenceVariableSubstitution inferenceVariableSubstitution) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isThrowsBoundOn(InferenceVariable inferenceVariable) {
+ return inferenceVariable.equals(this.inferenceVariable);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/ExpressionCompatibleWithType.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/ExpressionCompatibleWithType.java
new file mode 100644
index 000000000..30042f8a8
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/ExpressionCompatibleWithType.java
@@ -0,0 +1,335 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas;
+
+import com.github.javaparser.ast.expr.*;
+import com.github.javaparser.ast.stmt.*;
+import com.github.javaparser.ast.type.UnknownType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.resolution.types.ResolvedTypeVariable;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.logic.FunctionalInterfaceLogic;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.resolution.typeinference.*;
+import com.github.javaparser.utils.Pair;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import static com.github.javaparser.symbolsolver.resolution.typeinference.ExpressionHelper.isPolyExpression;
+import static com.github.javaparser.symbolsolver.resolution.typeinference.ExpressionHelper.isStandaloneExpression;
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isCompatibleInALooseInvocationContext;
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isProperType;
+import static java.util.stream.Collectors.*;
+
+/**
+ * An expression is compatible in a loose invocation context with type T
+ *
+ * @author Federico Tomassetti
+ */
+public class ExpressionCompatibleWithType extends ConstraintFormula {
+ private TypeSolver typeSolver;
+ private Expression expression;
+ private ResolvedType T;
+
+ public ExpressionCompatibleWithType(TypeSolver typeSolver, Expression expression, ResolvedType T) {
+ this.typeSolver = typeSolver;
+ this.expression = expression;
+ this.T = T;
+ }
+
+ @Override
+ public ReductionResult reduce(BoundSet currentBoundSet) {
+ // If T is a proper type, the constraint reduces to true if the expression is compatible in a loose
+ // invocation context with T (§5.3), and false otherwise.
+
+ if (isProperType(T)) {
+ if (isCompatibleInALooseInvocationContext(typeSolver, expression, T)) {
+ return ReductionResult.trueResult();
+ } else {
+ return ReductionResult.falseResult();
+ }
+ }
+
+ // Otherwise, if the expression is a standalone expression (§15.2) of type S, the constraint reduces
+ // to ‹S → T›.
+
+ if (isStandaloneExpression(expression)) {
+ ResolvedType s = JavaParserFacade.get(typeSolver).getType(expression, false);
+ return ReductionResult.empty().withConstraint(new TypeCompatibleWithType(typeSolver, s, T));
+ }
+
+ // Otherwise, the expression is a poly expression (§15.2). The result depends on the form of the expression:
+
+ if (isPolyExpression(expression)) {
+
+ // - If the expression is a parenthesized expression of the form ( Expression' ), the constraint reduces
+ // to ‹Expression' → T›.
+
+ if (expression instanceof EnclosedExpr) {
+ EnclosedExpr enclosedExpr = (EnclosedExpr)expression;
+ return ReductionResult.oneConstraint(new ExpressionCompatibleWithType(typeSolver, enclosedExpr.getInner(), T));
+ }
+
+ // - If the expression is a class instance creation expression or a method invocation expression, the
+ // constraint reduces to the bound set B3 which would be used to determine the expression's invocation
+ // type when targeting T, as defined in §18.5.2. (For a class instance creation expression, the
+ // corresponding "method" used for inference is defined in §15.9.3).
+ //
+ // This bound set may contain new inference variables, as well as dependencies between these new
+ // variables and the inference variables in T.
+
+ if (expression instanceof ObjectCreationExpr) {
+ BoundSet B3 = new TypeInference(typeSolver).invocationTypeInferenceBoundsSetB3();
+ return ReductionResult.bounds(B3);
+ }
+
+ if (expression instanceof MethodCallExpr) {
+ throw new UnsupportedOperationException();
+ }
+
+ // - If the expression is a conditional expression of the form e1 ? e2 : e3, the constraint reduces to two
+ // constraint formulas, ‹e2 → T› and ‹e3 → T›.
+
+ if (expression instanceof ConditionalExpr) {
+ ConditionalExpr conditionalExpr = (ConditionalExpr)expression;
+ return ReductionResult.withConstraints(
+ new ExpressionCompatibleWithType(typeSolver, conditionalExpr.getThenExpr(), T),
+ new ExpressionCompatibleWithType(typeSolver, conditionalExpr.getElseExpr(), T));
+ }
+
+ // - If the expression is a lambda expression or a method reference expression, the result is specified
+ // below.
+
+ // A constraint formula of the form ‹LambdaExpression → T›, where T mentions at least one inference variable, is reduced as follows:
+
+ if (expression instanceof LambdaExpr) {
+ LambdaExpr lambdaExpr = (LambdaExpr)expression;
+
+ // - If T is not a functional interface type (§9.8), the constraint reduces to false.
+
+ if (!FunctionalInterfaceLogic.isFunctionalInterfaceType(T)) {
+ return ReductionResult.falseResult();
+ }
+
+ // - Otherwise, let T' be the ground target type derived from T, as specified in §15.27.3. If §18.5.3
+ // is used to derive a functional interface type which is parameterized, then the test that
+ // F<A'1, ..., A'm> is a subtype of F<A1, ..., Am> is not performed (instead, it is asserted with a
+ // constraint formula below). Let the target function type for the lambda expression be the
+ // function type of T'. Then:
+
+ Pair<ResolvedType, Boolean> result = TypeHelper.groundTargetTypeOfLambda(lambdaExpr, T, typeSolver);
+ ResolvedType TFirst = result.a;
+ MethodType targetFunctionType = TypeHelper.getFunctionType(TFirst);
+ targetFunctionType = replaceTypeVariablesWithInferenceVariables(targetFunctionType);
+ if (result.b) {
+ throw new UnsupportedOperationException();
+ }
+
+ // - If no valid function type can be found, the constraint reduces to false.
+ //
+ // Federico: THIS SHOULD NOT HAPPEN, IN CASE WE WILL THROW AN EXCEPTION
+ //
+ // - Otherwise, the congruence of LambdaExpression with the target function type is asserted as
+ // follows:
+ //
+ // - If the number of lambda parameters differs from the number of parameter types of the function
+ // type, the constraint reduces to false.
+
+ if (targetFunctionType.getFormalArgumentTypes().size() != lambdaExpr.getParameters().size()) {
+ return ReductionResult.falseResult();
+ }
+
+ // - If the lambda expression is implicitly typed and one or more of the function type's parameter
+ // types is not a proper type, the constraint reduces to false.
+ //
+ // This condition never arises in practice, due to the handling of implicitly typed lambda
+ // expressions in §18.5.1 and the substitution applied to the target type in §18.5.2.
+
+ // - If the function type's result is void and the lambda body is neither a statement expression
+ // nor a void-compatible block, the constraint reduces to false.
+
+ if (targetFunctionType.getReturnType().isVoid()) {
+ throw new UnsupportedOperationException();
+ }
+
+ // - If the function type's result is not void and the lambda body is a block that is not
+ // value-compatible, the constraint reduces to false.
+
+ if (!targetFunctionType.getReturnType().isVoid() && lambdaExpr.getBody() instanceof BlockStmt
+ && !isValueCompatibleBlock(lambdaExpr.getBody())) {
+ return ReductionResult.falseResult();
+ }
+
+ // - Otherwise, the constraint reduces to all of the following constraint formulas:
+ List<ConstraintFormula> constraints = new LinkedList<>();
+
+ // - If the lambda parameters have explicitly declared types F1, ..., Fn and the function type
+ // has parameter types G1, ..., Gn, then i) for all i (1 ≤ i ≤ n), ‹Fi = Gi›, and ii) ‹T' <: T›.
+
+ boolean hasExplicitlyDeclaredTypes = lambdaExpr.getParameters().stream().anyMatch(p -> !(p.getType() instanceof UnknownType));
+ if (hasExplicitlyDeclaredTypes) {
+ throw new UnsupportedOperationException();
+ }
+
+ // - If the function type's return type is a (non-void) type R, assume the lambda's parameter
+ // types are the same as the function type's parameter types. Then:
+
+ if (!targetFunctionType.getReturnType().isVoid()) {
+
+ ResolvedType R = targetFunctionType.getReturnType();
+
+ if (TypeHelper.isProperType(R)) {
+
+ // - If R is a proper type, and if the lambda body or some result expression in the lambda body
+ // is not compatible in an assignment context with R, then false.
+
+ if (lambdaExpr.getBody() instanceof BlockStmt) {
+ List<Expression> resultExpressions = ExpressionHelper.getResultExpressions((BlockStmt)lambdaExpr.getBody());
+ for (Expression e : resultExpressions) {
+ if (!ExpressionHelper.isCompatibleInAssignmentContext(e, R, typeSolver)) {
+ return ReductionResult.falseResult();
+ }
+ }
+ } else {
+ Expression e = ((ExpressionStmt)lambdaExpr.getBody()).getExpression();
+ if (!ExpressionHelper.isCompatibleInAssignmentContext(e, R, typeSolver)) {
+ return ReductionResult.falseResult();
+ }
+ }
+ } else {
+ // - Otherwise, if R is not a proper type, then where the lambda body has the form Expression,
+ // the constraint ‹Expression → R›; or where the lambda body is a block with result
+ // expressions e1, ..., em, for all i (1 ≤ i ≤ m), ‹ei → R›.
+
+ if (lambdaExpr.getBody() instanceof BlockStmt) {
+ getAllReturnExpressions((BlockStmt)lambdaExpr.getBody()).forEach(e -> constraints.add(new ExpressionCompatibleWithType(typeSolver, e, R)));
+ } else {
+ // FEDERICO: Added - Start
+ for (int i=0;i<lambdaExpr.getParameters().size();i++) {
+ ResolvedType paramType = targetFunctionType.getFormalArgumentTypes().get(i);
+ TypeInferenceCache.record(typeSolver, lambdaExpr, lambdaExpr.getParameter(i).getNameAsString(), paramType);
+ }
+ // FEDERICO: Added - End
+ Expression e = ((ExpressionStmt)lambdaExpr.getBody()).getExpression();
+ constraints.add(new ExpressionCompatibleWithType(typeSolver, e, R));
+ }
+ }
+ }
+
+ return ReductionResult.withConstraints(constraints);
+ }
+
+ // A constraint formula of the form ‹MethodReference → T›, where T mentions at least one inference variable, is reduced as follows:
+
+ if (expression instanceof MethodReferenceExpr) {
+
+ // - If T is not a functional interface type, or if T is a functional interface type that does not have a function type (§9.9), the constraint reduces to false.
+ //
+ // - Otherwise, if there does not exist a potentially applicable method for the method reference when targeting T, the constraint reduces to false.
+ //
+ // - Otherwise, if the method reference is exact (§15.13.1), then let P1, ..., Pn be the parameter types of the function type of T, and let F1, ..., Fk be the parameter types of the potentially applicable method. The constraint reduces to a new set of constraints, as follows:
+ //
+ // - In the special case where n = k+1, the parameter of type P1 is to act as the target reference of the invocation. The method reference expression necessarily has the form ReferenceType :: [TypeArguments] Identifier. The constraint reduces to ‹P1 <: ReferenceType› and, for all i (2 ≤ i ≤ n), ‹Pi → Fi-1›.
+ //
+ // In all other cases, n = k, and the constraint reduces to, for all i (1 ≤ i ≤ n), ‹Pi → Fi›.
+ //
+ // - If the function type's result is not void, let R be its return type. Then, if the result of the potentially applicable compile-time declaration is void, the constraint reduces to false. Otherwise, the constraint reduces to ‹R' → R›, where R' is the result of applying capture conversion (§5.1.10) to the return type of the potentially applicable compile-time declaration.
+ //
+ // - Otherwise, the method reference is inexact, and:
+ //
+ // - If one or more of the function type's parameter types is not a proper type, the constraint reduces to false.
+ //
+ // This condition never arises in practice, due to the handling of inexact method references in §18.5.1 and the substitution applied to the target type in §18.5.2.
+ //
+ // - Otherwise, a search for a compile-time declaration is performed, as specified in §15.13.1. If there is no compile-time declaration for the method reference, the constraint reduces to false. Otherwise, there is a compile-time declaration, and:
+ //
+ // - If the result of the function type is void, the constraint reduces to true.
+ //
+ // - Otherwise, if the method reference expression elides TypeArguments, and the compile-time declaration is a generic method, and the return type of the compile-time declaration mentions at least one of the method's type parameters, then the constraint reduces to the bound set B3 which would be used to determine the method reference's invocation type when targeting the return type of the function type, as defined in §18.5.2. B3 may contain new inference variables, as well as dependencies between these new variables and the inference variables in T.
+ //
+ // - Otherwise, let R be the return type of the function type, and let R' be the result of applying capture conversion (§5.1.10) to the return type of the invocation type (§15.12.2.6) of the compile-time declaration. If R' is void, the constraint reduces to false; otherwise, the constraint reduces to ‹R' → R›.
+
+ throw new UnsupportedOperationException();
+ }
+
+ throw new RuntimeException("This should not happen");
+ }
+
+ throw new RuntimeException("This should not happen");
+ }
+
+ private List<Expression> getAllReturnExpressions(BlockStmt blockStmt) {
+ return blockStmt.findAll(ReturnStmt.class).stream()
+ .filter(r -> r.getExpression().isPresent())
+ .map(r -> r.getExpression().get())
+ .collect(toList());
+ }
+
+ private boolean isValueCompatibleBlock(Statement statement) {
+ // A block lambda body is value-compatible if it cannot complete normally (§14.21) and every return statement
+ // in the block has the form return Expression;.
+
+ if (statement instanceof BlockStmt) {
+ if (!ControlFlowLogic.getInstance().canCompleteNormally(statement)) {
+ return true;
+ }
+ List<ReturnStmt> returnStmts = statement.findAll(ReturnStmt.class);
+ return returnStmts.stream().allMatch(r -> r.getExpression().isPresent());
+ }
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ExpressionCompatibleWithType that = (ExpressionCompatibleWithType) o;
+
+ if (!typeSolver.equals(that.typeSolver)) return false;
+ if (!expression.equals(that.expression)) return false;
+ return T.equals(that.T);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = typeSolver.hashCode();
+ result = 31 * result + expression.hashCode();
+ result = 31 * result + T.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "ExpressionCompatibleWithType{" +
+ "typeSolver=" + typeSolver +
+ ", expression=" + expression +
+ ", T=" + T +
+ '}';
+ }
+
+ private MethodType replaceTypeVariablesWithInferenceVariables(MethodType methodType) {
+ // Find all type variable
+ Map<ResolvedTypeVariable, InferenceVariable> correspondences = new HashMap<>();
+ List<ResolvedType> newFormalArgumentTypes = new LinkedList<>();
+ for (ResolvedType formalArg : methodType.getFormalArgumentTypes()) {
+ newFormalArgumentTypes.add(replaceTypeVariablesWithInferenceVariables(formalArg, correspondences));
+ }
+ ResolvedType newReturnType = replaceTypeVariablesWithInferenceVariables(methodType.getReturnType(), correspondences);
+ return new MethodType(methodType.getTypeParameters(), newFormalArgumentTypes, newReturnType, methodType.getExceptionTypes());
+ }
+
+ private ResolvedType replaceTypeVariablesWithInferenceVariables(ResolvedType originalType, Map<ResolvedTypeVariable, InferenceVariable> correspondences) {
+ if (originalType.isTypeVariable()) {
+ if (!correspondences.containsKey(originalType.asTypeVariable())) {
+ correspondences.put(originalType.asTypeVariable(), InferenceVariable.unnamed(originalType.asTypeVariable().asTypeParameter()));
+ }
+ return correspondences.get(originalType.asTypeVariable());
+ }
+ if (originalType.isPrimitive()) {
+ return originalType;
+ }
+ throw new UnsupportedOperationException(originalType.toString());
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/LambdaThrowsCompatibleWithType.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/LambdaThrowsCompatibleWithType.java
new file mode 100644
index 000000000..b1c26ec68
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/LambdaThrowsCompatibleWithType.java
@@ -0,0 +1,67 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas;
+
+import com.github.javaparser.ast.expr.LambdaExpr;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.resolution.typeinference.BoundSet;
+import com.github.javaparser.symbolsolver.resolution.typeinference.ConstraintFormula;
+
+/**
+ * The checked exceptions thrown by the body of the LambdaExpression are declared by the throws clause of the
+ * function type derived from T.
+ *
+ * @author Federico Tomassetti
+ */
+public class LambdaThrowsCompatibleWithType extends ConstraintFormula {
+ private LambdaExpr lambdaExpression;
+ private ResolvedType T;
+
+ @Override
+ public ReductionResult reduce(BoundSet currentBoundSet) {
+ // A constraint formula of the form ‹LambdaExpression →throws T› is reduced as follows:
+ //
+ // - If T is not a functional interface type (§9.8), the constraint reduces to false.
+ //
+ // - Otherwise, let the target function type for the lambda expression be determined as specified in §15.27.3. If no valid function type can be found, the constraint reduces to false.
+ //
+ // - Otherwise, if the lambda expression is implicitly typed, and one or more of the function type's parameter types is not a proper type, the constraint reduces to false.
+ //
+ // This condition never arises in practice, due to the substitution applied to the target type in §18.5.2.
+ //
+ // - Otherwise, if the function type's return type is neither void nor a proper type, the constraint reduces to false.
+ //
+ // This condition never arises in practice, due to the substitution applied to the target type in §18.5.2.
+ //
+ // - Otherwise, let E1, ..., En be the types in the function type's throws clause that are not proper types. If the lambda expression is implicitly typed, let its parameter types be the function type's parameter types. If the lambda body is a poly expression or a block containing a poly result expression, let the targeted return type be the function type's return type. Let X1, ..., Xm be the checked exception types that the lambda body can throw (§11.2). Then there are two cases:
+ //
+ // - If n = 0 (the function type's throws clause consists only of proper types), then if there exists some i (1 ≤ i ≤ m) such that Xi is not a subtype of any proper type in the throws clause, the constraint reduces to false; otherwise, the constraint reduces to true.
+ //
+ // - If n > 0, the constraint reduces to a set of subtyping constraints: for all i (1 ≤ i ≤ m), if Xi is not a subtype of any proper type in the throws clause, then the constraints include, for all j (1 ≤ j ≤ n), ‹Xi <: Ej›. In addition, for all j (1 ≤ j ≤ n), the constraint reduces to the bound throws Ej.
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ LambdaThrowsCompatibleWithType that = (LambdaThrowsCompatibleWithType) o;
+
+ if (!lambdaExpression.equals(that.lambdaExpression)) return false;
+ return T.equals(that.T);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = lambdaExpression.hashCode();
+ result = 31 * result + T.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "LambdaThrowsCompatibleWithType{" +
+ "lambdaExpression=" + lambdaExpression +
+ ", T=" + T +
+ '}';
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/MethodReferenceThrowsCompatibleWithType.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/MethodReferenceThrowsCompatibleWithType.java
new file mode 100644
index 000000000..a59e3fbd5
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/MethodReferenceThrowsCompatibleWithType.java
@@ -0,0 +1,62 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas;
+
+import com.github.javaparser.ast.expr.MethodReferenceExpr;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.resolution.typeinference.BoundSet;
+import com.github.javaparser.symbolsolver.resolution.typeinference.ConstraintFormula;
+
+/**
+ * The checked exceptions thrown by the referenced method are declared by the throws clause of the function type
+ * derived from T.
+ *
+ * @author Federico Tomassetti
+ */
+public class MethodReferenceThrowsCompatibleWithType extends ConstraintFormula {
+ private MethodReferenceExpr methodReference;
+ private ResolvedType T;
+
+ @Override
+ public ReductionResult reduce(BoundSet currentBoundSet) {
+ // A constraint formula of the form ‹MethodReference →throws T› is reduced as follows:
+ //
+ // - If T is not a functional interface type, or if T is a functional interface type but does not have a function type (§9.9), the constraint reduces to false.
+ //
+ // - Otherwise, let the target function type for the method reference expression be the function type of T. If the method reference is inexact (§15.13.1) and one or more of the function type's parameter types is not a proper type, the constraint reduces to false.
+ //
+ // - Otherwise, if the method reference is inexact and the function type's result is neither void nor a proper type, the constraint reduces to false.
+ //
+ // - Otherwise, let E1, ..., En be the types in the function type's throws clause that are not proper types. Let X1, ..., Xm be the checked exceptions in the throws clause of the invocation type of the method reference's compile-time declaration (§15.13.2) (as derived from the function type's parameter types and return type). Then there are two cases:
+ //
+ // - If n = 0 (the function type's throws clause consists only of proper types), then if there exists some i (1 ≤ i ≤ m) such that Xi is not a subtype of any proper type in the throws clause, the constraint reduces to false; otherwise, the constraint reduces to true.
+ //
+ // - If n > 0, the constraint reduces to a set of subtyping constraints: for all i (1 ≤ i ≤ m), if Xi is not a subtype of any proper type in the throws clause, then the constraints include, for all j (1 ≤ j ≤ n), ‹Xi <: Ej›. In addition, for all j (1 ≤ j ≤ n), the constraint reduces to the bound throws Ej.
+
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ MethodReferenceThrowsCompatibleWithType that = (MethodReferenceThrowsCompatibleWithType) o;
+
+ if (!methodReference.equals(that.methodReference)) return false;
+ return T.equals(that.T);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = methodReference.hashCode();
+ result = 31 * result + T.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "MethodReferenceThrowsCompatibleWithType{" +
+ "methodReference=" + methodReference +
+ ", T=" + T +
+ '}';
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeCompatibleWithType.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeCompatibleWithType.java
new file mode 100644
index 000000000..1c03064a8
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeCompatibleWithType.java
@@ -0,0 +1,122 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas;
+
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl;
+import com.github.javaparser.symbolsolver.resolution.typeinference.BoundSet;
+import com.github.javaparser.symbolsolver.resolution.typeinference.ConstraintFormula;
+import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
+
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isCompatibleInALooseInvocationContext;
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isProperType;
+
+/**
+ * A type S is compatible in a loose invocation context with type T
+ *
+ * @author Federico Tomassetti
+ */
+public class TypeCompatibleWithType extends ConstraintFormula {
+ private ResolvedType s;
+ private ResolvedType t;
+ private TypeSolver typeSolver;
+
+ public TypeCompatibleWithType(TypeSolver typeSolver, ResolvedType s, ResolvedType t) {
+ this.typeSolver = typeSolver;
+ this.s = s;
+ this.t = t;
+ }
+
+ @Override
+ public ReductionResult reduce(BoundSet currentBoundSet) {
+ // A constraint formula of the form ‹S → T› is reduced as follows:
+ //
+ // 1. If S and T are proper types, the constraint reduces to true if S is compatible in a loose invocation context with T (§5.3), and false otherwise.
+
+ if (isProperType(s) && isProperType(t)) {
+ if (isCompatibleInALooseInvocationContext(s, t)) {
+ return ReductionResult.trueResult();
+ } else {
+ return ReductionResult.falseResult();
+ }
+ }
+
+ // 2. Otherwise, if S is a primitive type, let S' be the result of applying boxing conversion (§5.1.7) to S. Then the constraint reduces to ‹S' → T›.
+
+ if (s.isPrimitive()) {
+ ReflectionTypeSolver typeSolver = new ReflectionTypeSolver();
+ ResolvedType sFirst = new ReferenceTypeImpl(typeSolver.solveType(s.asPrimitive().getBoxTypeQName()), typeSolver);
+ return ReductionResult.oneConstraint(new TypeCompatibleWithType(typeSolver, sFirst, t));
+ }
+
+ // 3. Otherwise, if T is a primitive type, let T' be the result of applying boxing conversion (§5.1.7) to T. Then the constraint reduces to ‹S = T'›.
+
+ if (t.isPrimitive()) {
+ ReflectionTypeSolver typeSolver = new ReflectionTypeSolver();
+ ResolvedType tFirst = new ReferenceTypeImpl(typeSolver.solveType(t.asPrimitive().getBoxTypeQName()), typeSolver);
+ return ReductionResult.oneConstraint(new TypeSameAsType(s, tFirst));
+ }
+
+ // The fourth and fifth cases are implicit uses of unchecked conversion (§5.1.9). These, along with any use of
+ // unchecked conversion in the first case, may result in compile-time unchecked warnings, and may influence a
+ // method's invocation type (§15.12.2.6).
+
+ // 4. Otherwise, if T is a parameterized type of the form G<T1, ..., Tn>, and there exists no type of the
+ // form G<...> that is a supertype of S, but the raw type G is a supertype of S, then the constraint reduces
+ // to true.
+
+ if (t.isReferenceType() && !t.asReferenceType().getTypeDeclaration().getTypeParameters().isEmpty()) {
+ // FIXME I really cannot understand what the specification means...
+
+ // there exists a type of the form G<...> that is a supertype of S?
+ boolean condition1 = t.isAssignableBy(s);
+
+ // the raw type G is a supertype of S
+ ResolvedType G = t.asReferenceType().toRawType();
+ boolean condition2 = G.isAssignableBy(s);
+
+ if (!condition1 && condition2) {
+ return ReductionResult.trueResult();
+ }
+
+ //throw new UnsupportedOperationException();
+ }
+
+ // 5. Otherwise, if T is an array type of the form G<T1, ..., Tn>[]k, and there exists no type of the form
+ // G<...>[]k that is a supertype of S, but the raw type G[]k is a supertype of S, then the constraint
+ // reduces to true. (The notation []k indicates an array type of k dimensions.)
+
+ if (t.isArray()) {
+ throw new UnsupportedOperationException();
+ }
+
+ // 6. Otherwise, the constraint reduces to ‹S <: T›
+
+ return ReductionResult.empty().withConstraint(new TypeSubtypeOfType(typeSolver, s, t));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TypeCompatibleWithType that = (TypeCompatibleWithType) o;
+
+ if (!s.equals(that.s)) return false;
+ return t.equals(that.t);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = s.hashCode();
+ result = 31 * result + t.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "TypeCompatibleWithType{" +
+ "s=" + s +
+ ", t=" + t +
+ '}';
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeContainedByType.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeContainedByType.java
new file mode 100644
index 000000000..685270a4c
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeContainedByType.java
@@ -0,0 +1,95 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas;
+
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.resolution.typeinference.BoundSet;
+import com.github.javaparser.symbolsolver.resolution.typeinference.ConstraintFormula;
+
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isProperType;
+
+/**
+ * A type argument S is contained by a type argument T
+ *
+ * @author Federico Tomassetti
+ */
+public class TypeContainedByType extends ConstraintFormula {
+ private ResolvedType S;
+ private ResolvedType T;
+
+ @Override
+ public ReductionResult reduce(BoundSet currentBoundSet) {
+ // A constraint formula of the form ‹S <= T›, where S and T are type arguments (§4.5.1), is reduced as follows:
+ //
+ // - If T is a type:
+
+ if (isProperType(T) && !T.isWildcard()) {
+
+ // - If S is a type, the constraint reduces to ‹S = T›.
+ //
+ // - If S is a wildcard, the constraint reduces to false.
+
+ throw new UnsupportedOperationException();
+ }
+
+ // - If T is a wildcard of the form ?, the constraint reduces to true.
+
+ if (T.isWildcard() && !T.asWildcard().isBounded()) {
+ return ReductionResult.trueResult();
+ }
+
+ // - If T is a wildcard of the form ? extends T':
+
+ if (T.isWildcard() && T.asWildcard().isExtends()) {
+
+ // - If S is a type, the constraint reduces to ‹S <: T'›.
+ //
+ // - If S is a wildcard of the form ?, the constraint reduces to ‹Object <: T'›.
+ //
+ // - If S is a wildcard of the form ? extends S', the constraint reduces to ‹S' <: T'›.
+ //
+ // - If S is a wildcard of the form ? super S', the constraint reduces to ‹Object = T'›.
+
+ throw new UnsupportedOperationException();
+ }
+
+ // - If T is a wildcard of the form ? super T':
+
+ if (T.isWildcard() && T.asWildcard().isSuper()) {
+
+ // - If S is a type, the constraint reduces to ‹T' <: S›.
+ //
+ // - If S is a wildcard of the form ? super S', the constraint reduces to ‹T' <: S'›.
+ //
+ // - Otherwise, the constraint reduces to false.
+
+ throw new UnsupportedOperationException();
+ }
+
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TypeContainedByType that = (TypeContainedByType) o;
+
+ if (!S.equals(that.S)) return false;
+ return T.equals(that.T);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = S.hashCode();
+ result = 31 * result + T.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "TypeContainedByType{" +
+ "S=" + S +
+ ", T=" + T +
+ '}';
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeSameAsType.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeSameAsType.java
new file mode 100644
index 000000000..5fbdc51ae
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeSameAsType.java
@@ -0,0 +1,140 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas;
+
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.resolution.typeinference.BoundSet;
+import com.github.javaparser.symbolsolver.resolution.typeinference.ConstraintFormula;
+import com.github.javaparser.symbolsolver.resolution.typeinference.bounds.SameAsBound;
+
+import java.util.List;
+
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isInferenceVariable;
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isProperType;
+
+/**
+ * A type S is the same as a type T (§4.3.4), or a type argument S is the same as type argument T
+ *
+ * @author Federico Tomassetti
+ */
+public class TypeSameAsType extends ConstraintFormula {
+ private ResolvedType S;
+ private ResolvedType T;
+
+ public TypeSameAsType(ResolvedType s, ResolvedType t) {
+ S = s;
+ T = t;
+ }
+
+ @Override
+ public ReductionResult reduce(BoundSet currentBoundSet) {
+ // A constraint formula of the form ‹S = T›, where S and T are types, is reduced as follows:
+
+ if (!S.isWildcard() && !T.isWildcard()) {
+
+ // - If S and T are proper types, the constraint reduces to true if S is the same as T (§4.3.4), and false
+ // otherwise.
+
+ if (isProperType(S) && isProperType(T)) {
+ if (S.equals(T)) {
+ return ReductionResult.trueResult();
+ } else {
+ return ReductionResult.falseResult();
+ }
+ }
+
+ // - Otherwise, if S or T is the null type, the constraint reduces to false.
+
+ if (S.isNull() || T.isNull()) {
+ return ReductionResult.falseResult();
+ }
+
+ // - Otherwise, if S is an inference variable, α, and T is not a primitive type, the constraint reduces to the
+ // bound α = T.
+
+ if (isInferenceVariable(S) && !T.isPrimitive()) {
+ return ReductionResult.oneBound(new SameAsBound(S, T));
+ }
+
+ // - Otherwise, if T is an inference variable, α, and S is not a primitive type, the constraint reduces to the
+ // bound S = α.
+
+ if (isInferenceVariable(T) && !S.isPrimitive()) {
+ return ReductionResult.oneBound(new SameAsBound(S, T));
+ }
+
+ // - Otherwise, if S and T are class or interface types with the same erasure, where S has
+ // type arguments B1, ..., Bn and T has type arguments A1, ..., An, the constraint reduces to the following
+ // new constraints: for all i (1 ≤ i ≤ n), ‹Bi = Ai›.
+
+ if (S.isReferenceType() && T.isReferenceType()
+ && S.asReferenceType().toRawType().equals(T.asReferenceType().toRawType())) {
+ ReductionResult res = ReductionResult.empty();
+ List<ResolvedType> Bs = S.asReferenceType().typeParametersValues();
+ List<ResolvedType> As = T.asReferenceType().typeParametersValues();
+ for (int i = 0; i < Bs.size(); i++) {
+ res = res.withConstraint(new TypeSameAsType(Bs.get(i), As.get(i)));
+ }
+ return res;
+ }
+
+ // - Otherwise, if S and T are array types, S'[] and T'[], the constraint reduces to ‹S' = T'›.
+
+ if (S.isArray() && T.isArray()) {
+ return ReductionResult.oneConstraint(new TypeSameAsType(
+ S.asArrayType().getComponentType(),
+ T.asArrayType().getComponentType()));
+ }
+
+ // - Otherwise, the constraint reduces to false.
+
+ return ReductionResult.falseResult();
+ }
+
+ // Note that we do not address intersection types above, because it is impossible for reduction to encounter an
+ // intersection type that is not a proper type.
+
+ // A constraint formula of the form ‹S = T›, where S and T are type arguments (§4.5.1), is reduced as follows:
+ //
+ // - If S and T are types, the constraint is reduced as described above.
+ //
+ // - If S has the form ? and T has the form ?, the constraint reduces to true.
+ //
+ // - If S has the form ? and T has the form ? extends T', the constraint reduces to ‹Object = T'›.
+ //
+ // - If S has the form ? extends S' and T has the form ?, the constraint reduces to ‹S' = Object›.
+ //
+ // - If S has the form ? extends S' and T has the form ? extends T', the constraint reduces to ‹S' = T'›.
+ //
+ // - If S has the form ? super S' and T has the form ? super T', the constraint reduces to ‹S' = T'›.
+ //
+ // - Otherwise, the constraint reduces to false.
+
+
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TypeSameAsType that = (TypeSameAsType) o;
+
+ if (!S.equals(that.S)) return false;
+ return T.equals(that.T);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = S.hashCode();
+ result = 31 * result + T.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "TypeSameAsType{" +
+ "S=" + S +
+ ", T=" + T +
+ '}';
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeSubtypeOfType.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeSubtypeOfType.java
new file mode 100644
index 000000000..6bc7fdc81
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeSubtypeOfType.java
@@ -0,0 +1,139 @@
+package com.github.javaparser.symbolsolver.resolution.typeinference.constraintformulas;
+
+import com.github.javaparser.resolution.types.ResolvedIntersectionType;
+import com.github.javaparser.resolution.types.ResolvedType;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.model.typesystem.NullType;
+import com.github.javaparser.symbolsolver.resolution.typeinference.BoundSet;
+import com.github.javaparser.symbolsolver.resolution.typeinference.ConstraintFormula;
+import com.github.javaparser.symbolsolver.resolution.typeinference.bounds.SubtypeOfBound;
+
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isInferenceVariable;
+import static com.github.javaparser.symbolsolver.resolution.typeinference.TypeHelper.isProperType;
+
+/**
+ * A reference type S is a subtype of a reference type T
+ *
+ * @author Federico Tomassetti
+ */
+public class TypeSubtypeOfType extends ConstraintFormula {
+ private ResolvedType S;
+ private ResolvedType T;
+ private TypeSolver typeSolver;
+
+ public TypeSubtypeOfType(TypeSolver typeSolver, ResolvedType S, ResolvedType T) {
+ this.typeSolver = typeSolver;
+ this.S = S;
+ this.T = T;
+ }
+
+ @Override
+ public ReductionResult reduce(BoundSet currentBoundSet) {
+ // A constraint formula of the form ‹S <: T› is reduced as follows:
+ //
+ // - If S and T are proper types, the constraint reduces to true if S is a subtype of T (§4.10), and false otherwise.
+
+ if (isProperType(S) && isProperType(T)) {
+ if (T.isAssignableBy(S)) {
+ return ReductionResult.trueResult();
+ } else {
+ return ReductionResult.falseResult();
+ }
+ }
+
+ // - Otherwise, if S is the null type, the constraint reduces to true.
+
+ if (S instanceof NullType) {
+ return ReductionResult.trueResult();
+ }
+
+ // - Otherwise, if T is the null type, the constraint reduces to false.
+
+ if (T instanceof NullType) {
+ return ReductionResult.falseResult();
+ }
+
+ // - Otherwise, if S is an inference variable, α, the constraint reduces to the bound α <: T.
+
+ if (isInferenceVariable(S)) {
+ return ReductionResult.oneBound(new SubtypeOfBound(S, T));
+ }
+
+ // - Otherwise, if T is an inference variable, α, the constraint reduces to the bound S <: α.
+
+ if (isInferenceVariable(T)) {
+ return ReductionResult.oneBound(new SubtypeOfBound(S, T));
+ }
+
+ // FEDERICO - Added start
+ //if (T.isTypeVariable()) {
+ // return ReductionResult.oneBound(new SubtypeOfBound(S, T));
+ //}
+ // FEDERICO - Added end
+
+ // - Otherwise, the constraint is reduced according to the form of T:
+ //
+ // - If T is a parameterized class or interface type, or an inner class type of a parameterized class or interface type (directly or indirectly), let A1, ..., An be the type arguments of T. Among the supertypes of S, a corresponding class or interface type is identified, with type arguments B1, ..., Bn. If no such type exists, the constraint reduces to false. Otherwise, the constraint reduces to the following new constraints: for all i (1 ≤ i ≤ n), ‹Bi <= Ai›.
+ //
+ // - If T is any other class or interface type, then the constraint reduces to true if T is among the supertypes of S, and false otherwise.
+ //
+ // - If T is an array type, T'[], then among the supertypes of S that are array types, a most specific type is identified, S'[] (this may be S itself). If no such array type exists, the constraint reduces to false. Otherwise:
+ //
+ // - If neither S' nor T' is a primitive type, the constraint reduces to ‹S' <: T'›.
+ //
+ // - Otherwise, the constraint reduces to true if S' and T' are the same primitive type, and false otherwise.
+ //
+ // - If T is a type variable, there are three cases:
+
+ if (T.isTypeVariable()) {
+
+ // - If S is an intersection type of which T is an element, the constraint reduces to true.
+
+ if (S instanceof ResolvedIntersectionType) {
+ throw new UnsupportedOperationException();
+ }
+
+ // - Otherwise, if T has a lower bound, B, the constraint reduces to ‹S <: B›.
+
+ if (T.asTypeVariable().asTypeParameter().hasLowerBound()) {
+ return ReductionResult.oneConstraint(new TypeSubtypeOfType(typeSolver, S, T.asTypeVariable().asTypeParameter().getLowerBound()));
+ }
+
+ // - Otherwise, the constraint reduces to false.
+
+ return ReductionResult.falseResult();
+ }
+
+ //
+ // - If T is an intersection type, I1 & ... & In, the constraint reduces to the following new constraints: for all i (1 ≤ i ≤ n), ‹S <: Ii›.
+ //
+
+ throw new UnsupportedOperationException("S = "+ S + ", T = " + T);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TypeSubtypeOfType that = (TypeSubtypeOfType) o;
+
+ if (!S.equals(that.S)) return false;
+ return T.equals(that.T);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = S.hashCode();
+ result = 31 * result + T.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "TypeSubtypeOfType{" +
+ "S=" + S +
+ ", T=" + T +
+ '}';
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/AarTypeSolver.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/AarTypeSolver.java
new file mode 100644
index 000000000..0811ef18e
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/AarTypeSolver.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.github.javaparser.symbolsolver.resolution.typesolvers;
+
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class AarTypeSolver implements TypeSolver {
+
+ private TypeSolver parent;
+ private File aarFile;
+ private JarTypeSolver delegate;
+
+ public AarTypeSolver(File aarFile) throws IOException {
+ this.aarFile = aarFile;
+
+ JarFile jarFile = new JarFile(aarFile);
+ ZipEntry classesJarEntry = jarFile.getEntry("classes.jar");
+ if (classesJarEntry == null) {
+ throw new IllegalArgumentException(String.format("The given file (%s) is malformed: entry classes.jar was not found", aarFile.getAbsolutePath()));
+ }
+ delegate = new JarTypeSolver(jarFile.getInputStream(classesJarEntry));
+ }
+
+ @Override
+ public TypeSolver getParent() {
+ return parent;
+ }
+
+ @Override
+ public void setParent(TypeSolver parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public SymbolReference<ResolvedReferenceTypeDeclaration> tryToSolveType(String name) {
+ return delegate.tryToSolveType(name);
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/CombinedTypeSolver.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/CombinedTypeSolver.java
new file mode 100644
index 000000000..fe9f3755a
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/CombinedTypeSolver.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.resolution.typesolvers;
+
+import com.github.javaparser.resolution.UnsolvedSymbolException;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class CombinedTypeSolver implements TypeSolver {
+
+ private TypeSolver parent;
+ private List<TypeSolver> elements = new ArrayList<>();
+
+ public CombinedTypeSolver(TypeSolver... elements) {
+ for (TypeSolver el : elements) {
+ add(el);
+ }
+ }
+
+ @Override
+ public TypeSolver getParent() {
+ return parent;
+ }
+
+ @Override
+ public void setParent(TypeSolver parent) {
+ this.parent = parent;
+ }
+
+ public void add(TypeSolver typeSolver) {
+ this.elements.add(typeSolver);
+ typeSolver.setParent(this);
+ }
+
+ @Override
+ public SymbolReference<ResolvedReferenceTypeDeclaration> tryToSolveType(String name) {
+ for (TypeSolver ts : elements) {
+ SymbolReference<ResolvedReferenceTypeDeclaration> res = ts.tryToSolveType(name);
+ if (res.isSolved()) {
+ return res;
+ }
+ }
+ return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class);
+ }
+
+ @Override
+ public ResolvedReferenceTypeDeclaration solveType(String name) throws UnsolvedSymbolException {
+ SymbolReference<ResolvedReferenceTypeDeclaration> res = tryToSolveType(name);
+ if (res.isSolved()) {
+ return res.getCorrespondingDeclaration();
+ } else {
+ throw new UnsolvedSymbolException(name);
+ }
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/JarTypeSolver.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/JarTypeSolver.java
new file mode 100644
index 000000000..9ce9cbbcc
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/JarTypeSolver.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.github.javaparser.symbolsolver.resolution.typesolvers;
+
+import com.github.javaparser.resolution.UnsolvedSymbolException;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.symbolsolver.javassistmodel.JavassistFactory;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.NotFoundException;
+
+import java.io.*;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JarTypeSolver implements TypeSolver {
+
+ private static JarTypeSolver instance;
+
+ private TypeSolver parent;
+ private Map<String, ClasspathElement> classpathElements = new HashMap<>();
+ private ClassPool classPool = new ClassPool(false);
+
+ public JarTypeSolver(String pathToJar) throws IOException {
+ addPathToJar(pathToJar);
+ }
+
+ public JarTypeSolver(InputStream jarInputStream) throws IOException {
+ addPathToJar(jarInputStream);
+ }
+
+ public static JarTypeSolver getJarTypeSolver(String pathToJar) throws IOException {
+ if (instance == null) {
+ instance = new JarTypeSolver(pathToJar);
+ } else {
+ instance.addPathToJar(pathToJar);
+ }
+ return instance;
+ }
+
+ private File dumpToTempFile(InputStream inputStream) throws IOException {
+ File tempFile = File.createTempFile("jar_file_from_input_stream", ".jar");
+ tempFile.deleteOnExit();
+
+ byte[] buffer = new byte[8 * 1024];
+
+ try {
+ OutputStream output = new FileOutputStream(tempFile);
+ try {
+ int bytesRead;
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
+ output.write(buffer, 0, bytesRead);
+ }
+ } finally {
+ output.close();
+ }
+ } finally {
+ inputStream.close();
+ }
+ return tempFile;
+ }
+
+ private void addPathToJar(InputStream jarInputStream) throws IOException {
+ addPathToJar(dumpToTempFile(jarInputStream).getAbsolutePath());
+ }
+
+ private void addPathToJar(String pathToJar) throws IOException {
+ try {
+ classPool.appendClassPath(pathToJar);
+ classPool.appendSystemPath();
+ } catch (NotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ JarFile jarFile = new JarFile(pathToJar);
+ JarEntry entry = null;
+ Enumeration<JarEntry> e = jarFile.entries();
+ while (e.hasMoreElements()) {
+ entry = e.nextElement();
+ if (entry != null && !entry.isDirectory() && entry.getName().endsWith(".class")) {
+ String name = entryPathToClassName(entry.getName());
+ classpathElements.put(name, new ClasspathElement(jarFile, entry, name));
+ }
+ }
+ }
+
+ @Override
+ public TypeSolver getParent() {
+ return parent;
+ }
+
+ @Override
+ public void setParent(TypeSolver parent) {
+ this.parent = parent;
+ }
+
+ private String entryPathToClassName(String entryPath) {
+ if (!entryPath.endsWith(".class")) {
+ throw new IllegalStateException();
+ }
+ String className = entryPath.substring(0, entryPath.length() - ".class".length());
+ className = className.replace('/', '.');
+ className = className.replace('$', '.');
+ return className;
+ }
+
+ @Override
+ public SymbolReference<ResolvedReferenceTypeDeclaration> tryToSolveType(String name) {
+ try {
+ if (classpathElements.containsKey(name)) {
+ return SymbolReference.solved(
+ JavassistFactory.toTypeDeclaration(classpathElements.get(name).toCtClass(), getRoot()));
+ } else {
+ return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public ResolvedReferenceTypeDeclaration solveType(String name) throws UnsolvedSymbolException {
+ SymbolReference<ResolvedReferenceTypeDeclaration> ref = tryToSolveType(name);
+ if (ref.isSolved()) {
+ return ref.getCorrespondingDeclaration();
+ } else {
+ throw new UnsolvedSymbolException(name);
+ }
+ }
+
+ private class ClasspathElement {
+ private JarFile jarFile;
+ private JarEntry entry;
+ private String path;
+
+ ClasspathElement(JarFile jarFile, JarEntry entry, String path) {
+ this.jarFile = jarFile;
+ this.entry = entry;
+ this.path = path;
+ }
+
+ CtClass toCtClass() throws IOException {
+ try (InputStream is = jarFile.getInputStream(entry)) {
+ return classPool.makeClass(is);
+ }
+ }
+ }
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/JavaParserTypeSolver.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/JavaParserTypeSolver.java
new file mode 100644
index 000000000..17e335e09
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/JavaParserTypeSolver.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.resolution.typesolvers;
+
+import com.github.javaparser.JavaParser;
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.symbolsolver.javaparser.Navigator;
+import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class JavaParserTypeSolver implements TypeSolver {
+
+ private File srcDir;
+
+ private TypeSolver parent;
+
+ private Cache<String, Optional<CompilationUnit>> parsedFiles = CacheBuilder.newBuilder().softValues().build();
+ private Cache<String, List<CompilationUnit>> parsedDirectories = CacheBuilder.newBuilder().softValues().build();
+ private Cache<String, SymbolReference<ResolvedReferenceTypeDeclaration>> foundTypes = CacheBuilder.newBuilder().softValues().build();
+
+ public JavaParserTypeSolver(File srcDir) {
+ if (!srcDir.exists() || !srcDir.isDirectory()) {
+ throw new IllegalStateException("SrcDir does not exist or is not a directory: " + srcDir.getAbsolutePath());
+ }
+ this.srcDir = srcDir;
+ }
+
+ @Override
+ public String toString() {
+ return "JavaParserTypeSolver{" +
+ "srcDir=" + srcDir +
+ ", parent=" + parent +
+ '}';
+ }
+
+ @Override
+ public TypeSolver getParent() {
+ return parent;
+ }
+
+ @Override
+ public void setParent(TypeSolver parent) {
+ this.parent = parent;
+ }
+
+
+ private Optional<CompilationUnit> parse(File srcFile) {
+ try {
+ return parsedFiles.get(srcFile.getAbsolutePath(), () -> {
+ Optional<CompilationUnit> cu;
+ try {
+ cu = Optional.of(JavaParser.parse(srcFile));
+ } catch (FileNotFoundException e) {
+ cu = Optional.empty();
+ } catch (RuntimeException e) {
+ throw new RuntimeException("Issue while parsing " + srcFile.getAbsolutePath(), e);
+ }
+ return cu;
+ });
+ } catch (ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private List<CompilationUnit> parseDirectory(File srcDirectory) {
+ try {
+ return parsedDirectories.get(srcDirectory.getAbsolutePath(), () -> {
+ List<CompilationUnit> units = new ArrayList<>();
+ File[] files = srcDirectory.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ if (file.getName().toLowerCase().endsWith(".java")) {
+ Optional<CompilationUnit> unit = parse(file);
+ if (unit.isPresent()) {
+ units.add(unit.get());
+ }
+ }
+ }
+ }
+ return units;
+ });
+ } catch (ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public SymbolReference<ResolvedReferenceTypeDeclaration> tryToSolveType(String name) {
+ // TODO support enums
+ // TODO support interfaces
+ try {
+ return foundTypes.get(name, () -> {
+ SymbolReference<ResolvedReferenceTypeDeclaration> result = tryToSolveTypeUncached(name);
+ if (result.isSolved()) {
+ return SymbolReference.solved(result.getCorrespondingDeclaration());
+ }
+ return result;
+ });
+ } catch (ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private SymbolReference<ResolvedReferenceTypeDeclaration> tryToSolveTypeUncached(String name) {
+ String[] nameElements = name.split("\\.");
+
+ for (int i = nameElements.length; i > 0; i--) {
+ String filePath = srcDir.getAbsolutePath();
+ for (int j = 0; j < i; j++) {
+ filePath += "/" + nameElements[j];
+ }
+ filePath += ".java";
+
+ String typeName = "";
+ for (int j = i - 1; j < nameElements.length; j++) {
+ if (j != i - 1) {
+ typeName += ".";
+ }
+ typeName += nameElements[j];
+ }
+
+ File srcFile = new File(filePath);
+ {
+ Optional<CompilationUnit> compilationUnit = parse(srcFile);
+ if (compilationUnit.isPresent()) {
+ Optional<com.github.javaparser.ast.body.TypeDeclaration<?>> astTypeDeclaration = Navigator.findType(compilationUnit.get(), typeName);
+ if (astTypeDeclaration.isPresent()) {
+ return SymbolReference.solved(JavaParserFacade.get(this).getTypeDeclaration(astTypeDeclaration.get()));
+ }
+ }
+ }
+
+ {
+ List<CompilationUnit> compilationUnits = parseDirectory(srcFile.getParentFile());
+ for (CompilationUnit compilationUnit : compilationUnits) {
+ Optional<com.github.javaparser.ast.body.TypeDeclaration<?>> astTypeDeclaration = Navigator.findType(compilationUnit, typeName);
+ if (astTypeDeclaration.isPresent()) {
+ return SymbolReference.solved(JavaParserFacade.get(this).getTypeDeclaration(astTypeDeclaration.get()));
+ }
+ }
+ }
+ }
+
+ return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class);
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/MemoryTypeSolver.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/MemoryTypeSolver.java
new file mode 100644
index 000000000..2595c6d99
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/MemoryTypeSolver.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.resolution.typesolvers;
+
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A TypeSolver which only consider the TypeDeclarations provided to it.
+ *
+ * @author Federico Tomassetti
+ */
+public class MemoryTypeSolver implements TypeSolver {
+
+ private TypeSolver parent;
+ private Map<String, ResolvedReferenceTypeDeclaration> declarationMap = new HashMap<>();
+
+ @Override
+ public String toString() {
+ return "MemoryTypeSolver{" +
+ "parent=" + parent +
+ ", declarationMap=" + declarationMap +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof MemoryTypeSolver)) return false;
+
+ MemoryTypeSolver that = (MemoryTypeSolver) o;
+
+ if (parent != null ? !parent.equals(that.parent) : that.parent != null) return false;
+ return !(declarationMap != null ? !declarationMap.equals(that.declarationMap) : that.declarationMap != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = parent != null ? parent.hashCode() : 0;
+ result = 31 * result + (declarationMap != null ? declarationMap.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public TypeSolver getParent() {
+ return parent;
+ }
+
+ @Override
+ public void setParent(TypeSolver parent) {
+ this.parent = parent;
+ }
+
+ public void addDeclaration(String name, ResolvedReferenceTypeDeclaration typeDeclaration) {
+ this.declarationMap.put(name, typeDeclaration);
+ }
+
+ @Override
+ public SymbolReference<ResolvedReferenceTypeDeclaration> tryToSolveType(String name) {
+ if (declarationMap.containsKey(name)) {
+ return SymbolReference.solved(declarationMap.get(name));
+ } else {
+ return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class);
+ }
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/ReflectionTypeSolver.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/ReflectionTypeSolver.java
new file mode 100644
index 000000000..8e0547354
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/ReflectionTypeSolver.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2016 Federico Tomassetti
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.javaparser.symbolsolver.resolution.typesolvers;
+
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
+import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionFactory;
+
+import java.util.Optional;
+
+/**
+ * @author Federico Tomassetti
+ */
+public class ReflectionTypeSolver implements TypeSolver {
+
+ private TypeSolver parent;
+
+ public ReflectionTypeSolver(boolean jreOnly) {
+ this.jreOnly = jreOnly;
+ }
+
+ public ReflectionTypeSolver() {
+ this(true);
+ }
+
+ private boolean jreOnly;
+
+ @Override
+ public TypeSolver getParent() {
+ return parent;
+ }
+
+ @Override
+ public void setParent(TypeSolver parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public SymbolReference<ResolvedReferenceTypeDeclaration> tryToSolveType(String name) {
+ if (!jreOnly || (name.startsWith("java.") || name.startsWith("javax."))) {
+ try {
+ ClassLoader classLoader = ReflectionTypeSolver.class.getClassLoader();
+
+ // Some implementations could return null when the class was loaded through the bootstrap classloader
+ // see https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getClassLoader--
+ if (classLoader == null) {
+ throw new RuntimeException("The ReflectionTypeSolver has been probably loaded through the bootstrap class loader. This usage is not supported by the JavaSymbolSolver");
+ }
+
+ Class<?> clazz = classLoader.loadClass(name);
+ return SymbolReference.solved(ReflectionFactory.typeDeclarationFor(clazz, getRoot()));
+ } catch (ClassNotFoundException e) {
+ // it could be an inner class
+ int lastDot = name.lastIndexOf('.');
+ if (lastDot == -1) {
+ return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class);
+ } else {
+ String parentName = name.substring(0, lastDot);
+ String childName = name.substring(lastDot + 1);
+ SymbolReference<ResolvedReferenceTypeDeclaration> parent = tryToSolveType(parentName);
+ if (parent.isSolved()) {
+ Optional<ResolvedReferenceTypeDeclaration> innerClass = parent.getCorrespondingDeclaration().internalTypes()
+ .stream().filter(it -> it.getName().equals(childName)).findFirst();
+ if (innerClass.isPresent()) {
+ return SymbolReference.solved(innerClass.get());
+ } else {
+ return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class);
+ }
+ } else {
+ return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class);
+ }
+ }
+ }
+ } else {
+ return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class);
+ }
+ }
+
+}
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/utils/SymbolSolverQuickSetup.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/utils/SymbolSolverQuickSetup.java
new file mode 100644
index 000000000..e6d3d530b
--- /dev/null
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/utils/SymbolSolverQuickSetup.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2007-2010 Júlio Vilmar Gesser.
+ * Copyright (C) 2011, 2013-2018 The JavaParser Team.
+ *
+ * This file is part of JavaParser.
+ *
+ * JavaParser can be used either under the terms of
+ * a) the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * b) the terms of the Apache License
+ *
+ * You should have received a copy of both licenses in LICENCE.LGPL and
+ * LICENCE.APACHE. Please refer to those files for details.
+ *
+ * JavaParser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+
+package com.github.javaparser.symbolsolver.utils;
+
+import com.github.javaparser.JavaParser;
+import com.github.javaparser.ParseProblemException;
+import com.github.javaparser.ast.CompilationUnit;
+import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
+import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
+import com.github.javaparser.symbolsolver.resolution.typesolvers.JarTypeSolver;
+import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver;
+import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
+import com.github.javaparser.utils.Log;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.*;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+
+import static com.github.javaparser.utils.Utils.assertNotNull;
+import static java.nio.file.FileVisitResult.*;
+
+/**
+ * Utility class to add all jars and roots of java files of the provided path to a TypeSolver instance.
+ * It traverses the file directory tree and adds all files ending in either .java or .jar.
+ */
+public class SymbolSolverQuickSetup {
+
+ public interface DirFilter {
+ boolean filter(Path path);
+ }
+
+ private final Path root;
+ private CombinedTypeSolver typeSolver = new CombinedTypeSolver(new ReflectionTypeSolver(false));
+ private DirFilter dirFilter = path -> false;
+
+ public SymbolSolverQuickSetup(Path root) {
+ assertNotNull(root);
+ if (!Files.isDirectory(root)) {
+ throw new IllegalArgumentException("Only directories are allowed as root path!");
+ }
+ this.root = root.normalize();
+ Log.info("New symbol source root at \"%s\"", this.root);
+ }
+
+ public SymbolSolverQuickSetup(Path root, DirFilter dirFilter) {
+ this(root);
+ this.dirFilter = dirFilter;
+ }
+
+ public TypeSolver walk() throws IOException {
+ Files.walkFileTree(root, new JavaSymbolSolverWalker());
+ Files.walkFileTree(root, new JarVisitor());
+ return typeSolver;
+ }
+
+ public Optional<TypeSolver> tryToWalk() {
+ try {
+ return Optional.of(walk());
+ } catch (IOException e) {
+ Log.error(e, "Unable to walk root " + root);
+ return Optional.empty();
+ }
+ }
+
+ public TypeSolver getTypeSolver() {
+ return typeSolver;
+ }
+
+ /**
+ * The path that was passed in the constructor.
+ */
+ public Path getRoot() {
+ return root;
+ }
+
+ /**
+ * Walks the directory and adds the roots of the java files to the TypeSolver
+ */
+ private class JavaSymbolSolverWalker extends SimpleFileVisitor<Path> {
+
+ private final Set<Path> roots = new HashSet<>();
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attr) throws FileNotFoundException {
+ if (attr.isRegularFile()) {
+ PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:**.java");
+ if (matcher.matches(file)) {
+ try {
+ Optional<Path> root = JavaParser.parse(file.toFile()).getStorage()
+ .map(CompilationUnit.Storage::getSourceRoot);
+ if (root.isPresent()) {
+ typeSolver.add(new JavaParserTypeSolver(root.get().toFile()));
+ if (roots.add(root.get())) {
+ Log.trace("Added dir " + root.get() + " to the TypeSolver");
+ return SKIP_SIBLINGS;
+ }
+ }
+ } catch (ParseProblemException e) {
+ Log.error(e, "Unable to parse file " + file);
+ }
+ }
+ }
+ return CONTINUE;
+ }
+
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+ if (Files.isHidden(dir) || dirFilter.filter(dir) || roots.stream().anyMatch(dir::startsWith)) {
+ return SKIP_SUBTREE;
+ }
+ return CONTINUE;
+ }
+ }
+
+ private class JarVisitor extends SimpleFileVisitor<Path> {
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attr) throws IOException {
+ if (attr.isRegularFile()) {
+ PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:**.jar");
+ if (matcher.matches(file)) {
+ typeSolver.add(new JarTypeSolver(file.toString()));
+ }
+ }
+ return CONTINUE;
+ }
+ }
+}
+