All Downloads are FREE. Search and download functionalities are using the official Maven repository.

br.com.objectos.way.code.AbstractAnnotationProcessor Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014 Objectos, Fábrica de Software LTDA.
 *
 * 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 br.com.objectos.way.code;

import static com.google.common.collect.Lists.newArrayList;

import java.io.File;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.security.CodeSource;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.ElementFilter;
import javax.tools.Diagnostic;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;

import br.com.objectos.way.core.io.Directory;
import br.com.objectos.way.core.util.IterableAction;
import br.com.objectos.way.core.util.WayIterables;

import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;

/**
 * @author [email protected] (Marcio Endo)
 */
public abstract class AbstractAnnotationProcessor extends AbstractProcessor {

  private Directory classOutput;
  private Directory sourcePath;
  private String compilerJarPath;

  @Override
  public Set getSupportedAnnotationTypes() {
    Class type = annotationType();
    return ImmutableSet.of(type.getName());
  }

  @Override
  public SourceVersion getSupportedSourceVersion() {
    return SourceVersion.latestSupported();
  }

  @Override
  public synchronized void init(ProcessingEnvironment processingEnv) {
    super.init(processingEnv);

    Map optionMap = processingEnv.getOptions();
    String classOutput = optionMap.get("classOutput");
    if (classOutput != null) {
      this.classOutput = Directory.at(classOutput);
    }

    String sourcePath = optionMap.get("sourcePath");
    if (sourcePath != null) {
      this.sourcePath = Directory.at(sourcePath);
    }

    CodeSource codeSource = getClass().getProtectionDomain().getCodeSource();
    URL jar = codeSource.getLocation();
    compilerJarPath = jar.getPath();
  }

  @Override
  public boolean process(Set typeElements, RoundEnvironment roundEnv) {
    try {
      processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Starting");

      Set elementSet;
      elementSet = roundEnv.getElementsAnnotatedWith(annotationType());

      if (shouldProcessMethods()) {
        WayIterables.from(ElementFilter.methodsIn(elementSet))
            .transformAndConcat(new ExecutableElementToArtifactList())
            .execute(new CodeCanvasArtifactAction());
      }

      if (shouldProcessTypes()) {
        WayIterables.from(ElementFilter.typesIn(elementSet))
            .transformAndConcat(new TypeElementToArtifactList())
            .execute(new CodeCanvasArtifactAction());
      }

      return processReturnOnSuccess(typeElements, roundEnv);

    } catch (Throwable e) {
      String stackTrace = Throwables.getStackTraceAsString(e);
      String msg = Joiner.on("\n").join(e, stackTrace);
      processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, name() + " processor threw an exception: " + msg);
      return processReturnOnError(typeElements, roundEnv, e);

    }
  }

  protected abstract Class annotationType();

  protected abstract boolean shouldProcessMethods();

  protected abstract boolean shouldProcessTypes();

  protected List toArtifactList(TypeInfo typeInfo) {
    return ImmutableList.of();
  }

  protected List toArtifactList(TypeInfo typeInfo, MethodInfo methodInfo) {
    return toArtifactList(typeInfo);
  }

  protected void beforeWrite(CodeCanvasArtifact artifact) {
  }

  protected Object compileAndInstantiate(SimpleTypeInfo typeInfo)
      throws
      ClassNotFoundException {

    List options = newArrayList();

    File sourceFile = typeInfo.fileAt(sourcePath);
    options.add(sourceFile.getPath());
    options.add("-d");
    options.add(classOutput.getAbsolutePath());
    options.add("-classpath");
    options.add(compilerJarPath);
    options.add("-proc:none");

    JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
    javac.run(null, System.out, System.err, options.toArray(new String[] {}));

    return typeInfo.newInstance();

  }

  protected void info(String msg) {
    processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, msg);
  }

  protected String name() {
    return getClass().getSimpleName();
  }

  protected boolean processReturnOnError(Set typeElements, RoundEnvironment roundEnv, Throwable e) {
    return false;
  }

  protected boolean processReturnOnSuccess(Set typeElements, RoundEnvironment roundEnv) {
    return false;
  }

  private void reportError(String msg, Element e) {
    processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg, e);
  }

  private class ExecutableElementToArtifactList extends ElementToArtifactList {
    @Override
    List applyTo(ExecutableElement element) {
      List res = ImmutableList.of();

      Element enclosingElement = element.getEnclosingElement();

      if (Apt.isType(enclosingElement)) {
        TypeInfo typeInfo = TypeInfoTypeElement.wrap(processingEnvironmentWrapper, (TypeElement) enclosingElement);
        TypeParameterInfoMap typeParameterInfoMap = typeInfo.typeParameterInfoMap();

        MethodInfo methodInfo;
        methodInfo = MethodInfoExecutableElement.wrap(processingEnvironmentWrapper, element, typeParameterInfoMap);

        res = toArtifactList(typeInfo, methodInfo);
      }

      return res;
    }
  }

  private class TypeElementToArtifactList extends ElementToArtifactList {
    @Override
    List applyTo(TypeElement element) {
      TypeInfo typeInfo = TypeInfoTypeElement.wrap(processingEnvironmentWrapper, element);
      return toArtifactList(typeInfo);
    }
  }

  private abstract class ElementToArtifactList implements Function> {

    final ProcessingEnvironmentWrapper processingEnvironmentWrapper = ProcessingEnvironmentWrapper
        .wrapperOf(processingEnv);

    @Override
    public final List apply(E element) {
      try {
        return applyTo(element);
      } catch (Throwable e) {
        String stackTrace = Throwables.getStackTraceAsString(e);
        String msg = Joiner.on("\n").join(e, stackTrace);
        reportError(name() + " processor threw an exception: " + msg, element);
        return ImmutableList.of();
      }
    }

    abstract List applyTo(E element);

  }

  protected class CodeCanvasArtifactAction implements IterableAction {

    public CodeCanvasArtifactAction() {
    }

    @Override
    public void execute(Iterable list) {
      for (CodeCanvasArtifact artifact : list) {
        beforeWrite(artifact);
        artifact.write(processingEnv);
      }
    }

  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy