Eclipse Abstract Syntax Tree Diff

2019-05-07 13:42发布

问题:

Given the following code in Eclipse:

import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;

public class Question {
    public static void main(String[] args) {
        String source = "class Bob {}";
        ASTParser parser = ASTParser.newParser(AST.JLS3); 
        parser.setSource(source.toCharArray());
        CompilationUnit result = (CompilationUnit) parser.createAST(null);

        String source2 = "class Bob {public void MyMethod(){}}";
        ASTParser parser2 = ASTParser.newParser(AST.JLS3); 
        parser2.setSource(source2.toCharArray());
        CompilationUnit result2 = (CompilationUnit) parser2.createAST(null);
    }
}

How do you use the Eclipse Compare API (org.eclipse.compare) to find the AST difference? (And can this be done outside of a plugin?)

I'm looking at the following APIs

http://kickjava.com/src/org/eclipse/compare/structuremergeviewer/Differencer.java.htm http://kickjava.com/src/org/eclipse/jdt/internal/ui/compare/JavaStructureCreator.java.htm http://kickjava.com/src/org/eclipse/compare/CompareUI.java.htm

Can anyone point to example code (or an API - but the code is preferred).

回答1:

Given that Eclipse doesn't do AST differencing, perhaps what the OP wanted to find differences between two files in terms of the language constructs ignoring white spaces and comments. Our Smart Differencer tool compares two source files in terms of the langauge constructs (variables, expressions, statements, blocks, methods, ...) and describes the differences in terms of abstract editing operations over these elements (delete, copy, move, rename identifier in region, ...)



回答2:

GumTree does the job, for free :)

It also supports other languages such as javascript.



回答3:

Actually, checking for equality is simple, using the properties of an ASTNode. After that, it is up to you, how you want to get the differences. Check the code sample for equality testing:

public class ASTCompare {

    @SuppressWarnings("unchecked")
    static boolean equals(ASTNode left, ASTNode right) {
        // if both are null, they are equal, but if only one, they aren't
        if (left == null && right == null) {
            return true;
        } else if (left == null || right == null) {
            return false;
        }
        // if node types are the same we can assume that they will have the same
        // properties
        if (left.getNodeType() != right.getNodeType()) {
            return false;
        }
        List<StructuralPropertyDescriptor> props = left
                .structuralPropertiesForType();
        for (StructuralPropertyDescriptor property : props) {
            Object leftVal = left.getStructuralProperty(property);
            Object rightVal = right.getStructuralProperty(property);
            if (property.isSimpleProperty()) {
                // check for simple properties (primitive types, Strings, ...)
                // with normal equality
                if (!leftVal.equals(rightVal)) {
                    return false;
                }
            } else if (property.isChildProperty()) {
                // recursively call this function on child nodes
                if (!equals((ASTNode) leftVal, (ASTNode) rightVal)) {
                    return false;
                }
            } else if (property.isChildListProperty()) {
                Iterator<ASTNode> leftValIt = ((Iterable<ASTNode>) leftVal)
                        .iterator();
                Iterator<ASTNode> rightValIt = ((Iterable<ASTNode>) rightVal)
                        .iterator();
                while (leftValIt.hasNext() && rightValIt.hasNext()) {
                    // recursively call this function on child nodes
                    if (!equals(leftValIt.next(), rightValIt.next())) {
                        return false;
                    }
                }
                // one of the value lists have additional elements
                if (leftValIt.hasNext() || rightValIt.hasNext()) {
                    return false;
                }
            }
        }
        return true;
    }
}