解决符号和参考

与其他解析器一样,JavaParser接收源代码中存在的信息,并将其组织成一棵树,即抽象语法树。

在许多情况下,AST中显示的信息足以满足用户的需求。

但是,在其他情况下,您可能需要详细说明AST并计算其他信息。

特别是,您可能需要解析引用并找到节点之间的关系。

从某种意义上讲,这意味着跟踪新链接并将树转换为图形。

当我们找到Java代码中使用的名称时,我们会将其识别为符号。

符号是我们用来表示可能由名称表示的任何元素的术语。

符号可以是变量,参数,也可以是字段或类型。

当我们遇到这个表达式时:

container.element

什么是容器? 什么是元素? 也许容器是一个变量,而元素是该变量的字段。

或者,容器是一个类名,而元素是该类的静态字段。

在这个阶段我们还不知道,所以我们通常说符号。

JavaSymbolSolver 是一个库,它检查那些符号并告诉我们它们的真正含义,为我们找到该符号表示的变量声明,参数声明或类型声明。

例如考虑这种情况:

void aMethod() {
    int a;
    a = a + 1;
}

通过查看AST,我们知道 a = a + 1 对应于一个分配。

我们也知道我们正在将加1的结果分配给a的值。

有些事情我们不知道:

  • 在这种情况下是否有符号a?

  • 可以分配该符号吗?

  • 该符号的类型是什么?

考虑最后一点:如果a是一个整数,那么a + 1的结果将是一个整数。

但是a可以是一个String,在这种情况下,我们不求和1,而是将其字符串表示与a表示的String串联。

换句话说,仅查看AST,我们就无法说出我们正在执行哪种运算:代数和或字符串连接?

这取决于a的类型。

要回答这些问题,我们必须解决参考。

我们必须将+ 1中对a的引用连接到要引用的a的声明。

现在考虑以下情况:

class Bar {

 private String a;

    void aMethod() {
     float a;
        while (true) {
         int a;
         a = a + 1;
        }
    }
}

有很多符号。 解决参考意味着找到正确的参考,这可能很困难。

相信我们,事情会变得非常复杂。

幸运的是,您可以使用JavaSymbolSolver来解决JavaParser AST元素之间的引用。

如何设置JavaSymbolSolver?

JavaSymbolSolver现在与JavaParser核心库捆绑在一起。

这是因为保证每个版本的JavaSymbolSolver都可以与特定版本的JavaParser一起使用。

从历史上看并非如此,这些库是分开分发的。

如果您遇到与依赖性相关的问题,请更新至最新版本。

如果您以前选择不包含JavaSymbolSolver而是仅将javaparser-core工件包含在依赖项中,则必须更新到捆绑版本,然后需要替换这种依赖性。

源码

  • Bar.java
package com.github.houbb.javaparser.learn.chap5;

/**
 * @author binbin.hou
 * @since 1.0.0
 */
public class Bar {

    private String a;

    void aMethod() {
        while (true) {
            int a = 0;
            a = a + 1;
        }
    }

}
  • GetTypeOfReference.java
package com.github.houbb.javaparser.learn.chap5;

import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.AssignExpr;
import com.github.javaparser.resolution.types.ResolvedType;
import com.github.javaparser.symbolsolver.JavaSymbolSolver;
import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;

import java.io.File;
import java.io.FileNotFoundException;

/**
 * @author binbin.hou
 * @since 1.0.0
 */
public class GetTypeOfReference {

    /**
     * com.github.houbb.javaparser.learn.chap5
     */
    private static final String FILE_PATH = "src/main/java/" +
            "com/github/houbb/javaparser/learn/chap5/Bar.java";

    public static void main(String[] args) throws FileNotFoundException {
        TypeSolver typeSolver = new CombinedTypeSolver();

        JavaSymbolSolver symbolSolver = new JavaSymbolSolver(typeSolver);
        StaticJavaParser
                .getConfiguration()
                .setSymbolResolver(symbolSolver);

        CompilationUnit cu = StaticJavaParser.parse(new File(FILE_PATH));

        cu.findAll(AssignExpr.class).forEach(ae -> {
            ResolvedType resolvedType = ae.calculateResolvedType();
            System.out.println(ae.toString() + " is a: " + resolvedType);
        });
    }

}
  • 输出日志
a = a + 1 is a: PrimitiveTypeUsage{name='int'}

比较核心的一句代码:

TypeSolver typeSolver = new CombinedTypeSolver();

后续将进行深入学习。

指定在哪里寻找类型

在前面的示例中,我们已经看到了相同AST(即同一Java源文件)中元素之间的引用。

生活往往比这复杂,引用通常涉及其他文件。

考虑一下:

class MyClass extends MySuperClass {

    public void returnSomeValue() {
        System.out.println(aField.size();
    }

}

字段来自哪里?

我们不知道,它可能是MySuperClass中定义的字段。

或者,也许MySuperClass扩展了另一个名为MyExtraSuperClass的类,并且该类定义了名为aField的字段。

因此,我们需要检查MySuperClass来解决它。

好的,这就是JavaSymbolSolver将为我们做的事情。

为了做到这一点,JavaSymbolSolver将需要知道在哪里寻找类。

类可以是:

  • 在其他Java源文件中,如果它们是类,我们现在正在编译

  • 来自某些库的类文件或JAR中

  • 在JRE中,用于java.lang.Object或java.lang.String之类的类

不同的 TypeSolvers

根据上面的内容,您需要将不同的TypeSolvers传递给JavaSymbolSolver。

JarTypeSolver:此TypeSolver在JAR文件中查找类。您只需要指定JAR文件的位置即可。

JavaParserTypeSolver:此TypeSolver查找在源文件中定义的类。您只需要指定根目录。例如src目录。它将在与包相对应的目录中查找源文件。因此它将在指定的根目录下的目录a下的目录b中寻找类a.b.C。

ReflectionTypeSolver:有些类被定义为语言的一部分,并且没有JAR。诸如java.lang.Object之类的类。为了找到此类的定义,我们可以使用反射。

MemoryTypeSolver:此TypeSolver仅返回我们在其中记录的类。它主要用于测试。

CombinedTypeSolver:您通常希望将多个TypeSolver合并为一个。为此,您可以使用CombinedTypeSolver。

使用绝对名称解析类型

您可以根据需要直接使用TypeSolver。

这将查找给定其限定名称的元素定义。

考虑以下示例:

package com.github.houbb.javaparser.learn.chap5;

import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;

/**
 * @author binbin.hou
 * @since 1.0.0
 */
public class UsingTypeSolver {

    private static void showReferenceTypeDeclaration(ResolvedReferenceTypeDeclaration resolvedReferenceTypeDeclaration){

        System.out.println(String.format("== %s ==",
                resolvedReferenceTypeDeclaration.getQualifiedName()));
        System.out.println(" fields:");
        resolvedReferenceTypeDeclaration.getAllFields().forEach(f ->
                System.out.println(String.format("    %s %s", f.getType(), f.getName())));
        System.out.println(" methods:");
        resolvedReferenceTypeDeclaration.getAllMethods().forEach(m ->
                System.out.println(String.format("    %s", m.getQualifiedSignature())));
        System.out.println();
    }

    public static void main(String[] args) {
        TypeSolver typeSolver = new ReflectionTypeSolver();

        showReferenceTypeDeclaration(typeSolver.solveType("java.lang.Object"));
        showReferenceTypeDeclaration(typeSolver.solveType("java.lang.String"));
        showReferenceTypeDeclaration(typeSolver.solveType("java.util.List"));
    }

}

日志输出如下:

== java.lang.Object ==
 fields:
 methods:
    java.lang.Object.registerNatives()
    java.lang.Object.getClass()
    java.lang.Object.wait(long, int)
    java.lang.Object.clone()
    java.lang.Object.hashCode()
    java.lang.Object.finalize()
    java.lang.Object.wait()
    java.lang.Object.notify()
    java.lang.Object.equals(java.lang.Object)
    java.lang.Object.notifyAll()
    java.lang.Object.toString()
    java.lang.Object.wait(long)

== java.lang.String ==
 fields:
    ResolvedArrayType{PrimitiveTypeUsage{name='char'}} value
    PrimitiveTypeUsage{name='int'} hash
    PrimitiveTypeUsage{name='long'} serialVersionUID
    ResolvedArrayType{ReferenceType{java.io.ObjectStreamField, typeParametersMap=TypeParametersMap{nameToValue={}}}} serialPersistentFields
    ReferenceType{java.util.Comparator, typeParametersMap=TypeParametersMap{nameToValue={java.util.Comparator.T=ReferenceType{java.lang.String, typeParametersMap=TypeParametersMap{nameToValue={}}}}}} CASE_INSENSITIVE_ORDER
 methods:
    java.lang.String.substring(int)
    java.lang.String.valueOf(boolean)
    java.lang.Object.getClass()
    java.lang.String.endsWith(java.lang.String)
    java.lang.String.valueOf(char[], int, int)
    java.lang.String.valueOf(float)
    java.lang.String.length()
    java.lang.CharSequence.chars()
    java.lang.String.contentEquals(java.lang.StringBuffer)
    java.lang.Object.finalize()
    java.lang.String.valueOf(long)
    java.lang.String.charAt(int)
    java.lang.String.lastIndexOf(char[], int, int, char[], int, int, int)
    java.lang.String.toLowerCase(java.util.Locale)
    java.lang.String.startsWith(java.lang.String)
    java.lang.String.copyValueOf(char[])
    java.lang.Object.notifyAll()
    java.lang.String.toUpperCase()
    java.lang.String.lastIndexOf(char[], int, int, java.lang.String, int)
    java.lang.String.equalsIgnoreCase(java.lang.String)
    java.lang.String.checkBounds(byte[], int, int)
    java.lang.Object.clone()
    java.lang.CharSequence.codePoints()
    java.lang.String.join(java.lang.CharSequence, java.lang.Iterable<? extends java.lang.CharSequence>)
    java.lang.String.getBytes(int, int, byte[], int)
    java.lang.String.format(java.lang.String, java.lang.Object...)
    java.lang.String.replaceAll(java.lang.String, java.lang.String)
    java.lang.String.toUpperCase(java.util.Locale)
    java.lang.String.trim()
    java.lang.String.indexOf(int, int)
    java.lang.String.getBytes(java.nio.charset.Charset)
    java.lang.String.lastIndexOf(java.lang.String, int)
    java.lang.String.codePointAt(int)
    java.lang.String.getBytes()
    java.lang.String.replace(char, char)
    java.lang.String.concat(java.lang.String)
    java.lang.String.replace(java.lang.CharSequence, java.lang.CharSequence)
    java.lang.String.contains(java.lang.CharSequence)
    java.lang.String.toCharArray()
    java.lang.String.codePointCount(int, int)
    java.lang.String.valueOf(double)
    java.lang.String.lastIndexOfSupplementary(int, int)
    java.lang.String.indexOfSupplementary(int, int)
    java.lang.String.valueOf(char)
    java.lang.String.isEmpty()
    java.lang.String.getChars(int, int, char[], int)
    java.lang.String.toString()
    java.lang.String.offsetByCodePoints(int, int)
    java.lang.String.indexOf(char[], int, int, java.lang.String, int)
    java.lang.String.getChars(char[], int)
    java.lang.String.lastIndexOf(java.lang.String)
    java.lang.String.toLowerCase()
    java.lang.String.codePointBefore(int)
    java.lang.String.replaceFirst(java.lang.String, java.lang.String)
    java.lang.String.split(java.lang.String, int)
    java.lang.String.equals(java.lang.Object)
    java.lang.String.contentEquals(java.lang.CharSequence)
    java.lang.Object.wait(long)
    java.lang.String.valueOf(char[])
    java.lang.String.valueOf(java.lang.Object)
    java.lang.String.lastIndexOf(int, int)
    java.lang.String.indexOf(char[], int, int, char[], int, int, int)
    java.lang.Object.wait()
    java.lang.String.matches(java.lang.String)
    java.lang.String.getBytes(java.lang.String)
    java.lang.String.valueOf(int)
    java.lang.String.compareTo(java.lang.String)
    java.lang.Object.wait(long, int)
    java.lang.String.indexOf(java.lang.String)
    java.lang.String.startsWith(java.lang.String, int)
    java.lang.String.copyValueOf(char[], int, int)
    java.lang.String.hashCode()
    java.lang.String.lastIndexOf(int)
    java.lang.String.regionMatches(boolean, int, java.lang.String, int, int)
    java.lang.String.intern()
    java.lang.Comparable.compareTo(T)
    java.lang.String.split(java.lang.String)
    java.lang.Object.notify()
    java.lang.String.indexOf(java.lang.String, int)
    java.lang.String.format(java.util.Locale, java.lang.String, java.lang.Object...)
    java.lang.String.join(java.lang.CharSequence, java.lang.CharSequence...)
    java.lang.String.regionMatches(int, java.lang.String, int, int)
    java.lang.String.substring(int, int)
    java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder)
    java.lang.String.compareToIgnoreCase(java.lang.String)
    java.lang.Object.registerNatives()
    java.lang.String.indexOf(int)
    java.lang.String.subSequence(int, int)

== java.util.List ==
 fields:
 methods:
    java.lang.Object.finalize()
    java.lang.Object.notifyAll()
    java.util.List.addAll(int, java.util.Collection<? extends E>)
    java.lang.Iterable.forEach(java.util.function.Consumer<? super T>)
    java.lang.Object.notify()
    java.lang.Object.wait(long, int)
    java.lang.Object.wait(long)
    java.util.List.add(int, E)
    java.util.List.subList(int, int)
    java.util.Collection.stream()
    java.util.List.containsAll(java.util.Collection<? extends java.lang.Object>)
    java.util.List.listIterator()
    java.util.List.removeAll(java.util.Collection<? extends java.lang.Object>)
    java.util.List.spliterator()
    java.util.List.add(E)
    java.util.List.set(int, E)
    java.util.List.hashCode()
    java.util.List.remove(int)
    java.util.Collection.parallelStream()
    java.util.List.size()
    java.util.List.indexOf(java.lang.Object)
    java.util.List.get(int)
    java.lang.Object.clone()
    java.util.List.clear()
    java.util.List.contains(java.lang.Object)
    java.util.List.retainAll(java.util.Collection<? extends java.lang.Object>)
    java.util.List.iterator()
    java.util.List.remove(java.lang.Object)
    java.util.Collection.removeIf(java.util.function.Predicate<? super E>)
    java.lang.Object.getClass()
    java.lang.Object.registerNatives()
    java.util.List.addAll(java.util.Collection<? extends E>)
    java.util.List.isEmpty()
    java.util.List.lastIndexOf(java.lang.Object)
    java.lang.Object.wait()
    java.util.List.listIterator(int)
    java.lang.Object.toString()
    java.util.List.replaceAll(java.util.function.UnaryOperator<E>)
    java.util.List.toArray(T[])
    java.util.List.sort(java.util.Comparator<? super E>)
    java.util.List.equals(java.lang.Object)
    java.util.List.toArray()

在上下文中解析类型

仅提供类型名称时,TypeSolver才能查找类型。

但是,JavaSymbolSolver可以使用上下文查找与某个声明相对应的实际限定名称。

考虑一下:

栏的类型是什么?

可以是org.javaparser.examples.chapter5.Bar或org.javaparser.examples.chapter4.Bar。

JavaSymbolSolver将通过查看上下文并考虑诸如以下的内容来为您解决这一问题:

  • 使用该类型的类的名称

  • 导入指令

  • 当前包

您如何告诉JavaSymbolSolver使用哪个上下文?

好吧,它通过查看包含您感兴趣的节点的AST来确定这一点。

源码

package com.github.houbb.javaparser.learn.chap5;

import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.symbolsolver.JavaSymbolSolver;
import com.github.javaparser.symbolsolver.javaparser.Navigator;
import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;

import java.io.File;

/**
 * @author binbin.hou
 * @since 1.0.0
 */
public class ResolveTypeInContext {

    private static final String FILE_PATH = "src/main/java/" +
            "com/github/houbb/javaparser/learn/chap5/Foo.java";
    private static final String SRC_PATH = "src/main/java";

    public static void main(String[] args) throws Exception {
        TypeSolver reflectionTypeSolver = new ReflectionTypeSolver();
        TypeSolver javaParserTypeSolver = new JavaParserTypeSolver(new File(SRC_PATH));
        reflectionTypeSolver.setParent(reflectionTypeSolver);

        CombinedTypeSolver combinedSolver = new CombinedTypeSolver();
        combinedSolver.add(reflectionTypeSolver);
        combinedSolver.add(javaParserTypeSolver);

        JavaSymbolSolver symbolSolver = new JavaSymbolSolver(combinedSolver);
        StaticJavaParser
                .getConfiguration()
                .setSymbolResolver(symbolSolver);

        CompilationUnit cu = StaticJavaParser.parse(new File(FILE_PATH));

        FieldDeclaration fieldDeclaration = Navigator.findNodeOfGivenClass(cu, FieldDeclaration.class);

        System.out.println("Field type: " + fieldDeclaration.getVariables().get(0).getType()
                .resolve().asReferenceType().getQualifiedName());
    }

}
  • 输出
Field type: com.github.houbb.javaparser.learn.chap5.Bar

我们在这里做什么?

  1. 我们获得在FieldDeclaration中定义的第一个(也是唯一的)变量的类型

  2. 我们将该类型转换为JavaSymbolSolver类型,以实现传递JavaParser类型和要使用的上下文的目的。

JavaSymbolSolver ResolvedType是一种类型,其中包含您可能需要的有关该类型的所有信息。

在这种情况下,我们使用的上下文是fieldDeclaration。

这意味着我们希望JavaSymbolSolver考虑当引用类型Bar指向该特定节点的定义位置时引用类型的含义

考虑一下:

class Foo {
    Bar bar;
}

class Zum {
    Bar bar;

    class Bar {

    }
}

从JavaParser的角度来看,两个FieldDeclarations具有相同的类型:名为Bar的类型。

但是,相同的名称在不同的上下文中具有不同的含义。

因此,通过传递相同的JavaParser类型和上下文(第一个或第二个FieldDeclaration),JavaSymbolSolver将为我们提供不同的答案,并找出每种情况下使用的实际类型。

很整洁吧?

解决方法调用

JavaSymbolSolver可以帮助您的另一件事是解决方法调用。

您可能知道Java允许方法的重载,即,具有相同名称但具有不同签名的不同方法。

但是,这可以使特定调用调用哪种方法变得不清楚。

考虑这种情况:

package com.github.houbb.javaparser.learn.chap5;

/**
 * @author binbin.hou
 * @since 1.0.0
 */
public class A {

    public void foo(Object param) {
        System.out.println(1);
        System.out.println("hi");
        System.out.println(param);
    }
    
}

我们有三个调用,这三个调用引用了恰好具有相同名称的三个不同方法。

这是我们可以使用JavaSymbolSolver找出每种情况下调用哪个方法的方法:

代码

package com.github.houbb.javaparser.learn.chap5;

import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.symbolsolver.JavaSymbolSolver;
import com.github.javaparser.symbolsolver.model.resolution.TypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;

import java.io.File;

/**
 * @author binbin.hou
 * @since 1.0.0
 */
public class ResolveMethodCalls {

    /**
     * com.github.houbb.javaparser.learn.chap5
     */
    private static final String FILE_PATH = "src/main/java/" +
            "com/github/houbb/javaparser/learn/chap5/A.java";

    public static void main(String[] args) throws Exception {
        TypeSolver typeSolver = new ReflectionTypeSolver();

        JavaSymbolSolver symbolSolver = new JavaSymbolSolver(typeSolver);
        StaticJavaParser
                .getConfiguration()
                .setSymbolResolver(symbolSolver);

        CompilationUnit cu = StaticJavaParser.parse(new File(FILE_PATH));

        cu.findAll(MethodCallExpr.class).forEach(mce ->
                System.out.println(mce.resolve().getQualifiedSignature()));
    }

}
  • 日志输出
java.io.PrintStream.println(int)
java.io.PrintStream.println(java.lang.String)
java.io.PrintStream.println(java.lang.Object)

还要注意,JavaSymbolSolver会找出在特定上下文中可见的方法,它确定方法调用范围的类型(在本例中为System.out),依此类推。

这是相当多的工作,您可能不想自己做,尤其是当您可以仅使用此库并转到下一个问题时。

使用CombinedTypeSolver

在检查实际应用程序时,通常将使用CombinedTypeSolver将不同的其他TypeSolver分组为一个。

例如,假设您要检查的应用程序使用以下类:

  • 标准库

  • 3个不同的 jars

  • 2个包含源代码的目录

您可以创建一个CombinedTypeSolver,其中包含ReflectionTypeSolver的一个实例,3个JarTypeSolver的实例和2个JavaParserTypeSolver的实例。

代码看起来像这样:

package com.github.houbb.javaparser.learn.chap5;

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 java.io.File;
import java.io.IOException;

/**
 * @author binbin.hou
 * @since 1.0.0
 */
public class UsingCombinedTypeSolver {

    public static void main(String[] args) throws IOException {
        TypeSolver myTypeSolver = new CombinedTypeSolver(
                new ReflectionTypeSolver(),
                JarTypeSolver.getJarTypeSolver("jars/library1.jar"),
                JarTypeSolver.getJarTypeSolver("jars/library2.jar"),
                JarTypeSolver.getJarTypeSolver("jars/library3.jar"),
                new JavaParserTypeSolver(new File("src/main/java")),
                new JavaParserTypeSolver(new File("generated_code"))
        );
        // using myTypeSolver
    }
    
}

使用MemoryTypeSolver

正如我们预期的那样,这种TypeSolver通常不会在实际应用中使用。

相反,它主要用于测试。

让我们考虑一下JavaSymbolSolver项目中的一项真实测试:

package org.javaparser.examples.chapter5;

import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration;
import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration;
import com.github.javaparser.symbolsolver.core.resolution.Context;
import com.github.javaparser.symbolsolver.javaparsermodel.contexts.CompilationUnitContext;
import com.github.javaparser.symbolsolver.model.resolution.SymbolReference;
import com.github.javaparser.symbolsolver.resolution.typesolvers.MemoryTypeSolver;
import org.easymock.EasyMock;
import org.junit.Test;

import java.io.File;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class MemoryTypeSolverComplete {

    private static final String FILE_PATH = "src/main/java/org/javaparser/examples/chapter5/Foo.java";

    @Test
    public void solveTypeInSamePackage() throws Exception {
        CompilationUnit cu = StaticJavaParser.parse(new File(FILE_PATH));

        ResolvedReferenceTypeDeclaration otherClass = EasyMock.createMock(ResolvedReferenceTypeDeclaration.class);
        EasyMock.expect(otherClass.getQualifiedName()).andReturn("org.javaparser.examples.chapter5.Bar");

        /* Start of the relevant part */
        MemoryTypeSolver memoryTypeSolver = new MemoryTypeSolver();
        memoryTypeSolver.addDeclaration(
                "org.javaparser.examples.chapter5.Bar", otherClass);
        Context context = new CompilationUnitContext(cu, memoryTypeSolver);

        /* End of the relevant part */

        EasyMock.replay(otherClass);

        SymbolReference<ResolvedTypeDeclaration> ref = context.solveType("Bar");
        assertTrue(ref.isSolved());
        assertEquals("org.javaparser.examples.chapter5.Bar", ref.getCorrespondingDeclaration().getQualifiedName());
    }
}

在这种情况下,我们要验证其余代码是否在寻找一种特定类型(com.foo.OtherClassInSamePackage)。

我们使用MemoryTypeSolver并指示它在使用名称com.foo.OtherClassInSamePackage时返回特定的TypeDeclaration,而不是创建JAR只是在文本中使用它。

也可以使用它来使用特殊的类加载器来分析应用程序,从而在运行中或在其他奇怪的情况下生成类。

在大多数情况下,您可以忽略MemoryTypeSolver的存在。

摘要

此时,您应该对JavaSymbolSolver可以为您解决什么样的问题有所了解。

我们并不希望您回答关于库的所有问题,也没有探讨整个API,但是我们应该为您提供一个起点。

Javadoc还可以帮助您解决一些剩余的问题。 对于其他人,请随时与我们联系。

请记住,与JavaParser相比,JavaSymbolSolver还很年轻,并且随着时间的推移,其API可能会逐渐变得易于使用。

但是,由于有出色的JavaParser社区,该项目正在快速发展,并且已经在商业产品中使用。

参考资料

官方语法书