/* * Copyright 2015 Red Hat, Inc. and/or its affiliates. * * 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 org.uberfire.annotations.processors; import java.io.BufferedWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic.Kind; import javax.tools.JavaFileObject; /** * Contains a series of adaptations and workarounds to make annotation processors work well under Eclipse JDT APT. Does * not limit compatibility with other annotation processing environments (such as javac). */ public abstract class AbstractErrorAbsorbingProcessor extends AbstractProcessor { private Throwable rememberedInitError; protected AbstractErrorAbsorbingProcessor() { try { freemarker.log.Logger.selectLoggerLibrary(freemarker.log.Logger.LIBRARY_NONE); } catch (ClassNotFoundException e) { rememberedInitError = e; } } private static AnnotationMirror findAnnotationMirror(Element target, TypeElement annotationType) { final Name annotationTypeName = annotationType.getQualifiedName(); for (AnnotationMirror am : target.getAnnotationMirrors()) { if (GeneratorUtils.getQualifiedName(am).contentEquals(annotationTypeName)) { return am; } } return null; } /** * Wraps the given processing environment with one that protects against known bugs in the Eclipse annotation * processing implementation. */ @Override public synchronized void init(ProcessingEnvironment env) { super.init(new EclipseWorkaroundProcessingEnvironment(env)); } @Override public final boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { try { if (rememberedInitError != null) { throw rememberedInitError; } return processWithExceptions(annotations, roundEnv); } catch (Throwable e) { // eclipse JDT goes into an infinite loop when the annotation processor throws any exception // so we have to catch EVERYTHING, even Errors. StringWriter stringWriter = new StringWriter(); e.printStackTrace(new PrintWriter(stringWriter)); final String errorMessage = "Internal error in " + getClass().getName() + stringWriter.toString(); boolean emittedSpecificError = false; for (TypeElement annotation : annotations) { for (Element annotationTarget : roundEnv.getElementsAnnotatedWith(annotation)) { processingEnv.getMessager().printMessage( Kind.ERROR, errorMessage, annotationTarget, findAnnotationMirror(annotationTarget, annotation)); emittedSpecificError = true; } } // if the above loop caught nothing, the type we were called for didn't contain an annotation // we handle (maybe it was inherited). In this case, we'll just emit a non-location-specific error // so there is at least some sort of diagnostic message for the user to go on! if (!emittedSpecificError) { processingEnv.getMessager().printMessage( Kind.ERROR, errorMessage); } return false; } } /** * Subclasses must call this from their constructors if something throws an * exception during initialization of the instance. Once this method has * been called with a non-null throwable, the * {@link #processWithExceptions(Set, RoundEnvironment)} method will not be * called on this instance. * @param t the exception that occurred (and was caught) during instance * creation of this annotation processor instance. */ protected void rememberInitializationError(Throwable t) { rememberedInitError = t; } /** * Same contract as {@link #process(Set, RoundEnvironment)}, except that any * exceptions thrown are caught and printed as messages of type * {@link Kind#ERROR}. This is done to keep Eclipse JDT from going into an * infinite processing loop. */ protected abstract boolean processWithExceptions( Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) throws Exception; /** * Writes the given code to javac's Filer. */ protected final void writeCode(final String packageName, final String className, final StringBuffer code) throws IOException { JavaFileObject jfo = processingEnv.getFiler().createSourceFile(packageName + "." + className); Writer w = jfo.openWriter(); BufferedWriter bw = new BufferedWriter(w); bw.append(code); bw.close(); w.close(); } }