PowerUnitProcessor.java

/**
 * Powerunit - A JDK1.8 test framework
 * Copyright (C) 2014 Mathieu Boretti.
 *
 * This file is part of Powerunit
 *
 * Powerunit is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Powerunit is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Powerunit. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.powerunit.processor;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

import ch.powerunit.Categories;
import ch.powerunit.impl.validator.IgnoreProcessorValidator;
import ch.powerunit.impl.validator.ParameterProcessorValidator;
import ch.powerunit.impl.validator.ParametersProcessorValidator;
import ch.powerunit.impl.validator.RuleProcessorValidator;
import ch.powerunit.impl.validator.TestDelegateProcessorValidator;
import ch.powerunit.impl.validator.TestProcessorValidator;

@SupportedAnnotationTypes({ "ch.powerunit.Test", "ch.powerunit.Rule",
        "ch.powerunit.Parameters", "ch.powerunit.Parameter",
        "ch.powerunit.Ignore", "ch.powerunit.Categories",
        "ch.powerunit.TestDelegate" })
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class PowerUnitProcessor extends AbstractProcessor implements
        ParametersProcessorValidator, ParameterProcessorValidator,
        RuleProcessorValidator, TestProcessorValidator,
        IgnoreProcessorValidator, TestDelegateProcessorValidator {

    private boolean categoryDone = false;

    @Override
    public boolean process(Set<? extends TypeElement> annotations,
            RoundEnvironment roundEnv) {
        if (!roundEnv.processingOver()) {
            testAnnotationValidation(roundEnv);
            ruleAnnotationValidation(processingEnv, roundEnv);
            parametersValidation(processingEnv, roundEnv);
            parameterAnnotationValidation(processingEnv, roundEnv);
            ignoreAnnotationValidation(roundEnv);
            testDelegateAnnotationValidation(processingEnv, roundEnv);
            try {
                if (!categoryDone) {
                    generateCategorySuite(roundEnv);
                }
            } catch (IOException e) {
                error("Unable to generate test suite file, because of "
                        + e.getMessage());
            }
        }

        return true;
    }

    private void generateCategorySuite(RoundEnvironment roundEnv)
            throws IOException {
        categoryDone = true;
        Set<? extends Element> elements = roundEnv
                .getElementsAnnotatedWith(Categories.class);
        Map<String, List<TypeMirror>> categories = new HashMap<>();
        for (Element element : elements) {
            if (element.getKind() == ElementKind.CLASS) {
                TypeMirror current = element.asType();
                Categories ca = element.getAnnotation(Categories.class);
                if (ca != null) {
                    String cat[] = ca.value();
                    for (String c : cat) {
                        List<TypeMirror> lst = categories.get(c);
                        if (lst == null) {
                            lst = new ArrayList<>();
                        }
                        lst.add(current);
                        categories.put(c, lst);
                    }
                }
            }
        }
        JavaFileObject suite = processingEnv.getFiler()
                .createSourceFile("ch.powerunit.suite.Suites",
                        elements.toArray(new Element[] {}));
        try (PrintWriter w = new PrintWriter(suite.openWriter());) {
            w.println("package ch.powerunit.suite;");
            w.println();
            w.println("@javax.annotation.Generated(\"Test suites for powerunit\")");
            w.println("public final class Suites {");
            w.println();
            w.println();
            w.println("  private static final java.util.Map<String,Class<?>[]> suites;");
            w.println();
            w.println("  static {");
            w.println("    java.util.Map<String,Class<?>[]> tmp = new java.util.HashMap<>();");
            for (Map.Entry<String, List<TypeMirror>> cat : categories
                    .entrySet()) {
                w.println("    tmp.put(\"" + cat.getKey()
                        + "\",new Class<?>[]{");
                int counter = 0;
                for (TypeMirror tm : cat.getValue()) {
                    if (counter > 0) {
                        w.println(",");
                    }
                    w.print("      "
                            + ((DeclaredType) tm).asElement().toString()
                            + ".class");
                    counter++;
                }
                w.println();
                w.println("    });");
            }
            w.println("    suites=java.util.Collections.unmodifiableMap(tmp);");
            w.println("  }");
            w.println();
            w.println("  public static java.util.Map<String,Class<?>[]> getSuites() {");
            w.println("    return suites;");
            w.println("  }");
            w.println();
            w.println();
            w.println();
            w.println("  public static void main(String argv[]) {");
            w.println("    if (argv.length==1) {");
            w.println("       suites.keySet().stream().forEach((c)->{main(new String[]{argv[0],c});});");
            w.println("    return;");
            w.println("    }");
            w.println("    String name = argv[1];");
            w.println("    StringBuilder sb = new StringBuilder();");
            w.println("    for(Class<?> c:getSuites().get(name)) {");
            w.println("      sb.append(c.getName()).append(',');");
            w.println("    }");
            w.println("    ch.powerunit.PowerUnitMainRunner.main(new String[]{argv[0],sb.toString()});");
            w.println("  }");
            w.println();
            w.println("}");
        }
    }

    @Override
    public String elementAsString(Element ee) {
        return ee.toString() + " of " + ee.getEnclosingElement().toString();
    }

    @Override
    public void error(String msg) {
        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, msg);
    }

    @Override
    public void warn(String msg) {
        processingEnv.getMessager().printMessage(
                Diagnostic.Kind.MANDATORY_WARNING, msg);
    }

}