Eclipse AST not changing class files when changing

2019-09-07 00:16发布

问题:

I am trying to edit several Java classes with the Java AST. But my changes wont show in the Java class files.

What specifically do I want to do? I want to take an IPackageFragment and visit all the ICompilationUnits. For every declared class I want to set the super class to a specific class (using the qualified name of the super class, because it is an Xtend class). I also tried applying the edits via the Document class.

For example: A class main.model.someClass should inherit from wrappers.main.model.someClassWrapper

I am relatively new to the JDT API, so I cannot find the reason why the class files are not changed. I already checked this post, but it did not help me. I tried to stay as close as possible to the examples from How To Train the JDT Dragon other tips/examples I got from Stackoverflow. But it will not work.

This is how I do it:

private void editTypesIn(IPackageFragment myPackage) throws JavaModelException {
    for (ICompilationUnit unit : myPackage.getCompilationUnits()) {
        TypeVisitor visitor = new TypeVisitor(myPackage.getElementName(), unit);
        unit.becomeWorkingCopy(new NullProgressMonitor());
        CompilationUnit parse = parse(unit);
        parse.recordModifications();
        parse.accept(visitor);
    }
}

private static CompilationUnit parse(ICompilationUnit unit) {
    ASTParser parser = ASTParser.newParser(AST.JLS8);
    parser.setKind(ASTParser.K_COMPILATION_UNIT);
    parser.setSource(unit);
    parser.setResolveBindings(true);
    return (CompilationUnit) parser.createAST(null); // parse
}

And this is the visitor class:

public class TypeVisitor extends ASTVisitor {
    private final String currentPackage;
    private final ICompilationUnit compilationUnit;

    public TypeVisitor(String currentPackage, ICompilationUnit compilationUnit) {
        this.currentPackage = currentPackage;
        this.compilationUnit = compilationUnit;
    }

    @Override
    public boolean visit(TypeDeclaration node) {
        if (!node.isInterface()) { // is class
            setSuperClass(node, "wrappers." + currentPackage + "." + node.getName().toString() + "Wrapper");
        }
        return super.visit(node);
    }

    public void setSuperClass(TypeDeclaration declaration, String qualifiedName) {
        try {
            // create ast and rewrite:
            AST ast = declaration.getAST();
            ASTRewrite astRewrite = ASTRewrite.create(ast);
            // set super:
            Name name = ast.newName(qualifiedName);
            Type type = ast.newSimpleType(name);
            declaration.setSuperclassType(type);
            // apply changes
            TextEdit edits = astRewrite.rewriteAST();
            compilationUnit.applyTextEdit(edits, new NullProgressMonitor());
            compilationUnit.commitWorkingCopy(true, new NullProgressMonitor());
        } catch (JavaModelException exception) {
            exception.printStackTrace();
        } catch (IllegalArgumentException exception) {
            exception.printStackTrace();
        } catch (MalformedTreeException exception) {
            exception.printStackTrace();
        }
    }
}

Thanks in advance for any help!

回答1:

I guess your edits will be empty, right?

By calling CompilationUnit.recordModifications() you tell the AST to internally create an ASTRewrite into which modifications will be recorded.

By ASTRewrite.create(..) you are creating a second ASTRewrite which knows nothing of the modifications being recorded.

The only API for consuming the recorded modifications is CompilationUnit.rewrite(IDocument,Map) (see references to field AST.rewriter).

If you need to use your own ASTRewrite apply the "Descriptive approach", using, e.g., ASTRewrite.set(..).