diff --git a/AndroidAnnotations/androidannotations/.settings/org.eclipse.core.resources.prefs b/AndroidAnnotations/androidannotations/.settings/org.eclipse.core.resources.prefs index c584550030..d25a71a2d6 100644 --- a/AndroidAnnotations/androidannotations/.settings/org.eclipse.core.resources.prefs +++ b/AndroidAnnotations/androidannotations/.settings/org.eclipse.core.resources.prefs @@ -2,6 +2,7 @@ eclipse.preferences.version=1 encoding//src/main/java=UTF-8 encoding//src/main/java/rebel.xml=UTF-8 encoding//src/main/resources=UTF-8 +encoding//src/main/resources/androidannotations-version.properties=utf-8 encoding//src/main/resources/rebel.xml=UTF-8 encoding//src/test/java=UTF-8 encoding//src/test/resources=UTF-8 diff --git a/AndroidAnnotations/androidannotations/pom.xml b/AndroidAnnotations/androidannotations/pom.xml index 49d21ae26c..c5b2d0aa85 100644 --- a/AndroidAnnotations/androidannotations/pom.xml +++ b/AndroidAnnotations/androidannotations/pom.xml @@ -77,7 +77,16 @@ + + org.apache.maven.plugins + maven-resources-plugin + 2.5 + + ${project.build.sourceEncoding} + + + src/main/resources @@ -88,7 +97,14 @@ src/main/java - org/androidannotations/api/** + org/androidannotations/api/** + + + + src/main/resources + true + + **/*.properties diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java index 4f7de7646c..6da5f06240 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java @@ -21,11 +21,11 @@ import static org.androidannotations.helper.ModelConstants.TRACE_OPTION; import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; +import java.net.URL; import java.util.Collections; import java.util.HashSet; import java.util.Map; +import java.util.Properties; import java.util.Set; import javax.annotation.processing.AbstractProcessor; @@ -117,9 +117,11 @@ import org.androidannotations.annotations.rest.RestService; import org.androidannotations.annotations.sharedpreferences.Pref; import org.androidannotations.annotations.sharedpreferences.SharedPref; +import org.androidannotations.exception.ProcessingException; import org.androidannotations.generation.CodeModelGenerator; import org.androidannotations.helper.AndroidManifest; import org.androidannotations.helper.AndroidManifestFinder; +import org.androidannotations.helper.ErrorHelper; import org.androidannotations.helper.Option; import org.androidannotations.helper.TimeStats; import org.androidannotations.model.AndroidRes; @@ -270,7 +272,10 @@ @SupportedOptions({ TRACE_OPTION, ANDROID_MANIFEST_FILE_OPTION }) public class AndroidAnnotationProcessor extends AbstractProcessor { + private final Properties properties = new Properties(); private final TimeStats timeStats = new TimeStats(); + private final ErrorHelper errorHelper = new ErrorHelper(); + private Set supportedAnnotationNames; @Override @@ -279,6 +284,8 @@ public synchronized void init(ProcessingEnvironment processingEnv) { Messager messager = processingEnv.getMessager(); + loadPropertyFile(); + timeStats.setMessager(messager); messager.printMessage(Diagnostic.Kind.NOTE, "Starting AndroidAnnotations annotation processing"); @@ -290,16 +297,34 @@ public boolean process(Set annotations, RoundEnvironment timeStats.start("Whole Processing"); try { processThrowing(annotations, roundEnv); - } catch (Exception e) { + } catch (ProcessingException e) { handleException(annotations, roundEnv, e); + } catch (Exception e) { + handleException(annotations, roundEnv, new ProcessingException(e, null)); } timeStats.stop("Whole Processing"); timeStats.logStats(); return true; } - private void processThrowing(Set annotations, RoundEnvironment roundEnv) throws Exception { + private void loadPropertyFile() { + String filename = "androidannotations-version.properties"; + try { + URL url = getClass().getClassLoader().getResource(filename); + properties.load(url.openStream()); + } catch (Exception e) { + e.printStackTrace(); + Messager messager = processingEnv.getMessager(); + messager.printMessage(Diagnostic.Kind.NOTE, "AndroidAnnotations processing failed because " + filename + " couldn't be parsed : " + e.getLocalizedMessage()); + } + } + + private String getAAProcessorVersion() { + return properties.getProperty("version", "3.0+"); + } + + private void processThrowing(Set annotations, RoundEnvironment roundEnv) throws ProcessingException, Exception { if (nothingToDo(annotations, roundEnv)) { return; } @@ -372,7 +397,7 @@ private Option findRClasses(AndroidManifest androidManifest) throws IOE return Option.of(coumpoundRClass); } - private AnnotationElements validateAnnotations(AnnotationElementsHolder extractedModel, IRClass rClass, AndroidSystemServices androidSystemServices, AndroidManifest androidManifest) { + private AnnotationElements validateAnnotations(AnnotationElementsHolder extractedModel, IRClass rClass, AndroidSystemServices androidSystemServices, AndroidManifest androidManifest) throws ProcessingException, Exception { timeStats.start("Validate Annotations"); ModelValidator modelValidator = buildModelValidator(rClass, androidSystemServices, androidManifest); AnnotationElements validatedAnnotations = modelValidator.validate(extractedModel); @@ -468,7 +493,7 @@ private boolean traceActivated() { } } - private ProcessResult processAnnotations(AnnotationElements validatedModel, IRClass rClass, AndroidSystemServices androidSystemServices, AndroidManifest androidManifest) throws Exception { + private ProcessResult processAnnotations(AnnotationElements validatedModel, IRClass rClass, AndroidSystemServices androidSystemServices, AndroidManifest androidManifest) throws ProcessingException, Exception { timeStats.start("Process Annotations"); ModelProcessor modelProcessor = buildModelProcessor(rClass, androidSystemServices, androidManifest, validatedModel); ProcessResult processResult = modelProcessor.process(validatedModel); @@ -559,13 +584,13 @@ private void generateSources(ProcessResult processResult) throws IOException { timeStats.start("Generate Sources"); Messager messager = processingEnv.getMessager(); messager.printMessage(Diagnostic.Kind.NOTE, "Number of files generated by AndroidAnnotations: " + processResult.codeModel.countArtifacts()); - CodeModelGenerator modelGenerator = new CodeModelGenerator(processingEnv.getFiler(), messager); + CodeModelGenerator modelGenerator = new CodeModelGenerator(processingEnv.getFiler(), messager, getAAProcessorVersion()); modelGenerator.generate(processResult); timeStats.stop("Generate Sources"); } - private void handleException(Set annotations, RoundEnvironment roundEnv, Exception e) { - String errorMessage = "Unexpected error. Please report an issue on AndroidAnnotations, with the following content: " + stackTraceToString(e); + private void handleException(Set annotations, RoundEnvironment roundEnv, ProcessingException e) { + String errorMessage = errorHelper.getErrorMessage(processingEnv, e, getAAProcessorVersion()); Messager messager = processingEnv.getMessager(); messager.printMessage(Diagnostic.Kind.ERROR, errorMessage); @@ -580,13 +605,6 @@ private void handleException(Set annotations, RoundEnviro messager.printMessage(Diagnostic.Kind.ERROR, errorMessage, element); } - private String stackTraceToString(Throwable e) { - StringWriter writer = new StringWriter(); - PrintWriter pw = new PrintWriter(writer); - e.printStackTrace(pw); - return writer.toString(); - } - @Override public Set getSupportedAnnotationTypes() { if (supportedAnnotationNames == null) { diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/exception/ProcessingException.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/exception/ProcessingException.java new file mode 100644 index 0000000000..fde33fd53a --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/exception/ProcessingException.java @@ -0,0 +1,25 @@ +package org.androidannotations.exception; + +import javax.lang.model.element.Element; + +public class ProcessingException extends Exception { + + private static final long serialVersionUID = -1282996599471872615L; + + private Element element; + + public ProcessingException(Throwable cause, Element element) { + super(cause); + this.element = element; + } + + public ProcessingException(String message, Throwable cause, Element element) { + super(message, cause); + this.element = element; + } + + public Element getElement() { + return element; + } + +} diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/generation/CodeModelGenerator.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/generation/CodeModelGenerator.java index d96ffb7ffd..50495ef3fa 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/generation/CodeModelGenerator.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/generation/CodeModelGenerator.java @@ -28,10 +28,12 @@ public class CodeModelGenerator { private final Filer filer; private final Messager messager; + private final String aaVersion; - public CodeModelGenerator(Filer filer, Messager messager) { + public CodeModelGenerator(Filer filer, Messager messager, String aaVersion) { this.filer = filer; this.messager = messager; + this.aaVersion = aaVersion; } public void generate(ProcessResult processResult) throws IOException { @@ -41,7 +43,7 @@ public void generate(ProcessResult processResult) throws IOException { SourceCodewriter sourceCodeWriter = new SourceCodewriter(filer, messager, processResult.originatingElements); - PrologCodeWriter prologCodeWriter = new PrologCodeWriter(sourceCodeWriter, "DO NOT EDIT THIS FILE, IT HAS BEEN GENERATED USING AndroidAnnotations.\n"); + PrologCodeWriter prologCodeWriter = new PrologCodeWriter(sourceCodeWriter, "DO NOT EDIT THIS FILE, IT HAS BEEN GENERATED USING AndroidAnnotations " + aaVersion + ".\n"); processResult.codeModel.build(prologCodeWriter, new ResourceCodeWriter(filer)); } diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ErrorHelper.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ErrorHelper.java new file mode 100644 index 0000000000..d55c3bb87f --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/ErrorHelper.java @@ -0,0 +1,101 @@ +package org.androidannotations.helper; + +import java.io.BufferedReader; +import java.io.CharArrayWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.Element; +import javax.lang.model.util.Elements; + +import org.androidannotations.exception.ProcessingException; + +public class ErrorHelper { + + public String getErrorMessage(ProcessingEnvironment processingEnv, ProcessingException e, String aaVersion) { + String errorMessage = "Unexpected error in AndroidAnnotations " + aaVersion + "!\n" // + + "You should check if there is already an issue about it on https://github.com/excilys/androidannotations/search?q=" + urlEncodedErrorMessage(e) + "&type=Issues\n" // + + "If none exists, please open a new one with the following content and tell us if you can reproduce it or not. Don't forget to give us as much information as you can (like parts of your code in failure).\n"; + errorMessage += "Java version: " + getJavaCompilerVersion() + "\n"; + errorMessage += "Javac processors options: " + annotationProcessorOptions(processingEnv) + "\n"; + errorMessage += "Stacktrace: " + stackTraceToString(e.getCause()); + + Element element = e.getElement(); + if (element != null) { + errorMessage += "Thrown from: " + elementContainer(element) + "\n"; + errorMessage += "Element (" + element.getClass().getSimpleName() + "): " + elementFullString(processingEnv, element) + "\n"; + } + + return errorMessage; + } + + private String elementFullString(ProcessingEnvironment processingEnv, Element element) { + Elements elementUtils = processingEnv.getElementUtils(); + CharArrayWriter writer = new CharArrayWriter(); + elementUtils.printElements(writer, element); + return writer.toString(); + } + + private String elementContainer(Element element) { + Element enclosingElement = element.getEnclosingElement(); + return enclosingElement != null ? enclosingElement.toString() : ""; + } + + private String annotationProcessorOptions(ProcessingEnvironment processingEnv) { + Map options = processingEnv.getOptions(); + Set> optionsEntries = options.entrySet(); + + String result = ""; + for (Entry optionEntry : optionsEntries) { + result += optionEntry.getKey() + "=" + optionEntry.getValue() + ", "; + } + return result.length() > 2 ? result.substring(0, result.length() - 2) : result; + } + + private String getJavaCompilerVersion() { + ProcessBuilder pb = new ProcessBuilder("javac", "-version"); + pb.redirectErrorStream(true); + + BufferedReader in = null; + try { + Process process = pb.start(); + in = new BufferedReader(new InputStreamReader(process.getInputStream())); + String buffer = in.readLine(); + process.waitFor(); + return buffer; + } catch (Exception e) { + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + } + return "unknown"; + } + + private String urlEncodedErrorMessage(Throwable e) { + try { + return URLEncoder.encode(e.getCause().getClass().getName(), "UTF-8"); + } catch (UnsupportedEncodingException e1) { + return ""; + } + } + + private String stackTraceToString(Throwable e) { + StringWriter writer = new StringWriter(); + PrintWriter pw = new PrintWriter(writer); + e.printStackTrace(pw); + return writer.toString(); + } + +} diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ModelProcessor.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ModelProcessor.java index 7e70c22d0f..9aad843c6a 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ModelProcessor.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/processing/ModelProcessor.java @@ -24,6 +24,7 @@ import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; +import org.androidannotations.exception.ProcessingException; import org.androidannotations.model.AnnotationElements; import org.androidannotations.model.AnnotationElements.AnnotatedAndRootElements; @@ -59,7 +60,7 @@ public void register(GeneratingElementProcessor processor) { typeProcessors.add(processor); } - public ProcessResult process(AnnotationElements validatedModel) throws Exception { + public ProcessResult process(AnnotationElements validatedModel) throws ProcessingException, Exception { JCodeModel codeModel = new JCodeModel(); @@ -75,7 +76,7 @@ public ProcessResult process(AnnotationElements validatedModel) throws Exception * extend them). */ if (!isAbstractClass(annotatedElement)) { - processor.process(annotatedElement, codeModel, eBeansHolder); + processThrowing(processor, annotatedElement, codeModel, eBeansHolder); } } /* @@ -99,7 +100,7 @@ public ProcessResult process(AnnotationElements validatedModel) throws Exception * elements that are not validated, and therefore not available. */ if (holder != null) { - processor.process(elements.annotatedElement, codeModel, holder); + processThrowing(processor, elements.annotatedElement, codeModel, holder); } } @@ -120,7 +121,7 @@ public ProcessResult process(AnnotationElements validatedModel) throws Exception */ if (!isAbstractClass(enclosingElement)) { EBeanHolder holder = eBeansHolder.getEBeanHolder(enclosingElement); - processor.process(annotatedElement, codeModel, holder); + processThrowing(processor, annotatedElement, codeModel, holder); } } @@ -132,6 +133,22 @@ public ProcessResult process(AnnotationElements validatedModel) throws Exception eBeansHolder.getApiClassesToGenerate()); } + private void processThrowing(GeneratingElementProcessor processor, Element element, JCodeModel codeModel, EBeansHolder eBeansHolder) throws Exception, ProcessingException { + try { + processor.process(element, codeModel, eBeansHolder); + } catch (Exception e) { + throw new ProcessingException(e, element); + } + } + + private void processThrowing(DecoratingElementProcessor processor, Element element, JCodeModel codeModel, EBeanHolder eBeanHolder) throws Exception, ProcessingException { + try { + processor.process(element, codeModel, eBeanHolder); + } catch (Exception e) { + throw new ProcessingException(e, element); + } + } + private boolean isAbstractClass(Element annotatedElement) { if (annotatedElement instanceof TypeElement) { TypeElement typeElement = (TypeElement) annotatedElement; diff --git a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/ModelValidator.java b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/ModelValidator.java index a414b7d5cd..295aa08c82 100644 --- a/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/ModelValidator.java +++ b/AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/validation/ModelValidator.java @@ -22,6 +22,7 @@ import javax.lang.model.element.Element; +import org.androidannotations.exception.ProcessingException; import org.androidannotations.model.AnnotationElements; import org.androidannotations.model.AnnotationElementsHolder; @@ -33,7 +34,7 @@ public void register(ElementValidator validator) { validators.add(validator); } - public AnnotationElements validate(AnnotationElementsHolder extractedModel) { + public AnnotationElements validate(AnnotationElementsHolder extractedModel) throws ProcessingException, Exception { /* * We currently do not validate the elements on the ancestors, assuming @@ -52,7 +53,7 @@ public AnnotationElements validate(AnnotationElementsHolder extractedModel) { validatedElements.putRootAnnotatedElements(annotationName, validatedAnnotatedElements); for (Element annotatedElement : annotatedElements) { - if (validator.validate(annotatedElement, validatedElements)) { + if (validateThrowing(validator, annotatedElement, validatedElements)) { validatedAnnotatedElements.add(annotatedElement); } } @@ -60,4 +61,12 @@ public AnnotationElements validate(AnnotationElementsHolder extractedModel) { return validatedElements; } + private boolean validateThrowing(ElementValidator validator, Element element, AnnotationElements validatedElements) throws Exception, ProcessingException { + try { + return validator.validate(element, validatedElements); + } catch (Exception e) { + throw new ProcessingException(e, element); + } + } + } diff --git a/AndroidAnnotations/androidannotations/src/main/resources/androidannotations-version.properties b/AndroidAnnotations/androidannotations/src/main/resources/androidannotations-version.properties new file mode 100644 index 0000000000..e5683df88c --- /dev/null +++ b/AndroidAnnotations/androidannotations/src/main/resources/androidannotations-version.properties @@ -0,0 +1 @@ +version=${project.version} \ No newline at end of file