Java tutorial
/* * Copyright 2014 Google Inc. All rights reserved. * * 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.google.devtools.kythe.platform.java; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.devtools.kythe.common.FormattingLogger; import com.google.devtools.kythe.platform.java.filemanager.CompilationUnitBasedJavaFileManager; import com.google.devtools.kythe.platform.shared.FileDataProvider; import com.google.devtools.kythe.proto.Analysis.CompilationUnit; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.util.JavacTask; import com.sun.tools.javac.api.JavacTaskImpl; import java.io.Writer; import java.nio.charset.Charset; import java.util.List; import javax.annotation.processing.Processor; import javax.tools.Diagnostic; import javax.tools.Diagnostic.Kind; import javax.tools.DiagnosticCollector; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; /** Provides a {@link JavacAnalyzer} with access to compilation information. */ public class JavaCompilationDetails { private final JavacTask javac; private final DiagnosticCollector<JavaFileObject> diagnostics; private final Iterable<? extends CompilationUnitTree> asts; private final CompilationUnit compilationUnit; private final Throwable analysisCrash; private final String encoding; private static final FormattingLogger logger = FormattingLogger.getLogger(JavaCompilationDetails.class); private static final Charset DEFAULT_ENCODING = UTF_8; private static final Predicate<Diagnostic<?>> ERROR_DIAGNOSTIC = new Predicate<Diagnostic<?>>() { @Override public boolean apply(Diagnostic<?> diag) { return diag.getKind() == Kind.ERROR; } }; public static JavaCompilationDetails createDetails(CompilationUnit compilationUnit, FileDataProvider fileDataProvider, boolean useStdErr) { return createDetails(compilationUnit, fileDataProvider, false, ImmutableList.<Processor>of(), useStdErr); } public static JavaCompilationDetails createDetails(CompilationUnit compilationUnit, FileDataProvider fileDataProvider, boolean isLocalAnalysis, List<Processor> processors) { return createDetails(compilationUnit, fileDataProvider, isLocalAnalysis, processors, false); } public static JavaCompilationDetails createDetails(CompilationUnit compilationUnit, FileDataProvider fileDataProvider, boolean isLocalAnalysis, List<Processor> processors, boolean useStdErr) { JavaCompiler compiler = JavacAnalysisDriver.getCompiler(); DiagnosticCollector<JavaFileObject> diagnosticsCollector = new DiagnosticCollector<>(); // Get the compilation options List<String> options = optionsFromCompilationUnit(compilationUnit, processors, isLocalAnalysis); String encoding = JavacOptionsUtils.getEncodingOption(options); // Create a StandardFileManager that uses the fileDataProvider and compilationUnit StandardJavaFileManager fileManager = new CompilationUnitBasedJavaFileManager(fileDataProvider, compilationUnit, compiler.getStandardFileManager(diagnosticsCollector, null, null), encoding); Iterable<? extends JavaFileObject> sources = fileManager .getJavaFileObjectsFromStrings(compilationUnit.getSourceFileList()); // If we use no writer, output will go to stdErr. The NullWriter is /dev/null. Writer javacOut = useStdErr ? null : NullWriter.getInstance(); // Get a task for compiling the current CompilationUnit. JavacTaskImpl javacTask = (JavacTaskImpl) compiler.getTask(javacOut, fileManager, diagnosticsCollector, options, null, sources); if (!processors.isEmpty()) { javacTask.setProcessors(processors); } Throwable analysisCrash = null; Iterable<? extends CompilationUnitTree> compilationUnits = null; try { compilationUnits = javacTask.parse(); javacTask.analyze(); } catch (Throwable e) { logger.severefmt(e, "Unexpected error in javac analysis of {%s}", compilationUnit.getVName()); analysisCrash = e; } return new JavaCompilationDetails(javacTask, diagnosticsCollector, compilationUnits, compilationUnit, analysisCrash, encoding); } private JavaCompilationDetails(JavacTask javac, DiagnosticCollector<JavaFileObject> diagnostics, Iterable<? extends CompilationUnitTree> asts, CompilationUnit compilationUnit, Throwable analysisCrash, String encoding) { this.javac = javac; this.diagnostics = diagnostics; this.asts = asts; this.compilationUnit = compilationUnit; this.analysisCrash = analysisCrash; this.encoding = Preconditions.checkNotNull(encoding); } public boolean inBadCompilationState() { return analysisCrash != null || asts == null; } public boolean hasCompileErrors() { return Iterables.any(diagnostics.getDiagnostics(), ERROR_DIAGNOSTIC); } /** * @return Javac compiler instance initialized for current analysis target. */ public JavacTask getJavac() { return javac; } /** * @return Diagnostics reported while analyzing the code for the current analysis target. */ public List<Diagnostic<? extends JavaFileObject>> getDiagnostics() { return diagnostics.getDiagnostics(); } /** * @return DiagnosticsCollector for the current analysis target. */ DiagnosticCollector<JavaFileObject> getDiagnosticsCollector() { return diagnostics; } /** * @return AST for the current analysis target. */ public Iterable<? extends CompilationUnitTree> getAsts() { return asts; } /** * @return The protocol buffer describing the current analysis target. */ public CompilationUnit getCompilationUnit() { return compilationUnit; } /** * @return Any unexpected crash that might have occurred during javac analysis. */ public Throwable getAnalysisCrash() { return analysisCrash; } /** * @return The encoding for the source files in this compilation */ public String getEncoding() { return encoding; } /** * Modify options so the compiler can find the classpath and sourcepath. * As well as disable any annotation processor. * * @param isLocalAnalysis when true we do not add jre jars to the classpath. * Adding jre jars to classpath for local analysis done by * {@link com.google.devtools.kythe.platform.java.local.LocalJavacAnalysisDriver} * will cause the analysis to fail. */ private static List<String> optionsFromCompilationUnit(CompilationUnit compilationUnit, List<Processor> processors, boolean isLocalAnalysis) { // Start with the default options, and then add in source // Turn on all warnings as well. List<String> options = Lists.newArrayList(compilationUnit.getArgumentList()); options = JavacOptionsUtils.useAllWarnings(options); options = JavacOptionsUtils.ensureEncodingSet(options, DEFAULT_ENCODING.name()); if (!isLocalAnalysis) { JavacOptionsUtils.appendJREJarsToClasspath(options); } if (processors.isEmpty()) { options.add("-proc:none"); } return ImmutableList.copyOf(options); } /** Writes nothing, used to reduce noise from the javac analysis output. */ private static class NullWriter extends Writer { private static NullWriter instance = null; private NullWriter() { } public static NullWriter getInstance() { if (instance == null) { instance = new NullWriter(); } return instance; } @Override public void flush() { } @Override public void close() { } @Override public void write(char cbuf[], int off, int len) { } } }