-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
introducing NameLogic and first tests
- Loading branch information
1 parent
b494dbd
commit 0f83d70
Showing
4 changed files
with
722 additions
and
0 deletions.
There are no files selected for viewing
176 changes: 176 additions & 0 deletions
176
...er-core/src/main/java/com/github/javaparser/symbolsolver/resolution/naming/NameLogic.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
package com.github.javaparser.symbolsolver.resolution.naming; | ||
|
||
import com.github.javaparser.ast.ImportDeclaration; | ||
import com.github.javaparser.ast.Node; | ||
import com.github.javaparser.ast.PackageDeclaration; | ||
import com.github.javaparser.ast.body.*; | ||
import com.github.javaparser.ast.expr.*; | ||
import com.github.javaparser.ast.stmt.ReturnStmt; | ||
import com.github.javaparser.ast.type.ClassOrInterfaceType; | ||
import com.github.javaparser.ast.type.TypeParameter; | ||
|
||
/** | ||
* NameLogic contains a set of static methods to implement the abstraction of a "Name" as defined | ||
* in Chapter 6 of the JLS. This code could be moved to an interface or base class in a successive version of | ||
* JavaParser. | ||
*/ | ||
public class NameLogic { | ||
|
||
/** | ||
* Is the given node a non-qualified name? | ||
* | ||
* @throws IllegalArgumentException if the node is not a name | ||
*/ | ||
public static boolean isSimpleName(Node node) { | ||
return !isQualifiedName(node); | ||
} | ||
|
||
/** | ||
* Is the given node a qualified name? | ||
* | ||
* @throws IllegalArgumentException if the node is not a name | ||
*/ | ||
public static boolean isQualifiedName(Node node) { | ||
if (!isAName(node)) { | ||
throw new IllegalArgumentException(); | ||
} | ||
return nameAsString(node).contains("."); | ||
} | ||
|
||
/** | ||
* Does the Node represent a Name? | ||
* | ||
* Note that while most specific AST classes either always represent names or never represent names | ||
* there are exceptions as the FieldAccessExpr | ||
*/ | ||
public static boolean isAName(Node node) { | ||
if (node instanceof FieldAccessExpr) { | ||
FieldAccessExpr fieldAccessExpr = (FieldAccessExpr)node; | ||
return isAName(fieldAccessExpr.getScope()); | ||
} else { | ||
return node instanceof SimpleName || node instanceof Name | ||
|| node instanceof ClassOrInterfaceType || node instanceof NameExpr; | ||
} | ||
} | ||
|
||
/** | ||
* What is the Role of the given name? Does it represent a Declaration or a Reference? | ||
* | ||
* This classification is purely syntactical, i.e., it does not require symbol resolution. For this reason in the | ||
* future this could be moved to the core module of JavaParser. | ||
*/ | ||
public static NameRole classifyRole(Node name) { | ||
if (!isAName(name)) { | ||
throw new IllegalArgumentException("The given node is not a name"); | ||
} | ||
if (!name.getParentNode().isPresent()) { | ||
throw new IllegalArgumentException("We cannot understand the role of a name if it has no parent"); | ||
} | ||
if (whenParentIs(Name.class, name, (p, c) -> p.getQualifier().isPresent() && p.getQualifier().get() == c)) { | ||
return classifyRole(name.getParentNode().get()); | ||
} | ||
if (whenParentIs(PackageDeclaration.class, name, (p, c) -> p.getName() == c)) { | ||
return NameRole.DECLARATION; | ||
} | ||
if (whenParentIs(ImportDeclaration.class, name, (p, c) -> p.getName() == c)) { | ||
return NameRole.REFERENCE; | ||
} | ||
if (whenParentIs(MarkerAnnotationExpr.class, name, (p, c) -> p.getName() == c)) { | ||
return NameRole.REFERENCE; | ||
} | ||
if (whenParentIs(ClassOrInterfaceDeclaration.class, name, (p, c) -> p.getName() == c)) { | ||
return NameRole.DECLARATION; | ||
} | ||
if (whenParentIs(ClassOrInterfaceType.class, name, (p, c) -> p.getName() == c)) { | ||
return NameRole.REFERENCE; | ||
} | ||
if (whenParentIs(VariableDeclarator.class, name, (p, c) -> p.getName() == c)) { | ||
return NameRole.DECLARATION; | ||
} | ||
if (whenParentIs(NameExpr.class, name, (p, c) -> p.getName() == c)) { | ||
return NameRole.REFERENCE; | ||
} | ||
if (whenParentIs(FieldAccessExpr.class, name, (p, c) -> p.getName() == c)) { | ||
return NameRole.REFERENCE; | ||
} | ||
if (whenParentIs(MethodDeclaration.class, name, (p, c) -> p.getName() == c)) { | ||
return NameRole.DECLARATION; | ||
} | ||
if (whenParentIs(Parameter.class, name, (p, c) -> p.getName() == c)) { | ||
return NameRole.DECLARATION; | ||
} | ||
if (whenParentIs(MethodCallExpr.class, name, (p, c) -> p.getName() == c)) { | ||
return NameRole.REFERENCE; | ||
} | ||
if (whenParentIs(ConstructorDeclaration.class, name, (p, c) -> p.getName() == c)) { | ||
return NameRole.DECLARATION; | ||
} | ||
if (whenParentIs(TypeParameter.class, name, (p, c) -> p.getName() == c)) { | ||
return NameRole.DECLARATION; | ||
} | ||
if (whenParentIs(EnumDeclaration.class, name, (p, c) -> p.getName() == c)) { | ||
return NameRole.DECLARATION; | ||
} | ||
if (whenParentIs(EnumConstantDeclaration.class, name, (p, c) -> p.getName() == c)) { | ||
return NameRole.DECLARATION; | ||
} | ||
if (whenParentIs(FieldAccessExpr.class, name, (p, c) -> p.getName() == c || p.getScope() == c)) { | ||
return NameRole.REFERENCE; | ||
} | ||
if (whenParentIs(ObjectCreationExpr.class, name, (p, c) -> p.getType() == c)) { | ||
return NameRole.REFERENCE; | ||
} | ||
if (whenParentIs(ReturnStmt.class, name, (p, c) -> p.getExpression().isPresent() && p.getExpression().get() == c)) { | ||
return NameRole.REFERENCE; | ||
} | ||
if (name.getParentNode().isPresent() && NameLogic.isAName(name.getParentNode().get())) { | ||
return classifyRole(name.getParentNode().get()); | ||
} | ||
throw new UnsupportedOperationException("Unable to classify role of name contained in " | ||
+ name.getParentNode().get().getClass().getSimpleName()); | ||
} | ||
|
||
/** | ||
* Return the string representation of the name | ||
*/ | ||
public static String nameAsString(Node name) { | ||
if (!isAName(name)) { | ||
throw new IllegalArgumentException("A name was expected"); | ||
} | ||
if (name instanceof Name) { | ||
return ((Name)name).asString(); | ||
} else if (name instanceof SimpleName) { | ||
return ((SimpleName) name).getIdentifier(); | ||
} else if (name instanceof ClassOrInterfaceType) { | ||
return ((ClassOrInterfaceType) name).asString(); | ||
} else if (name instanceof FieldAccessExpr) { | ||
FieldAccessExpr fieldAccessExpr = (FieldAccessExpr) name; | ||
if (isAName(fieldAccessExpr.getScope())) { | ||
return nameAsString(fieldAccessExpr.getScope()) + "." + nameAsString(fieldAccessExpr.getName()); | ||
} else { | ||
throw new IllegalArgumentException(); | ||
} | ||
} else if (name instanceof NameExpr) { | ||
return ((NameExpr)name).getNameAsString(); | ||
} else { | ||
throw new UnsupportedOperationException("Unknown type of name found: " + name + " (" | ||
+ name.getClass().getCanonicalName() + ")"); | ||
} | ||
} | ||
|
||
private interface PredicateOnParentAndChild<P extends Node, C extends Node> { | ||
boolean isSatisfied(P parent, C child); | ||
} | ||
|
||
private static <P extends Node, C extends Node> boolean whenParentIs(Class<P> parentClass, | ||
C child, | ||
PredicateOnParentAndChild<P, C> predicate) { | ||
if (child.getParentNode().isPresent()) { | ||
Node parent = child.getParentNode().get(); | ||
return parentClass.isInstance(parent) && predicate.isSatisfied(parentClass.cast(parent), child); | ||
} else { | ||
return false; | ||
} | ||
} | ||
|
||
} |
9 changes: 9 additions & 0 deletions
9
...ver-core/src/main/java/com/github/javaparser/symbolsolver/resolution/naming/NameRole.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.github.javaparser.symbolsolver.resolution.naming; | ||
|
||
/** | ||
* Each Name can be part either of a Declaration or a Reference to a Declaration. | ||
*/ | ||
public enum NameRole { | ||
DECLARATION, | ||
REFERENCE | ||
} |
75 changes: 75 additions & 0 deletions
75
...test/java/com/github/javaparser/symbolsolver/resolution/naming/AbstractNameLogicTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package com.github.javaparser.symbolsolver.resolution.naming; | ||
|
||
import com.github.javaparser.*; | ||
import com.github.javaparser.ast.Node; | ||
import com.github.javaparser.symbolsolver.JavaSymbolSolver; | ||
import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; | ||
import com.github.javaparser.symbolsolver.resolution.AbstractResolutionTest; | ||
|
||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.stream.Collectors; | ||
|
||
import static org.junit.Assert.assertTrue; | ||
|
||
public abstract class AbstractNameLogicTest extends AbstractResolutionTest { | ||
|
||
protected Node getNameInCodeTollerant(String code, String name, ParseStart parseStart) { | ||
return getNameInCode(code, name, parseStart, true, Optional.empty()); | ||
} | ||
|
||
protected Node getNameInCodeTollerant(String code, String name, ParseStart parseStart, TypeSolver typeSolver) { | ||
return getNameInCode(code, name, parseStart, true, Optional.of(typeSolver)); | ||
} | ||
|
||
protected Node getNameInCode(String code, String name, ParseStart parseStart) { | ||
return getNameInCode(code, name, parseStart, false, Optional.empty()); | ||
} | ||
|
||
private Node getNameInCode(String code, String name, ParseStart parseStart, boolean tollerant, | ||
Optional<TypeSolver> typeSolver) { | ||
ParserConfiguration parserConfiguration = new ParserConfiguration(); | ||
parserConfiguration.setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_10); | ||
if (typeSolver.isPresent()) { | ||
parserConfiguration.setSymbolResolver(new JavaSymbolSolver(typeSolver.get())); | ||
} | ||
ParseResult<? extends Node> parseResult = new JavaParser(parserConfiguration).parse(parseStart, new StringProvider(code)); | ||
if (!parseResult.isSuccessful()) { | ||
parseResult.getProblems().forEach(p -> System.out.println("ERR: " + p)); | ||
} | ||
assertTrue(parseResult.isSuccessful()); | ||
Node root = parseResult.getResult().get(); | ||
List<Node> allNames = root.findAll(Node.class).stream() | ||
.filter(NameLogic::isAName) | ||
.collect(Collectors.toList()); | ||
List<Node> matchingNames = allNames.stream() | ||
.filter(n -> NameLogic.nameAsString(n).equals(name)) | ||
.collect(Collectors.toList()); | ||
// In case of one name being contained in other as is, we remove it | ||
for (int i=0;i<matchingNames.size();i++) { | ||
Node container = matchingNames.get(i); | ||
for (int j=i+1;j<matchingNames.size();j++) { | ||
Node contained = matchingNames.get(j); | ||
if (contained.getParentNode().isPresent() && contained.getParentNode().get() == container | ||
&& NameLogic.nameAsString(contained).equals(NameLogic.nameAsString(container))) { | ||
matchingNames.remove(j); | ||
j--; | ||
} | ||
} | ||
} | ||
|
||
if (matchingNames.size() == 0) { | ||
throw new IllegalArgumentException("Not found. Names found: " + String.join(", ", | ||
allNames.stream().map(NameLogic::nameAsString).collect(Collectors.toList()))); | ||
} else if (matchingNames.size() > 1) { | ||
if (tollerant) { | ||
return matchingNames.get(matchingNames.size() - 1); | ||
} else { | ||
throw new IllegalArgumentException("Ambiguous: there are several matching."); | ||
} | ||
} else { | ||
return matchingNames.get(0); | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.