From 09232ff6b0ea19e850482f3a87ac6ef1c063c759 Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Wed, 10 Jan 2018 13:52:19 +0100 Subject: rename JSS modules --- javaparser-symbol-solver-core/.gitignore | 5 + javaparser-symbol-solver-core/pom.xml | 214 ++++++ .../javaparser/symbolsolver/JavaSymbolSolver.java | 188 +++++ .../symbolsolver/SourceFileInfoExtractor.java | 217 ++++++ .../symbolsolver/core/resolution/Context.java | 98 +++ .../core/resolution/ContextHelper.java | 51 ++ .../common/MethodDeclarationCommonLogic.java | 94 +++ .../symbolsolver/javaparser/Navigator.java | 311 ++++++++ .../symbolsolver/javaparser/package-info.java | 20 + .../javaparsermodel/DefaultVisitorAdapter.java | 470 ++++++++++++ .../javaparsermodel/JavaParserFacade.java | 573 +++++++++++++++ .../javaparsermodel/JavaParserFactory.java | 138 ++++ .../LambdaArgumentTypePlaceholder.java | 72 ++ .../javaparsermodel/TypeExtractor.java | 535 ++++++++++++++ .../javaparsermodel/UnsolvedSymbolException.java | 67 ++ .../contexts/AbstractJavaParserContext.java | 196 +++++ .../AbstractMethodLikeDeclarationContext.java | 101 +++ .../contexts/AnonymousClassDeclarationContext.java | 186 +++++ .../contexts/CatchClauseContext.java | 54 ++ .../ClassOrInterfaceDeclarationContext.java | 109 +++ .../contexts/CompilationUnitContext.java | 291 ++++++++ .../contexts/ConstructorContext.java | 35 + .../javaparsermodel/contexts/ContextHelper.java | 74 ++ .../contexts/EnumDeclarationContext.java | 82 +++ .../contexts/FieldAccessContext.java | 108 +++ .../contexts/ForStatementContext.java | 70 ++ .../contexts/ForechStatementContext.java | 61 ++ .../contexts/JavaParserTypeDeclarationAdapter.java | 133 ++++ .../contexts/LambdaExprContext.java | 194 +++++ .../contexts/MethodCallExprContext.java | 433 +++++++++++ .../javaparsermodel/contexts/MethodContext.java | 35 + .../javaparsermodel/contexts/StatementContext.java | 200 +++++ .../contexts/SwitchEntryContext.java | 80 ++ .../contexts/TryWithResourceContext.java | 87 +++ .../DefaultConstructorDeclaration.java | 82 +++ .../javaparsermodel/declarations/Helper.java | 85 +++ .../JavaParserAnnotationDeclaration.java | 103 +++ .../JavaParserAnnotationMemberDeclaration.java | 40 + .../JavaParserAnonymousClassDeclaration.java | 205 ++++++ .../declarations/JavaParserClassDeclaration.java | 392 ++++++++++ .../JavaParserConstructorDeclaration.java | 103 +++ .../JavaParserEnumConstantDeclaration.java | 59 ++ .../declarations/JavaParserEnumDeclaration.java | 349 +++++++++ .../declarations/JavaParserFieldDeclaration.java | 123 ++++ .../JavaParserInterfaceDeclaration.java | 335 +++++++++ .../declarations/JavaParserMethodDeclaration.java | 168 +++++ .../JavaParserParameterDeclaration.java | 100 +++ .../declarations/JavaParserSymbolDeclaration.java | 177 +++++ .../declarations/JavaParserTypeAdapter.java | 138 ++++ .../declarations/JavaParserTypeParameter.java | 224 ++++++ .../JavaParserTypeVariableDeclaration.java | 187 +++++ .../declarators/AbstractSymbolDeclarator.java | 35 + .../declarators/FieldSymbolDeclarator.java | 46 ++ .../declarators/NoSymbolDeclarator.java | 40 + .../declarators/ParameterSymbolDeclarator.java | 42 ++ .../declarators/VariableSymbolDeclarator.java | 52 ++ .../symbolsolver/javaparsermodel/package-info.java | 20 + .../javassistmodel/JavassistClassDeclaration.java | 395 ++++++++++ .../JavassistConstructorDeclaration.java | 146 ++++ .../JavassistEnumConstantDeclaration.java | 54 ++ .../javassistmodel/JavassistEnumDeclaration.java | 275 +++++++ .../javassistmodel/JavassistFactory.java | 85 +++ .../javassistmodel/JavassistFieldDeclaration.java | 94 +++ .../JavassistInterfaceDeclaration.java | 292 ++++++++ .../javassistmodel/JavassistMethodDeclaration.java | 198 +++++ .../JavassistParameterDeclaration.java | 80 ++ .../JavassistTypeDeclarationAdapter.java | 93 +++ .../javassistmodel/JavassistTypeParameter.java | 114 +++ .../javassistmodel/JavassistUtils.java | 209 ++++++ .../symbolsolver/javassistmodel/package-info.java | 20 + .../symbolsolver/model/typesystem/LazyType.java | 120 +++ .../model/typesystem/ReferenceTypeImpl.java | 211 ++++++ .../reflectionmodel/MyObjectProvider.java | 36 + .../reflectionmodel/ReflectionClassAdapter.java | 190 +++++ .../ReflectionClassDeclaration.java | 344 +++++++++ .../ReflectionConstructorDeclaration.java | 95 +++ .../ReflectionEnumConstantDeclaration.java | 31 + .../reflectionmodel/ReflectionEnumDeclaration.java | 201 ++++++ .../reflectionmodel/ReflectionFactory.java | 121 ++++ .../ReflectionFieldDeclaration.java | 97 +++ .../ReflectionInterfaceDeclaration.java | 307 ++++++++ .../ReflectionMethodDeclaration.java | 151 ++++ .../ReflectionMethodResolutionLogic.java | 144 ++++ .../ReflectionParameterDeclaration.java | 75 ++ .../reflectionmodel/ReflectionTypeParameter.java | 130 ++++ .../comparators/ClassComparator.java | 25 + .../comparators/MethodComparator.java | 25 + .../comparators/ParameterComparator.java | 19 + .../symbolsolver/reflectionmodel/package-info.java | 20 + .../resolution/ConstructorResolutionLogic.java | 226 ++++++ .../resolution/MethodResolutionLogic.java | 696 ++++++++++++++++++ .../symbolsolver/resolution/SymbolDeclarator.java | 30 + .../symbolsolver/resolution/SymbolSolver.java | 172 +++++ .../resolution/typeinference/Bound.java | 106 +++ .../resolution/typeinference/BoundSet.java | 804 +++++++++++++++++++++ .../typeinference/ConstraintFormula.java | 125 ++++ .../typeinference/ConstraintFormulaSet.java | 53 ++ .../resolution/typeinference/ControlFlowLogic.java | 290 ++++++++ .../resolution/typeinference/ExpressionHelper.java | 137 ++++ .../typeinference/InferenceVariable.java | 93 +++ .../InferenceVariableSubstitution.java | 36 + .../resolution/typeinference/Instantiation.java | 51 ++ .../resolution/typeinference/InstantiationSet.java | 68 ++ .../resolution/typeinference/MethodType.java | 55 ++ .../resolution/typeinference/ProperLowerBound.java | 50 ++ .../resolution/typeinference/ProperUpperBound.java | 50 ++ .../resolution/typeinference/Substitution.java | 45 ++ .../resolution/typeinference/TypeHelper.java | 405 +++++++++++ .../resolution/typeinference/TypeInference.java | 719 ++++++++++++++++++ .../typeinference/TypeInferenceCache.java | 56 ++ .../typeinference/bounds/CapturesBound.java | 69 ++ .../typeinference/bounds/FalseBound.java | 42 ++ .../typeinference/bounds/SameAsBound.java | 96 +++ .../typeinference/bounds/SubtypeOfBound.java | 98 +++ .../typeinference/bounds/ThrowsBound.java | 62 ++ .../ExpressionCompatibleWithType.java | 338 +++++++++ .../LambdaThrowsCompatibleWithType.java | 67 ++ .../MethodReferenceThrowsCompatibleWithType.java | 62 ++ .../constraintformulas/TypeCompatibleWithType.java | 122 ++++ .../constraintformulas/TypeContainedByType.java | 95 +++ .../constraintformulas/TypeSameAsType.java | 140 ++++ .../constraintformulas/TypeSubtypeOfType.java | 139 ++++ .../resolution/typesolvers/AarTypeSolver.java | 60 ++ .../resolution/typesolvers/CombinedTypeSolver.java | 76 ++ .../resolution/typesolvers/JarTypeSolver.java | 167 +++++ .../typesolvers/JavaParserTypeSolver.java | 178 +++++ .../resolution/typesolvers/MemoryTypeSolver.java | 86 +++ .../typesolvers/ReflectionTypeSolver.java | 94 +++ 128 files changed, 19342 insertions(+) create mode 100644 javaparser-symbol-solver-core/.gitignore create mode 100644 javaparser-symbol-solver-core/pom.xml create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/JavaSymbolSolver.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/SourceFileInfoExtractor.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/resolution/Context.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/core/resolution/ContextHelper.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/declarations/common/MethodDeclarationCommonLogic.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparser/Navigator.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparser/package-info.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/DefaultVisitorAdapter.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFacade.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFactory.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/LambdaArgumentTypePlaceholder.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/TypeExtractor.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/UnsolvedSymbolException.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractJavaParserContext.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractMethodLikeDeclarationContext.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AnonymousClassDeclarationContext.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/CatchClauseContext.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ClassOrInterfaceDeclarationContext.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/CompilationUnitContext.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ConstructorContext.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ContextHelper.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/EnumDeclarationContext.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/FieldAccessContext.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ForStatementContext.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/ForechStatementContext.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/JavaParserTypeDeclarationAdapter.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/LambdaExprContext.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/MethodCallExprContext.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/MethodContext.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/StatementContext.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/SwitchEntryContext.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/TryWithResourceContext.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/DefaultConstructorDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/Helper.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserAnnotationDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserAnnotationMemberDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserAnonymousClassDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserClassDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserConstructorDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserEnumConstantDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserEnumDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserFieldDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserInterfaceDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserMethodDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserParameterDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserSymbolDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeAdapter.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeParameter.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeVariableDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/AbstractSymbolDeclarator.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/FieldSymbolDeclarator.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/NoSymbolDeclarator.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/ParameterSymbolDeclarator.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarators/VariableSymbolDeclarator.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/package-info.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistClassDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistConstructorDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistEnumConstantDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistEnumDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistFactory.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistFieldDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistInterfaceDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistMethodDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistParameterDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistTypeDeclarationAdapter.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistTypeParameter.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/JavassistUtils.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javassistmodel/package-info.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/model/typesystem/LazyType.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/model/typesystem/ReferenceTypeImpl.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/MyObjectProvider.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionClassAdapter.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionClassDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionConstructorDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionEnumConstantDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionEnumDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionFactory.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionFieldDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionInterfaceDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionMethodDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionMethodResolutionLogic.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionParameterDeclaration.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/ReflectionTypeParameter.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/comparators/ClassComparator.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/comparators/MethodComparator.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/comparators/ParameterComparator.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/reflectionmodel/package-info.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/ConstructorResolutionLogic.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/MethodResolutionLogic.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/SymbolDeclarator.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/SymbolSolver.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/Bound.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/BoundSet.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ConstraintFormula.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ConstraintFormulaSet.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ControlFlowLogic.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ExpressionHelper.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/InferenceVariable.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/InferenceVariableSubstitution.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/Instantiation.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/InstantiationSet.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/MethodType.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ProperLowerBound.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/ProperUpperBound.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/Substitution.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/TypeHelper.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/TypeInference.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/TypeInferenceCache.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/CapturesBound.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/FalseBound.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/SameAsBound.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/SubtypeOfBound.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/bounds/ThrowsBound.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/ExpressionCompatibleWithType.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/LambdaThrowsCompatibleWithType.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/MethodReferenceThrowsCompatibleWithType.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeCompatibleWithType.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeContainedByType.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeSameAsType.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/TypeSubtypeOfType.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/AarTypeSolver.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/CombinedTypeSolver.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/JarTypeSolver.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/JavaParserTypeSolver.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/MemoryTypeSolver.java create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typesolvers/ReflectionTypeSolver.java (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/.gitignore b/javaparser-symbol-solver-core/.gitignore new file mode 100644 index 000000000..b114bf5fb --- /dev/null +++ b/javaparser-symbol-solver-core/.gitignore @@ -0,0 +1,5 @@ +target +build +/.classpath +/.project +.settings diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml new file mode 100644 index 000000000..73a216939 --- /dev/null +++ b/javaparser-symbol-solver-core/pom.xml @@ -0,0 +1,214 @@ + + + + javaparser-parent + com.github.javaparser + 3.5.10-SNAPSHOT + + 4.0.0 + + javaparser-symbol-solver-core + jar + A Symbol Solver for Java, built on top of JavaParser (core) + + + + GNU Lesser General Public License + http://www.gnu.org/licenses/lgpl-3.0.html + repo + + + Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + A business-friendly OSS license + + + + + 1.8 + ${maven.build.timestamp} + + + + + com.github.javaparser + java-symbol-solver-logic + ${project.version} + compile + + + org.javassist + javassist + 3.22.0-GA + compile + + + com.github.javaparser + java-symbol-solver-model + ${project.version} + compile + + + com.google.guava + guava + 23.4-jre + compile + + + + + + + com.helger.maven + ph-javacc-maven-plugin + + + javacc + + javacc + + + ${project.build.sourceEncoding} + ${java.version} + + + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + 1.16 + + + + org.codehaus.mojo.signature + java18 + 1.0 + + + + + animal-sniffer + verify + + check + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-versions + verify + + enforce + + + + + + ${java.version} + + + + ${java.version} + + + + + + + + org.codehaus.mojo + extra-enforcer-rules + 1.0-beta-6 + + + + + + biz.aQute.bnd + bnd-maven-plugin + ${bnd-maven-plugin.version} + + + + bnd-process + + + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + -parameters + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.0.0 + + + add-source + generate-sources + + add-source + + + + src/main/javacc-support + + + + + + + org.codehaus.mojo + templating-maven-plugin + 1.0.0 + + + filter-src + + filter-sources + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + com.github.javaparser.core + + + + + + + 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..eeceff88c --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/JavaSymbolSolver.java @@ -0,0 +1,188 @@ +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.ArrayType; +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.javaparser.Navigator; +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; + +import java.util.Optional; + +/** + * 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 resolveDeclaration(Node node, Class 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 = Navigator.findAncestor(node, 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 = Navigator.findAncestor(node, 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 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 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 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 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 = Navigator.findAncestor(node, CallableDeclaration.class).get(); + ResolvedMethodLikeDeclaration resolvedMethodLikeDeclaration; + if (callableDeclaration.isConstructorDeclaration()) { + resolvedMethodLikeDeclaration = callableDeclaration.asConstructorDeclaration().resolve(); + } else { + resolvedMethodLikeDeclaration = callableDeclaration.asMethodDeclaration().resolve(); + } + for (int i=0;i T toResolvedType(Type javaparserType, Class 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..c82e2b3ef --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/SourceFileInfoExtractor.java @@ -0,0 +1,217 @@ +/* + * 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; + +/** + * 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 ((getParentNode(node) instanceof ImportDeclaration) || (getParentNode(node) instanceof Expression) + || (getParentNode(node) instanceof MethodDeclaration) + || (getParentNode(node) instanceof PackageDeclaration)) { + // skip + } else if ((getParentNode(node) instanceof Statement) || (getParentNode(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 methodDeclarationSymbolReference) { + if (methodDeclarationSymbolReference.isSolved()) { + return methodDeclarationSymbolReference.getCorrespondingDeclaration().getQualifiedSignature(); + } else { + return "UNSOLVED"; + } + } + + private List collectAllNodes(Node node) { + List 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 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 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 solveGenericType(String name, TypeSolver typeSolver) { + return Optional.empty(); + } + + default SymbolReference 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 solveSymbol(String name, TypeSolver typeSolver); + + default Optional solveSymbolAsValue(String name, TypeSolver typeSolver) { + SymbolReference 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 solveConstructor(List 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 solveMethod(String name, List 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 solveMethodAsUsage(String name, List argumentsTypes, TypeSolver typeSolver) { + SymbolReference 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 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 parameterTypes) { + ResolvedType returnType = replaceTypeParams(methodDeclaration.getReturnType(), typeSolver, context); + List 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 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 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 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..82e0101ea --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparser/Navigator.java @@ -0,0 +1,311 @@ +/* + * 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.LinkedList; +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 + } + + public static Node getParentNode(Node node) { + Node parent = node.getParentNode().orElse(null); + return parent; + } + + public static Node requireParentNode(Node node) { + Node parent = getParentNode(node); + if (parent == null) { + throw new IllegalStateException("Parent not found, the node does not appear to be inserted in a correct AST"); + } + return parent; + } + + public static Optional> findType(CompilationUnit cu, String qualifiedName) { + if (cu.getTypes().isEmpty()) { + return Optional.empty(); + } + + final String typeName = getOuterTypeName(qualifiedName); + Optional> 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> findType(TypeDeclaration td, String qualifiedName) { + final String typeName = getOuterTypeName(qualifiedName); + + Optional> 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> 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.getName().getId().equals(name)) { + if (found != null) { + throw new IllegalStateException("Ambiguous getName"); + } + found = md; + } + } + } + if (found == null) { + throw new IllegalStateException("No method with given 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 NameExpr findNameExpression(Node node, String name) { + if (node instanceof NameExpr) { + NameExpr nameExpr = (NameExpr) node; + if (nameExpr.getName() != null && nameExpr.getName().getId().equals(name)) { + return nameExpr; + } + } + for (Node child : node.getChildNodes()) { + NameExpr res = findNameExpression(child, name); + if (res != null) { + return res; + } + } + return null; + } + + public static SimpleName findSimpleName(Node node, String name) { + if (node instanceof SimpleName) { + SimpleName nameExpr = (SimpleName) node; + if (nameExpr.getId() != null && nameExpr.getId().equals(name)) { + return nameExpr; + } + } + for (Node child : node.getChildNodes()) { + SimpleName res = findSimpleName(child, name); + if (res != null) { + return res; + } + } + return null; + } + + public static MethodCallExpr findMethodCall(Node node, String methodName) { + if (node instanceof MethodCallExpr) { + MethodCallExpr methodCallExpr = (MethodCallExpr) node; + if (methodCallExpr.getName().getId().equals(methodName)) { + return methodCallExpr; + } + } + for (Node child : node.getChildNodes()) { + MethodCallExpr res = findMethodCall(child, methodName); + if (res != null) { + return res; + } + } + return null; + } + + public static VariableDeclarator demandVariableDeclaration(Node node, String name) { + if (node instanceof VariableDeclarator) { + VariableDeclarator variableDeclarator = (VariableDeclarator) node; + if (variableDeclarator.getName().getId().equals(name)) { + return variableDeclarator; + } + } + for (Node child : node.getChildNodes()) { + VariableDeclarator res = demandVariableDeclaration(child, name); + if (res != null) { + return res; + } + } + return null; + } + + public static ClassOrInterfaceDeclaration demandClassOrInterface(CompilationUnit compilationUnit, String qualifiedName) { + Optional> res = findType(compilationUnit, qualifiedName); + if (!res.isPresent()) { + throw new IllegalStateException("No type named '" + qualifiedName + "'found"); + } + if (!(res.get() instanceof ClassOrInterfaceDeclaration)) { + throw new IllegalStateException("Type is not a class or an interface, it is " + res.get().getClass().getCanonicalName()); + } + ClassOrInterfaceDeclaration cd = (ClassOrInterfaceDeclaration) res.get(); + return cd; + } + + public static SwitchStmt findSwitch(Node node) { + SwitchStmt res = findSwitchHelper(node); + if (res == null) { + throw new IllegalArgumentException(); + } else { + return res; + } + } + + /** + * @deprecated use Node.findFirst instead + */ + @Deprecated + public static N findNodeOfGivenClass(Node node, Class clazz) { + N res = findNodeOfGivenClassHelper(node, clazz); + if (res == null) { + throw new IllegalArgumentException(); + } else { + return res; + } + } + + /** + * @deprecated use Node.findAll instead + */ + @Deprecated + public static List findAllNodesOfGivenClass(Node node, Class clazz) { + List res = new LinkedList<>(); + findAllNodesOfGivenClassHelper(node, clazz, res); + return res; + } + + public static ReturnStmt findReturnStmt(MethodDeclaration method) { + return findNodeOfGivenClass(method, ReturnStmt.class); + } + + public static Optional findAncestor(Node node, Class clazz) { + if (!node.getParentNode().isPresent()) { + return Optional.empty(); + } else if (clazz.isInstance(node.getParentNode().get())) { + return Optional.of(clazz.cast(node.getParentNode().get())); + } else { + return findAncestor(node.getParentNode().get(), 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 SwitchStmt findSwitchHelper(Node node) { + if (node instanceof SwitchStmt) { + return (SwitchStmt) node; + } + for (Node child : node.getChildNodes()) { + SwitchStmt resChild = findSwitchHelper(child); + if (resChild != null) { + return resChild; + } + } + return null; + } + + private static N findNodeOfGivenClassHelper(Node node, Class clazz) { + if (clazz.isInstance(node)) { + return clazz.cast(node); + } + for (Node child : node.getChildNodes()) { + N resChild = findNodeOfGivenClassHelper(child, clazz); + if (resChild != null) { + return resChild; + } + } + return null; + } + + private static void findAllNodesOfGivenClassHelper(Node node, Class clazz, List collector) { + if (clazz.isInstance(node)) { + collector.add(clazz.cast(node)); + } + for (Node child : node.getChildNodes()) { + findAllNodesOfGivenClassHelper(child, clazz, collector); + } + } +} 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..dc9d86241 --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/DefaultVisitorAdapter.java @@ -0,0 +1,470 @@ +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 { + @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()); + } +} 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..39e0d29ea --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFacade.java @@ -0,0 +1,573 @@ +/* + * 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.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.getParentNode; + +/** + * 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 instances = new WeakHashMap<>(); + private TypeSolver typeSolver; + private SymbolSolver symbolSolver; + private Map cacheWithLambdasSolved = new IdentityHashMap<>(); + private Map 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()) { + Optional solved = context.solveGenericType(type.describe(), typeSolver); + if (solved.isPresent()) { + return solved.get(); + } else { + return type; + } + } else 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); + } + } else { + return type; + } + } else { + ResolvedType result = type; + return result; + } + } + + public SymbolReference solve(NameExpr nameExpr) { + return symbolSolver.solveSymbol(nameExpr.getName().getId(), nameExpr); + } + + public SymbolReference solve(SimpleName nameExpr) { + return symbolSolver.solveSymbol(nameExpr.getId(), nameExpr); + } + + public SymbolReference solve(Expression expr) { + if (expr instanceof NameExpr) { + return solve((NameExpr) expr); + } else { + throw new IllegalArgumentException(expr.getClass().getCanonicalName()); + } + } + + public SymbolReference solve(MethodCallExpr methodCallExpr) { + return solve(methodCallExpr, true); + } + + public SymbolReference solve(ObjectCreationExpr objectCreationExpr) { + return solve(objectCreationExpr, true); + } + + public SymbolReference solve(ExplicitConstructorInvocationStmt explicitConstructorInvocationStmt) { + return solve(explicitConstructorInvocationStmt, true); + } + + public SymbolReference solve(ExplicitConstructorInvocationStmt explicitConstructorInvocationStmt, boolean solveLambdas) { + List argumentTypes = new LinkedList<>(); + List placeholders = new LinkedList<>(); + + solveArguments(explicitConstructorInvocationStmt, explicitConstructorInvocationStmt.getArguments(), solveLambdas, argumentTypes, placeholders); + + Optional 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 sr = JavaParserFactory.getContext(classNode, typeSolver).solveType(classNode.getNameAsString(), typeSolver); + if (sr.isSolved()) { + typeDecl = sr.getCorrespondingDeclaration(); + } + } + if (typeDecl == null) { + return SymbolReference.unsolved(ResolvedConstructorDeclaration.class); + } + SymbolReference res = ConstructorResolutionLogic.findMostApplicable(((ResolvedClassDeclaration) typeDecl).getConstructors(), argumentTypes, typeSolver); + for (LambdaArgumentTypePlaceholder placeholder : placeholders) { + placeholder.setMethod(res); + } + return res; + } + + public SymbolReference 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 clazz = typeSolver.tryToSolveType(className); + if (clazz.isSolved()){ + return SymbolReference.solved(clazz.getCorrespondingDeclaration()); + } + // Attempt to resolve locally in Compilation unit + Optional cu = node.getAncestorOfType(CompilationUnit.class); + if (cu.isPresent()){ + Optional 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 solve(ObjectCreationExpr objectCreationExpr, boolean solveLambdas) { + List argumentTypes = new LinkedList<>(); + List 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 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 args, boolean solveLambdas, List argumentTypes, + List 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 (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 solve(MethodCallExpr methodCallExpr, boolean solveLambdas) { + List argumentTypes = new LinkedList<>(); + List placeholders = new LinkedList<>(); + + solveArguments(methodCallExpr, methodCallExpr.getArguments(), solveLambdas, argumentTypes, placeholders); + + SymbolReference res = JavaParserFactory.getContext(methodCallExpr, typeSolver).solveMethod(methodCallExpr.getName().getId(), argumentTypes, false, typeSolver); + for (LambdaArgumentTypePlaceholder placeholder : placeholders) { + placeholder.setMethod(res); + } + return res; + } + + public SymbolReference solve(AnnotationExpr annotationExpr) { + Context context = JavaParserFactory.getContext(annotationExpr, typeSolver); + SymbolReference 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 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 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 find(Map 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. + * + * @param map + * @return + */ + private Optional find(Map map, LambdaExpr lambdaExpr) { + for (Node key : map.keySet()) { + if (key instanceof LambdaExpr) { + LambdaExpr keyLambdaExpr = (LambdaExpr) key; + if (keyLambdaExpr.toString().equals(lambdaExpr.toString()) && getParentNode(keyLambdaExpr) == getParentNode(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 typeDeclarationSymbolReference = JavaParserFactory.getContext(classOrInterfaceType, typeSolver).solveType(classOrInterfaceType.getName().getId(), typeSolver); + if (!typeDeclarationSymbolReference.isSolved()) { + throw new UnsupportedOperationException(); + } + List 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. + * + * @return + */ + 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; + } else if (node instanceof EnumDeclaration) { + return (EnumDeclaration) node; + } else if (getParentNode(node) == null) { + throw new IllegalArgumentException(); + } else { + return findContainingTypeDecl(getParentNode(node)); + } + } + + protected com.github.javaparser.ast.Node findContainingTypeDeclOrObjectCreationExpr(Node node) { + if (node instanceof ClassOrInterfaceDeclaration) { + return (ClassOrInterfaceDeclaration) node; + } else if (node instanceof EnumDeclaration) { + return (EnumDeclaration) node; + } else if (getParentNode(node) == null) { + throw new IllegalArgumentException(); + } else if (getParentNode(node) instanceof ObjectCreationExpr && !((ObjectCreationExpr)getParentNode(node)).getArguments().contains(node)) { + return getParentNode(node); + } else { + return findContainingTypeDeclOrObjectCreationExpr(getParentNode(node)); + } + } + + public ResolvedType convertToUsageVariableType(VariableDeclarator var) { + ResolvedType type = JavaParserFacade.get(typeSolver).convertToUsage(var.getType(), var); + return type; + } + + public ResolvedType convertToUsage(com.github.javaparser.ast.type.Type type, Node context) { + if (type instanceof UnknownType) { + throw new IllegalArgumentException("Unknown 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; + } else { + 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 ref = context.solveType(name, typeSolver); + if (!ref.isSolved()) { + throw new UnsolvedSymbolException(name); + } + ResolvedTypeDeclaration typeDeclaration = ref.getCorrespondingDeclaration(); + List 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.getExtendedTypes().isPresent() && !wildcardType.getSuperTypes().isPresent()) { + return ResolvedWildcard.extendsBound(convertToUsage(wildcardType.getExtendedTypes().get(), context)); // removed (ReferenceTypeImpl) + } else if (!wildcardType.getExtendedTypes().isPresent() && wildcardType.getSuperTypes().isPresent()) { + return ResolvedWildcard.superBound(convertToUsage(wildcardType.getSuperTypes().get(), context)); // removed (ReferenceTypeImpl) + } else if (!wildcardType.getExtendedTypes().isPresent() && !wildcardType.getSuperTypes().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 { + 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 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 = 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().get().begin.line + ") " + 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); + } else { + return getTypeOfThisIn(getParentNode(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..1004bc31e --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/JavaParserFactory.java @@ -0,0 +1,138 @@ +/* + * 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; + +/** + * @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 = getParentNode(node); + if (parentNode instanceof ObjectCreationExpr && node == ((ObjectCreationExpr) parentNode).getType()) { + return getContext(getParentNode(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((VariableDeclarationExpr) (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 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 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..1e15571d7 --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/TypeExtractor.java @@ -0,0 +1,535 @@ +package com.github.javaparser.symbolsolver.javaparsermodel; + +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.NodeList; +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.stmt.Statement; +import com.github.javaparser.ast.type.UnknownType; +import com.github.javaparser.resolution.MethodUsage; +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.getParentNode; + +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 (getParentNode(node) instanceof FieldDeclaration) { + return facade.convertToUsageVariableType(node); + } else if (getParentNode(node) instanceof VariableDeclarationExpr) { + return facade.convertToUsageVariableType(node); + } else { + throw new UnsupportedOperationException(getParentNode(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 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 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 sr = typeSolver.tryToSolveType(node.getScope().toString()); + if (sr.isSolved()) { + return solveDotExpressionType(sr.getCorrespondingDeclaration(), node); + } + } + Optional value = null; + try { + value = new SymbolSolver(typeSolver).solveSymbolAsValue(node.getField().getId(), node); + } catch (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 sref = typeSolver.tryToSolveType(node.toString()); + if (sref.isSolved()) { + return new ReferenceTypeImpl(sref.getCorrespondingDeclaration(), typeSolver); + } + } + if (value != null && value.isPresent()) { + return value.get().getType(); + } else { + throw new UnsolvedSymbolException(node.getField().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 = new SymbolSolver(typeSolver).solveSymbolAsValue(node.getName().getId(), node); + if (!value.isPresent()) { + throw new UnsolvedSymbolException("Solving " + node, node.getName().getId()); + } else { + return value.get().getType(); + } + } + + @Override + public ResolvedType visit(ObjectCreationExpr node, Boolean solveLambdas) { + ResolvedType type = facade.convertToUsage(node.getType(), node); + return type; + } + + @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 clazz = typeSolver.tryToSolveType(className); + if (clazz.isSolved()){ + return new ReferenceTypeImpl(clazz.getCorrespondingDeclaration(),typeSolver); + } + // Attempt to resolve locally in Compilation unit + Optional cu = node.getAncestorOfType(CompilationUnit.class); + if (cu.isPresent()){ + Optional 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 (getParentNode(node) instanceof MethodCallExpr) { + MethodCallExpr callExpr = (MethodCallExpr) getParentNode(node); + int pos = JavaParserSymbolDeclaration.getParamPos(node); + SymbolReference refMethod = facade.solve(callExpr); + if (!refMethod.isSolved()) { + throw new UnsolvedSymbolException(getParentNode(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 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 = facade.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 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 + // 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(); + NodeList statements = blockStmt.getStatements(); + + // Get all the return statements in the lambda block + List returnStmts = blockStmt.getNodesByType(ReturnStmt.class); + + if (returnStmts.size() > 0){ + + actualType = returnStmts.stream() + .map(returnStmt -> { + Optional expression = returnStmt.getExpression(); + if (expression.isPresent()){ + return facade.getType(expression.get()); + } else{ + return 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 (getParentNode(node) instanceof MethodCallExpr) { + MethodCallExpr callExpr = (MethodCallExpr) getParentNode(node); + int pos = JavaParserSymbolDeclaration.getParamPos(node); + SymbolReference refMethod = facade.solve(callExpr, false); + if (!refMethod.isSolved()) { + throw new UnsolvedSymbolException(getParentNode(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 = facade.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 functionalMethod = FunctionalInterfaceLogic.getFunctionalMethod(result); + if (functionalMethod.isPresent()) { + if (node instanceof MethodReferenceExpr) { + MethodReferenceExpr methodReferenceExpr = (MethodReferenceExpr) node; + + ResolvedType actualType = facade.toMethodUsage(methodReferenceExpr).returnType(); + ResolvedType formalType = functionalMethod.get().returnType(); + + InferenceContext inferenceContext = new InferenceContext(MyObjectProvider.INSTANCE); + inferenceContext.addPair(formalType, actualType); + result = inferenceContext.resolve(inferenceContext.addSingle(result)); + } + } + + return result; + } else { + return refMethod.getCorrespondingDeclaration().getParam(pos).getType(); + } + } else { + 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); + } else { + 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..21dcf7c9e --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/UnsolvedSymbolException.java @@ -0,0 +1,67 @@ +/* + * 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.symbolsolver.model.resolution.UnsolvedSymbolException} instead + */ +// Use the one in model instead +@Deprecated +public class UnsolvedSymbolException extends RuntimeException { + + private String context; + private String name; + private TypeSolver typeSolver; + + public UnsolvedSymbolException(String name, TypeSolver typeSolver) { + super("Unsolved symbol : " + name + " using typesolver " + typeSolver); + this.typeSolver = typeSolver; + this.name = name; + } + + public UnsolvedSymbolException(Context context, String name) { + super("Unsolved symbol in " + context + " : " + name); + this.context = context.toString(); + this.name = name; + } + + public UnsolvedSymbolException(String context, String name) { + super("Unsolved symbol in " + context + " : " + name); + this.context = context; + this.name = name; + } + + 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..e2f348ce1 --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/AbstractJavaParserContext.java @@ -0,0 +1,196 @@ +/* + * 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.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentNode; + +/** + * @author Federico Tomassetti + */ +public abstract class AbstractJavaParserContext implements Context { + + protected N wrappedNode; + protected TypeSolver typeSolver; + + /// + /// Static methods + /// + + public static final SymbolReference 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 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() { + if (getParentNode(wrappedNode) instanceof MethodCallExpr) { + MethodCallExpr parentCall = (MethodCallExpr) getParentNode(wrappedNode); + boolean found = false; + if (parentCall.getArguments() != null) { + for (Expression expression : parentCall.getArguments()) { + if (expression == wrappedNode) { + found = true; + } + } + } + if (found) { + Node notMethod = getParentNode(wrappedNode); + while (notMethod instanceof MethodCallExpr) { + notMethod = getParentNode(notMethod); + } + return JavaParserFactory.getContext(notMethod, typeSolver); + } + } + Node notMethod = getParentNode(wrappedNode); + while (notMethod instanceof MethodCallExpr || notMethod instanceof FieldAccessExpr) { + notMethod = getParentNode(notMethod); + } + if (notMethod == null) { + return null; + } + return JavaParserFactory.getContext(notMethod, typeSolver); + } + + /// + /// Protected methods + /// + + protected Optional solveWithAsValue(SymbolDeclarator symbolDeclarator, String name, TypeSolver typeSolver) { + return symbolDeclarator.getSymbolDeclarations().stream() + .filter(d -> d.getName().equals(name)) + .map(d -> Value.from(d)) + .findFirst(); + } + + protected Collection findTypeDeclarations(Optional optScope, TypeSolver typeSolver) { + if (optScope.isPresent()) { + Expression scope = optScope.get(); + + // consider static methods + if (scope instanceof NameExpr) { + NameExpr scopeAsName = (NameExpr) scope; + SymbolReference symbolReference = this.solveType(scopeAsName.getName().getId(), typeSolver); + if (symbolReference.isSolved() && symbolReference.getCorrespondingDeclaration().isType()) { + return Arrays.asList(symbolReference.getCorrespondingDeclaration().asReferenceType()); + } + } + + ResolvedType typeOfScope = null; + try { + typeOfScope = JavaParserFacade.get(typeSolver).getType(scope); + } catch (Exception e) { + throw new RuntimeException(String.format("Issue calculating the type of the scope of " + this), e); + } + if (typeOfScope.isWildcard()) { + if (typeOfScope.asWildcard().isExtends() || typeOfScope.asWildcard().isSuper()) { + return Arrays.asList(typeOfScope.asWildcard().getBoundedType().asReferenceType().getTypeDeclaration()); + } else { + return Arrays.asList(new ReflectionClassDeclaration(Object.class, typeSolver).asReferenceType()); + } + } else if (typeOfScope.isArray()) { + // method call on array are Object methods + return Arrays.asList(new ReflectionClassDeclaration(Object.class, typeSolver).asReferenceType()); + } else if (typeOfScope.isTypeVariable()) { + Collection result = new ArrayList<>(); + for (ResolvedTypeParameterDeclaration.Bound bound : typeOfScope.asTypeParameter().getBounds()) { + result.add(bound.getType().asReferenceType().getTypeDeclaration()); + } + return result; + } else if (typeOfScope.isConstraint()){ + return Arrays.asList(typeOfScope.asConstraintType().getBound().asReferenceType().getTypeDeclaration()); + } else { + return Arrays.asList(typeOfScope.asReferenceType().getTypeDeclaration()); + } + } else { + ResolvedType typeOfScope = JavaParserFacade.get(typeSolver).getTypeOfThisIn(wrappedNode); + return Arrays.asList(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..4e7b760bd --- /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 + & NodeWithTypeParameters> extends AbstractJavaParserContext { + + public AbstractMethodLikeDeclarationContext(T wrappedNode, TypeSolver typeSolver) { + super(wrappedNode, typeSolver); + } + + public final SymbolReference solveSymbol(String name, TypeSolver typeSolver) { + for (Parameter parameter : wrappedNode.getParameters()) { + SymbolDeclarator sb = JavaParserFactory.getSymbolDeclarator(parameter, typeSolver); + SymbolReference 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 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 solveSymbolAsValue(String name, TypeSolver typeSolver) { + for (Parameter parameter : wrappedNode.getParameters()) { + SymbolDeclarator sb = JavaParserFactory.getSymbolDeclarator(parameter, typeSolver); + Optional 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 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 localTypes = wrappedNode.getChildNodesByType( + 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 solveMethod( + String name, List 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 { + + 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 solveMethod(String name, + List argumentsTypes, + boolean staticOnly, + TypeSolver typeSolver) { + List 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 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 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 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 solveType(String name, TypeSolver typeSolver) { + List typeDeclarations = + myDeclaration + .findMembersOfKind(com.github.javaparser.ast.body.TypeDeclaration.class); + + Optional> 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> 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> 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 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 { + + public CatchClauseContext(CatchClause wrappedNode, TypeSolver typeSolver) { + super(wrappedNode, typeSolver); + } + + public final SymbolReference solveSymbol(String name, TypeSolver typeSolver) { + SymbolDeclarator sb = JavaParserFactory.getSymbolDeclarator(wrappedNode.getParameter(), typeSolver); + SymbolReference 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 solveSymbolAsValue(String name, TypeSolver typeSolver) { + SymbolDeclarator sb = JavaParserFactory.getSymbolDeclarator(wrappedNode.getParameter(), typeSolver); + Optional 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 solveMethod( + String name, List 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 { + + 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 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 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 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 solveType(String name, TypeSolver typeSolver) { + return javaParserTypeDeclarationAdapter.solveType(name, typeSolver); + } + + @Override + public SymbolReference solveMethod(String name, List argumentsTypes, boolean staticOnly, TypeSolver typeSolver) { + return javaParserTypeDeclarationAdapter.solveMethod(name, argumentsTypes, staticOnly, typeSolver); + } + + public SymbolReference solveConstructor(List 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 { + + /// + /// 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 solveSymbol(String name, TypeSolver typeSolver) { + + // solve absolute references + String itName = name; + while (itName.contains(".")) { + String typeName = getType(itName); + String memberName = getMember(itName); + SymbolReference 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 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 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 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 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 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 ref = typeSolver.tryToSolveType(qName); + if (ref.isSolved()) { + return SymbolReference.adapt(ref, ResolvedTypeDeclaration.class); + } + } + + // Look in the java.lang package + SymbolReference 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 solveMethod(String name, List 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 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 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 { + + /// + /// 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 solveMethodAsUsage(ResolvedTypeDeclaration typeDeclaration, String name, + List argumentsTypes, TypeSolver typeSolver, + Context invokationContext, List 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 { + + private JavaParserTypeDeclarationAdapter javaParserTypeDeclarationAdapter; + + public EnumDeclarationContext(EnumDeclaration wrappedNode, TypeSolver typeSolver) { + super(wrappedNode, typeSolver); + this.javaParserTypeDeclarationAdapter = new JavaParserTypeDeclarationAdapter(wrappedNode, typeSolver, + getDeclaration(), this); + } + + @Override + public SymbolReference 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 solveType(String name, TypeSolver typeSolver) { + return javaParserTypeDeclarationAdapter.solveType(name, typeSolver); + } + + @Override + public SymbolReference solveMethod(String name, List 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..860fe6bc4 --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/FieldAccessContext.java @@ -0,0 +1,108 @@ +/* + * 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 { + + private static final String ARRAY_LENGTH_FIELD_NAME = "length"; + + public FieldAccessContext(FieldAccessExpr wrappedNode, TypeSolver typeSolver) { + super(wrappedNode, typeSolver); + } + + @Override + public SymbolReference solveSymbol(String name, TypeSolver typeSolver) { + if (wrappedNode.getField().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 solveType(String name, TypeSolver typeSolver) { + return JavaParserFactory.getContext(requireParentNode(wrappedNode), typeSolver).solveType(name, typeSolver); + } + + @Override + public SymbolReference solveMethod(String name, List parameterTypes, boolean staticOnly, TypeSolver typeSolver) { + return JavaParserFactory.getContext(requireParentNode(wrappedNode), typeSolver).solveMethod(name, parameterTypes, false, typeSolver); + } + + @Override + public Optional solveSymbolAsValue(String name, TypeSolver typeSolver) { + Expression scope = wrappedNode.getScope(); + if (wrappedNode.getField().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 typeUsage = typeOfScope.asReferenceType().getFieldType(name); + if (typeUsage.isPresent()) { + return Optional.of(new Value(typeUsage.get(), name)); + } else { + return Optional.empty(); + } + } else { + return Optional.empty(); + } + } else { + return getParent().solveSymbolAsValue(name, typeSolver); + } + } + + public SymbolReference solveField(String name, TypeSolver typeSolver) { + Collection 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..77965bd08 --- /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.getParentNode; + +public class ForStatementContext extends AbstractJavaParserContext { + + public ForStatementContext(ForStmt wrappedNode, TypeSolver typeSolver) { + super(wrappedNode, typeSolver); + } + + @Override + public SymbolReference 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 (getParentNode(wrappedNode) instanceof NodeWithStatements) { + return StatementContext.solveInBlock(name, typeSolver, wrappedNode); + } else { + return getParent().solveSymbol(name, typeSolver); + } + } + + @Override + public SymbolReference solveMethod(String name, List 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..ed874899f --- /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.getParentNode; + +public class ForechStatementContext extends AbstractJavaParserContext { + + public ForechStatementContext(ForeachStmt wrappedNode, TypeSolver typeSolver) { + super(wrappedNode, typeSolver); + } + + @Override + public SymbolReference 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 (getParentNode(wrappedNode) instanceof BlockStmt) { + return StatementContext.solveInBlock(name, typeSolver, wrappedNode); + } else { + return getParent().solveSymbol(name, typeSolver); + } + } + } + + @Override + public SymbolReference solveMethod(String name, List 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 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 solveMethod(String name, List argumentsTypes, boolean staticOnly, TypeSolver typeSolver) { + List 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 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 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 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 solveConstructor(List 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..fe2ec8ba9 --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/LambdaExprContext.java @@ -0,0 +1,194 @@ +/* + * 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 javassist.compiler.ast.Pair; + +import java.util.*; + +import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentNode; + +/** + * @author Federico Tomassetti + */ +public class LambdaExprContext extends AbstractJavaParserContext { + + public LambdaExprContext(LambdaExpr wrappedNode, TypeSolver typeSolver) { + super(wrappedNode, typeSolver); + } + + @Override + public Optional 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 (getParentNode(wrappedNode) instanceof MethodCallExpr) { + MethodCallExpr methodCallExpr = (MethodCallExpr) getParentNode(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 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 (getParentNode(wrappedNode) instanceof VariableDeclarator) { + VariableDeclarator variableDeclarator = (VariableDeclarator) getParentNode(wrappedNode); + ResolvedType t = JavaParserFacade.get(typeSolver).convertToUsageVariableType(variableDeclarator); + Optional functionalMethod = FunctionalInterfaceLogic.getFunctionalMethod(t); + if (functionalMethod.isPresent()) { + ResolvedType lambdaType = functionalMethod.get().getParamType(index); + + // Replace parameter from declarator + Map inferredTypes = new HashMap<>(); + if (lambdaType.isReferenceType()) { + for (com.github.javaparser.utils.Pair 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 solveSymbol(String name, TypeSolver typeSolver) { + for (Parameter parameter : wrappedNode.getParameters()) { + SymbolDeclarator sb = JavaParserFactory.getSymbolDeclarator(parameter, typeSolver); + SymbolReference 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 solveType(String name, TypeSolver typeSolver) { + return getParent().solveType(name, typeSolver); + } + + @Override + public SymbolReference solveMethod( + String name, List argumentsTypes, boolean staticOnly, TypeSolver typeSolver) { + return getParent().solveMethod(name, argumentsTypes, false, typeSolver); + } + + /// + /// Protected methods + /// + + protected final Optional 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 { + + /// + /// Constructors + /// + + public MethodCallExprContext(MethodCallExpr wrappedNode, TypeSolver typeSolver) { + super(wrappedNode, typeSolver); + } + + /// + /// Public methods + /// + + @Override + public Optional solveGenericType(String name, TypeSolver typeSolver) { + if(wrappedNode.getScope().isPresent()){ + ResolvedType typeOfScope = JavaParserFacade.get(typeSolver).getType(wrappedNode.getScope().get()); + Optional res = typeOfScope.asReferenceType().getGenericParameterByName(name); + return res; + } else{ + return Optional.empty(); + } + } + + @Override + public String toString() { + return "MethodCallExprContext{wrapped=" + wrappedNode + "}"; + } + + @Override + public Optional solveMethodAsUsage(String name, List 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 ref = solveType(className, typeSolver); + if (ref.isSolved()) { + SymbolReference 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 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 typeArguments = new ArrayList<>(); + for (com.github.javaparser.ast.type.Type ty : wrappedNode.getTypeArguments().get()) { + typeArguments.add(JavaParserFacade.get(typeSolver).convertToUsage(ty)); + } + + List 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 solveSymbol(String name, TypeSolver typeSolver) { + return getParent().solveSymbol(name, typeSolver); + } + + @Override + public Optional solveSymbolAsValue(String name, TypeSolver typeSolver) { + Context parentContext = getParent(); + return parentContext.solveSymbolAsValue(name, typeSolver); + } + + @Override + public SymbolReference solveMethod(String name, List argumentsTypes, boolean staticOnly, TypeSolver typeSolver) { + Collection rrtds = findTypeDeclarations(wrappedNode.getScope(), typeSolver); + for (ResolvedReferenceTypeDeclaration rrtd : rrtds) { + SymbolReference res = MethodResolutionLogic.solveMethodInType(rrtd, name, argumentsTypes, false, typeSolver); + if (res.isSolved()) { + return res; + } + } + return SymbolReference.unsolved(ResolvedMethodDeclaration.class); + } + + /// + /// Private methods + /// + + private Optional solveMethodAsUsage(ResolvedReferenceType refType, String name, + List argumentsTypes, TypeSolver typeSolver, + Context invokationContext) { + Optional 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) + // and considering that Stream has this method: + // + // R collect(Collector collector) + // + // and collector has this method: + // + // static Collector> toList() + // + // In this case collect.R has to be equal to List + // And toList.T has to be equal to ? super Stream.T + // Therefore R has to be equal to List. + // In our example Stream.T equal to String, so the R (and the result of the call to collect) is + // List + + Map 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 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 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 actualParamTypes) { + Map 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 + 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 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" + 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 solveMethodAsUsage(ResolvedTypeVariable tp, String name, List argumentsTypes, TypeSolver typeSolver, Context invokationContext) { + for (ResolvedTypeParameterDeclaration.Bound bound : tp.asTypeParameter().getBounds()) { + Optional methodUsage = solveMethodAsUsage(bound.getType(), name, argumentsTypes, typeSolver, invokationContext); + if (methodUsage.isPresent()) { + return methodUsage; + } + } + return Optional.empty(); + } + + private Optional solveMethodAsUsage(ResolvedType type, String name, List 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 inferredTypes) { + if (type.isReferenceType()) { + for (Pair 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 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 { + + /// + /// 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..51dd2d6d0 --- /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.getParentNode; + +/** + * @author Federico Tomassetti + */ +public class StatementContext extends AbstractJavaParserContext { + + public StatementContext(N wrappedNode, TypeSolver typeSolver) { + super(wrappedNode, typeSolver); + } + + public static SymbolReference solveInBlock(String name, TypeSolver typeSolver, Statement stmt) { + if (!(getParentNode(stmt) instanceof NodeWithStatements)) { + throw new IllegalArgumentException(); + } + NodeWithStatements blockStmt = (NodeWithStatements) getParentNode(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 symbolReference = solveWith(symbolDeclarator, name); + if (symbolReference.isSolved()) { + return symbolReference; + } + } + + // if nothing is found we should ask the parent context + return JavaParserFactory.getContext(getParentNode(stmt), typeSolver).solveSymbol(name, typeSolver); + } + + public static Optional solveInBlockAsValue(String name, TypeSolver typeSolver, Statement stmt) { + if (!(getParentNode(stmt) instanceof NodeWithStatements)) { + throw new IllegalArgumentException(); + } + NodeWithStatements blockStmt = (NodeWithStatements) getParentNode(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 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(getParentNode(stmt), typeSolver).solveSymbolAsValue(name, typeSolver); + } + + @Override + public Optional 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 symbolReference = solveWithAsValue(symbolDeclarator, name, typeSolver); + if (symbolReference.isPresent()) { + return symbolReference; + } + + // we should look in all the statements preceding, treating them as SymbolDeclarators + if (getParentNode(wrappedNode) instanceof com.github.javaparser.ast.body.MethodDeclaration) { + return getParent().solveSymbolAsValue(name, typeSolver); + } + if (getParentNode(wrappedNode) instanceof LambdaExpr) { + return getParent().solveSymbolAsValue(name, typeSolver); + } + if (getParentNode(wrappedNode) instanceof IfStmt) { + return getParent().solveSymbolAsValue(name, typeSolver); + } + if (!(getParentNode(wrappedNode) instanceof NodeWithStatements)) { + return getParent().solveSymbolAsValue(name, typeSolver); + } + NodeWithStatements nodeWithStmt = (NodeWithStatements) getParentNode(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 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 symbolReference = solveWith(symbolDeclarator, name); + if (symbolReference.isSolved()) { + return symbolReference; + } + + // we should look in all the statements preceding, treating them as SymbolDeclarators + if (getParentNode(wrappedNode) instanceof com.github.javaparser.ast.body.MethodDeclaration) { + return getParent().solveSymbol(name, typeSolver); + } + if (getParentNode(wrappedNode) instanceof com.github.javaparser.ast.body.ConstructorDeclaration) { + return getParent().solveSymbol(name, typeSolver); + } + if (getParentNode(wrappedNode) instanceof LambdaExpr) { + return getParent().solveSymbol(name, typeSolver); + } + if (!(getParentNode(wrappedNode) instanceof NodeWithStatements)) { + return getParent().solveSymbol(name, typeSolver); + } + NodeWithStatements nodeWithStmt = (NodeWithStatements) getParentNode(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 solveMethod(String name, List argumentsTypes, boolean staticOnly, TypeSolver typeSolver) { + return getParent().solveMethod(name, argumentsTypes, false, typeSolver); + } + + @Override + public SymbolReference 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..053cc2de1 --- /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.getParentNode; + +/** + * @author Federico Tomassetti + */ +public class SwitchEntryContext extends AbstractJavaParserContext { + + public SwitchEntryContext(SwitchEntryStmt wrappedNode, TypeSolver typeSolver) { + super(wrappedNode, typeSolver); + } + + @Override + public SymbolReference solveSymbol(String name, TypeSolver typeSolver) { + SwitchStmt switchStmt = (SwitchStmt) getParentNode(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 symbolReference = solveWith(symbolDeclarator, name); + if (symbolReference.isSolved()) { + return symbolReference; + } + } + } + } + + return getParent().solveSymbol(name, typeSolver); + } + + @Override + public SymbolReference solveMethod(String name, List 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..de6516436 --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/TryWithResourceContext.java @@ -0,0 +1,87 @@ +/* + * 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; + +public class TryWithResourceContext extends AbstractJavaParserContext { + + public TryWithResourceContext(TryStmt wrappedNode, TypeSolver typeSolver) { + super(wrappedNode, typeSolver); + } + + @Override + public Optional 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 (getParentNode(wrappedNode) instanceof BlockStmt) { + return StatementContext.solveInBlockAsValue(name, typeSolver, wrappedNode); + } else { + return getParent().solveSymbolAsValue(name, typeSolver); + } + } + + @Override + public SymbolReference 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 (getParentNode(wrappedNode) instanceof BlockStmt) { + return StatementContext.solveInBlock(name, typeSolver, wrappedNode); + } else { + return getParent().solveSymbol(name, typeSolver); + } + } + + @Override + public SymbolReference solveMethod(String name, List 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 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..18bc92cd1 --- /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 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 p = ((CompilationUnit) container).getPackageDeclaration(); + if (p.isPresent()) { + return p.get().getName().toString(); + } + } else if (container != null) { + return getPackageName(getParentNode(container)); + } + return ""; + } + + static String getClassName(String base, Node container) { + if (container instanceof com.github.javaparser.ast.body.ClassOrInterfaceDeclaration) { + String b = getClassName(base, getParentNode(container)); + 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, getParentNode(container)); + 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, getParentNode(container)); + } + 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..c9431f6b6 --- /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 getAncestors() { + throw new UnsupportedOperationException(); + } + + @Override + public List getAllFields() { + throw new UnsupportedOperationException(); + } + + @Override + public Set 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(getParentNode(wrappedNode)); + if (containerName.isEmpty()) { + return wrappedNode.getName().getId(); + } else { + return containerName + "." + wrappedNode.getName(); + } + } + + @Override + public String getName() { + return wrappedNode.getName().getId(); + } + + @Override + public List getTypeParameters() { + throw new UnsupportedOperationException(); + } + + @Override + public Optional containerType() { + throw new UnsupportedOperationException("containerType is not supported for " + this.getClass().getCanonicalName()); + } + + @Override + public List 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..7a6abeba0 --- /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 List findMembersOfKind(final Class memberClass) { + if (wrappedNode.getAnonymousClassBody().isPresent()) { + return wrappedNode + .getAnonymousClassBody() + .get() + .stream() + .filter(node -> memberClass.isAssignableFrom(node.getClass())) + .map(node -> (T) node) + .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 getInterfaces() { + return + superTypeDeclaration + .asReferenceType().getAncestors() + .stream() + .filter(type -> type.getTypeDeclaration().isInterface()) + .collect(Collectors.toList()); + } + + @Override + public List 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 getAncestors() { + return + ImmutableList. + builder() + .add(getSuperClass()) + .addAll(superTypeDeclaration.asReferenceType().getAncestors()) + .build(); + } + + @Override + public List getAllFields() { + + List 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 superClassFields = + getSuperClass().getTypeDeclaration().getAllFields(); + + List interfaceFields = + getInterfaces().stream() + .flatMap(inteface -> inteface.getTypeDeclaration().getAllFields().stream()) + .collect(Collectors.toList()); + + return + ImmutableList + .builder() + .addAll(myFields) + .addAll(superClassFields) + .addAll(interfaceFields) + .build(); + } + + @Override + public Set 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(getParentNode(wrappedNode)); + if (containerName.isEmpty()) { + return getName(); + } else { + return containerName + "." + getName(); + } + } + + @Override + public Set 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 getTypeParameters() { + return Lists.newArrayList(); + } + + @Override + public Optional 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..7a6e198da --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserClassDeclaration.java @@ -0,0 +1,392 @@ +/* + * 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.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.UnsolvedSymbolException; +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 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 getAllFields() { + List 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 solveMethod(String name, List 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 getInterfaces() { + List interfaces = new ArrayList<>(); + if (wrappedNode.getImplementedTypes() != null) { + for (ClassOrInterfaceType t : wrappedNode.getImplementedTypes()) { + interfaces.add(toReferenceType(t)); + } + } + return interfaces; + } + + @Override + public List getConstructors() { + List 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 solveType(String name, TypeSolver typeSolver) { + if (this.wrappedNode.getName().getId().equals(name)) { + return SymbolReference.solved(this); + } + SymbolReference 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 getAncestors() { + List ancestors = new ArrayList<>(); + 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 getDeclaredMethods() { + Set 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 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 internalTypes() { + Set 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 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 ref = solveType(className, typeSolver); + if (!ref.isSolved()) { + Optional 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 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 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..3ceda1a4e --- /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.getParentNode; + +/** + * @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) getParentNode(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..464aa7b83 --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserEnumDeclaration.java @@ -0,0 +1,349 @@ +/* + * 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.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.javaparsermodel.UnsolvedSymbolException; +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 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 getDeclaredMethods() { + Set 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) { + // Enums cannot be extended + if (other.getQualifiedName().equals(this.getQualifiedName())) { + return true; + } + if (other.getQualifiedName().equals(Enum.class.getCanonicalName())) { + return true; + } + // Enum implements Comparable and Serializable + if (other.getQualifiedName().equals(Comparable.class.getCanonicalName())) { + return true; + } + if (other.getQualifiedName().equals(Serializable.class.getCanonicalName())) { + return true; + } + if (other.getQualifiedName().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 solveMethodAsUsage(String name, List parameterTypes, + TypeSolver typeSolver, Context invokationContext, List 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 getAllFields() { + List 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 getAncestors() { + List 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 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 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 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 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 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 internalTypes() { + Set 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 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..fe55deeb4 --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserFieldDeclaration.java @@ -0,0 +1,123 @@ +/* + * 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; + +/** + * @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 (!(getParentNode(variableDeclarator) instanceof com.github.javaparser.ast.body.FieldDeclaration)) { + throw new IllegalStateException(getParentNode(variableDeclarator).getClass().getCanonicalName()); + } + this.wrappedNode = (com.github.javaparser.ast.body.FieldDeclaration) getParentNode(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) getParentNode(enumConstantDeclaration); + return new ReferenceTypeImpl(new JavaParserEnumDeclaration(enumDeclaration, typeSolver), typeSolver); + } else { + ResolvedType retType = JavaParserFacade.get(typeSolver).convert(variableDeclarator.getType(), wrappedNode); + return retType; + } + } + + @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 typeDeclaration = Navigator.findAncestor(wrappedNode, com.github.javaparser.ast.body.TypeDeclaration.class); + if (typeDeclaration.isPresent()) { + return JavaParserFacade.get(typeSolver).getTypeDeclaration(typeDeclaration.get()); + } else { + 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..5d33bcdce --- /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.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.UnsolvedSymbolException; +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 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 getDeclaredMethods() { + Set 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 getInterfacesExtended() { + List 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 getAllFields() { + List 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 solveType(String name, TypeSolver typeSolver) { + if (this.wrappedNode.getName().getId().equals(name)) { + return SymbolReference.solved(this); + } + SymbolReference 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 getAncestors() { + List 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 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 internalTypes() { + Set 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 containerType() { + return javaParserTypeAdapter.containerType(); + } + + /// + /// Private methods + /// + + private ResolvedReferenceType toReferenceType(ClassOrInterfaceType classOrInterfaceType) { + SymbolReference 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 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..b352e905a --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserMethodDeclaration.java @@ -0,0 +1,168 @@ +/* + * 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.getParentNode; + +/** + * @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 (getParentNode(wrappedNode) instanceof ObjectCreationExpr) { + ObjectCreationExpr parentNode = (ObjectCreationExpr) getParentNode(wrappedNode); + return new JavaParserAnonymousClassDeclaration(parentNode, typeSolver); + } else { + return JavaParserFactory.toTypeDeclaration(getParentNode(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 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 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 = 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..9b8578849 --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserSymbolDeclaration.java @@ -0,0 +1,177 @@ +/* + * 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.getParentNode; + +/** + * @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 : getParentNode(parameter).getChildNodes()) { + if (node == parameter) { + return pos; + } else if (node instanceof Parameter) { + pos++; + } + } + return pos; + } + + public static int getParamPos(Node node) { + if (getParentNode(node) instanceof MethodCallExpr) { + MethodCallExpr call = (MethodCallExpr) getParentNode(node); + for (int i = 0; i < call.getArguments().size(); i++) { + if (call.getArguments().get(i) == node) return i; + } + throw new IllegalStateException(); + } else { + 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 (getParentNode(wrappedNode) instanceof LambdaExpr) { + int pos = getParamPos(parameter); + ResolvedType lambdaType = JavaParserFacade.get(typeSolver).getType(getParentNode(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 { + ResolvedType rawType = null; + 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); + } else { + return rawType; + } + } + } else if (wrappedNode instanceof VariableDeclarator) { + VariableDeclarator variableDeclarator = (VariableDeclarator) wrappedNode; + if (getParentNode(wrappedNode) instanceof VariableDeclarationExpr) { + ResolvedType type = JavaParserFacade.get(typeSolver).convert(variableDeclarator.getType(), JavaParserFactory.getContext(wrappedNode, typeSolver)); + return type; + } else if (getParentNode(wrappedNode) instanceof FieldDeclaration) { + return JavaParserFacade.get(typeSolver).convert(variableDeclarator.getType(), JavaParserFactory.getContext(wrappedNode, typeSolver)); + } else { + throw new UnsupportedOperationException(getParentNode(wrappedNode).getClass().getCanonicalName()); + } + } else { + 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..c65b2d817 --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeAdapter.java @@ -0,0 +1,138 @@ +package com.github.javaparser.symbolsolver.javaparsermodel.declarations; + +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.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.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 & NodeWithMembers & NodeWithTypeParameters> { + + 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 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 solveType(String name, TypeSolver typeSolver) { + if (this.wrappedNode.getTypeParameters() != null) { + for (com.github.javaparser.ast.type.TypeParameter typeParameter : this.wrappedNode.getTypeParameters()) { + 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 containerType() { + Optional parent = wrappedNode.getParentNode(); + return parent.isPresent() ? + Optional.of(JavaParserFactory.toTypeDeclaration(parent.get(), typeSolver)) : + Optional.empty(); + } + + public List getFieldsForDeclaredVariables() { + ArrayList 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..79eaf350f --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/declarations/JavaParserTypeParameter.java @@ -0,0 +1,224 @@ +/* + * 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.getParentNode; + +/** + * @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 getDeclaredMethods() { + return Collections.emptySet(); + } + + public SymbolReference solveMethod(String name, List 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 = getParentNode(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 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 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); + Bound bound = Bound.extendsBound(type); + return bound; + } + + 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 getAllFields() { + return new ArrayList<>(); + } + + @Override + public List getAncestors() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isTypeParameter() { + return true; + } + + @Override + public boolean hasDirectlyAnnotation(String canonicalName) { + throw new UnsupportedOperationException(); + } + + @Override + public List 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 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 solveMethod(String name, List 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 getAllFields() { + return new ArrayList<>(); + } + + @Override + public List getAncestors() { + throw new UnsupportedOperationException(); + } + + @Override + public Set 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 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 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 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 { + + public FieldSymbolDeclarator(FieldDeclaration wrappedNode, TypeSolver typeSolver) { + super(wrappedNode, typeSolver); + } + + @Override + public List getSymbolDeclarations() { + List 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 extends AbstractSymbolDeclarator { + + public NoSymbolDeclarator(N wrappedNode, TypeSolver typeSolver) { + super(wrappedNode, typeSolver); + } + + @Override + public List 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 { + + public ParameterSymbolDeclarator(Parameter wrappedNode, TypeSolver typeSolver) { + super(wrappedNode, typeSolver); + } + + @Override + public List getSymbolDeclarations() { + List 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..be6a66924 --- /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 { + + public VariableSymbolDeclarator(VariableDeclarationExpr wrappedNode, TypeSolver typeSolver) { + super(wrappedNode, typeSolver); + if (getParentNode(wrappedNode) instanceof FieldDeclaration) { + throw new IllegalArgumentException(); + } + } + + @Override + public List getSymbolDeclarations() { + List symbols = wrappedNode.getVariables().stream().map( + v -> JavaParserSymbolDeclaration.localVar(v, typeSolver) + ).collect( + Collectors.toCollection(() -> new LinkedList<>())); + return symbols; + } + +} 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 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 solveMethodAsUsage(String name, List argumentsTypes, TypeSolver typeSolver, + Context invokationContext, List typeParameterValues) { + return JavassistUtils.getMethodUsage(ctClass, name, argumentsTypes, typeSolver, invokationContext); + } + + @Deprecated + public SymbolReference 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 ref = solveSymbolForFQN(name, typeSolver, superclassFQN); + if (ref.isSolved()) { + return ref; + } + + String[] interfaceFQNs = getInterfaceFQNs(); + for (String interfaceFQN : interfaceFQNs) { + SymbolReference interfaceRef = solveSymbolForFQN(name, typeSolver, interfaceFQN); + if (interfaceRef.isSolved()) { + return interfaceRef; + } + } + + return SymbolReference.unsolved(ResolvedValueDeclaration.class); + } + + private SymbolReference 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 getAncestors() { + List ancestors = new LinkedList<>(); + if (getSuperClass() != null) { + ancestors.add(getSuperClass()); + } + ancestors.addAll(getInterfaces()); + return ancestors; + } + + @Deprecated + public SymbolReference solveMethod(String name, List argumentsTypes, boolean staticOnly) { + List candidates = new ArrayList<>(); + Predicate 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 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 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 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 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 getTypeParameters() { + return javassistTypeDeclarationAdapter.getTypeParameters(); + } + + @Override + public AccessSpecifier accessSpecifier() { + return JavassistFactory.modifiersToAccessLevel(ctClass.getModifiers()); + } + + @Override + public List getConstructors() { + return javassistTypeDeclarationAdapter.getConstructors(); + } + + @Override + public Optional containerType() { + return javassistTypeDeclarationAdapter.containerType(); + } + + @Override + public Set 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 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 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 getAncestors() { + // Direct ancestors of an enum are java.lang.Enum and interfaces + List 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 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 getAllFields() { + return javassistTypeDeclarationAdapter.getDeclaredFields(); + } + + @Override + public Set 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 getTypeParameters() { + return javassistTypeDeclarationAdapter.getTypeParameters(); + } + + @Override + public Optional containerType() { + return javassistTypeDeclarationAdapter.containerType(); + } + + public SymbolReference solveMethod(String name, List argumentsTypes, boolean staticOnly) { + List candidates = new ArrayList<>(); + Predicate 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 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 solveMethodAsUsage(String name, List argumentsTypes, TypeSolver typeSolver, + Context invokationContext, List typeParameterValues) { + return JavassistUtils.getMethodUsage(ctClass, name, argumentsTypes, typeSolver, invokationContext); + } + + @Override + public Set 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 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 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 interfaceRef = solveSymbolForFQN(name, typeSolver, interfaceFQN); + if (interfaceRef.isSolved()) { + return interfaceRef; + } + } + + return SymbolReference.unsolved(ResolvedValueDeclaration.class); + } + + private SymbolReference 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 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 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 solveMethodAsUsage(String name, List argumentsTypes, TypeSolver typeSolver, + Context invokationContext, List typeParameterValues) { + + return JavassistUtils.getMethodUsage(ctClass, name, argumentsTypes, typeSolver, invokationContext); + } + + @Deprecated + public SymbolReference solveMethod(String name, List argumentsTypes, boolean staticOnly) { + List candidates = new ArrayList<>(); + Predicate 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 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 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 getAllFields() { + return javassistTypeDeclarationAdapter.getDeclaredFields(); + } + + @Override + public boolean isAssignableBy(ResolvedReferenceTypeDeclaration other) { + throw new UnsupportedOperationException(); + } + + @Override + public List getAncestors() { + List 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 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 getTypeParameters() { + return javassistTypeDeclarationAdapter.getTypeParameters(); + } + + @Override + public AccessSpecifier accessSpecifier() { + return JavassistFactory.modifiersToAccessLevel(ctClass.getModifiers()); + } + + @Override + public ResolvedInterfaceDeclaration asInterface() { + return this; + } + + + @Deprecated + public SymbolReference 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 interfaceRef = solveSymbolForFQN(name, typeSolver, interfaceFQN); + if (interfaceRef.isSolved()) { + return interfaceRef; + } + } + + return SymbolReference.unsolved(ResolvedValueDeclaration.class); + } + + private SymbolReference 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 containerType() { + return javassistTypeDeclarationAdapter.containerType(); + } + + @Override + public Set 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 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 parameterTypes) { + return new MethodDeclarationCommonLogic(this, typeSolver).resolveTypeVariables(context, parameterTypes); + } + + @Override + public boolean isAbstract() { + return Modifier.isAbstract(ctMethod.getModifiers()); + } + + @Override + public List 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 getDeclaredMethods() { + return Arrays.stream(ctClass.getDeclaredMethods()) + .map(m -> new JavassistMethodDeclaration(m, typeSolver)).collect(Collectors.toSet()); + } + + public List getConstructors() { + return Arrays.stream(ctClass.getConstructors()) + .map(m -> new JavassistConstructorDeclaration(m, typeSolver)).collect(Collectors.toList()); + } + + public List getDeclaredFields() { + List fieldDecls = new ArrayList<>(); + collectDeclaredFields(ctClass, fieldDecls); + return fieldDecls; + } + + private void collectDeclaredFields(CtClass ctClass, List 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 getTypeParameters() { + if (null == ctClass.getGenericSignature()) { + return Collections.emptyList(); + } else { + try { + SignatureAttribute.ClassSignature classSignature = + SignatureAttribute.toClassSignature(ctClass.getGenericSignature()); + return Arrays.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 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 getBounds() { + List 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 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 getMethodUsage(CtClass ctClass, String name, List 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 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 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 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 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 types = new ArrayList<>(); + types.add(ResolvedWildcard.UNBOUNDED); + return types; + } + List 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 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 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 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 type = getGenericParameterByName(typeName, typeParametrizable); + return type.orElseGet(() -> new ReferenceTypeImpl( + typeSolver.solveType(removeTypeArguments(internalNameToCanonicalName(typeName))), + typeSolver)); + } + + private static Optional getGenericParameterByName(String typeName, ResolvedTypeParametrizable typeParametrizable) { + Optional 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..a7d05d143 --- /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 provider; + + public LazyType(Function 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().isArray(); + } + + @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 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..8397a6702 --- /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( + tp -> new ResolvedTypeVariable(tp) + ).collect(Collectors.toList()), typeSolver); + } + + @Override + protected ResolvedReferenceType create(ResolvedReferenceTypeDeclaration typeDeclaration, List 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 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 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 getDeclaredMethods() { + // TODO replace variables + Set 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 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 typeParametersCorrected = result.asReferenceType().typeParametersValues(); + typeParametersCorrected.set(i, transformedTp); + result = create(typeDeclaration, typeParametersCorrected); + } + i++; + } + return result; + } + + public List getAllAncestors() { + // We need to go through the inheritance line and propagate the type parametes + + List 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 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 getInterfaces() { + List interfaces = new ArrayList<>(); + for (java.lang.reflect.Type superInterface : clazz.getGenericInterfaces()) { + if (superInterface instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) superInterface; + List 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 getAncestors() { + List 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 getAllFields() { + ArrayList 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 getDeclaredMethods() { + return Arrays.stream(clazz.getDeclaredMethods()) + .filter(m -> !m.isSynthetic() && !m.isBridge()) + .map(m -> new ReflectionMethodDeclaration(m, typeSolver)) + .collect(Collectors.toSet()); + } + + public List getTypeParameters() { + List 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 getConstructors() { + return Arrays.stream(clazz.getConstructors()) + .map(m -> new ReflectionConstructorDeclaration(m, typeSolver)) + .collect(Collectors.toList()); + } + + public Optional 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 getDeclaredMethods() { + return reflectionClassAdapter.getDeclaredMethods(); + } + + @Override + public List 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 solveMethod(String name, List argumentsTypes, boolean staticOnly) { + List methods = new ArrayList<>(); + Predicate 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 ref = MethodResolutionLogic.solveMethodInType(superClass, name, argumentsTypes, staticOnly, typeSolver); + if (ref.isSolved()) { + methods.add(ref.getCorrespondingDeclaration()); + } + } + for (ResolvedReferenceType interfaceDeclaration : getInterfaces()) { + SymbolReference 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 solveMethodAsUsage(String name, List argumentsTypes, TypeSolver typeSolver, Context invokationContext, List typeParameterValues) { + List 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 ref = ContextHelper.solveMethodAsUsage(superClass, name, argumentsTypes, typeSolver, invokationContext, typeParameterValues); + if (ref.isPresent()) { + methods.add(ref.get()); + } + } + for (ResolvedReferenceType interfaceDeclaration : getInterfaces()) { + Optional ref = ContextHelper.solveMethodAsUsage(interfaceDeclaration.getTypeDeclaration(), name, argumentsTypes, typeSolver, invokationContext, typeParameterValues); + if (ref.isPresent()) { + methods.add(ref.get()); + } + } + Optional 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 getAllFields() { + return reflectionClassAdapter.getAllFields(); + } + + @Deprecated + public SymbolReference 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 getInterfaces() { + return reflectionClassAdapter.getInterfaces(); + } + + @Override + public boolean isInterface() { + return clazz.isInterface(); + } + + @Override + public List getTypeParameters() { + return reflectionClassAdapter.getTypeParameters(); + } + + @Override + public AccessSpecifier accessSpecifier() { + return ReflectionFactory.modifiersToAccessLevel(this.clazz.getModifiers()); + } + + @Override + public List getConstructors() { + return reflectionClassAdapter.getConstructors(); + } + + @Override + public Optional containerType() { + return reflectionClassAdapter.containerType(); + } + + @Override + public Set 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 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 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 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 getAllFields() { + return reflectionClassAdapter.getAllFields(); + } + + @Override + public Set 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 getTypeParameters() { + return reflectionClassAdapter.getTypeParameters(); + } + + public SymbolReference solveMethod(String name, List parameterTypes, boolean staticOnly) { + return ReflectionMethodResolutionLogic.solveMethod(name, parameterTypes, staticOnly, + typeSolver,this, clazz); + } + + public Optional solveMethodAsUsage(String name, List parameterTypes, TypeSolver typeSolver, + Context invokationContext, List typeParameterValues) { + Optional 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 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 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 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 solveMethod(String name, List 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 solveMethodAsUsage(String name, List parameterTypes, TypeSolver typeSolver, + Context invokationContext, List typeParameterValues) { + Optional 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 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 getAllFields() { + return reflectionClassAdapter.getAllFields(); + } + + @Deprecated + public SymbolReference 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 getAncestors() { + return reflectionClassAdapter.getAncestors(); + } + + @Override + public Set 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 getInterfacesExtended() { + List res = new ArrayList<>(); + for (Class i : clazz.getInterfaces()) { + res.add(new ReferenceTypeImpl(new ReflectionInterfaceDeclaration(i, typeSolver), typeSolver)); + } + return res; + } + + @Override + public Optional containerType() { + return reflectionClassAdapter.containerType(); + } + + @Override + public Set 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 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 getTypeParameters() { + return Arrays.stream(method.getTypeParameters()).map((refTp) -> new ReflectionTypeParameter(refTp, false, typeSolver)).collect(Collectors.toList()); + } + + public MethodUsage resolveTypeVariables(Context context, List 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 solveMethod(String name, List parameterTypes, boolean staticOnly, + TypeSolver typeSolver, ResolvedReferenceTypeDeclaration scopeType, + Class clazz){ + List methods = new ArrayList<>(); + Predicate 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 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 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 solveMethodAsUsage(String name, List argumentsTypes, TypeSolver typeSolver, + Context invokationContext, List 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 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 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 ref = MethodResolutionLogic.solveMethodInType(objectClass.getTypeDeclaration(), name, argumentsTypes, typeSolver); + if (ref.isSolved()) { + MethodUsage usage = replaceParams(typeParameterValues, objectClass.getTypeDeclaration(), ref.getCorrespondingDeclaration()); + methods.add(usage); + } + } + + final List 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 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 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 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> { + + @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 { + + @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 { + + @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 groupVariadicParamValues(List argumentsTypes, int startVariadic, + ResolvedType variadicType) { + List res = new ArrayList<>(argumentsTypes.subList(0, startVariadic)); + List 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 variadicValues) { + if (variadicValues.isEmpty()) { + throw new IllegalArgumentException(); + } + // TODO implement this decently + return variadicValues.get(0); + } + + public static boolean isApplicable(ResolvedConstructorDeclaration constructor, List argumentsTypes, + TypeSolver typeSolver) { + return isApplicable(constructor, argumentsTypes, typeSolver, false); + } + + private static boolean isApplicable(ResolvedConstructorDeclaration constructor, List 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 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 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 findMostApplicable( + List constructors, List argumentsTypes, TypeSolver typeSolver) { + SymbolReference res = + findMostApplicable(constructors, argumentsTypes, typeSolver, false); + if (res.isSolved()) { + return res; + } + return findMostApplicable(constructors, argumentsTypes, typeSolver, true); + } + + public static SymbolReference findMostApplicable( + List constructors, List argumentsTypes, TypeSolver typeSolver, boolean wildcardTolerance) { + List 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 groupVariadicParamValues(List argumentsTypes, int startVariadic, ResolvedType variadicType) { + List res = new ArrayList<>(argumentsTypes.subList(0, startVariadic)); + List 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 variadicValues) { + if (variadicValues.isEmpty()) { + throw new IllegalArgumentException(); + } + // TODO implement this decently + return variadicValues.get(0); + } + + public static boolean isApplicable(ResolvedMethodDeclaration method, String name, List argumentsTypes, TypeSolver typeSolver) { + return isApplicable(method, name, argumentsTypes, typeSolver, false); + } + + private static boolean isApplicable(ResolvedMethodDeclaration method, String name, List 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 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 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 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 matchedParameters) { + if (actual.getQualifiedName().equals(expected.getQualifiedName())) { + return isAssignableMatchTypeParametersMatchingQName(expected, actual, matchedParameters); + } else { + List 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 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 <-> List + // 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 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 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 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 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 typeParameters = method.getDeclaration().getTypeParameters(); + typeParameters.addAll(method.declaringType().getTypeParameters()); + + if (expectedType.describe().equals(actualType.describe())){ + return true; + } + + Map 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 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 getMethodsWithoutDuplicates(List methods) { + Set 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 res = new ArrayList<>(); + Set 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 findMostApplicable(List methods, + String name, List argumentsTypes, TypeSolver typeSolver) { + SymbolReference res = findMostApplicable(methods, name, argumentsTypes, typeSolver, false); + if (res.isSolved()) { + return res; + } + return findMostApplicable(methods, name, argumentsTypes, typeSolver, true); + } + + public static SymbolReference findMostApplicable(List methods, + String name, List argumentsTypes, TypeSolver typeSolver, boolean wildcardTolerance) { + List 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 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 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 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 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 findMostApplicableUsage(List methods, String name, List argumentsTypes, TypeSolver typeSolver) { + List 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 solveMethodInType(ResolvedTypeDeclaration typeDeclaration, + String name, List 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 solveMethodInType(ResolvedTypeDeclaration typeDeclaration, + String name, List 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 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 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..7336fa1cb --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/SymbolSolver.java @@ -0,0 +1,172 @@ +/* + * 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.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.UnsolvedSymbolException; +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 solveSymbol(String name, Context context) { + return context.solveSymbol(name, typeSolver); + } + + public SymbolReference solveSymbol(String name, Node node) { + return solveSymbol(name, JavaParserFactory.getContext(node, typeSolver)); + } + + public Optional solveSymbolAsValue(String name, Context context) { + return context.solveSymbolAsValue(name, typeSolver); + } + + public Optional solveSymbolAsValue(String name, Node node) { + Context context = JavaParserFactory.getContext(node, typeSolver); + return solveSymbolAsValue(name, context); + } + + public SymbolReference solveType(String name, Context context) { + return context.solveType(name, typeSolver); + } + + public SymbolReference solveType(String name, Node node) { + return solveType(name, JavaParserFactory.getContext(node, typeSolver)); + } + + public MethodUsage solveMethod(String methodName, List argumentsTypes, Context context) { + SymbolReference decl = context.solveMethod(methodName, argumentsTypes, false, typeSolver); + if (!decl.isSolved()) { + throw new UnsolvedSymbolException(context, methodName); + } + return new MethodUsage(decl.getCorrespondingDeclaration()); + } + + public MethodUsage solveMethod(String methodName, List 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 ref = JavaParserFactory.getContext(type, typeSolver).solveType(name, typeSolver); + if (!ref.isSolved()) { + throw new UnsolvedSymbolException(JavaParserFactory.getContext(type, typeSolver), name); + } + return ref.getCorrespondingDeclaration(); + } else { + throw new UnsupportedOperationException(type.getClass().getCanonicalName()); + } + } + + public ResolvedType solveTypeUsage(String name, Context context) { + Optional genericType = context.solveGenericType(name, typeSolver); + if (genericType.isPresent()) { + return genericType.get(); + } + ResolvedReferenceTypeDeclaration typeDeclaration = typeSolver.solveType(name); + ReferenceTypeImpl typeUsage = new ReferenceTypeImpl(typeDeclaration, typeSolver); + return typeUsage; + } + + /** + * Solve any possible visible symbols including: fields, internal types, type variables, the type itself or its + * containers. + *

+ * It should contain its own private fields but not inherited private fields. + */ + public SymbolReference 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 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 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 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 isProperLowerBound() { + return Optional.empty(); + } + + Optional isProperLowerBoundFor(InferenceVariable inferenceVariable) { + Optional partial = isProperLowerBound(); + if (partial.isPresent() && partial.get().getInferenceVariable().equals(inferenceVariable)) { + return partial; + } else { + return Optional.empty(); + } + } + + Optional isProperUpperBoundFor(InferenceVariable inferenceVariable) { + Optional 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 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 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> findPairSameAs(Predicate> condition) { + for (int i=0;i pair = new Pair(si, sj); + if (condition.test(pair)) { + return Optional.of(pair); + } + } + } + } + } + return Optional.empty(); + } + + public boolean isEmpty() { + return bounds.isEmpty(); + } + + interface Processor { + R process(B1 a, B2 b, R initialValue); + } + + private T forEachPairSameAs(Processor processor, T initialValue) { + T currentValue = initialValue; + for (int i=0;i T forEachPairSameAndSubtype(Processor processor, T initialValue) { + T currentValue = initialValue; + for (int i=0;i T forEachPairSubtypeAndSubtype(Processor processor, T initialValue) { + T currentValue = initialValue; + for (int i=0;i> findPairsOfCommonAncestors(ResolvedReferenceType r1, ResolvedReferenceType r2) { + List set1 = new LinkedList<>(); + set1.add(r1); + set1.addAll(r1.getAllAncestors()); + List set2 = new LinkedList<>(); + set2.add(r2); + set2.addAll(r2.getAllAncestors()); + List> 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 and a supertype of T of the form G (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> pairs = findPairsOfCommonAncestors(S, T); + for (Pair pair : pairs) { + for (int i=0;i = capture(G), 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 allInferenceVariables() { + Set 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 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 List> buildAllSubsetsOfSize(Set allElements, int desiredSize) { + if (desiredSize == allElements.size()) { + return Arrays.asList(allElements); + } else { + List> res = new LinkedList<>(); + for (T element : allElements) { + Set subset = allButOne(allElements, element); + res.addAll(buildAllSubsetsOfSize(subset, desiredSize)); + } + return res; + } + } + + private Set allButOne(Set elements, T element) { + Set set = new HashSet(elements); + set.remove(element); + return set; + } + + /** + * there exists no non-empty proper subset of { α1, ..., αn } with this property. + */ + private Optional> smallestSetWithProperty(Set uninstantiatedVariables, + List dependencies) { + for (int i=1;i<=uninstantiatedVariables.size();i++) { + for (Set 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 alphas, List 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 performResolution(List variablesToResolve, TypeSolver typeSolver) { + + if (this.containsFalse()) { + return Optional.empty(); + } + + List 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 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 uninstantiatedPortionOfV = new HashSet<>(); + for (InferenceVariable v : V) { + if (!hasInstantiationFor(v)) { + uninstantiatedPortionOfV.add(v); + } + } + for (Set 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 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 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> allPossibleSetsWithProperty(Set allElements, List dependencies) { + Set> result = new HashSet<>(); + for (int i=1;i<=allElements.size();i++) { + for (Set aSubSet : buildAllSubsetsOfSize(allElements, i)){ + if (hasProperty(aSubSet, dependencies)) { + result.add(aSubSet); + } + } + } + return result; + } + + private boolean thereAreProperSubsets(Set aSet, Set> allPossibleSets) { + for (Set anotherSet : allPossibleSets) { + if (!anotherSet.equals(aSet)) { + if (isTheFirstAProperSubsetOfTheSecond(anotherSet, aSet)) { + return true; + } + } + } + return false; + } + + private boolean isTheFirstAProperSubsetOfTheSecond(Set subset, Set originalSet) { + return originalSet.containsAll(subset) && originalSet.size() > subset.size(); + } + + private Set> allSetsWithProperty(Set allElements, List dependencies) { + Set> allPossibleSets = allPossibleSetsWithProperty(allElements, dependencies); + Set> selected = new HashSet<>(); + for (Set 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 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 constraintFormulas; + + public BoundSet getBoundSet() { + return boundSet; + } + + public List 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 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 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 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 containedTryStmts(Statement statement) { + throw new UnsupportedOperationException(); + } + + private

boolean parentIs(Node node, Class

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 visitor = new GenericVisitorAdapter(){ + @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 visitor = new GenericVisitorAdapter(){ + @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 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 instantiate(List typeParameterDeclarations) { + List 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 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 inferenceVariables; + private List 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 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 typeParameters; + private List formalArgumentTypes; + private ResolvedType returnType; + private List exceptionTypes; + + public static MethodType fromMethodUsage(MethodUsage methodUsage) { + return new MethodType(methodUsage.getDeclaration().getTypeParameters(), methodUsage.getParamTypes(), + methodUsage.returnType(), methodUsage.exceptionTypes()); + } + + public MethodType(List typeParameters, List formalArgumentTypes, + ResolvedType returnType, + List exceptionTypes) { + this.typeParameters = typeParameters; + this.formalArgumentTypes = formalArgumentTypes; + this.returnType = returnType; + this.exceptionTypes = exceptionTypes; + } + + public List getTypeParameters() { + return typeParameters; + } + + public List getFormalArgumentTypes() { + return formalArgumentTypes; + } + + public ResolvedType getReturnType() { + return returnType; + } + + public List 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 typeParameterDeclarations; + private List 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 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 ResolvedPrimitiveType.ALL.stream().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 correspondingPrimitiveTypeForS = ResolvedPrimitiveType.ALL.stream().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 usedInferenceVariables(ResolvedType type) { + if (isInferenceVariable(type)) { + return new HashSet<>(Arrays.asList((InferenceVariable)type)); + } + if (type.isReferenceType()) { + Set 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 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 and List, simply intersecting the sets ST(List) = { List, Collection, Object } and ST(List) = { List, Collection, 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) = { List, Collection, Object } and EST(List) = { 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, List }. 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, G) = G + // + //lcp(G) = G + // + //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 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 TIs = new LinkedList<>(); + List AIs = originalType.typeParametersValues(); + List 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(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 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 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 instantiationSetOpt = typeInference.instantiationInference(call, methodDeclaration); + if (instantiationSetOpt.isPresent()) { + return instantiationSetToMethodUsage(methodDeclaration, instantiationSetOpt.get()); + } else { + throw new IllegalArgumentException(); + } + } + + /// + /// Public instance methods + /// + + public Optional instantiationInference(MethodCallExpr methodCallExpr, ResolvedMethodDeclaration methodDeclaration) { + return instantiationInference(methodCallExpr.getArguments(), methodDeclaration); + } + + public Optional instantiationInference(List 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 Ps = methodDeclaration.getTypeParameters(); + List alphas = InferenceVariable.instantiate(Ps); + Substitution theta = Substitution.empty(); + for (int i=0;i Fs = formalParameterTypes(methodDeclaration); + List es = argumentExpressions; + + Optional 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 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 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= 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, 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), 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 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 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, 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 is not a well-formed type (that is, the type arguments are not within their bounds), or if F is not a subtype of F, no valid parameterization exists. Otherwise, the inferred parameterization is either F, if all the type arguments are types, or the non-wildcard parameterization (§9.9) of F, 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 paramTypes = new LinkedList<>(); + for (int i=0;i typeParameterDeclarations, List 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 formalParameterTypes(ResolvedMethodDeclaration methodDeclaration) { + List types = new LinkedList<>(); + for (int i=0;i 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 testForApplicabilityByStrictInvocation(List Fs, List 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 testForApplicabilityByLooseInvocation(List Fs, List 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 Fs, List es, Substitution theta, int k) { + ConstraintFormulaSet constraintFormulaSet = ConstraintFormulaSet.empty(); + for (int i=0;i testForApplicabilityByVariableArityInvocation(List Fs, List 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 FsFirst = new LinkedList<>(); + for (int i=0;i>> typeForLambdaParameters = new HashMap<>(); + private static Map>> 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 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 _inferenceVariables) { + if (!inferenceVariables.containsKey(typeSolver)) { + inferenceVariables.put(typeSolver, new IdentityHashMap<>()); + } + inferenceVariables.get(typeSolver).put(lambdaExpr, _inferenceVariables); + } + + public static Optional> 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): The variables α1, ..., αn represent the result of capture conversion (§5.1.10) + * applied to G (where A1, ..., An may be types or wildcards and may mention inference variables). + * + * @author Federico Tomassetti + */ +public class CapturesBound extends Bound { + private List inferenceVariables; + private List typesOrWildcards; + + public CapturesBound(List inferenceVariables, List typesOrWildcards) { + this.inferenceVariables = inferenceVariables; + this.typesOrWildcards = typesOrWildcards; + } + + @Override + public boolean isSatisfied(InferenceVariableSubstitution inferenceVariableSubstitution) { + throw new UnsupportedOperationException(); + } + + @Override + public Set usedInferenceVariables() { + throw new UnsupportedOperationException(); + } + + public List getInferenceVariables() { + return inferenceVariables; + } + + public List 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 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 usedInferenceVariables() { + Set 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 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 usedInferenceVariables() { + Set variables = new HashSet<>(); + variables.addAll(TypeHelper.usedInferenceVariables(s)); + variables.addAll(TypeHelper.usedInferenceVariables(t)); + return variables; + } + + public ResolvedType getT() { + return t; + } + + @Override + public Optional isProperUpperBound() { + if (isInferenceVariable(s) && isProperType(t)) { + return Optional.of(new ProperUpperBound((InferenceVariable) s, t)); + } + return Optional.empty(); + } + + @Override + public Optional 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 usedInferenceVariables() { + Set 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..81b0b348c --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/typeinference/constraintformulas/ExpressionCompatibleWithType.java @@ -0,0 +1,338 @@ +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.javaparser.Navigator; +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 java.util.stream.Collectors; + +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; + +/** + * 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 is a subtype of F 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 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 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 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()).stream().forEach(e -> constraints.add(new ExpressionCompatibleWithType(typeSolver, e, R))); + } else { + // FEDERICO: Added - Start + for (int i=0;i getAllReturnExpressions(BlockStmt blockStmt) { + return Navigator.findAllNodesOfGivenClass(blockStmt, ReturnStmt.class).stream() + .filter(r -> r.getExpression().isPresent()) + .map(r -> r.getExpression().get()) + .collect(Collectors.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) { + BlockStmt blockStmt = (BlockStmt)statement; + if (!ControlFlowLogic.getInstance().canCompleteNormally(statement)) { + return true; + } + List returnStmts = Navigator.findAllNodesOfGivenClass(statement, ReturnStmt.class); + return returnStmts.stream().allMatch(r -> r.getExpression().isPresent()); + } else { + 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 correspondences = new HashMap<>(); + List 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 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, 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[]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 Bs = S.asReferenceType().typeParametersValues(); + List 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 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..5f55cbbfc --- /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.declarations.ResolvedReferenceTypeDeclaration; +import com.github.javaparser.symbolsolver.javaparsermodel.UnsolvedSymbolException; +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 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 tryToSolveType(String name) { + for (TypeSolver ts : elements) { + SymbolReference res = ts.tryToSolveType(name); + if (res.isSolved()) { + return res; + } + } + return SymbolReference.unsolved(ResolvedReferenceTypeDeclaration.class); + } + + @Override + public ResolvedReferenceTypeDeclaration solveType(String name) throws UnsolvedSymbolException { + SymbolReference 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 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 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 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 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> parsedFiles = CacheBuilder.newBuilder().softValues().build(); + private Cache> parsedDirectories = CacheBuilder.newBuilder().softValues().build(); + private Cache> 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 parse(File srcFile) { + try { + return parsedFiles.get(srcFile.getAbsolutePath(), () -> { + Optional 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 parseDirectory(File srcDirectory) { + try { + return parsedDirectories.get(srcDirectory.getAbsolutePath(), () -> { + List units = new ArrayList<>(); + File[] files = srcDirectory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.getName().toLowerCase().endsWith(".java")) { + Optional unit = parse(file); + if (unit.isPresent()) { + units.add(unit.get()); + } + } + } + } + return units; + }); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + } + + @Override + public SymbolReference tryToSolveType(String name) { + // TODO support enums + // TODO support interfaces + try { + return foundTypes.get(name, () -> { + SymbolReference result = tryToSolveTypeUncached(name); + if (result.isSolved()) { + return SymbolReference.solved(result.getCorrespondingDeclaration()); + } + return result; + }); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + } + + private SymbolReference 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 = parse(srcFile); + if (compilationUnit.isPresent()) { + Optional> astTypeDeclaration = Navigator.findType(compilationUnit.get(), typeName); + if (astTypeDeclaration.isPresent()) { + return SymbolReference.solved(JavaParserFacade.get(this).getTypeDeclaration(astTypeDeclaration.get())); + } + } + } + + { + List compilationUnits = parseDirectory(srcFile.getParentFile()); + for (CompilationUnit compilationUnit : compilationUnits) { + Optional> 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 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 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 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 parent = tryToSolveType(parentName); + if (parent.isSolved()) { + Optional 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); + } + } + +} -- cgit v1.2.3 From 78869e27b5abef4c86f9b2570d773a2a12bcaf3b Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Wed, 10 Jan 2018 14:03:59 +0100 Subject: fix dependencies and path issues --- javaparser-symbol-solver-core/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml index 73a216939..fc35e4649 100644 --- a/javaparser-symbol-solver-core/pom.xml +++ b/javaparser-symbol-solver-core/pom.xml @@ -34,7 +34,7 @@ com.github.javaparser - java-symbol-solver-logic + javaparser-symbol-solver-logic ${project.version} compile @@ -46,7 +46,7 @@ com.github.javaparser - java-symbol-solver-model + javaparser-symbol-solver-model ${project.version} compile -- cgit v1.2.3 From 825ae6cea669b48d29be7a3c67ca0a9d8a6f6a77 Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Thu, 11 Jan 2018 14:12:36 +0100 Subject: cleaning plugins specified in poms for JSS modules --- javaparser-symbol-solver-core/pom.xml | 224 +++++++++++----------------------- 1 file changed, 71 insertions(+), 153 deletions(-) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml index fc35e4649..4a8ac10d2 100644 --- a/javaparser-symbol-solver-core/pom.xml +++ b/javaparser-symbol-solver-core/pom.xml @@ -58,157 +58,75 @@ - - - - com.helger.maven - ph-javacc-maven-plugin - - - javacc - - javacc - - - ${project.build.sourceEncoding} - ${java.version} - - - - - - org.codehaus.mojo - animal-sniffer-maven-plugin - 1.16 - - - - org.codehaus.mojo.signature - java18 - 1.0 - - - - - animal-sniffer - verify - - check - - - - - - org.apache.maven.plugins - maven-enforcer-plugin - 3.0.0-M1 - - - enforce-versions - verify - - enforce - - - - - - ${java.version} - - - - ${java.version} - - - - - - - - org.codehaus.mojo - extra-enforcer-rules - 1.0-beta-6 - - - - - - biz.aQute.bnd - bnd-maven-plugin - ${bnd-maven-plugin.version} - - - - bnd-process - - - - - - - org.apache.maven.plugins - maven-jar-plugin - ${maven-jar-plugin.version} - - - ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - - -parameters - - - - - org.codehaus.mojo - build-helper-maven-plugin - 3.0.0 - - - add-source - generate-sources - - add-source - - - - src/main/javacc-support - - - - - - - org.codehaus.mojo - templating-maven-plugin - 1.0.0 - - - filter-src - - filter-sources - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - com.github.javaparser.core - - - - - - + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + 1.16 + + + + org.codehaus.mojo.signature + java18 + 1.0 + + + + + animal-sniffer + verify + + check + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M1 + + + enforce-versions + verify + + enforce + + + + + + ${java.version} + + + + ${java.version} + + + + + + + + org.codehaus.mojo + extra-enforcer-rules + 1.0-beta-6 + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + com.github.javaparser.symbolsolver.core + + + + + + -- cgit v1.2.3 From 5b3a820b8505dcbebf7ab5768927974849dad291 Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Fri, 12 Jan 2018 21:21:48 +0100 Subject: Fix build warnings --- javaparser-symbol-solver-core/pom.xml | 1 + 1 file changed, 1 insertion(+) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml index 4a8ac10d2..489ea13eb 100644 --- a/javaparser-symbol-solver-core/pom.xml +++ b/javaparser-symbol-solver-core/pom.xml @@ -119,6 +119,7 @@ org.apache.maven.plugins maven-jar-plugin + ${maven-jar-plugin.version} -- cgit v1.2.3 From 307bb82665e75e056f7897a5a461323ae38d925d Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Fri, 12 Jan 2018 21:27:17 +0100 Subject: [maven-release-plugin] prepare release javaparser-parent-3.5.10 --- javaparser-symbol-solver-core/pom.xml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml index 489ea13eb..fe59cf734 100644 --- a/javaparser-symbol-solver-core/pom.xml +++ b/javaparser-symbol-solver-core/pom.xml @@ -1,10 +1,9 @@ - + javaparser-parent com.github.javaparser - 3.5.10-SNAPSHOT + 3.5.10 4.0.0 -- cgit v1.2.3 From 51231841545b81a0ed14e79aad87984b25d1dd39 Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Fri, 12 Jan 2018 21:27:25 +0100 Subject: [maven-release-plugin] prepare for next development iteration --- javaparser-symbol-solver-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml index fe59cf734..a5c6a33bc 100644 --- a/javaparser-symbol-solver-core/pom.xml +++ b/javaparser-symbol-solver-core/pom.xml @@ -3,7 +3,7 @@ javaparser-parent com.github.javaparser - 3.5.10 + 3.5.11-SNAPSHOT 4.0.0 -- cgit v1.2.3 From ddfff937fb4801f662becfdd9f65e5002f66bcab Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Mon, 15 Jan 2018 19:19:59 +0100 Subject: Add Intellij files that shouldn't have been ignored --- .../javaparser-symbol-solver-core.iml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 javaparser-symbol-solver-core/javaparser-symbol-solver-core.iml (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/javaparser-symbol-solver-core.iml b/javaparser-symbol-solver-core/javaparser-symbol-solver-core.iml new file mode 100644 index 000000000..b1e38075a --- /dev/null +++ b/javaparser-symbol-solver-core/javaparser-symbol-solver-core.iml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file -- cgit v1.2.3 From c53db6d26f3c2cdbb8612cca765b56e5bbc6dacc Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Thu, 18 Jan 2018 20:52:24 +0100 Subject: Clean up poms --- javaparser-symbol-solver-core/pom.xml | 210 ++++++++++++++++------------------ 1 file changed, 101 insertions(+), 109 deletions(-) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml index a5c6a33bc..08f86b1e9 100644 --- a/javaparser-symbol-solver-core/pom.xml +++ b/javaparser-symbol-solver-core/pom.xml @@ -1,16 +1,17 @@ - - - javaparser-parent - com.github.javaparser - 3.5.11-SNAPSHOT - - 4.0.0 + + + javaparser-parent + com.github.javaparser + 3.5.11-SNAPSHOT + + 4.0.0 + + javaparser-symbol-solver-core + jar + A Symbol Solver for Java, built on top of JavaParser (core) - javaparser-symbol-solver-core - jar - A Symbol Solver for Java, built on top of JavaParser (core) - GNU Lesser General Public License @@ -30,103 +31,94 @@ ${maven.build.timestamp} - - - com.github.javaparser - javaparser-symbol-solver-logic - ${project.version} - compile - - - org.javassist - javassist - 3.22.0-GA - compile - - - com.github.javaparser - javaparser-symbol-solver-model - ${project.version} - compile - - - com.google.guava - guava - 23.4-jre - compile - - + + + com.github.javaparser + javaparser-symbol-solver-logic + ${project.version} + + + org.javassist + javassist + + + com.github.javaparser + javaparser-symbol-solver-model + ${project.version} + + + com.google.guava + guava + + - - - - org.codehaus.mojo - animal-sniffer-maven-plugin - 1.16 - - - - org.codehaus.mojo.signature - java18 - 1.0 - - - - - animal-sniffer - verify - - check - - - - - - org.apache.maven.plugins - maven-enforcer-plugin - 3.0.0-M1 - - - enforce-versions - verify - - enforce - - - - - - ${java.version} - - - - ${java.version} - - - - - - - - org.codehaus.mojo - extra-enforcer-rules - 1.0-beta-6 - - - - - - org.apache.maven.plugins - maven-jar-plugin - ${maven-jar-plugin.version} - - - - com.github.javaparser.symbolsolver.core - - - - - - + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + + + + org.codehaus.mojo.signature + java18 + 1.0 + + + + + animal-sniffer + verify + + check + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + enforce-versions + verify + + enforce + + + + + + ${java.version} + + + + ${java.version} + + + + + + + + org.codehaus.mojo + extra-enforcer-rules + 1.0-beta-6 + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + com.github.javaparser.symbolsolver.core + + + + + + -- cgit v1.2.3 From c7028224ae4c9336d23ba708fb50181e7ecc2ffd Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Sun, 21 Jan 2018 13:08:49 +0100 Subject: [maven-release-plugin] prepare release javaparser-parent-3.5.11 --- javaparser-symbol-solver-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml index a5c6a33bc..1d88aecc8 100644 --- a/javaparser-symbol-solver-core/pom.xml +++ b/javaparser-symbol-solver-core/pom.xml @@ -3,7 +3,7 @@ javaparser-parent com.github.javaparser - 3.5.11-SNAPSHOT + 3.5.11 4.0.0 -- cgit v1.2.3 From 084dbc7a23336a7a1575efa998090dc5a42d29f6 Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Sun, 21 Jan 2018 13:08:56 +0100 Subject: [maven-release-plugin] prepare for next development iteration --- javaparser-symbol-solver-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml index 1d88aecc8..ab2b843e0 100644 --- a/javaparser-symbol-solver-core/pom.xml +++ b/javaparser-symbol-solver-core/pom.xml @@ -3,7 +3,7 @@ javaparser-parent com.github.javaparser - 3.5.11 + 3.5.12-SNAPSHOT 4.0.0 -- cgit v1.2.3 From 9a228915f61892f6ba99fcf1e42e42e1e2622a00 Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Wed, 24 Jan 2018 23:52:36 +0100 Subject: Deal with no common type for variables being available. --- .../javaparsermodel/contexts/FieldAccessContext.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'javaparser-symbol-solver-core') 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 index 860fe6bc4..65ccedf00 100644 --- 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 @@ -53,7 +53,7 @@ public class FieldAccessContext extends AbstractJavaParserContext solveSymbol(String name, TypeSolver typeSolver) { - if (wrappedNode.getField().toString().equals(name)) { + 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); @@ -75,18 +75,14 @@ public class FieldAccessContext extends AbstractJavaParserContext solveSymbolAsValue(String name, TypeSolver typeSolver) { Expression scope = wrappedNode.getScope(); - if (wrappedNode.getField().toString().equals(name)) { + 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 typeUsage = typeOfScope.asReferenceType().getFieldType(name); - if (typeUsage.isPresent()) { - return Optional.of(new Value(typeUsage.get(), name)); - } else { - return Optional.empty(); - } + return typeUsage.map(resolvedType -> new Value(resolvedType, name)); } else { return Optional.empty(); } -- cgit v1.2.3 From 99241880abc2965c1075fe2858c018c5432306fc Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Sun, 28 Jan 2018 21:10:35 +0100 Subject: [maven-release-plugin] prepare release javaparser-parent-3.5.12 --- javaparser-symbol-solver-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml index 791766e38..f05de6349 100644 --- a/javaparser-symbol-solver-core/pom.xml +++ b/javaparser-symbol-solver-core/pom.xml @@ -3,7 +3,7 @@ javaparser-parent com.github.javaparser - 3.5.12-SNAPSHOT + 3.5.12 4.0.0 -- cgit v1.2.3 From 456f2d70d19fbae748dd910ac8849b71e47036ba Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Sun, 28 Jan 2018 21:10:41 +0100 Subject: [maven-release-plugin] prepare for next development iteration --- javaparser-symbol-solver-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml index f05de6349..7172e13fa 100644 --- a/javaparser-symbol-solver-core/pom.xml +++ b/javaparser-symbol-solver-core/pom.xml @@ -3,7 +3,7 @@ javaparser-parent com.github.javaparser - 3.5.12 + 3.5.13-SNAPSHOT 4.0.0 -- cgit v1.2.3 From 3e3d341e3ec7012fa11a8675b761a42de0a9bea4 Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Sun, 28 Jan 2018 21:16:38 +0100 Subject: [maven-release-plugin] rollback the release of javaparser-parent-3.5.12 --- javaparser-symbol-solver-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml index 7172e13fa..791766e38 100644 --- a/javaparser-symbol-solver-core/pom.xml +++ b/javaparser-symbol-solver-core/pom.xml @@ -3,7 +3,7 @@ javaparser-parent com.github.javaparser - 3.5.13-SNAPSHOT + 3.5.12-SNAPSHOT 4.0.0 -- cgit v1.2.3 From a4a3828b14c8b03bad45e3a150093759bcbf36fc Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Sun, 28 Jan 2018 22:06:03 +0100 Subject: [maven-release-plugin] prepare release javaparser-parent-3.5.12 --- javaparser-symbol-solver-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml index 791766e38..f05de6349 100644 --- a/javaparser-symbol-solver-core/pom.xml +++ b/javaparser-symbol-solver-core/pom.xml @@ -3,7 +3,7 @@ javaparser-parent com.github.javaparser - 3.5.12-SNAPSHOT + 3.5.12 4.0.0 -- cgit v1.2.3 From a3d8a24c8c2becb0790b0c7cfca03171c39c2f4b Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Sun, 28 Jan 2018 22:06:12 +0100 Subject: [maven-release-plugin] prepare for next development iteration --- javaparser-symbol-solver-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml index f05de6349..7172e13fa 100644 --- a/javaparser-symbol-solver-core/pom.xml +++ b/javaparser-symbol-solver-core/pom.xml @@ -3,7 +3,7 @@ javaparser-parent com.github.javaparser - 3.5.12 + 3.5.13-SNAPSHOT 4.0.0 -- cgit v1.2.3 From fc0a1b721f63bd05ff54cfbb68488b32a6023ae2 Mon Sep 17 00:00:00 2001 From: dhardtke Date: Fri, 2 Feb 2018 04:21:11 +0100 Subject: fix(symbol solver): Fix infinite recursion when solving java.lang.Object --- .../declarations/JavaParserClassDeclaration.java | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'javaparser-symbol-solver-core') 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 index 7a6e198da..9b1979aad 100644 --- 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 @@ -290,16 +290,21 @@ public class JavaParserClassDeclaration extends AbstractClassDeclaration { @Override public List getAncestors() { List ancestors = new ArrayList<>(); - 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); + + // 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; } -- cgit v1.2.3 From b54617e765d73b3ce0d187cf12ad8da382bce439 Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Sat, 3 Feb 2018 15:45:46 +0100 Subject: Cleaning up JSS code --- .../javaparser/symbolsolver/JavaSymbolSolver.java | 10 +- .../symbolsolver/SourceFileInfoExtractor.java | 9 +- .../symbolsolver/javaparser/Navigator.java | 168 +++++---------------- .../javaparsermodel/JavaParserFacade.java | 104 ++++++------- .../javaparsermodel/JavaParserFactory.java | 15 +- .../javaparsermodel/TypeExtractor.java | 114 ++++++-------- .../javaparsermodel/UnsolvedSymbolException.java | 6 +- .../contexts/AbstractJavaParserContext.java | 49 +++--- .../AbstractMethodLikeDeclarationContext.java | 2 +- .../contexts/ForStatementContext.java | 4 +- .../contexts/ForechStatementContext.java | 4 +- .../contexts/LambdaExprContext.java | 11 +- .../javaparsermodel/contexts/StatementContext.java | 34 ++--- .../contexts/SwitchEntryContext.java | 4 +- .../contexts/TryWithResourceContext.java | 5 +- .../javaparsermodel/declarations/Helper.java | 8 +- .../JavaParserAnnotationDeclaration.java | 2 +- .../JavaParserAnonymousClassDeclaration.java | 4 +- .../declarations/JavaParserClassDeclaration.java | 2 +- .../JavaParserEnumConstantDeclaration.java | 4 +- .../declarations/JavaParserEnumDeclaration.java | 17 ++- .../declarations/JavaParserFieldDeclaration.java | 18 +-- .../JavaParserInterfaceDeclaration.java | 2 +- .../declarations/JavaParserMethodDeclaration.java | 9 +- .../declarations/JavaParserSymbolDeclaration.java | 32 ++-- .../declarations/JavaParserTypeAdapter.java | 18 ++- .../declarations/JavaParserTypeParameter.java | 7 +- .../declarators/VariableSymbolDeclarator.java | 16 +- .../symbolsolver/resolution/SymbolSolver.java | 9 +- .../ExpressionCompatibleWithType.java | 15 +- .../resolution/typesolvers/CombinedTypeSolver.java | 2 +- 31 files changed, 281 insertions(+), 423 deletions(-) (limited to 'javaparser-symbol-solver-core') 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 index eeceff88c..77550c736 100644 --- 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 @@ -8,21 +8,17 @@ 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.ArrayType; 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.javaparser.Navigator; 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; -import java.util.Optional; - /** * This implementation of the SymbolResolver wraps the functionalities of the library to make them easily usable * from JavaParser nodes. @@ -66,7 +62,7 @@ public class JavaSymbolSolver implements SymbolResolver { } } if (node instanceof EnumConstantDeclaration) { - ResolvedEnumDeclaration enumDeclaration = Navigator.findAncestor(node, EnumDeclaration.class).get().resolve().asEnum(); + 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); @@ -88,7 +84,7 @@ public class JavaSymbolSolver implements SymbolResolver { } } if (node instanceof AnnotationMemberDeclaration) { - ResolvedAnnotationDeclaration annotationDeclaration = Navigator.findAncestor(node, AnnotationDeclaration.class).get().resolve(); + 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); @@ -153,7 +149,7 @@ public class JavaSymbolSolver implements SymbolResolver { if (node instanceof Parameter) { if (ResolvedParameterDeclaration.class.equals(resultClass)) { Parameter parameter = (Parameter)node; - CallableDeclaration callableDeclaration = Navigator.findAncestor(node, CallableDeclaration.class).get(); + CallableDeclaration callableDeclaration = node.findParent(CallableDeclaration.class).get(); ResolvedMethodLikeDeclaration resolvedMethodLikeDeclaration; if (callableDeclaration.isConstructorDeclaration()) { resolvedMethodLikeDeclaration = callableDeclaration.asConstructorDeclaration().resolve(); 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 index c82e2b3ef..8d2c07b05 100644 --- 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 @@ -43,6 +43,7 @@ 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. @@ -114,11 +115,11 @@ public class SourceFileInfoExtractor { if (node instanceof ClassOrInterfaceDeclaration) { solveTypeDecl((ClassOrInterfaceDeclaration) node); } else if (node instanceof Expression) { - if ((getParentNode(node) instanceof ImportDeclaration) || (getParentNode(node) instanceof Expression) - || (getParentNode(node) instanceof MethodDeclaration) - || (getParentNode(node) instanceof PackageDeclaration)) { + if ((requireParentNode(node) instanceof ImportDeclaration) || (requireParentNode(node) instanceof Expression) + || (requireParentNode(node) instanceof MethodDeclaration) + || (requireParentNode(node) instanceof PackageDeclaration)) { // skip - } else if ((getParentNode(node) instanceof Statement) || (getParentNode(node) instanceof VariableDeclarator)) { + } 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()); 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 index 82e0101ea..dc3ea62e8 100644 --- 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 @@ -25,7 +25,6 @@ import com.github.javaparser.ast.expr.SimpleName; import com.github.javaparser.ast.stmt.ReturnStmt; import com.github.javaparser.ast.stmt.SwitchStmt; -import java.util.LinkedList; import java.util.List; import java.util.Optional; @@ -40,17 +39,16 @@ public final class Navigator { // prevent instantiation } + /** + * @deprecated use Node.getParentNode + */ + @Deprecated public static Node getParentNode(Node node) { - Node parent = node.getParentNode().orElse(null); - return parent; + return node.getParentNode().orElse(null); } public static Node requireParentNode(Node node) { - Node parent = getParentNode(node); - if (parent == null) { - throw new IllegalStateException("Parent not found, the node does not appear to be inserted in a correct AST"); - } - return parent; + return node.getParentNode().orElseThrow(() -> new IllegalStateException("Parent not found, the node does not appear to be inserted in a correct AST")); } public static Optional> findType(CompilationUnit cu, String qualifiedName) { @@ -109,7 +107,7 @@ public final class Navigator { for (BodyDeclaration bd : cd.getMembers()) { if (bd instanceof MethodDeclaration) { MethodDeclaration md = (MethodDeclaration) bd; - if (md.getName().getId().equals(name)) { + if (md.getNameAsString().equals(name)) { if (found != null) { throw new IllegalStateException("Ambiguous getName"); } @@ -118,7 +116,7 @@ public final class Navigator { } } if (found == null) { - throw new IllegalStateException("No method with given name"); + throw new IllegalStateException("No method called " + name); } return found; } @@ -137,126 +135,57 @@ public final class Navigator { throw new IllegalStateException("No field with given name"); } - public static NameExpr findNameExpression(Node node, String name) { - if (node instanceof NameExpr) { - NameExpr nameExpr = (NameExpr) node; - if (nameExpr.getName() != null && nameExpr.getName().getId().equals(name)) { - return nameExpr; - } - } - for (Node child : node.getChildNodes()) { - NameExpr res = findNameExpression(child, name); - if (res != null) { - return res; - } - } - return null; + public static Optional findNameExpression(Node node, String name) { + return node.findFirst(NameExpr.class, n -> n.getNameAsString().equals(name)); } - public static SimpleName findSimpleName(Node node, String name) { - if (node instanceof SimpleName) { - SimpleName nameExpr = (SimpleName) node; - if (nameExpr.getId() != null && nameExpr.getId().equals(name)) { - return nameExpr; - } - } - for (Node child : node.getChildNodes()) { - SimpleName res = findSimpleName(child, name); - if (res != null) { - return res; - } - } - return null; + public static Optional findSimpleName(Node node, String name) { + return node.findFirst(SimpleName.class, n -> n.asString().equals(name)); } - public static MethodCallExpr findMethodCall(Node node, String methodName) { - if (node instanceof MethodCallExpr) { - MethodCallExpr methodCallExpr = (MethodCallExpr) node; - if (methodCallExpr.getName().getId().equals(methodName)) { - return methodCallExpr; - } - } - for (Node child : node.getChildNodes()) { - MethodCallExpr res = findMethodCall(child, methodName); - if (res != null) { - return res; - } - } - return null; + + public static Optional findMethodCall(Node node, String methodName) { + return node.findFirst(MethodCallExpr.class, n -> n.getNameAsString().equals(methodName)); } - public static VariableDeclarator demandVariableDeclaration(Node node, String name) { - if (node instanceof VariableDeclarator) { - VariableDeclarator variableDeclarator = (VariableDeclarator) node; - if (variableDeclarator.getName().getId().equals(name)) { - return variableDeclarator; - } - } - for (Node child : node.getChildNodes()) { - VariableDeclarator res = demandVariableDeclaration(child, name); - if (res != null) { - return res; - } - } - return null; + public static Optional demandVariableDeclaration(Node node, String name) { + return node.findFirst(VariableDeclarator.class, n -> n.getNameAsString().equals(name)); } public static ClassOrInterfaceDeclaration demandClassOrInterface(CompilationUnit compilationUnit, String qualifiedName) { - Optional> res = findType(compilationUnit, qualifiedName); - if (!res.isPresent()) { - throw new IllegalStateException("No type named '" + qualifiedName + "'found"); - } - if (!(res.get() instanceof ClassOrInterfaceDeclaration)) { - throw new IllegalStateException("Type is not a class or an interface, it is " + res.get().getClass().getCanonicalName()); - } - ClassOrInterfaceDeclaration cd = (ClassOrInterfaceDeclaration) res.get(); - return cd; + 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) { - SwitchStmt res = findSwitchHelper(node); - if (res == null) { - throw new IllegalArgumentException(); - } else { - return res; - } + return findSwitchHelper(node).orElseThrow(IllegalArgumentException::new); } - /** - * @deprecated use Node.findFirst instead - */ - @Deprecated - public static N findNodeOfGivenClass(Node node, Class clazz) { - N res = findNodeOfGivenClassHelper(node, clazz); - if (res == null) { - throw new IllegalArgumentException(); - } else { - return res; - } + public static N findNodeOfGivenClass(Node node, Class clazz) { + return node.findFirst(clazz).orElseThrow(IllegalArgumentException::new); } /** * @deprecated use Node.findAll instead */ @Deprecated - public static List findAllNodesOfGivenClass(Node node, Class clazz) { - List res = new LinkedList<>(); - findAllNodesOfGivenClassHelper(node, clazz, res); - return res; + public static List findAllNodesOfGivenClass(Node node, Class 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 Optional findAncestor(Node node, Class clazz) { - if (!node.getParentNode().isPresent()) { - return Optional.empty(); - } else if (clazz.isInstance(node.getParentNode().get())) { - return Optional.of(clazz.cast(node.getParentNode().get())); - } else { - return findAncestor(node.getParentNode().get(), clazz); - } + return node.findParent(clazz); } /// @@ -274,38 +203,17 @@ public final class Navigator { return ""; } - private static SwitchStmt findSwitchHelper(Node node) { + private static Optional findSwitchHelper(Node node) { + // TODO can be replaced by findFirst with the correct algorithm. if (node instanceof SwitchStmt) { - return (SwitchStmt) node; + return Optional.of((SwitchStmt) node); } for (Node child : node.getChildNodes()) { - SwitchStmt resChild = findSwitchHelper(child); - if (resChild != null) { + Optional resChild = findSwitchHelper(child); + if (resChild.isPresent()) { return resChild; } } - return null; - } - - private static N findNodeOfGivenClassHelper(Node node, Class clazz) { - if (clazz.isInstance(node)) { - return clazz.cast(node); - } - for (Node child : node.getChildNodes()) { - N resChild = findNodeOfGivenClassHelper(child, clazz); - if (resChild != null) { - return resChild; - } - } - return null; - } - - private static void findAllNodesOfGivenClassHelper(Node node, Class clazz, List collector) { - if (clazz.isInstance(node)) { - collector.add(clazz.cast(node)); - } - for (Node child : node.getChildNodes()) { - findAllNodesOfGivenClassHelper(child, clazz, collector); - } + return Optional.empty(); } } 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 index 39e0d29ea..4a4783dd4 100644 --- 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 @@ -25,6 +25,7 @@ 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; @@ -43,7 +44,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; -import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentNode; +import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode; /** * Class to be used by final users to solve symbols for JavaParser ASTs. @@ -95,13 +96,9 @@ public class JavaParserFacade { protected static ResolvedType solveGenericTypes(ResolvedType type, Context context, TypeSolver typeSolver) { if (type.isTypeVariable()) { - Optional solved = context.solveGenericType(type.describe(), typeSolver); - if (solved.isPresent()) { - return solved.get(); - } else { - return type; - } - } else if (type.isWildcard()) { + 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); @@ -110,13 +107,9 @@ public class JavaParserFacade { } else { return ResolvedWildcard.superBound(boundResolved); } - } else { - return type; } - } else { - ResolvedType result = type; - return result; } + return type; } public SymbolReference solve(NameExpr nameExpr) { @@ -128,11 +121,7 @@ public class JavaParserFacade { } public SymbolReference solve(Expression expr) { - if (expr instanceof NameExpr) { - return solve((NameExpr) expr); - } else { - throw new IllegalArgumentException(expr.getClass().getCanonicalName()); - } + return expr.toNameExpr().map(this::solve).orElseThrow(() -> new IllegalArgumentException(expr.getClass().getCanonicalName())); } public SymbolReference solve(MethodCallExpr methodCallExpr) { @@ -180,21 +169,21 @@ public class JavaParserFacade { return res; } - public SymbolReference solve(ThisExpr node){ + public SymbolReference solve(ThisExpr node) { // If 'this' is prefixed by a class eg. MyClass.this - if (node.getClassExpr().isPresent()){ + if (node.getClassExpr().isPresent()) { // Get the class name String className = node.getClassExpr().get().toString(); // Attempt to resolve using a typeSolver SymbolReference clazz = typeSolver.tryToSolveType(className); - if (clazz.isSolved()){ + if (clazz.isSolved()) { return SymbolReference.solved(clazz.getCorrespondingDeclaration()); } // Attempt to resolve locally in Compilation unit Optional cu = node.getAncestorOfType(CompilationUnit.class); - if (cu.isPresent()){ + if (cu.isPresent()) { Optional classByName = cu.get().getClassByName(className); - if (classByName.isPresent()){ + if (classByName.isPresent()) { return SymbolReference.solved(getTypeDeclaration(classByName.get())); } } @@ -233,7 +222,7 @@ public class JavaParserFacade { } else { try { argumentTypes.add(JavaParserFacade.get(typeSolver).getType(parameterValue, solveLambdas)); - } catch (UnsolvedSymbolException e) { + } 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", @@ -332,15 +321,12 @@ public class JavaParserFacade { /** * For some reasons LambdaExprs are duplicate and the equals method is not implemented correctly. - * - * @param map - * @return */ private Optional find(Map map, LambdaExpr lambdaExpr) { for (Node key : map.keySet()) { if (key instanceof LambdaExpr) { LambdaExpr keyLambdaExpr = (LambdaExpr) key; - if (keyLambdaExpr.toString().equals(lambdaExpr.toString()) && getParentNode(keyLambdaExpr) == getParentNode(lambdaExpr)) { + if (keyLambdaExpr.toString().equals(lambdaExpr.toString()) && requireParentNode(keyLambdaExpr) == requireParentNode(lambdaExpr)) { return Optional.of(map.get(keyLambdaExpr)); } } @@ -385,8 +371,6 @@ public class JavaParserFacade { /** * Should return more like a TypeApplication: a TypeDeclaration and possible typeParametersValues or array * modifiers. - * - * @return */ private ResolvedType getTypeConcrete(Node node, boolean solveLambdas) { if (node == null) throw new IllegalArgumentException(); @@ -396,37 +380,35 @@ public class JavaParserFacade { protected com.github.javaparser.ast.body.TypeDeclaration findContainingTypeDecl(Node node) { if (node instanceof ClassOrInterfaceDeclaration) { return (ClassOrInterfaceDeclaration) node; - } else if (node instanceof EnumDeclaration) { + } + if (node instanceof EnumDeclaration) { return (EnumDeclaration) node; - } else if (getParentNode(node) == null) { - throw new IllegalArgumentException(); - } else { - return findContainingTypeDecl(getParentNode(node)); } + return findContainingTypeDecl(requireParentNode(node)); + } - protected com.github.javaparser.ast.Node findContainingTypeDeclOrObjectCreationExpr(Node node) { + protected Node findContainingTypeDeclOrObjectCreationExpr(Node node) { if (node instanceof ClassOrInterfaceDeclaration) { - return (ClassOrInterfaceDeclaration) node; - } else if (node instanceof EnumDeclaration) { - return (EnumDeclaration) node; - } else if (getParentNode(node) == null) { - throw new IllegalArgumentException(); - } else if (getParentNode(node) instanceof ObjectCreationExpr && !((ObjectCreationExpr)getParentNode(node)).getArguments().contains(node)) { - return getParentNode(node); - } else { - return findContainingTypeDeclOrObjectCreationExpr(getParentNode(node)); + 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) { - ResolvedType type = JavaParserFacade.get(typeSolver).convertToUsage(var.getType(), var); - return type; + return get(typeSolver).convertToUsage(var.getType(), var); } public ResolvedType convertToUsage(com.github.javaparser.ast.type.Type type, Node context) { - if (type instanceof UnknownType) { - throw new IllegalArgumentException("Unknown type"); + if (type.isUnknownType()) { + throw new IllegalArgumentException("Inferred lambda parameter type"); } return convertToUsage(type, JavaParserFactory.getContext(context, typeSolver)); } @@ -440,9 +422,8 @@ public class JavaParserFacade { String name = classOrInterfaceType.getName().getId(); if (classOrInterfaceType.getScope().isPresent()) { return qName(classOrInterfaceType.getScope().get()) + "." + name; - } else { - return name; } + return name; } protected ResolvedType convertToUsage(com.github.javaparser.ast.type.Type type, Context context) { @@ -475,11 +456,11 @@ public class JavaParserFacade { return ResolvedPrimitiveType.byName(((com.github.javaparser.ast.type.PrimitiveType) type).getType().name()); } else if (type instanceof WildcardType) { WildcardType wildcardType = (WildcardType) type; - if (wildcardType.getExtendedTypes().isPresent() && !wildcardType.getSuperTypes().isPresent()) { - return ResolvedWildcard.extendsBound(convertToUsage(wildcardType.getExtendedTypes().get(), context)); // removed (ReferenceTypeImpl) - } else if (!wildcardType.getExtendedTypes().isPresent() && wildcardType.getSuperTypes().isPresent()) { - return ResolvedWildcard.superBound(convertToUsage(wildcardType.getSuperTypes().get(), context)); // removed (ReferenceTypeImpl) - } else if (!wildcardType.getExtendedTypes().isPresent() && !wildcardType.getSuperTypes().isPresent()) { + 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()); @@ -490,7 +471,7 @@ public class JavaParserFacade { 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; + UnionType unionType = (UnionType) type; return new ResolvedUnionType(unionType.getElements().stream().map(el -> convertToUsage(el, context)).collect(Collectors.toList())); } else { throw new UnsupportedOperationException(type.getClass().getCanonicalName()); @@ -523,16 +504,16 @@ public class JavaParserFacade { Optional 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().get().begin.line + ") " + context + ". Parameter types: " + params); + + 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); + return getTypeDeclaration((TypeDeclaration) node); } else if (node instanceof ObjectCreationExpr) { - return new JavaParserAnonymousClassDeclaration((ObjectCreationExpr)node, typeSolver); + return new JavaParserAnonymousClassDeclaration((ObjectCreationExpr) node, typeSolver); } else { throw new IllegalArgumentException(); } @@ -555,9 +536,8 @@ public class JavaParserFacade { } else if (node instanceof ObjectCreationExpr && ((ObjectCreationExpr) node).getAnonymousClassBody().isPresent()) { JavaParserAnonymousClassDeclaration anonymousDeclaration = new JavaParserAnonymousClassDeclaration((ObjectCreationExpr) node, typeSolver); return new ReferenceTypeImpl(anonymousDeclaration, typeSolver); - } else { - return getTypeOfThisIn(getParentNode(node)); } + return getTypeOfThisIn(requireParentNode(node)); } public ResolvedReferenceTypeDeclaration getTypeDeclaration(com.github.javaparser.ast.body.TypeDeclaration typeDeclaration) { 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 index 1004bc31e..8bd3496fd 100644 --- 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 @@ -38,6 +38,7 @@ 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 @@ -72,7 +73,7 @@ public class JavaParserFactory { } else if (node instanceof TryStmt) { return new TryWithResourceContext((TryStmt) node, typeSolver); } else if (node instanceof Statement) { - return new StatementContext((Statement) node, typeSolver); + return new StatementContext<>((Statement) node, typeSolver); } else if (node instanceof CatchClause) { return new CatchClauseContext((CatchClause) node, typeSolver); } else if (node instanceof ObjectCreationExpr && @@ -85,9 +86,9 @@ public class JavaParserFactory { return getContext(node.getParentNode().get().getParentNode().get(), typeSolver); } } - final Node parentNode = getParentNode(node); + final Node parentNode = requireParentNode(node); if (parentNode instanceof ObjectCreationExpr && node == ((ObjectCreationExpr) parentNode).getType()) { - return getContext(getParentNode(parentNode), typeSolver); + 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"); @@ -106,15 +107,15 @@ public class JavaParserFactory { if (expressionStmt.getExpression() instanceof VariableDeclarationExpr) { return new VariableSymbolDeclarator((VariableDeclarationExpr) (expressionStmt.getExpression()), typeSolver); } else { - return new NoSymbolDeclarator(expressionStmt, typeSolver); + return new NoSymbolDeclarator<>(expressionStmt, typeSolver); } } else if (node instanceof IfStmt) { - return new NoSymbolDeclarator((IfStmt) node, typeSolver); + return new NoSymbolDeclarator<>((IfStmt) node, typeSolver); } else if (node instanceof ForeachStmt) { ForeachStmt foreachStmt = (ForeachStmt) node; - return new VariableSymbolDeclarator((VariableDeclarationExpr) (foreachStmt.getVariable()), typeSolver); + return new VariableSymbolDeclarator(foreachStmt.getVariable(), typeSolver); } else { - return new NoSymbolDeclarator(node, typeSolver); + return new NoSymbolDeclarator<>(node, typeSolver); } } 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 index 1e15571d7..44c8d2715 100644 --- 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 @@ -1,7 +1,6 @@ package com.github.javaparser.symbolsolver.javaparsermodel; import com.github.javaparser.ast.CompilationUnit; -import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.FieldDeclaration; import com.github.javaparser.ast.body.Parameter; @@ -10,9 +9,9 @@ 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.stmt.Statement; 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; @@ -41,7 +40,8 @@ import java.util.logging.ConsoleHandler; import java.util.logging.Level; import java.util.logging.Logger; -import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentNode; +import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode; +import static com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade.solveGenericTypes; public class TypeExtractor extends DefaultVisitorAdapter { @@ -64,13 +64,12 @@ public class TypeExtractor extends DefaultVisitorAdapter { @Override public ResolvedType visit(VariableDeclarator node, Boolean solveLambdas) { - if (getParentNode(node) instanceof FieldDeclaration) { + if (requireParentNode(node) instanceof FieldDeclaration) { return facade.convertToUsageVariableType(node); - } else if (getParentNode(node) instanceof VariableDeclarationExpr) { + } else if (requireParentNode(node) instanceof VariableDeclarationExpr) { return facade.convertToUsageVariableType(node); - } else { - throw new UnsupportedOperationException(getParentNode(node).getClass().getCanonicalName()); } + throw new UnsupportedOperationException(requireParentNode(node).getClass().getCanonicalName()); } @Override @@ -191,14 +190,14 @@ public class TypeExtractor extends DefaultVisitorAdapter { return solveDotExpressionType( typeAccessedStatically.getCorrespondingDeclaration().asReferenceType(), node); } - } else if (node.getScope()instanceof ThisExpr){ + } else if (node.getScope() instanceof ThisExpr) { // If we are accessing through a 'this' expression, first resolve the type // corresponding to 'this' SymbolReference solve = facade.solve((ThisExpr) node.getScope()); // If found get it's declaration and get the field in there - if (solve.isSolved()){ + if (solve.isSolved()) { ResolvedTypeDeclaration correspondingDeclaration = solve.getCorrespondingDeclaration(); - if (correspondingDeclaration instanceof ResolvedReferenceTypeDeclaration){ + if (correspondingDeclaration instanceof ResolvedReferenceTypeDeclaration) { return solveDotExpressionType(correspondingDeclaration.asReferenceType(), node); } } @@ -210,10 +209,10 @@ public class TypeExtractor extends DefaultVisitorAdapter { return solveDotExpressionType(sr.getCorrespondingDeclaration(), node); } } - Optional value = null; + Optional value = Optional.empty(); try { - value = new SymbolSolver(typeSolver).solveSymbolAsValue(node.getField().getId(), node); - } catch (UnsolvedSymbolException use) { + 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 sref = typeSolver.tryToSolveType(node.toString()); @@ -221,11 +220,10 @@ public class TypeExtractor extends DefaultVisitorAdapter { return new ReferenceTypeImpl(sref.getCorrespondingDeclaration(), typeSolver); } } - if (value != null && value.isPresent()) { + if (value.isPresent()) { return value.get().getType(); - } else { - throw new UnsolvedSymbolException(node.getField().getId()); } + throw new com.github.javaparser.resolution.UnsolvedSymbolException(node.getName().getId()); } @Override @@ -287,7 +285,7 @@ public class TypeExtractor extends DefaultVisitorAdapter { logger.finest("getType on name expr " + node); Optional value = new SymbolSolver(typeSolver).solveSymbolAsValue(node.getName().getId(), node); if (!value.isPresent()) { - throw new UnsolvedSymbolException("Solving " + node, node.getName().getId()); + throw new com.github.javaparser.resolution.UnsolvedSymbolException("Solving " + node, node.getName().getId()); } else { return value.get().getType(); } @@ -295,26 +293,25 @@ public class TypeExtractor extends DefaultVisitorAdapter { @Override public ResolvedType visit(ObjectCreationExpr node, Boolean solveLambdas) { - ResolvedType type = facade.convertToUsage(node.getType(), node); - return type; + 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()){ + if (node.getClassExpr().isPresent()) { // Get the class name String className = node.getClassExpr().get().toString(); // Attempt to resolve using a typeSolver SymbolReference clazz = typeSolver.tryToSolveType(className); - if (clazz.isSolved()){ - return new ReferenceTypeImpl(clazz.getCorrespondingDeclaration(),typeSolver); + if (clazz.isSolved()) { + return new ReferenceTypeImpl(clazz.getCorrespondingDeclaration(), typeSolver); } // Attempt to resolve locally in Compilation unit Optional cu = node.getAncestorOfType(CompilationUnit.class); - if (cu.isPresent()){ + if (cu.isPresent()) { Optional classByName = cu.get().getClassByName(className); - if (classByName.isPresent()){ + if (classByName.isPresent()) { return new ReferenceTypeImpl(facade.getTypeDeclaration(classByName.get()), typeSolver); } } @@ -362,12 +359,12 @@ public class TypeExtractor extends DefaultVisitorAdapter { @Override public ResolvedType visit(LambdaExpr node, Boolean solveLambdas) { - if (getParentNode(node) instanceof MethodCallExpr) { - MethodCallExpr callExpr = (MethodCallExpr) getParentNode(node); + if (requireParentNode(node) instanceof MethodCallExpr) { + MethodCallExpr callExpr = (MethodCallExpr) requireParentNode(node); int pos = JavaParserSymbolDeclaration.getParamPos(node); SymbolReference refMethod = facade.solve(callExpr); if (!refMethod.isSolved()) { - throw new UnsolvedSymbolException(getParentNode(node).toString(), callExpr.getName().getId()); + throw new com.github.javaparser.resolution.UnsolvedSymbolException(requireParentNode(node).toString(), callExpr.getName().getId()); } logger.finest("getType on lambda expr " + refMethod.getCorrespondingDeclaration().getName()); if (solveLambdas) { @@ -384,7 +381,7 @@ public class TypeExtractor extends DefaultVisitorAdapter { NameExpr nameExpr = (NameExpr) scope; try { SymbolReference type = JavaParserFactory.getContext(nameExpr, typeSolver).solveType(nameExpr.getName().getId(), typeSolver); - if (type.isSolved()){ + if (type.isSolved()) { staticCall = true; } } catch (Exception e) { @@ -402,7 +399,7 @@ public class TypeExtractor extends DefaultVisitorAdapter { // We need to replace the type variables Context ctx = JavaParserFactory.getContext(node, typeSolver); - result = facade.solveGenericTypes(result, ctx, 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 @@ -424,28 +421,19 @@ public class TypeExtractor extends DefaultVisitorAdapter { ResolvedType actualType; if (lambdaExpr.getBody() instanceof ExpressionStmt) { - actualType = facade.getType(((ExpressionStmt)lambdaExpr.getBody()).getExpression()); + actualType = facade.getType(((ExpressionStmt) lambdaExpr.getBody()).getExpression()); } else if (lambdaExpr.getBody() instanceof BlockStmt) { BlockStmt blockStmt = (BlockStmt) lambdaExpr.getBody(); - NodeList statements = blockStmt.getStatements(); // Get all the return statements in the lambda block - List returnStmts = blockStmt.getNodesByType(ReturnStmt.class); - - if (returnStmts.size() > 0){ + List returnStmts = blockStmt.findAll(ReturnStmt.class); + if (returnStmts.size() > 0) { actualType = returnStmts.stream() - .map(returnStmt -> { - Optional expression = returnStmt.getExpression(); - if (expression.isPresent()){ - return facade.getType(expression.get()); - } else{ - return ResolvedVoidType.INSTANCE; - } - }) - .filter(x -> x != null && !x.isVoid() && !x.isNull()) - .findFirst() - .orElse(ResolvedVoidType.INSTANCE); + .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; @@ -465,7 +453,7 @@ public class TypeExtractor extends DefaultVisitorAdapter { // if the functional method returns void anyway // we don't need to bother inferring types - if (!(formalType instanceof ResolvedVoidType)){ + if (!(formalType instanceof ResolvedVoidType)) { lambdaCtx.addPair(result, functionalTypeWithReturn); result = lambdaCtx.resolve(lambdaCtx.addSingle(result)); } @@ -482,12 +470,12 @@ public class TypeExtractor extends DefaultVisitorAdapter { @Override public ResolvedType visit(MethodReferenceExpr node, Boolean solveLambdas) { - if (getParentNode(node) instanceof MethodCallExpr) { - MethodCallExpr callExpr = (MethodCallExpr) getParentNode(node); + if (requireParentNode(node) instanceof MethodCallExpr) { + MethodCallExpr callExpr = (MethodCallExpr) requireParentNode(node); int pos = JavaParserSymbolDeclaration.getParamPos(node); SymbolReference refMethod = facade.solve(callExpr, false); if (!refMethod.isSolved()) { - throw new UnsolvedSymbolException(getParentNode(node).toString(), callExpr.getName().getId()); + 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)); @@ -496,40 +484,34 @@ public class TypeExtractor extends DefaultVisitorAdapter { ResolvedType result = usage.getParamType(pos); // We need to replace the type variables Context ctx = JavaParserFactory.getContext(node, typeSolver); - result = facade.solveGenericTypes(result, ctx, 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 functionalMethod = FunctionalInterfaceLogic.getFunctionalMethod(result); - if (functionalMethod.isPresent()) { - if (node instanceof MethodReferenceExpr) { - MethodReferenceExpr methodReferenceExpr = (MethodReferenceExpr) node; + if (FunctionalInterfaceLogic.getFunctionalMethod(result).isPresent()) { + MethodReferenceExpr methodReferenceExpr = node; - ResolvedType actualType = facade.toMethodUsage(methodReferenceExpr).returnType(); - ResolvedType formalType = functionalMethod.get().returnType(); + 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)); - } + InferenceContext inferenceContext = new InferenceContext(MyObjectProvider.INSTANCE); + inferenceContext.addPair(formalType, actualType); + result = inferenceContext.resolve(inferenceContext.addSingle(result)); } return result; - } else { - return refMethod.getCorrespondingDeclaration().getParam(pos).getType(); } - } else { - throw new UnsupportedOperationException("The type of a method reference expr depends on the position and its return value"); + 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); - } else { - throw new IllegalArgumentException("Cannot resolve the type of a field with multiple variable declarations. Pick one"); } + 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 index 21dcf7c9e..f7ab7d11e 100644 --- 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 @@ -22,7 +22,7 @@ import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; /** * @author Federico Tomassetti * - * @deprecated Use {@link com.github.javaparser.symbolsolver.model.resolution.UnsolvedSymbolException} instead + * @deprecated Use {@link com.github.javaparser.resolution.UnsolvedSymbolException} instead */ // Use the one in model instead @Deprecated @@ -32,24 +32,28 @@ public class UnsolvedSymbolException extends RuntimeException { 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"; 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 index e2f348ce1..cc008a527 100644 --- 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 @@ -35,13 +35,11 @@ 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.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Optional; +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 @@ -55,7 +53,7 @@ public abstract class AbstractJavaParserContext implements Conte /// Static methods /// - public static final SymbolReference solveWith(SymbolDeclarator symbolDeclarator, String name) { + public static SymbolReference solveWith(SymbolDeclarator symbolDeclarator, String name) { for (ResolvedValueDeclaration decl : symbolDeclarator.getSymbolDeclarations()) { if (decl.getName().equals(name)) { return SymbolReference.solved(decl); @@ -109,8 +107,9 @@ public abstract class AbstractJavaParserContext implements Conte @Override public final Context getParent() { - if (getParentNode(wrappedNode) instanceof MethodCallExpr) { - MethodCallExpr parentCall = (MethodCallExpr) getParentNode(wrappedNode); + 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()) { @@ -120,16 +119,16 @@ public abstract class AbstractJavaParserContext implements Conte } } if (found) { - Node notMethod = getParentNode(wrappedNode); + Node notMethod = parent; while (notMethod instanceof MethodCallExpr) { - notMethod = getParentNode(notMethod); + notMethod = requireParentNode(notMethod); } return JavaParserFactory.getContext(notMethod, typeSolver); } } - Node notMethod = getParentNode(wrappedNode); + Node notMethod = parent; while (notMethod instanceof MethodCallExpr || notMethod instanceof FieldAccessExpr) { - notMethod = getParentNode(notMethod); + notMethod = notMethod.getParentNode().orElse(null); } if (notMethod == null) { return null; @@ -144,7 +143,7 @@ public abstract class AbstractJavaParserContext implements Conte protected Optional solveWithAsValue(SymbolDeclarator symbolDeclarator, String name, TypeSolver typeSolver) { return symbolDeclarator.getSymbolDeclarations().stream() .filter(d -> d.getName().equals(name)) - .map(d -> Value.from(d)) + .map(Value::from) .findFirst(); } @@ -157,40 +156,38 @@ public abstract class AbstractJavaParserContext implements Conte NameExpr scopeAsName = (NameExpr) scope; SymbolReference symbolReference = this.solveType(scopeAsName.getName().getId(), typeSolver); if (symbolReference.isSolved() && symbolReference.getCorrespondingDeclaration().isType()) { - return Arrays.asList(symbolReference.getCorrespondingDeclaration().asReferenceType()); + return singletonList(symbolReference.getCorrespondingDeclaration().asReferenceType()); } } - ResolvedType typeOfScope = null; + ResolvedType typeOfScope; try { typeOfScope = JavaParserFacade.get(typeSolver).getType(scope); } catch (Exception e) { - throw new RuntimeException(String.format("Issue calculating the type of the scope of " + this), 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 Arrays.asList(typeOfScope.asWildcard().getBoundedType().asReferenceType().getTypeDeclaration()); + return singletonList(typeOfScope.asWildcard().getBoundedType().asReferenceType().getTypeDeclaration()); } else { - return Arrays.asList(new ReflectionClassDeclaration(Object.class, typeSolver).asReferenceType()); + return singletonList(new ReflectionClassDeclaration(Object.class, typeSolver).asReferenceType()); } } else if (typeOfScope.isArray()) { // method call on array are Object methods - return Arrays.asList(new ReflectionClassDeclaration(Object.class, typeSolver).asReferenceType()); + return singletonList(new ReflectionClassDeclaration(Object.class, typeSolver).asReferenceType()); } else if (typeOfScope.isTypeVariable()) { Collection result = new ArrayList<>(); for (ResolvedTypeParameterDeclaration.Bound bound : typeOfScope.asTypeParameter().getBounds()) { result.add(bound.getType().asReferenceType().getTypeDeclaration()); } return result; - } else if (typeOfScope.isConstraint()){ - return Arrays.asList(typeOfScope.asConstraintType().getBound().asReferenceType().getTypeDeclaration()); - } else { - return Arrays.asList(typeOfScope.asReferenceType().getTypeDeclaration()); + } else if (typeOfScope.isConstraint()) { + return singletonList(typeOfScope.asConstraintType().getBound().asReferenceType().getTypeDeclaration()); } - } else { - ResolvedType typeOfScope = JavaParserFacade.get(typeSolver).getTypeOfThisIn(wrappedNode); - return Arrays.asList(typeOfScope.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 index 4e7b760bd..d611d71a5 100644 --- 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 @@ -79,7 +79,7 @@ public abstract class AbstractMethodLikeDeclarationContext } // Local types - List localTypes = wrappedNode.getChildNodesByType( + List 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)) { 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 index 77965bd08..cf3fe3b0c 100644 --- 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 @@ -32,7 +32,7 @@ import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; import java.util.List; -import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentNode; +import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode; public class ForStatementContext extends AbstractJavaParserContext { @@ -55,7 +55,7 @@ public class ForStatementContext extends AbstractJavaParserContext { } } - if (getParentNode(wrappedNode) instanceof NodeWithStatements) { + if (requireParentNode(wrappedNode) instanceof NodeWithStatements) { return StatementContext.solveInBlock(name, typeSolver, wrappedNode); } else { return getParent().solveSymbol(name, 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 index ed874899f..ba412023d 100644 --- 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 @@ -28,7 +28,7 @@ import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; import java.util.List; -import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentNode; +import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode; public class ForechStatementContext extends AbstractJavaParserContext { @@ -45,7 +45,7 @@ public class ForechStatementContext extends AbstractJavaParserContext { int index = 0; for (ResolvedValueDeclaration decl : sb.getSymbolDeclarations()) { if (decl.getName().equals(name)) { - if (getParentNode(wrappedNode) instanceof MethodCallExpr) { - MethodCallExpr methodCallExpr = (MethodCallExpr) getParentNode(wrappedNode); + 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); @@ -101,8 +100,8 @@ public class LambdaExprContext extends AbstractJavaParserContext { } else{ return Optional.empty(); } - } else if (getParentNode(wrappedNode) instanceof VariableDeclarator) { - VariableDeclarator variableDeclarator = (VariableDeclarator) getParentNode(wrappedNode); + } else if (requireParentNode(wrappedNode) instanceof VariableDeclarator) { + VariableDeclarator variableDeclarator = (VariableDeclarator) requireParentNode(wrappedNode); ResolvedType t = JavaParserFacade.get(typeSolver).convertToUsageVariableType(variableDeclarator); Optional functionalMethod = FunctionalInterfaceLogic.getFunctionalMethod(t); if (functionalMethod.isPresent()) { 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 index 51dd2d6d0..8acab7a8a 100644 --- 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 @@ -34,7 +34,7 @@ import com.github.javaparser.symbolsolver.resolution.SymbolDeclarator; 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 @@ -46,10 +46,10 @@ public class StatementContext extends AbstractJavaParserCon } public static SymbolReference solveInBlock(String name, TypeSolver typeSolver, Statement stmt) { - if (!(getParentNode(stmt) instanceof NodeWithStatements)) { + if (!(requireParentNode(stmt) instanceof NodeWithStatements)) { throw new IllegalArgumentException(); } - NodeWithStatements blockStmt = (NodeWithStatements) getParentNode(stmt); + NodeWithStatements blockStmt = (NodeWithStatements) requireParentNode(stmt); int position = -1; for (int i = 0; i < blockStmt.getStatements().size(); i++) { if (blockStmt.getStatements().get(i).equals(stmt)) { @@ -68,14 +68,14 @@ public class StatementContext extends AbstractJavaParserCon } // if nothing is found we should ask the parent context - return JavaParserFactory.getContext(getParentNode(stmt), typeSolver).solveSymbol(name, typeSolver); + return JavaParserFactory.getContext(requireParentNode(stmt), typeSolver).solveSymbol(name, typeSolver); } public static Optional solveInBlockAsValue(String name, TypeSolver typeSolver, Statement stmt) { - if (!(getParentNode(stmt) instanceof NodeWithStatements)) { + if (!(requireParentNode(stmt) instanceof NodeWithStatements)) { throw new IllegalArgumentException(); } - NodeWithStatements blockStmt = (NodeWithStatements) getParentNode(stmt); + NodeWithStatements blockStmt = (NodeWithStatements) requireParentNode(stmt); int position = -1; for (int i = 0; i < blockStmt.getStatements().size(); i++) { if (blockStmt.getStatements().get(i).equals(stmt)) { @@ -94,7 +94,7 @@ public class StatementContext extends AbstractJavaParserCon } // if nothing is found we should ask the parent context - return JavaParserFactory.getContext(getParentNode(stmt), typeSolver).solveSymbolAsValue(name, typeSolver); + return JavaParserFactory.getContext(requireParentNode(stmt), typeSolver).solveSymbolAsValue(name, typeSolver); } @Override @@ -108,19 +108,19 @@ public class StatementContext extends AbstractJavaParserCon } // we should look in all the statements preceding, treating them as SymbolDeclarators - if (getParentNode(wrappedNode) instanceof com.github.javaparser.ast.body.MethodDeclaration) { + if (requireParentNode(wrappedNode) instanceof com.github.javaparser.ast.body.MethodDeclaration) { return getParent().solveSymbolAsValue(name, typeSolver); } - if (getParentNode(wrappedNode) instanceof LambdaExpr) { + if (requireParentNode(wrappedNode) instanceof LambdaExpr) { return getParent().solveSymbolAsValue(name, typeSolver); } - if (getParentNode(wrappedNode) instanceof IfStmt) { + if (requireParentNode(wrappedNode) instanceof IfStmt) { return getParent().solveSymbolAsValue(name, typeSolver); } - if (!(getParentNode(wrappedNode) instanceof NodeWithStatements)) { + if (!(requireParentNode(wrappedNode) instanceof NodeWithStatements)) { return getParent().solveSymbolAsValue(name, typeSolver); } - NodeWithStatements nodeWithStmt = (NodeWithStatements) getParentNode(wrappedNode); + NodeWithStatements nodeWithStmt = (NodeWithStatements) requireParentNode(wrappedNode); int position = -1; for (int i = 0; i < nodeWithStmt.getStatements().size(); i++) { if (nodeWithStmt.getStatements().get(i).equals(wrappedNode)) { @@ -154,19 +154,19 @@ public class StatementContext extends AbstractJavaParserCon } // we should look in all the statements preceding, treating them as SymbolDeclarators - if (getParentNode(wrappedNode) instanceof com.github.javaparser.ast.body.MethodDeclaration) { + if (requireParentNode(wrappedNode) instanceof com.github.javaparser.ast.body.MethodDeclaration) { return getParent().solveSymbol(name, typeSolver); } - if (getParentNode(wrappedNode) instanceof com.github.javaparser.ast.body.ConstructorDeclaration) { + if (requireParentNode(wrappedNode) instanceof com.github.javaparser.ast.body.ConstructorDeclaration) { return getParent().solveSymbol(name, typeSolver); } - if (getParentNode(wrappedNode) instanceof LambdaExpr) { + if (requireParentNode(wrappedNode) instanceof LambdaExpr) { return getParent().solveSymbol(name, typeSolver); } - if (!(getParentNode(wrappedNode) instanceof NodeWithStatements)) { + if (!(requireParentNode(wrappedNode) instanceof NodeWithStatements)) { return getParent().solveSymbol(name, typeSolver); } - NodeWithStatements nodeWithStmt = (NodeWithStatements) getParentNode(wrappedNode); + NodeWithStatements nodeWithStmt = (NodeWithStatements) requireParentNode(wrappedNode); int position = -1; for (int i = 0; i < nodeWithStmt.getStatements().size(); i++) { if (nodeWithStmt.getStatements().get(i).equals(wrappedNode)) { 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 index 053cc2de1..15967295b 100644 --- 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 @@ -31,7 +31,7 @@ import com.github.javaparser.symbolsolver.resolution.SymbolDeclarator; import java.util.List; -import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentNode; +import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode; /** * @author Federico Tomassetti @@ -44,7 +44,7 @@ public class SwitchEntryContext extends AbstractJavaParserContext solveSymbol(String name, TypeSolver typeSolver) { - SwitchStmt switchStmt = (SwitchStmt) getParentNode(wrappedNode); + SwitchStmt switchStmt = (SwitchStmt) requireParentNode(wrappedNode); ResolvedType type = JavaParserFacade.get(typeSolver).getType(switchStmt.getSelector()); if (type.isReferenceType() && type.asReferenceType().getTypeDeclaration().isEnum()) { if (type instanceof ReferenceTypeImpl) { 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 index de6516436..80932e563 100644 --- 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 @@ -33,6 +33,7 @@ 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 { @@ -53,7 +54,7 @@ public class TryWithResourceContext extends AbstractJavaParserContext { } } - if (getParentNode(wrappedNode) instanceof BlockStmt) { + if (requireParentNode(wrappedNode) instanceof BlockStmt) { return StatementContext.solveInBlockAsValue(name, typeSolver, wrappedNode); } else { return getParent().solveSymbolAsValue(name, typeSolver); @@ -72,7 +73,7 @@ public class TryWithResourceContext extends AbstractJavaParserContext { } } - if (getParentNode(wrappedNode) instanceof BlockStmt) { + if (requireParentNode(wrappedNode) instanceof BlockStmt) { return StatementContext.solveInBlock(name, typeSolver, wrappedNode); } else { return getParent().solveSymbol(name, typeSolver); 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 index 18bc92cd1..d34e16412 100644 --- 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 @@ -55,14 +55,14 @@ class Helper { return p.get().getName().toString(); } } else if (container != null) { - return getPackageName(getParentNode(container)); + 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, getParentNode(container)); + String b = getClassName(base, container.getParentNode().orElse(null)); String cn = ((com.github.javaparser.ast.body.ClassOrInterfaceDeclaration) container).getName().getId(); if (b.isEmpty()) { return cn; @@ -70,7 +70,7 @@ class Helper { return b + "." + cn; } } else if (container instanceof com.github.javaparser.ast.body.EnumDeclaration) { - String b = getClassName(base, getParentNode(container)); + String b = getClassName(base, container.getParentNode().orElse(null)); String cn = ((com.github.javaparser.ast.body.EnumDeclaration) container).getName().getId(); if (b.isEmpty()) { return cn; @@ -78,7 +78,7 @@ class Helper { return b + "." + cn; } } else if (container != null) { - return getClassName(base, getParentNode(container)); + 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 index c9431f6b6..1f6c6f33f 100644 --- 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 @@ -70,7 +70,7 @@ public class JavaParserAnnotationDeclaration extends AbstractTypeDeclaration imp @Override public String getQualifiedName() { - String containerName = Helper.containerName(getParentNode(wrappedNode)); + String containerName = Helper.containerName(wrappedNode.getParentNode().orElse(null)); if (containerName.isEmpty()) { return wrappedNode.getName().getId(); } else { 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 index 7a6abeba0..3d2cb8f69 100644 --- 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 @@ -51,7 +51,7 @@ public class JavaParserAnonymousClassDeclaration extends AbstractClassDeclaratio .get() .stream() .filter(node -> memberClass.isAssignableFrom(node.getClass())) - .map(node -> (T) node) + .map(memberClass::cast) .collect(Collectors.toList()); } else { return Collections.emptyList(); @@ -171,7 +171,7 @@ public class JavaParserAnonymousClassDeclaration extends AbstractClassDeclaratio @Override public String getQualifiedName() { - String containerName = Helper.containerName(getParentNode(wrappedNode)); + String containerName = Helper.containerName(wrappedNode.getParentNode().orElse(null)); if (containerName.isEmpty()) { return getName(); } else { 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 index 9b1979aad..bb9f91cc6 100644 --- 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 @@ -21,13 +21,13 @@ 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.javaparsermodel.UnsolvedSymbolException; import com.github.javaparser.symbolsolver.logic.AbstractClassDeclaration; import com.github.javaparser.symbolsolver.model.resolution.SymbolReference; import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; 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 index 3ceda1a4e..6f909582d 100644 --- 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 @@ -22,7 +22,7 @@ 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.getParentNode; +import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode; /** * @author Federico Tomassetti @@ -39,7 +39,7 @@ public class JavaParserEnumConstantDeclaration implements ResolvedEnumConstantDe @Override public ResolvedType getType() { - return new ReferenceTypeImpl(new JavaParserEnumDeclaration((EnumDeclaration) getParentNode(wrappedNode), typeSolver), typeSolver); + return new ReferenceTypeImpl(new JavaParserEnumDeclaration((EnumDeclaration) requireParentNode(wrappedNode), typeSolver), typeSolver); } @Override 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 index 464aa7b83..818c0604c 100644 --- 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 @@ -22,6 +22,7 @@ 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; @@ -30,7 +31,6 @@ import com.github.javaparser.resolution.types.parametrization.ResolvedTypeParame 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.UnsolvedSymbolException; import com.github.javaparser.symbolsolver.logic.AbstractTypeDeclaration; import com.github.javaparser.symbolsolver.model.resolution.SymbolReference; import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; @@ -49,12 +49,12 @@ public class JavaParserEnumDeclaration extends AbstractTypeDeclaration implement private TypeSolver typeSolver; private com.github.javaparser.ast.body.EnumDeclaration wrappedNode; - private JavaParserTypeAdapter javaParserTypeAdapter; + private JavaParserTypeAdapter javaParserTypeAdapter; public JavaParserEnumDeclaration(com.github.javaparser.ast.body.EnumDeclaration wrappedNode, TypeSolver typeSolver) { this.wrappedNode = wrappedNode; this.typeSolver = typeSolver; - this.javaParserTypeAdapter = new JavaParserTypeAdapter(wrappedNode, typeSolver); + this.javaParserTypeAdapter = new JavaParserTypeAdapter<>(wrappedNode, typeSolver); } @Override @@ -106,21 +106,22 @@ public class JavaParserEnumDeclaration extends AbstractTypeDeclaration implement @Override public boolean canBeAssignedTo(ResolvedReferenceTypeDeclaration other) { + String otherName = other.getQualifiedName(); // Enums cannot be extended - if (other.getQualifiedName().equals(this.getQualifiedName())) { + if (otherName.equals(this.getQualifiedName())) { return true; } - if (other.getQualifiedName().equals(Enum.class.getCanonicalName())) { + if (otherName.equals(Enum.class.getCanonicalName())) { return true; } // Enum implements Comparable and Serializable - if (other.getQualifiedName().equals(Comparable.class.getCanonicalName())) { + if (otherName.equals(Comparable.class.getCanonicalName())) { return true; } - if (other.getQualifiedName().equals(Serializable.class.getCanonicalName())) { + if (otherName.equals(Serializable.class.getCanonicalName())) { return true; } - if (other.getQualifiedName().equals(Object.class.getCanonicalName())) { + if (otherName.equals(Object.class.getCanonicalName())) { return true; } return false; 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 index fe55deeb4..1c9e00511 100644 --- 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 @@ -31,6 +31,7 @@ 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 @@ -48,10 +49,10 @@ public class JavaParserFieldDeclaration implements ResolvedFieldDeclaration { } this.variableDeclarator = variableDeclarator; this.typeSolver = typeSolver; - if (!(getParentNode(variableDeclarator) instanceof com.github.javaparser.ast.body.FieldDeclaration)) { - throw new IllegalStateException(getParentNode(variableDeclarator).getClass().getCanonicalName()); + 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) getParentNode(variableDeclarator); + this.wrappedNode = (com.github.javaparser.ast.body.FieldDeclaration) requireParentNode(variableDeclarator); } public JavaParserFieldDeclaration(EnumConstantDeclaration enumConstantDeclaration, TypeSolver typeSolver) { @@ -65,12 +66,10 @@ public class JavaParserFieldDeclaration implements ResolvedFieldDeclaration { @Override public ResolvedType getType() { if (enumConstantDeclaration != null) { - com.github.javaparser.ast.body.EnumDeclaration enumDeclaration = (com.github.javaparser.ast.body.EnumDeclaration) getParentNode(enumConstantDeclaration); + com.github.javaparser.ast.body.EnumDeclaration enumDeclaration = (com.github.javaparser.ast.body.EnumDeclaration) requireParentNode(enumConstantDeclaration); return new ReferenceTypeImpl(new JavaParserEnumDeclaration(enumDeclaration, typeSolver), typeSolver); - } else { - ResolvedType retType = JavaParserFacade.get(typeSolver).convert(variableDeclarator.getType(), wrappedNode); - return retType; } + return JavaParserFacade.get(typeSolver).convert(variableDeclarator.getType(), wrappedNode); } @Override @@ -113,11 +112,10 @@ public class JavaParserFieldDeclaration implements ResolvedFieldDeclaration { @Override public ResolvedTypeDeclaration declaringType() { - Optional typeDeclaration = Navigator.findAncestor(wrappedNode, com.github.javaparser.ast.body.TypeDeclaration.class); + Optional typeDeclaration = wrappedNode.findParent(com.github.javaparser.ast.body.TypeDeclaration.class); if (typeDeclaration.isPresent()) { return JavaParserFacade.get(typeSolver).getTypeDeclaration(typeDeclaration.get()); - } else { - throw new IllegalStateException(); } + 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 index 5d33bcdce..34955a0c0 100644 --- 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 @@ -22,13 +22,13 @@ 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.javaparsermodel.UnsolvedSymbolException; import com.github.javaparser.symbolsolver.logic.AbstractTypeDeclaration; import com.github.javaparser.symbolsolver.model.resolution.SymbolReference; import com.github.javaparser.symbolsolver.model.resolution.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 index b352e905a..4f3cb3569 100644 --- 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 @@ -34,7 +34,7 @@ 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.getParentNode; +import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode; /** * @author Federico Tomassetti @@ -59,12 +59,11 @@ public class JavaParserMethodDeclaration implements ResolvedMethodDeclaration { @Override public ResolvedReferenceTypeDeclaration declaringType() { - if (getParentNode(wrappedNode) instanceof ObjectCreationExpr) { - ObjectCreationExpr parentNode = (ObjectCreationExpr) getParentNode(wrappedNode); + if (requireParentNode(wrappedNode) instanceof ObjectCreationExpr) { + ObjectCreationExpr parentNode = (ObjectCreationExpr) requireParentNode(wrappedNode); return new JavaParserAnonymousClassDeclaration(parentNode, typeSolver); - } else { - return JavaParserFactory.toTypeDeclaration(getParentNode(wrappedNode), typeSolver); } + return JavaParserFactory.toTypeDeclaration(requireParentNode(wrappedNode), typeSolver); } @Override 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 index 9b8578849..f1f1ffb69 100644 --- 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 @@ -32,7 +32,7 @@ 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.getParentNode; +import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode; /** * @author Federico Tomassetti @@ -69,7 +69,7 @@ public class JavaParserSymbolDeclaration implements ResolvedValueDeclaration { public static int getParamPos(Parameter parameter) { int pos = 0; - for (Node node : getParentNode(parameter).getChildNodes()) { + for (Node node : requireParentNode(parameter).getChildNodes()) { if (node == parameter) { return pos; } else if (node instanceof Parameter) { @@ -80,15 +80,14 @@ public class JavaParserSymbolDeclaration implements ResolvedValueDeclaration { } public static int getParamPos(Node node) { - if (getParentNode(node) instanceof MethodCallExpr) { - MethodCallExpr call = (MethodCallExpr) getParentNode(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(); - } else { - throw new IllegalArgumentException(); } + throw new IllegalArgumentException(); } @Override @@ -123,16 +122,16 @@ public class JavaParserSymbolDeclaration implements ResolvedValueDeclaration { public ResolvedType getType() { if (wrappedNode instanceof Parameter) { Parameter parameter = (Parameter) wrappedNode; - if (getParentNode(wrappedNode) instanceof LambdaExpr) { + if (requireParentNode(wrappedNode) instanceof LambdaExpr) { int pos = getParamPos(parameter); - ResolvedType lambdaType = JavaParserFacade.get(typeSolver).getType(getParentNode(wrappedNode)); + 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 { - ResolvedType rawType = null; + 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 { @@ -140,23 +139,18 @@ public class JavaParserSymbolDeclaration implements ResolvedValueDeclaration { } if (parameter.isVarArgs()) { return new ResolvedArrayType(rawType); - } else { - return rawType; } + return rawType; } } else if (wrappedNode instanceof VariableDeclarator) { VariableDeclarator variableDeclarator = (VariableDeclarator) wrappedNode; - if (getParentNode(wrappedNode) instanceof VariableDeclarationExpr) { - ResolvedType type = JavaParserFacade.get(typeSolver).convert(variableDeclarator.getType(), JavaParserFactory.getContext(wrappedNode, typeSolver)); - return type; - } else if (getParentNode(wrappedNode) instanceof FieldDeclaration) { + 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)); - } else { - throw new UnsupportedOperationException(getParentNode(wrappedNode).getClass().getCanonicalName()); } - } else { - throw new UnsupportedOperationException(wrappedNode.getClass().getCanonicalName()); } + throw new UnsupportedOperationException(wrappedNode.getClass().getCanonicalName()); } @Override 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 index c65b2d817..198083f7a 100644 --- 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 @@ -1,6 +1,7 @@ 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; @@ -8,6 +9,7 @@ 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; @@ -28,7 +30,7 @@ import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentN /** * @author Federico Tomassetti */ -public class JavaParserTypeAdapter & NodeWithMembers & NodeWithTypeParameters> { +public class JavaParserTypeAdapter & NodeWithMembers> { private T wrappedNode; private TypeSolver typeSolver; @@ -79,8 +81,9 @@ public class JavaParserTypeAdapter & Node } public SymbolReference solveType(String name, TypeSolver typeSolver) { - if (this.wrappedNode.getTypeParameters() != null) { - for (com.github.javaparser.ast.type.TypeParameter typeParameter : this.wrappedNode.getTypeParameters()) { + if(wrappedNode instanceof NodeWithTypeParameters) { + NodeList 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)); } @@ -115,14 +118,13 @@ public class JavaParserTypeAdapter & Node } public Optional containerType() { - Optional parent = wrappedNode.getParentNode(); - return parent.isPresent() ? - Optional.of(JavaParserFactory.toTypeDeclaration(parent.get(), typeSolver)) : - Optional.empty(); + return wrappedNode + .getParentNode() + .map(node -> JavaParserFactory.toTypeDeclaration(node, typeSolver)); } public List getFieldsForDeclaredVariables() { - ArrayList fields = new ArrayList<>(); + List fields = new ArrayList<>(); if (wrappedNode.getMembers() != null) { for (BodyDeclaration member : this.wrappedNode.getMembers()) { if (member instanceof com.github.javaparser.ast.body.FieldDeclaration) { 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 index 79eaf350f..0d1f42e68 100644 --- 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 @@ -36,7 +36,7 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import static com.github.javaparser.symbolsolver.javaparser.Navigator.getParentNode; +import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode; /** * @author Federico Tomassetti @@ -115,7 +115,7 @@ public class JavaParserTypeParameter extends AbstractTypeDeclaration implements @Override public ResolvedTypeParametrizable getContainer() { - Node parentNode = getParentNode(wrappedNode); + 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); @@ -147,8 +147,7 @@ public class JavaParserTypeParameter extends AbstractTypeDeclaration implements private Bound toBound(ClassOrInterfaceType classOrInterfaceType, TypeSolver typeSolver) { ResolvedType type = JavaParserFacade.get(typeSolver).convertToUsage(classOrInterfaceType, classOrInterfaceType); - Bound bound = Bound.extendsBound(type); - return bound; + return Bound.extendsBound(type); } public Context getContext() { 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 index be6a66924..88865de0d 100644 --- 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 @@ -35,18 +35,18 @@ public class VariableSymbolDeclarator extends AbstractSymbolDeclarator { + if (p instanceof FieldDeclaration) { + throw new IllegalArgumentException(); + } + }); } @Override public List getSymbolDeclarations() { - List symbols = wrappedNode.getVariables().stream().map( - v -> JavaParserSymbolDeclaration.localVar(v, typeSolver) - ).collect( - Collectors.toCollection(() -> new LinkedList<>())); - return symbols; + 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/resolution/SymbolSolver.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/SymbolSolver.java index 7336fa1cb..ee4a735e8 100644 --- 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 @@ -19,6 +19,7 @@ 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; @@ -26,7 +27,6 @@ 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.UnsolvedSymbolException; import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserClassDeclaration; import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserEnumDeclaration; import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserInterfaceDeclaration; @@ -84,7 +84,7 @@ public class SymbolSolver { public MethodUsage solveMethod(String methodName, List argumentsTypes, Context context) { SymbolReference decl = context.solveMethod(methodName, argumentsTypes, false, typeSolver); if (!decl.isSolved()) { - throw new UnsolvedSymbolException(context, methodName); + throw new UnsolvedSymbolException(context.toString(), methodName); } return new MethodUsage(decl.getCorrespondingDeclaration()); } @@ -101,7 +101,7 @@ public class SymbolSolver { String name = ((ClassOrInterfaceType) type).getName().getId(); SymbolReference ref = JavaParserFactory.getContext(type, typeSolver).solveType(name, typeSolver); if (!ref.isSolved()) { - throw new UnsolvedSymbolException(JavaParserFactory.getContext(type, typeSolver), name); + throw new UnsolvedSymbolException(JavaParserFactory.getContext(type, typeSolver).toString(), name); } return ref.getCorrespondingDeclaration(); } else { @@ -115,8 +115,7 @@ public class SymbolSolver { return genericType.get(); } ResolvedReferenceTypeDeclaration typeDeclaration = typeSolver.solveType(name); - ReferenceTypeImpl typeUsage = new ReferenceTypeImpl(typeDeclaration, typeSolver); - return typeUsage; + return new ReferenceTypeImpl(typeDeclaration, typeSolver); } /** 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 index 81b0b348c..30042f8a8 100644 --- 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 @@ -5,7 +5,6 @@ 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.javaparser.Navigator; import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade; import com.github.javaparser.symbolsolver.logic.FunctionalInterfaceLogic; import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; @@ -16,12 +15,12 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; 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 @@ -204,7 +203,7 @@ public class ExpressionCompatibleWithType extends ConstraintFormula { // expressions e1, ..., em, for all i (1 ≤ i ≤ m), ‹ei → R›. if (lambdaExpr.getBody() instanceof BlockStmt) { - getAllReturnExpressions((BlockStmt)lambdaExpr.getBody()).stream().forEach(e -> constraints.add(new ExpressionCompatibleWithType(typeSolver, e, R))); + getAllReturnExpressions((BlockStmt)lambdaExpr.getBody()).forEach(e -> constraints.add(new ExpressionCompatibleWithType(typeSolver, e, R))); } else { // FEDERICO: Added - Start for (int i=0;i getAllReturnExpressions(BlockStmt blockStmt) { - return Navigator.findAllNodesOfGivenClass(blockStmt, ReturnStmt.class).stream() + return blockStmt.findAll(ReturnStmt.class).stream() .filter(r -> r.getExpression().isPresent()) .map(r -> r.getExpression().get()) - .collect(Collectors.toList()); + .collect(toList()); } private boolean isValueCompatibleBlock(Statement statement) { @@ -272,15 +271,13 @@ public class ExpressionCompatibleWithType extends ConstraintFormula { // in the block has the form return Expression;. if (statement instanceof BlockStmt) { - BlockStmt blockStmt = (BlockStmt)statement; if (!ControlFlowLogic.getInstance().canCompleteNormally(statement)) { return true; } - List returnStmts = Navigator.findAllNodesOfGivenClass(statement, ReturnStmt.class); + List returnStmts = statement.findAll(ReturnStmt.class); return returnStmts.stream().allMatch(r -> r.getExpression().isPresent()); - } else { - return false; } + return false; } @Override 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 index 5f55cbbfc..fe9f3755a 100644 --- 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 @@ -16,8 +16,8 @@ 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.javaparsermodel.UnsolvedSymbolException; import com.github.javaparser.symbolsolver.model.resolution.SymbolReference; import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; -- cgit v1.2.3 From dc4e9708586775b0f5af9cab3cd199aa7f930ece Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Sun, 4 Feb 2018 20:26:24 +0100 Subject: [maven-release-plugin] prepare release javaparser-parent-3.5.13 --- javaparser-symbol-solver-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml index 7172e13fa..af74d808f 100644 --- a/javaparser-symbol-solver-core/pom.xml +++ b/javaparser-symbol-solver-core/pom.xml @@ -3,7 +3,7 @@ javaparser-parent com.github.javaparser - 3.5.13-SNAPSHOT + 3.5.13 4.0.0 -- cgit v1.2.3 From f6c16b7b8475106c61a3d64b0262769647252488 Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Sun, 4 Feb 2018 20:26:47 +0100 Subject: [maven-release-plugin] prepare for next development iteration --- javaparser-symbol-solver-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml index af74d808f..b8ddc3545 100644 --- a/javaparser-symbol-solver-core/pom.xml +++ b/javaparser-symbol-solver-core/pom.xml @@ -3,7 +3,7 @@ javaparser-parent com.github.javaparser - 3.5.13 + 3.5.14-SNAPSHOT 4.0.0 -- cgit v1.2.3 From be10f7278344dacb0bcc43553579b5add828f9b7 Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Mon, 5 Feb 2018 21:21:02 +0100 Subject: Basic var support --- .../symbolsolver/javaparsermodel/DefaultVisitorAdapter.java | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'javaparser-symbol-solver-core') 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 index dc9d86241..499ef3bd0 100644 --- 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 @@ -467,4 +467,9 @@ public class DefaultVisitorAdapter implements GenericVisitor Date: Sun, 11 Feb 2018 22:04:13 +0100 Subject: [maven-release-plugin] prepare release javaparser-parent-3.5.14 --- javaparser-symbol-solver-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml index b8ddc3545..72431395b 100644 --- a/javaparser-symbol-solver-core/pom.xml +++ b/javaparser-symbol-solver-core/pom.xml @@ -3,7 +3,7 @@ javaparser-parent com.github.javaparser - 3.5.14-SNAPSHOT + 3.5.14 4.0.0 -- cgit v1.2.3 From 8bc64c3e36027da80cd2f096f532638bf3ff7c5e Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Sun, 11 Feb 2018 22:04:19 +0100 Subject: [maven-release-plugin] prepare for next development iteration --- javaparser-symbol-solver-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml index 72431395b..600ad6823 100644 --- a/javaparser-symbol-solver-core/pom.xml +++ b/javaparser-symbol-solver-core/pom.xml @@ -3,7 +3,7 @@ javaparser-parent com.github.javaparser - 3.5.14 + 3.5.15-SNAPSHOT 4.0.0 -- cgit v1.2.3 From 6d59e3083ed82de3490c2f5ca35a07bb73806fea Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Wed, 14 Feb 2018 18:42:21 +0100 Subject: Make a simple "var" inferrer. --- .../javaparser/symbolsolver/javaparsermodel/JavaParserFacade.java | 3 +++ .../javaparser/symbolsolver/model/typesystem/ReferenceTypeImpl.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'javaparser-symbol-solver-core') 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 index 4a4783dd4..efe4e9b56 100644 --- 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 @@ -473,6 +473,9 @@ public class JavaParserFacade { } 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) { + final VariableDeclarator variableDeclarator = (VariableDeclarator)type.getParentNode().get(); + return variableDeclarator.getInitializer().get().calculateResolvedType(); } else { throw new UnsupportedOperationException(type.getClass().getCanonicalName()); } 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 index 8397a6702..927253e24 100644 --- 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 @@ -47,7 +47,7 @@ public class ReferenceTypeImpl extends ResolvedReferenceType { public static ResolvedReferenceType undeterminedParameters(ResolvedReferenceTypeDeclaration typeDeclaration, TypeSolver typeSolver) { return new ReferenceTypeImpl(typeDeclaration, typeDeclaration.getTypeParameters().stream().map( - tp -> new ResolvedTypeVariable(tp) + ResolvedTypeVariable::new ).collect(Collectors.toList()), typeSolver); } -- cgit v1.2.3 From 427dd53b9ebedcb0bdb687007eb0faf2de734df4 Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Wed, 14 Feb 2018 19:49:56 +0100 Subject: Better error handling for "var" inferrer. --- .../symbolsolver/javaparsermodel/JavaParserFacade.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'javaparser-symbol-solver-core') 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 index efe4e9b56..f73dd986c 100644 --- 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 @@ -474,8 +474,14 @@ public class JavaParserFacade { UnionType unionType = (UnionType) type; return new ResolvedUnionType(unionType.getElements().stream().map(el -> convertToUsage(el, context)).collect(Collectors.toList())); } else if (type instanceof VarType) { - final VariableDeclarator variableDeclarator = (VariableDeclarator)type.getParentNode().get(); - return variableDeclarator.getInitializer().get().calculateResolvedType(); + 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()); } -- cgit v1.2.3 From 6e4bbd65b70e1ca074d8aa7039cb3b01d35411b5 Mon Sep 17 00:00:00 2001 From: Federico Tomassetti Date: Mon, 19 Feb 2018 23:10:36 +0100 Subject: make ResolvedPrimitiveType an enum --- .../javaparser/symbolsolver/resolution/typeinference/TypeHelper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'javaparser-symbol-solver-core') 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 index 604689b78..01d041d11 100644 --- 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 @@ -137,7 +137,7 @@ public class TypeHelper { if (!referenceType.isReferenceType()) { return false; } - return ResolvedPrimitiveType.ALL.stream().anyMatch(pt -> referenceType.asReferenceType().getQualifiedName().equals(pt.getBoxTypeQName())); + return Arrays.stream(ResolvedPrimitiveType.values()).anyMatch(pt -> referenceType.asReferenceType().getQualifiedName().equals(pt.getBoxTypeQName())); } private static ResolvedType toUnboxedType(ResolvedReferenceType referenceType) { @@ -149,7 +149,7 @@ public class TypeHelper { } private static boolean areCompatibleThroughWideningReferenceConversion(ResolvedType s, ResolvedType t) { - Optional correspondingPrimitiveTypeForS = ResolvedPrimitiveType.ALL.stream().filter(pt -> pt.getBoxTypeQName().equals(s.asReferenceType().getQualifiedName())).findFirst(); + Optional correspondingPrimitiveTypeForS = Arrays.stream(ResolvedPrimitiveType.values()).filter(pt -> pt.getBoxTypeQName().equals(s.asReferenceType().getQualifiedName())).findFirst(); if (!correspondingPrimitiveTypeForS.isPresent()) { return false; } -- cgit v1.2.3 From aaad77930f7537d2607fcbe014e01e19a627a2ff Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Sun, 25 Feb 2018 18:58:34 +0100 Subject: [maven-release-plugin] prepare release javaparser-parent-3.5.15 --- javaparser-symbol-solver-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml index 600ad6823..22c8da0ad 100644 --- a/javaparser-symbol-solver-core/pom.xml +++ b/javaparser-symbol-solver-core/pom.xml @@ -3,7 +3,7 @@ javaparser-parent com.github.javaparser - 3.5.15-SNAPSHOT + 3.5.15 4.0.0 -- cgit v1.2.3 From c24bacda0a76b0a5c28a9741a67794905b0a281c Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Sun, 25 Feb 2018 18:58:41 +0100 Subject: [maven-release-plugin] prepare for next development iteration --- javaparser-symbol-solver-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/pom.xml b/javaparser-symbol-solver-core/pom.xml index 22c8da0ad..97ff66478 100644 --- a/javaparser-symbol-solver-core/pom.xml +++ b/javaparser-symbol-solver-core/pom.xml @@ -3,7 +3,7 @@ javaparser-parent com.github.javaparser - 3.5.15 + 3.5.16-SNAPSHOT 4.0.0 -- cgit v1.2.3 From 0b893c58ae4e0b4a2f8b20eb1582fc55972e863f Mon Sep 17 00:00:00 2001 From: Daan Schipper Date: Mon, 19 Feb 2018 13:44:30 +0100 Subject: SourceRoot implementation for JSS --- .../symbolsolver/utils/SymbolSourceRoot.java | 147 +++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/utils/SymbolSourceRoot.java (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/utils/SymbolSourceRoot.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/utils/SymbolSourceRoot.java new file mode 100644 index 000000000..e5450e19d --- /dev/null +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/utils/SymbolSourceRoot.java @@ -0,0 +1,147 @@ +/* + * 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.javaparsermodel.JavaParserFacade; +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 java.util.logging.Level; + +import static com.github.javaparser.utils.Utils.assertNotNull; +import static java.nio.file.FileVisitResult.CONTINUE; +import static java.nio.file.FileVisitResult.SKIP_SIBLINGS; + +public class SymbolSourceRoot { + + private static java.util.logging.Logger logger = java.util.logging.Logger.getLogger(JavaParserFacade.class.getCanonicalName()); + + private final Path root; + private CombinedTypeSolver typeSolver = new CombinedTypeSolver(new ReflectionTypeSolver(false)); + + public SymbolSourceRoot(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 TypeSolver walk() throws IOException { + Files.walkFileTree(root, new JavaSymbolSolverWalker()); + Files.walkFileTree(root, new JarVisitor()); + return typeSolver; + } + + public Optional tryToWalk() { + try { + Files.walkFileTree(root, new JavaSymbolSolverWalker()); + Files.walkFileTree(root, new JarVisitor()); + return Optional.of(typeSolver); + } catch (IOException e) { + 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 { + + private final Set 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 root = JavaParser.parse(file.toFile()).getStorage() + .map(CompilationUnit.Storage::getSourceRoot); + if (root.isPresent()) { + typeSolver.add(new JavaParserTypeSolver(root.get().toFile())); + roots.add(root.get()); + logger.log(Level.FINE, "Added dir " + root.get() + " to the TypeSolver"); + return SKIP_SIBLINGS; + } + } catch (ParseProblemException e) { + logger.log(Level.WARNING, "Unable to parse file " + file, e); + } + } + } + return CONTINUE; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + if (Files.isHidden(dir) || roots.stream().anyMatch(dir::startsWith)) { + return SKIP_SIBLINGS; + } + return CONTINUE; + } + } + + private class JarVisitor extends SimpleFileVisitor { + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attr) { + if (attr.isRegularFile()) { + PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:**.jar"); + if (matcher.matches(file)) { + try { + typeSolver.add(new JarTypeSolver(file.toString())); + } catch (IOException e) { + logger.log(Level.WARNING, "IOException for file " + file, e); + } + } + } + return CONTINUE; + } + } +} + -- cgit v1.2.3 From 9b6720e36b5a49705d837eab841911b8849367c4 Mon Sep 17 00:00:00 2001 From: Daan Schipper Date: Mon, 19 Feb 2018 14:10:32 +0100 Subject: minor adjustments based on feedback --- .../github/javaparser/symbolsolver/utils/SymbolSourceRoot.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'javaparser-symbol-solver-core') diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/utils/SymbolSourceRoot.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/utils/SymbolSourceRoot.java index e5450e19d..386c5815d 100644 --- a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/utils/SymbolSourceRoot.java +++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/utils/SymbolSourceRoot.java @@ -40,6 +40,7 @@ import java.util.HashSet; import java.util.Optional; import java.util.Set; import java.util.logging.Level; +import java.util.logging.Logger; import static com.github.javaparser.utils.Utils.assertNotNull; import static java.nio.file.FileVisitResult.CONTINUE; @@ -47,7 +48,7 @@ import static java.nio.file.FileVisitResult.SKIP_SIBLINGS; public class SymbolSourceRoot { - private static java.util.logging.Logger logger = java.util.logging.Logger.getLogger(JavaParserFacade.class.getCanonicalName()); + private static Logger logger = Logger.getLogger(JavaParserFacade.class.getCanonicalName()); private final Path root; private CombinedTypeSolver typeSolver = new CombinedTypeSolver(new ReflectionTypeSolver(false)); @@ -69,10 +70,9 @@ public class SymbolSourceRoot { public Optional tryToWalk() { try { - Files.walkFileTree(root, new JavaSymbolSolverWalker()); - Files.walkFileTree(root, new JarVisitor()); - return Optional.of(typeSolver); + return Optional.of(walk()); } catch (IOException e) { + logger.log(Level.WARNING, "Unable to walk root " + root, e); return Optional.empty(); } } -- cgit v1.2.3 From 354773c7727dfde5a6393e0fc4e42b35fe5df48e Mon Sep 17 00:00:00 2001 From: Daan Schipper Date: Mon, 19 Feb 2018 14:24:19 +0100 Subject: rename of class, added some comments and added one final check for skipping a directory only when the root is succesfully added to known roots --- .../symbolsolver/utils/SymbolSolverQuickSetup.java | 152 +++++++++++++++++++++ .../symbolsolver/utils/SymbolSourceRoot.java | 147 -------------------- 2 files changed, 152 insertions(+), 147 deletions(-) create mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/utils/SymbolSolverQuickSetup.java delete mode 100644 javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/utils/SymbolSourceRoot.java (limited to 'javaparser-symbol-solver-core') 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..a9287d7d7 --- /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.javaparsermodel.JavaParserFacade; +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 java.util.logging.Level; +import java.util.logging.Logger; + +import static com.github.javaparser.utils.Utils.assertNotNull; +import static java.nio.file.FileVisitResult.CONTINUE; +import static java.nio.file.FileVisitResult.SKIP_SIBLINGS; + +/** + * 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 { + + private static Logger logger = Logger.getLogger(JavaParserFacade.class.getCanonicalName()); + + private final Path root; + private CombinedTypeSolver typeSolver = new CombinedTypeSolver(new ReflectionTypeSolver(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 TypeSolver walk() throws IOException { + Files.walkFileTree(root, new JavaSymbolSolverWalker()); + Files.walkFileTree(root, new JarVisitor()); + return typeSolver; + } + + public Optional tryToWalk() { + try { + return Optional.of(walk()); + } catch (IOException e) { + logger.log(Level.WARNING, "Unable to walk root " + root, e); + 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 { + + private final Set 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 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())) { + logger.log(Level.FINE, "Added dir " + root.get() + " to the TypeSolver"); + return SKIP_SIBLINGS; + } + } + } catch (ParseProblemException e) { + logger.log(Level.WARNING, "Unable to parse file " + file, e); + } + } + } + return CONTINUE; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + if (Files.isHidden(dir) || roots.stream().anyMatch(dir::startsWith)) { + return SKIP_SIBLINGS; + } + return CONTINUE; + } + } + + private class JarVisitor extends SimpleFileVisitor { + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attr) { + if (attr.isRegularFile()) { + PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:**.jar"); + if (matcher.matches(file)) { + try { + typeSolver.add(new JarTypeSolver(file.toString())); + } catch (IOException e) { + logger.log(Level.WARNING, "IOException for file " + file, e); + } + } + } + return CONTINUE; + } + } +} + diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/utils/SymbolSourceRoot.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/utils/SymbolSourceRoot.java deleted file mode 100644 index 386c5815d..000000000 --- a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/utils/SymbolSourceRoot.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * 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.javaparsermodel.JavaParserFacade; -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 java.util.logging.Level; -import java.util.logging.Logger; - -import static com.github.javaparser.utils.Utils.assertNotNull; -import static java.nio.file.FileVisitResult.CONTINUE; -import static java.nio.file.FileVisitResult.SKIP_SIBLINGS; - -public class SymbolSourceRoot { - - private static Logger logger = Logger.getLogger(JavaParserFacade.class.getCanonicalName()); - - private final Path root; - private CombinedTypeSolver typeSolver = new CombinedTypeSolver(new ReflectionTypeSolver(false)); - - public SymbolSourceRoot(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 TypeSolver walk() throws IOException { - Files.walkFileTree(root, new JavaSymbolSolverWalker()); - Files.walkFileTree(root, new JarVisitor()); - return typeSolver; - } - - public Optional tryToWalk() { - try { - return Optional.of(walk()); - } catch (IOException e) { - logger.log(Level.WARNING, "Unable to walk root " + root, e); - 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 { - - private final Set 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 root = JavaParser.parse(file.toFile()).getStorage() - .map(CompilationUnit.Storage::getSourceRoot); - if (root.isPresent()) { - typeSolver.add(new JavaParserTypeSolver(root.get().toFile())); - roots.add(root.get()); - logger.log(Level.FINE, "Added dir " + root.get() + " to the TypeSolver"); - return SKIP_SIBLINGS; - } - } catch (ParseProblemException e) { - logger.log(Level.WARNING, "Unable to parse file " + file, e); - } - } - } - return CONTINUE; - } - - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - if (Files.isHidden(dir) || roots.stream().anyMatch(dir::startsWith)) { - return SKIP_SIBLINGS; - } - return CONTINUE; - } - } - - private class JarVisitor extends SimpleFileVisitor { - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attr) { - if (attr.isRegularFile()) { - PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:**.jar"); - if (matcher.matches(file)) { - try { - typeSolver.add(new JarTypeSolver(file.toString())); - } catch (IOException e) { - logger.log(Level.WARNING, "IOException for file " + file, e); - } - } - } - return CONTINUE; - } - } -} - -- cgit v1.2.3 From 5606e3b0255a8f1250734f3a429b381371a3c9f8 Mon Sep 17 00:00:00 2001 From: Daan Schipper Date: Mon, 19 Feb 2018 16:16:18 +0100 Subject: unit test --- .../github/javaparser/symbolsolver/utils/SymbolSolverQuickSetup.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'javaparser-symbol-solver-core') 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 index a9287d7d7..de6628f2b 100644 --- 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 @@ -48,7 +48,7 @@ import static java.nio.file.FileVisitResult.SKIP_SIBLINGS; /** * 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. + * It traverses the file directory tree and adds all files ending in either .java or .jar. */ public class SymbolSolverQuickSetup { -- cgit v1.2.3 From d1d92de58ca4fb191ba8cc405505a19adb0284dc Mon Sep 17 00:00:00 2001 From: Daan Schipper Date: Mon, 19 Feb 2018 16:25:14 +0100 Subject: make use of javaparser log framework --- .../symbolsolver/utils/SymbolSolverQuickSetup.java | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) (limited to 'javaparser-symbol-solver-core') 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 index de6628f2b..29319fb5d 100644 --- 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 @@ -24,7 +24,6 @@ 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.javaparsermodel.JavaParserFacade; import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; import com.github.javaparser.symbolsolver.resolution.typesolvers.JarTypeSolver; @@ -39,8 +38,6 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.HashSet; import java.util.Optional; import java.util.Set; -import java.util.logging.Level; -import java.util.logging.Logger; import static com.github.javaparser.utils.Utils.assertNotNull; import static java.nio.file.FileVisitResult.CONTINUE; @@ -52,8 +49,6 @@ import static java.nio.file.FileVisitResult.SKIP_SIBLINGS; */ public class SymbolSolverQuickSetup { - private static Logger logger = Logger.getLogger(JavaParserFacade.class.getCanonicalName()); - private final Path root; private CombinedTypeSolver typeSolver = new CombinedTypeSolver(new ReflectionTypeSolver(false)); @@ -76,7 +71,7 @@ public class SymbolSolverQuickSetup { try { return Optional.of(walk()); } catch (IOException e) { - logger.log(Level.WARNING, "Unable to walk root " + root, e); + Log.error(e, "Unable to walk root " + root); return Optional.empty(); } } @@ -110,12 +105,12 @@ public class SymbolSolverQuickSetup { if (root.isPresent()) { typeSolver.add(new JavaParserTypeSolver(root.get().toFile())); if (roots.add(root.get())) { - logger.log(Level.FINE, "Added dir " + root.get() + " to the TypeSolver"); + Log.trace("Added dir " + root.get() + " to the TypeSolver"); return SKIP_SIBLINGS; } } } catch (ParseProblemException e) { - logger.log(Level.WARNING, "Unable to parse file " + file, e); + Log.error(e, "Unable to parse file " + file); } } } @@ -134,15 +129,11 @@ public class SymbolSolverQuickSetup { private class JarVisitor extends SimpleFileVisitor { @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attr) { + public FileVisitResult visitFile(Path file, BasicFileAttributes attr) throws IOException { if (attr.isRegularFile()) { PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:**.jar"); if (matcher.matches(file)) { - try { - typeSolver.add(new JarTypeSolver(file.toString())); - } catch (IOException e) { - logger.log(Level.WARNING, "IOException for file " + file, e); - } + typeSolver.add(new JarTypeSolver(file.toString())); } } return CONTINUE; -- cgit v1.2.3 From 3402a5ccbb267d30f4ac826b87f90cc6363fecf1 Mon Sep 17 00:00:00 2001 From: Daan Schipper Date: Tue, 27 Feb 2018 13:33:24 +0100 Subject: DirFilter to skip certain directories, and skipping sub tree while walking file tree instead of skipping siblings --- .../symbolsolver/utils/SymbolSolverQuickSetup.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'javaparser-symbol-solver-core') 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 index 29319fb5d..e6d3d530b 100644 --- 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 @@ -40,8 +40,7 @@ import java.util.Optional; import java.util.Set; import static com.github.javaparser.utils.Utils.assertNotNull; -import static java.nio.file.FileVisitResult.CONTINUE; -import static java.nio.file.FileVisitResult.SKIP_SIBLINGS; +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. @@ -49,8 +48,13 @@ import static java.nio.file.FileVisitResult.SKIP_SIBLINGS; */ 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); @@ -61,6 +65,11 @@ public class SymbolSolverQuickSetup { 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()); @@ -119,8 +128,8 @@ public class SymbolSolverQuickSetup { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - if (Files.isHidden(dir) || roots.stream().anyMatch(dir::startsWith)) { - return SKIP_SIBLINGS; + if (Files.isHidden(dir) || dirFilter.filter(dir) || roots.stream().anyMatch(dir::startsWith)) { + return SKIP_SUBTREE; } return CONTINUE; } -- cgit v1.2.3 From c620101966295e302b9ff94a73a0e2ab29c7ec6d Mon Sep 17 00:00:00 2001 From: Danny van Bruggen Date: Tue, 27 Feb 2018 20:59:12 +0100 Subject: Fix bug in LazyType --- .../com/github/javaparser/symbolsolver/model/typesystem/LazyType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'javaparser-symbol-solver-core') 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 index a7d05d143..44dbdec1c 100644 --- 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 @@ -64,7 +64,7 @@ public class LazyType implements ResolvedType { @Override public boolean isWildcard() { - return getType().isArray(); + return getType().isWildcard(); } @Override -- cgit v1.2.3