JDT源formmatter:跳过一个方法,格式不正确

时间:2016-07-22 14:53:05

标签: eclipse-jdt

我有一个SourceFormatter类应该格式化提供的源代码并用空格替换所有选项卡并将大括号放在一个新行上:

import java.util.Hashtable;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.ToolFactory;
import org.eclipse.jdt.core.formatter.CodeFormatter;
import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
import org.eclipse.text.edits.TextEdit;

public class SourceFormatter
{

    private static final IProgressMonitor PROGRESS_MONITOR = new NullProgressMonitor();


    public void formatCompilationUnitSource(final ICompilationUnit compilationUnit) throws JavaModelException
    {
        final Hashtable<String, String> options = JavaCore.getDefaultOptions();
        options.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, JavaCore.SPACE);
        options.put(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_METHOD_DECLARATION,
                    DefaultCodeFormatterConstants.NEXT_LINE);
        final CodeFormatter formatter = ToolFactory.createCodeFormatter(options, ToolFactory.M_FORMAT_EXISTING);
        final ISourceRange range = compilationUnit.getSourceRange();
        final TextEdit formatEdit = formatter.format(CodeFormatter.K_COMPILATION_UNIT,
                                                     compilationUnit.getSource(),
                                                     range.getOffset(),
                                                     range.getLength(),
                                                     0,
                                                     null);
        if (formatEdit != null && formatEdit.hasChildren())
        {
            compilationUnit.applyTextEdit(formatEdit, PROGRESS_MONITOR);
        }
        compilationUnit.save(PROGRESS_MONITOR, true);
    }

}

问题在于并非每个标签都替换为空格而不是每个标尺都放在一个新行上。以下测试突出显示了该问题。请参阅body()方法..

import static org.junit.Assert.*;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.LibraryLocation;
import org.junit.Before;
import org.junit.Test;

import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.io.Files;

import my.project.SourceFormatter;

public class SourceFormatterTest
{

    private static final String TEST_PRJ_PKG_NAME = "test.project";

    private static final NullProgressMonitor PROGRESS_MONITOR = new NullProgressMonitor();

    private IProject testProject;

    private IJavaProject testJavaProject;

    private IPackageFragment testPackage;

    private File javaFile;

    private SourceFormatter sourceFormatter;

    @Before
    public void setup() throws Exception
    {
        final IWorkspaceRoot wksRoot = ResourcesPlugin.getWorkspace().getRoot();

        testProject = wksRoot.getProject(TEST_PRJ_PKG_NAME);
        testProject.create(PROGRESS_MONITOR);
        testProject.open(PROGRESS_MONITOR);

        final IProjectDescription description = testProject.getDescription();
        description.setNatureIds(new String[] { JavaCore.NATURE_ID });
        testProject.setDescription(description, null);

        this.testJavaProject = JavaCore.create(testProject);
        this.testJavaProject.open(PROGRESS_MONITOR);

        final IFolder binFolder = testProject.getFolder("bin");
        if (!binFolder.exists())
        {
            binFolder.create(false, true, null);
        }
        this.testJavaProject.setOutputLocation(binFolder.getFullPath(), null);

        final List<IClasspathEntry> entries = new ArrayList<IClasspathEntry>();
        final IVMInstall vmInstall = JavaRuntime.getDefaultVMInstall();
        final LibraryLocation[] locations = JavaRuntime.getLibraryLocations(vmInstall);
        for (final LibraryLocation element : locations)
        {
            entries.add(JavaCore.newLibraryEntry(element.getSystemLibraryPath(), null, null));
        }
        // add libs to project class path
        this.testJavaProject.setRawClasspath(entries.toArray(new IClasspathEntry[entries.size()]), null);

        final IFolder sourceFolder = testProject.getFolder("src");
        sourceFolder.create(false, true, null);

        final IPackageFragmentRoot root = this.testJavaProject.getPackageFragmentRoot(sourceFolder);
        final IClasspathEntry[] oldEntries = this.testJavaProject.getRawClasspath();
        final IClasspathEntry[] newEntries = new IClasspathEntry[oldEntries.length + 1];
        System.arraycopy(oldEntries, 0, newEntries, 0, oldEntries.length);
        newEntries[oldEntries.length] = JavaCore.newSourceEntry(root.getPath());
        this.testJavaProject.setRawClasspath(newEntries, null);

        this.testPackage = this.testJavaProject.getPackageFragmentRoot(sourceFolder)
                .createPackageFragment(TEST_PRJ_PKG_NAME, false, null);

        File packageDirectory = this.testPackage.getResource().getRawLocation().toFile();
        final File javaFile = new File(packageDirectory, "Test.java");

        this.javaFile = new File(javaFile.getAbsolutePath());

        this.sourceFormatter = new SourceFormatter();
    }

    @Test
    public void testFormatCompilationUnitSource() throws Exception
    {
        // Prepare file
        final List<String> lines = Lists.newArrayList("package " + this.testPackage.getElementName() + ";",
                                                      "",
                                                      "import test.project.Procedure;",
                                                      "import test.project.ConfirmationStatus;",
                                                      "",
                                                      "public class Test {",
                                                      "",
                                                      "    public void body(){", //This method isn't formatted as expected
                                                      "        step(\"step1\", this::step1);",
                                                      "\u0009\u0009step(\"step2\", this::step2);",
                                                      "    }",
                                                      "",
                                                      "    public ConfirmationStatus step1(){",
                                                      "        //this is some previous content to preserve",
                                                      "        return ConfirmationStatus.CONFIRMED;",
                                                      "    }",
                                                      "",
                                                      "    public ConfirmationStatus step2()",
                                                      "    {",
                                                      "\u0009\u0009//this is some previous content to preserve",
                                                      "\u0009\u0009return ConfirmationStatus.CONFIRMED;",
                                                      "    }",
                                                      "",
                                                      "}",
                                                      "",
                                                      "");
        String sources = Joiner.on("\n").join(lines);
        ICompilationUnit compilationUnit = this.testPackage
                .createCompilationUnit("TestProcedure.java", sources, false, PROGRESS_MONITOR);
        compilationUnit.open(PROGRESS_MONITOR);

        // Run test
        this.sourceFormatter.formatCompilationUnitSource(compilationUnit);

        // Asserts

        final List<String> expectedLines = Lists.newArrayList("package " + this.testPackage.getElementName() + ";",
                                                              "",
                                                              "import test.project.Procedure;",
                                                              "import test.project.ConfirmationStatus;",
                                                              "",
                                                              "public class TestProcedure extends Procedure {",
                                                              "",
                                                              "    public void body()",
                                                              "    {",
                                                              "        step(\"step1\", this::step1);",
                                                              "        step(\"step2\", this::step2);",
                                                              "    }",
                                                              "",
                                                              "    public ConfirmationStatus step1()",
                                                              "    {",
                                                              "        //this is some previous content to preserve",
                                                              "        return ConfirmationStatus.CONFIRMED;",
                                                              "    }",
                                                              "",
                                                              "    public ConfirmationStatus step2()",
                                                              "    {",
                                                              "        //this is some previous content to preserve",
                                                              "        return ConfirmationStatus.CONFIRMED;",
                                                              "    }",
                                                              "",
                                                              "}",
                                                              "");

        final String expectedFileContents = Joiner.on("\n").join(expectedLines);
        final String actualFileContents = Files.toString(this.javaFile, Charsets.UTF_8);

        assertEquals(expectedFileContents, actualFileContents);

    }

}

我错过了什么?为什么body()方法没有格式化?我无法弄清楚我做错了什么...... 该测试应该可以作为JUnit插件测试运行。

1 个答案:

答案 0 :(得分:1)

没有JavaCore.getDefaultOptions()格式符合预期。

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.ToolFactory;
import org.eclipse.jdt.core.formatter.CodeFormatter;
import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.text.edits.TextEdit;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class CodeFormatterTest {

    private CodeFormatter codeFormatter;

    @Before
    public void setUp() {
    Map<String, String> options = new HashMap<>();
        options.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, "1.8");
        options.put(JavaCore.COMPILER_COMPLIANCE, "1.8");
        options.put(JavaCore.COMPILER_SOURCE, "1.8");
        options.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, JavaCore.SPACE);
        options.put(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_METHOD_DECLARATION,
            DefaultCodeFormatterConstants.NEXT_LINE);

        codeFormatter = ToolFactory.createCodeFormatter(options);
    }

    @Test
    public void testCanFormat() {
        final List<String> lines = Arrays.asList("package test;",
                "",
                "import test.project.Procedure;",
                "import test.project.ConfirmationStatus;",
                "",
                "public class TestProcedure extends Procedure {", // FIXED
                "",
                "    public void body(){",
                "        step(\"step1\", this::step1);",
                "\u0009\u0009step(\"step2\", this::step2);",
                "    }",
                "",
                "    public ConfirmationStatus step1(){",
                "        //this is some previous content to preserve",
                "        return ConfirmationStatus.CONFIRMED;",
                "    }",
                "",
                "    public ConfirmationStatus step2()",
                "    {",
                "\u0009\u0009//this is some previous content to preserve",
                "\u0009\u0009return ConfirmationStatus.CONFIRMED;",
                "    }",
                "",
                "}",
                "",
                "");
        String originSource = lines
                .stream()
                .collect(Collectors.joining(String.format("%n")));

        String formattedSource = format(originSource, CodeFormatter.K_COMPILATION_UNIT);

        final List<String> expectedLines = Arrays.asList("package test;",
                "",
                "import test.project.Procedure;",
                "import test.project.ConfirmationStatus;",
                "",
                "public class TestProcedure extends Procedure {",
                "",
                "    public void body()",
                "    {",
                "        step(\"step1\", this::step1);",
                "        step(\"step2\", this::step2);",
                "    }",
                "",
                "    public ConfirmationStatus step1()",
                "    {",
                "        //this is some previous content to preserve",
                "        return ConfirmationStatus.CONFIRMED;",
                "    }",
                "",
                "    public ConfirmationStatus step2()",
                "    {",
                "        //this is some previous content to preserve",
                "        return ConfirmationStatus.CONFIRMED;",
                "    }",
                "",
                "}",
                "");
        String expectedFormattedSource = expectedLines
                .stream()
                .collect(Collectors.joining(String.format("%n")));

        Assert.assertEquals(expectedFormattedSource, formattedSource);
    }

    public String format(String source, int kind) {
        IDocument doc = new Document(source);
        TextEdit edit = codeFormatter.format(kind, doc.get(), 0,
                doc.get().length(), 0, null);
        if (edit != null) {
            try {
                edit.apply(doc);
                source = doc.get();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return source;
    }

}