com.j2swift.util.JdtParser.java Source code

Java tutorial

Introduction

Here is the source code for com.j2swift.util.JdtParser.java

Source

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.j2swift.util;

import java.io.File;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.FileASTRequestor;

import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.j2swift.Options;

/**
 * Adapts JDT's ASTParser to a more convenient interface for parsing source
 * strings into CompilationUnit trees.
 *
 * @author Tom Ball, Keith Stanger
 */
public class JdtParser {

    private static final Logger logger = Logger.getLogger(JdtParser.class.getName());

    private Map<String, String> compilerOptions = initCompilerOptions();
    private List<String> classpathEntries = Lists.newArrayList();
    private List<String> sourcepathEntries = Lists.newArrayList();
    private String encoding = null;
    private boolean includeRunningVMBootclasspath = true;

    private static Map<String, String> initCompilerOptions() {
        Map<String, String> compilerOptions = Maps.newHashMap();
        String version = Options.getSourceVersion();
        compilerOptions.put(org.eclipse.jdt.core.JavaCore.COMPILER_SOURCE, version);
        compilerOptions.put(org.eclipse.jdt.core.JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, version);
        compilerOptions.put(org.eclipse.jdt.core.JavaCore.COMPILER_COMPLIANCE, version);
        return compilerOptions;
    }

    public void addClasspathEntry(String entry) {
        if (isValidPathEntry(entry)) {
            classpathEntries.add(entry);
        }
    }

    public void addClasspathEntries(Iterable<String> entries) {
        for (String entry : entries) {
            addClasspathEntry(entry);
        }
    }

    private static final Splitter PATH_SPLITTER = Splitter.on(":").omitEmptyStrings();

    public void addClasspathEntries(String entries) {
        addClasspathEntries(PATH_SPLITTER.split(entries));
    }

    public void addSourcepathEntry(String entry) {
        if (isValidPathEntry(entry)) {
            sourcepathEntries.add(entry);
        }
    }

    public void prependSourcepathEntry(String entry) {
        if (isValidPathEntry(entry)) {
            sourcepathEntries.add(0, entry);
        }
    }

    public void addSourcepathEntries(Iterable<String> entries) {
        for (String entry : entries) {
            addSourcepathEntry(entry);
        }
    }

    public void addSourcepathEntries(String entries) {
        addSourcepathEntries(PATH_SPLITTER.split(entries));
    }

    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

    public void setIncludeRunningVMBootclasspath(boolean includeVMBootclasspath) {
        includeRunningVMBootclasspath = includeVMBootclasspath;
    }

    public void setEnableDocComments(boolean enable) {
        // BodyDeclaration.getJavadoc() always returns null without this option enabled,
        // so by default no doc comments are generated.
        compilerOptions.put(org.eclipse.jdt.core.JavaCore.COMPILER_DOC_COMMENT_SUPPORT,
                enable ? "enabled" : "disabled");
    }

    public CompilationUnit parseWithoutBindings(String unitName, String source) {
        return parse(unitName, source, false);
    }

    public CompilationUnit parseWithBindings(String unitName, String source) {
        return parse(unitName, source, true);
    }

    private CompilationUnit parse(String unitName, String source, boolean resolveBindings) {
        ASTParser parser = newASTParser(resolveBindings);
        parser.setUnitName(unitName);
        parser.setSource(source.toCharArray());
        CompilationUnit unit = (CompilationUnit) parser.createAST(null);
        if (checkCompilationErrors(unitName, unit)) {
            return unit;
        } else {
            return null;
        }
    }

    /**
     * Handler to be provided when parsing multiple files. The provided
     * implementation is called with the parsed units.
     */
    public interface Handler {
        public void handleParsedUnit(String path, CompilationUnit unit);
    }

    public void parseFiles(Collection<String> paths, final Handler handler) {
        ASTParser parser = newASTParser(true);
        FileASTRequestor astRequestor = new FileASTRequestor() {
            @Override
            public void acceptAST(String sourceFilePath, CompilationUnit ast) {
                logger.fine("acceptAST: " + sourceFilePath);
                if (checkCompilationErrors(sourceFilePath, ast)) {
                    handler.handleParsedUnit(sourceFilePath, ast);
                }
            }
        };
        // JDT fails to resolve all secondary bindings unless there are the same
        // number of "binding key" strings as source files. It doesn't appear to
        // matter what the binding key strings should be (as long as they're non-
        // null), so the paths array is reused.
        String[] pathsArray = paths.toArray(new String[paths.size()]);
        parser.createASTs(pathsArray, getEncodings(pathsArray.length), pathsArray, astRequestor, null);
    }

    @SuppressWarnings("deprecation")
    private ASTParser newASTParser(boolean resolveBindings) {
        ASTParser parser;
        if (Options.isJava8Translator()) {
            parser = ASTParser.newParser(AST.JLS8);
        } else {
            parser = ASTParser.newParser(AST.JLS4); // Java 7
        }

        parser.setCompilerOptions(compilerOptions);
        parser.setResolveBindings(resolveBindings);
        parser.setEnvironment(toArray(classpathEntries), toArray(sourcepathEntries),
                getEncodings(sourcepathEntries.size()), includeRunningVMBootclasspath);
        return parser;
    }

    private String[] toArray(List<String> list) {
        return list.toArray(new String[list.size()]);
    }

    private boolean isValidPathEntry(String path) {
        // JDT requires that all path elements exist and can hold class files.
        File f = new File(path);
        return f.exists() && (f.isDirectory() || path.endsWith(".jar"));
    }

    private String[] getEncodings(int length) {
        if (encoding == null) {
            return null;
        }
        String[] encodings = new String[length];
        Arrays.fill(encodings, encoding);
        return encodings;
    }

    protected boolean checkCompilationErrors(String filename, CompilationUnit unit) {
        boolean hasErrors = false;
        for (IProblem problem : unit.getProblems()) {
            if (problem.isError()) {
                ErrorUtil.error(
                        String.format("%s:%s: %s", filename, problem.getSourceLineNumber(), problem.getMessage()));
                hasErrors = true;
            }
        }
        return !hasErrors;
    }
}