Package juzu.impl.compiler

Source Code of juzu.impl.compiler.Compiler$Builder

/*
* Copyright 2013 eXo Platform SAS
*
* 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 juzu.impl.compiler;

import juzu.impl.common.Tools;
import juzu.impl.common.FileKey;
import juzu.impl.compiler.file.JavaFileObjectImpl;
import juzu.impl.compiler.file.SimpleFileManager;
import juzu.impl.fs.Filter;
import juzu.impl.fs.Visitor;
import juzu.impl.fs.spi.ReadFileSystem;
import juzu.impl.fs.spi.ReadWriteFileSystem;
import juzu.impl.fs.spi.ram.RAMFileSystem;
import juzu.impl.common.Location;
import juzu.impl.common.Spliterator;

import javax.annotation.processing.Processor;
import javax.inject.Provider;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticListener;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

/** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */
public class Compiler {

  public static Builder builder() {
    return new Builder(null, null, null, new ArrayList<ReadFileSystem<?>>());
  }

  public static class Builder {

    /** . */
    private ReadFileSystem<?> sourcePath;

    /** . */
    private ReadWriteFileSystem<?> classOutput;

    /** . */
    private ReadWriteFileSystem<?> sourceOutput;

    /** . */
    private List<ReadFileSystem<?>> classPaths;

    /** . */
    private Provider<? extends Processor> processor;

    /** . */
    private Provider<? extends JavaCompiler> javaCompiler;

    /** . */
    private CompilerConfig config;

    private Builder(
      ReadFileSystem<?> sourcePath,
      ReadWriteFileSystem<?> sourceOutput,
      ReadWriteFileSystem<?> classOutput,
      List<ReadFileSystem<?>> classPaths) {
      Provider<JavaCompiler> javaCompiler = new Provider<JavaCompiler>() {
        public JavaCompiler get() {
          return ToolProvider.getSystemJavaCompiler();
        }
      };
      this.processor = null;
      this.javaCompiler = javaCompiler;
      this.sourcePath = sourcePath;
      this.sourceOutput = sourceOutput;
      this.classOutput = classOutput;
      this.classPaths = classPaths;
      this.config = new CompilerConfig();
    }

    public Builder javaCompiler(Provider<? extends JavaCompiler> javaCompiler) {
      this.javaCompiler = javaCompiler;
      return this;
    }

    public Builder processor(Provider<? extends Processor> processor) {
      this.processor = processor;
      return this;
    }

    public Builder classOutput(ReadWriteFileSystem<?> classOutput) {
      this.classOutput = classOutput;
      return this;
    }

    public Builder output(ReadWriteFileSystem<?> output) {
      this.classOutput = this.sourceOutput = output;
      return this;
    }

    public Builder sourcePath(ReadFileSystem<?> sourcePath) {
      this.sourcePath = sourcePath;
      return this;
    }

    public Builder sourceOutput(ReadWriteFileSystem<?> sourceOutput) {
      this.sourceOutput = sourceOutput;
      return this;
    }

    public Builder addClassPath(ReadFileSystem<?> classPath) {
      classPaths.add(classPath);
      return this;
    }

    public Builder addClassPath(Iterable<ReadFileSystem<?>> classPaths) {
      for (ReadFileSystem<?> classPath : classPaths) {
        addClassPath(classPath);
      }
      return this;
    }

    public Builder config(CompilerConfig config) {
      this.config = config;
      return this;
    }

    public Compiler build() {
      if (sourcePath == null) {
        throw new IllegalStateException("No null source path");
      }
      if (classOutput == null) {
        throw new IllegalStateException("No null class output");
      }
      if (sourceOutput == null) {
        throw new IllegalStateException("No null source output");
      }
      Compiler compiler = new Compiler(
        javaCompiler.get(),
        sourcePath,
        classPaths,
        sourceOutput,
        classOutput,
        config
      );
      if (processor != null) {
        compiler.addAnnotationProcessor(processor.get());
      }
      return compiler;
    }
  }

  /** . */
  private JavaCompiler javaCompiler;

  /** . */
  private Set<Processor> processors;

  /** . */
  private ReadFileSystem<?> sourcePath;

  /** . */
  private ReadWriteFileSystem<?> classOutput;

  /** . */
  private ReadWriteFileSystem<?> sourceOutput;

  /** . */
  private Collection<ReadFileSystem<?>> classPaths;

  /** . */
  private CompilerConfig config;

  public Compiler(
    ReadFileSystem<?> sourcePath,
    ReadWriteFileSystem<?> output,
    CompilerConfig config) {
    this(sourcePath, output, output, config);
  }

  public Compiler(
    ReadFileSystem<?> sourcePath,
    ReadWriteFileSystem<?> sourceOutput,
    ReadWriteFileSystem<?> classOutput,
    CompilerConfig config) {
    this(sourcePath, Collections.<ReadFileSystem<?>>emptyList(), sourceOutput, classOutput, config);
  }

  public Compiler(
    ReadFileSystem<?> sourcePath,
    ReadFileSystem<?> classPath,
    ReadWriteFileSystem<?> sourceOutput,
    ReadWriteFileSystem<?> classOutput,
    CompilerConfig config) {
    this(sourcePath, Collections.<ReadFileSystem<?>>singletonList(classPath), sourceOutput, classOutput, config);
  }

  public Compiler(
      ReadFileSystem<?> sourcePath,
      Collection<ReadFileSystem<?>> classPaths,
      ReadWriteFileSystem<?> sourceOutput,
      ReadWriteFileSystem<?> classOutput,
      CompilerConfig config) {
    this(ToolProvider.getSystemJavaCompiler(), sourcePath, classPaths, sourceOutput, classOutput, config);
  }

  public Compiler(
    JavaCompiler javaCompiler,
    ReadFileSystem<?> sourcePath,
    Collection<ReadFileSystem<?>> classPaths,
    ReadWriteFileSystem<?> sourceOutput,
    ReadWriteFileSystem<?> classOutput,
    CompilerConfig config) {
    this.sourcePath = sourcePath;
    this.classPaths = classPaths;
    this.sourceOutput = sourceOutput;
    this.classOutput = classOutput;
    this.javaCompiler = javaCompiler;
    this.processors = new HashSet<Processor>();
    this.config = config;
  }

  public void addAnnotationProcessor(Processor annotationProcessorType) {
    if (annotationProcessorType == null) {
      throw new NullPointerException("No null processor allowed");
    }
    processors.add(annotationProcessorType);
  }

  public void compile(String... compilationUnits) throws IOException, CompilationException {
    // Copy anything that is not a java file
    RAMFileSystem sourcePath1 = new RAMFileSystem();
    sourcePath.copy(new Filter() {
      public boolean acceptDir(Object dir, String name) throws IOException {
        return true;
      }

      public boolean acceptFile(Object file, String name) throws IOException {
        return !name.endsWith(".java");
      }
    }, sourcePath1);

    //
    VirtualFileManager fileManager = new VirtualFileManager(
      javaCompiler.getStandardFileManager(null, null, null),
      sourcePath1,
      classPaths,
      sourceOutput,
      classOutput
    );

    //
    Collection<JavaFileObject> files = getFromSourcePath(sourcePath, compilationUnits);

    //
    compile(fileManager, files);
  }

  private <P> Collection<JavaFileObject> getFromSourcePath(ReadFileSystem<P> fs, String... compilationUnits) throws IOException {
    SimpleFileManager<P> manager = new SimpleFileManager<P>(StandardLocation.SOURCE_PATH, fs);
    ArrayList<String> tmp = new ArrayList<String>();
    final ArrayList<JavaFileObject> javaFiles = new ArrayList<JavaFileObject>();
    for (String compilationUnit : compilationUnits) {
      tmp.clear();
      ArrayList<String> names = Spliterator.split(compilationUnit.substring(1), '/', tmp);
      String name = tmp.get(tmp.size() - 1);
      if (!name.endsWith(".java")) {
        throw new IllegalArgumentException("Illegal compilation unit: " + compilationUnit);
      }
      P file = manager.getFileSystem().getPath(names);
      if (file == null) {
        throw new IllegalArgumentException("Could not find compilation unit: " + compilationUnit);
      }
      names.remove(names.size() - 1);
      String pkg = Tools.join('.', names);
      FileKey key = FileKey.newJavaName(pkg, name);
      javaFiles.add(manager.getReadable(key));
    }
    return javaFiles;
  }

  public void compile() throws IOException, CompilationException {
    VirtualFileManager fileManager = new VirtualFileManager(
      javaCompiler.getStandardFileManager(null, null, null),
      sourcePath,
      classPaths,
      sourceOutput,
      classOutput
    );
    compile(fileManager, getFromSourcePath(fileManager.sourcePath));
  }

  private <P> Collection<JavaFileObject> getFromSourcePath(final SimpleFileManager<P> fileManager) throws IOException {
    final ArrayList<JavaFileObject> javaFiles = new ArrayList<JavaFileObject>();
    final StringBuilder buffer = new StringBuilder();
    fileManager.getFileSystem().traverse(new Visitor.Default<P>() {
      @Override
      public void enterDir(Object dir, String name) throws IOException {
        if (name.length() > 0) {
          buffer.append(name).append('.');
        }
      }

      public void file(P file, String name) throws IOException {
        if (name.endsWith(".java")) {
          FileKey key = FileKey.newJavaName(buffer.substring(0, buffer.length() - 1), name);
          JavaFileObject fileObject = fileManager.getReadable(key);
          javaFiles.add(fileObject);
        }
      }

      @Override
      public void leaveDir(Object dir, String name) throws IOException {
        if (name.length() > 0) {
          buffer.setLength(buffer.length() - name.length() - 1);
        }
      }
    });
    return javaFiles;
  }

  private void compile(
      VirtualFileManager fileManager,
      Collection<JavaFileObject> compilationUnits) throws IOException, CompilationException {
    if (compilationUnits.isEmpty()) {
      if (!config.getForce()) {
        return;
      }
      else {
        URI uri = URI.create("/Dumb.java");
        compilationUnits = Collections.<JavaFileObject>singleton(new SimpleJavaFileObject(uri, JavaFileObject.Kind.SOURCE) {
          @Override
          public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
            return "public class Dumb {}";
          }
        });
      }
    }

    //
    final List<CompilationError> errors = new ArrayList<CompilationError>();
    DiagnosticListener<JavaFileObject> listener = new DiagnosticListener<JavaFileObject>() {
      public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
        if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
          int columnNumber = (int)diagnostic.getColumnNumber();
          int lineNumber = (int)diagnostic.getLineNumber();
          Location location = (columnNumber > 0 && lineNumber > 0) ? new Location(columnNumber, lineNumber) : null;

          // We pass the default locale instead of null as ECJ has issues with null
          String s = diagnostic.getMessage(Locale.getDefault());

          //
          Message message = Message.parse(s);

          // Best effort to get a java.io.File
          JavaFileObject obj = diagnostic.getSource();
          String source = null;
          File resolvedFile = null;
          if (obj != null) {
            URI uri = obj.toUri();
            source = uri.getPath();
            if (obj instanceof JavaFileObjectImpl) {
              JavaFileObjectImpl foo = (JavaFileObjectImpl)obj;
              try {
                resolvedFile = foo.getFile();
              }
              catch (Exception ignore) {
              }
            } else {
              // We are likely in eclipse (with its JavaFileObject wrapper) and we should get the file from the URI
              if (uri.getScheme().equals("file")) {
                resolvedFile = new File(uri);
              }
            }
          }

          //
          errors.add(new CompilationError(
              message != null ? message.getCode() : null,
              message != null ? Arrays.asList(message.getArguments()): Collections.<String>emptyList(),
              source, resolvedFile, location, s));
        }
      }
    };

    //
    List<String> options = new ArrayList<String>();
    options.add("-g");
    for (String optionName : config.getProcessorOptionNames()) {
      options.add("-A" + optionName + "=" + config.getProcessorOptionValue(optionName));
    }

    //
    JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileManager, listener, options, null, compilationUnits);
    task.setProcessors(processors);

    // We don't use the return value because sometime it says it is failed although
    // it is not, need to investigate this at some piont
    boolean ok = task.call();

    // Clear caches
    fileManager.sourceOutput.clearCache();
    fileManager.classOutput.clearCache();
    fileManager.sourceOutput.clearCache();
    if (fileManager.classPath != null) {
      fileManager.classPath.clearCache();
    }

    // Clear processors as we should not reuse them
    processors.clear();

    //
    if (!ok) {
      throw new CompilationException(errors);
    }
  }
}
TOP

Related Classes of juzu.impl.compiler.Compiler$Builder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.