Skip to content

Commit a4803f2

Browse files
committed
Merge pull request androidannotations#417 from excilys/327_handle_manifest_errors
handle manifest errors. Fixes androidannotations#327
2 parents ce9fb5e + 1fe8511 commit a4803f2

14 files changed

Lines changed: 278 additions & 78 deletions

File tree

‎AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/AndroidAnnotationProcessor.java‎

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
import org.androidannotations.generation.CodeModelGenerator;
111111
import org.androidannotations.helper.AndroidManifest;
112112
import org.androidannotations.helper.AndroidManifestFinder;
113+
import org.androidannotations.helper.Option;
113114
import org.androidannotations.helper.TimeStats;
114115
import org.androidannotations.model.AndroidRes;
115116
import org.androidannotations.model.AndroidSystemServices;
@@ -355,19 +356,29 @@ private void processThrowing(Set<? extends TypeElement> annotations, RoundEnviro
355356

356357
AnnotationElementsHolder extractedModel = extractAnnotations(annotations, roundEnv);
357358

358-
AndroidManifest androidManifest = extractAndroidManifest();
359+
Option<AndroidManifest> androidManifestOption = extractAndroidManifest();
359360

360-
IRClass rClass = findRClasses(androidManifest);
361+
if (androidManifestOption.isAbsent()) {
362+
return;
363+
}
364+
365+
AndroidManifest androidManifest = androidManifestOption.get();
366+
367+
Option<IRClass> rClassOption = findRClasses(androidManifest);
368+
369+
if (rClassOption.isAbsent()) {
370+
return;
371+
}
372+
373+
IRClass rClass = rClassOption.get();
361374

362375
AndroidSystemServices androidSystemServices = new AndroidSystemServices();
363376

364377
AnnotationElements validatedModel = validateAnnotations(extractedModel, rClass, androidSystemServices, androidManifest);
365378

366-
if (validatedModel != null) {
367-
ProcessResult processResult = processAnnotations(validatedModel, rClass, androidSystemServices, androidManifest);
379+
ProcessResult processResult = processAnnotations(validatedModel, rClass, androidSystemServices, androidManifest);
368380

369-
generateSources(processResult);
370-
}
381+
generateSources(processResult);
371382
}
372383

373384
private boolean nothingToDo(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
@@ -382,39 +393,39 @@ private AnnotationElementsHolder extractAnnotations(Set<? extends TypeElement> a
382393
return extractedModel;
383394
}
384395

385-
private AndroidManifest extractAndroidManifest() {
396+
private Option<AndroidManifest> extractAndroidManifest() {
386397
timeStats.start("Extract Manifest");
387398
AndroidManifestFinder finder = new AndroidManifestFinder(processingEnv);
388-
AndroidManifest manifest = finder.extractAndroidManifest();
399+
Option<AndroidManifest> manifest = finder.extractAndroidManifest();
389400
timeStats.stop("Extract Manifest");
390401
return manifest;
391402
}
392403

393-
private IRClass findRClasses(AndroidManifest androidManifest) throws IOException {
404+
private Option<IRClass> findRClasses(AndroidManifest androidManifest) throws IOException {
394405
timeStats.start("Find R Classes");
395406
ProjectRClassFinder rClassFinder = new ProjectRClassFinder(processingEnv);
396-
IRClass rClass = rClassFinder.find(androidManifest);
407+
408+
Option<IRClass> rClass = rClassFinder.find(androidManifest);
397409

398410
AndroidRClassFinder androidRClassFinder = new AndroidRClassFinder(processingEnv);
399411

400-
IRClass androidRClass = androidRClassFinder.find();
412+
Option<IRClass> androidRClass = androidRClassFinder.find();
401413

402-
CoumpoundRClass coumpoundRClass = new CoumpoundRClass(rClass, androidRClass);
414+
if (rClass.isAbsent() || androidRClass.isAbsent()) {
415+
return Option.absent();
416+
}
417+
418+
IRClass coumpoundRClass = new CoumpoundRClass(rClass.get(), androidRClass.get());
403419

404420
timeStats.stop("Find R Classes");
405421

406-
return coumpoundRClass;
422+
return Option.of(coumpoundRClass);
407423
}
408424

409425
private AnnotationElements validateAnnotations(AnnotationElementsHolder extractedModel, IRClass rClass, AndroidSystemServices androidSystemServices, AndroidManifest androidManifest) {
410426
timeStats.start("Validate Annotations");
411-
AnnotationElements validatedAnnotations;
412-
if (rClass != null) {
413-
ModelValidator modelValidator = buildModelValidator(rClass, androidSystemServices, androidManifest);
414-
validatedAnnotations = modelValidator.validate(extractedModel);
415-
} else {
416-
validatedAnnotations = null;
417-
}
427+
ModelValidator modelValidator = buildModelValidator(rClass, androidSystemServices, androidManifest);
428+
AnnotationElements validatedAnnotations = modelValidator.validate(extractedModel);
418429
timeStats.stop("Validate Annotations");
419430
return validatedAnnotations;
420431
}

‎AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/helper/AndroidManifestFinder.java‎

Lines changed: 55 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,14 @@ public AndroidManifestFinder(ProcessingEnvironment processingEnv) {
5050
this.processingEnv = processingEnv;
5151
}
5252

53-
public AndroidManifest extractAndroidManifest() {
54-
try {
55-
return extractAndroidManifestThrowing();
56-
} catch (Exception e) {
57-
if (e instanceof RuntimeException) {
58-
throw (RuntimeException) e;
59-
}
60-
throw new RuntimeException(e);
53+
public Option<AndroidManifest> extractAndroidManifest() {
54+
Option<File> androidManifestFileOption = findManifestFile();
55+
56+
if (androidManifestFileOption.isAbsent()) {
57+
return Option.absent();
6158
}
62-
}
6359

64-
private AndroidManifest extractAndroidManifestThrowing() throws Exception {
65-
File androidManifestFile = findManifestFileThrowing();
60+
File androidManifestFile = androidManifestFileOption.get();
6661

6762
String projectDirectory = androidManifestFile.getParent();
6863

@@ -71,39 +66,41 @@ private AndroidManifest extractAndroidManifestThrowing() throws Exception {
7166
boolean libraryProject = false;
7267
if (projectProperties.exists()) {
7368
Properties properties = new Properties();
74-
properties.load(new FileInputStream(projectProperties));
75-
76-
if (properties.containsKey("android.library")) {
77-
String androidLibraryProperty = properties.getProperty("android.library");
78-
libraryProject = androidLibraryProperty.equals("true");
79-
80-
Messager messager = processingEnv.getMessager();
81-
messager.printMessage(Kind.NOTE, "Found android.library property in project.properties, value: " + libraryProject);
69+
try {
70+
properties.load(new FileInputStream(projectProperties));
71+
if (properties.containsKey("android.library")) {
72+
String androidLibraryProperty = properties.getProperty("android.library");
73+
libraryProject = androidLibraryProperty.equals("true");
74+
75+
Messager messager = processingEnv.getMessager();
76+
messager.printMessage(Kind.NOTE, "Found android.library property in project.properties, value: " + libraryProject);
77+
}
78+
} catch (IOException ignored) {
8279
}
83-
8480
}
8581

86-
return parseThrowing(androidManifestFile, libraryProject);
82+
return parse(androidManifestFile, libraryProject);
8783
}
8884

89-
private File findManifestFileThrowing() throws Exception {
85+
private Option<File> findManifestFile() {
9086
if (processingEnv.getOptions().containsKey(ANDROID_MANIFEST_FILE_OPTION)) {
9187
return findManifestInSpecifiedPath();
9288
} else {
9389
return findManifestInParentsDirectories();
9490
}
9591
}
9692

97-
private File findManifestInSpecifiedPath() {
93+
private Option<File> findManifestInSpecifiedPath() {
9894
String path = processingEnv.getOptions().get(ANDROID_MANIFEST_FILE_OPTION);
9995
File androidManifestFile = new File(path);
10096
Messager messager = processingEnv.getMessager();
10197
if (!androidManifestFile.exists()) {
102-
throw new IllegalStateException("Could not find the AndroidManifest.xml file in specified path : " + path);
98+
messager.printMessage(Kind.ERROR, "Could not find the AndroidManifest.xml file in specified path : " + path);
99+
return Option.absent();
103100
} else {
104101
messager.printMessage(Kind.NOTE, "AndroidManifest.xml file found: " + androidManifestFile.toString());
105102
}
106-
return androidManifestFile;
103+
return Option.of(androidManifestFile);
107104
}
108105

109106
/**
@@ -113,10 +110,18 @@ private File findManifestInSpecifiedPath() {
113110
* find the AndroidManifest.xml file. Any better solution will be
114111
* appreciated.
115112
*/
116-
private File findManifestInParentsDirectories() throws IOException, URISyntaxException {
113+
private Option<File> findManifestInParentsDirectories() {
117114
Filer filer = processingEnv.getFiler();
118115

119-
JavaFileObject dummySourceFile = filer.createSourceFile("dummy" + System.currentTimeMillis());
116+
Messager messager = processingEnv.getMessager();
117+
118+
JavaFileObject dummySourceFile;
119+
try {
120+
dummySourceFile = filer.createSourceFile("dummy" + System.currentTimeMillis());
121+
} catch (IOException ignored) {
122+
messager.printMessage(Kind.ERROR, "Could not find the AndroidManifest.xml file, unable to create a dummy source file to locate the source folder");
123+
return Option.absent();
124+
}
120125
String dummySourceFilePath = dummySourceFile.toUri().toString();
121126

122127
if (dummySourceFilePath.startsWith("file:")) {
@@ -127,10 +132,13 @@ private File findManifestInParentsDirectories() throws IOException, URISyntaxExc
127132
dummySourceFilePath = "file://" + dummySourceFilePath;
128133
}
129134

130-
Messager messager = processingEnv.getMessager();
131-
messager.printMessage(Kind.NOTE, "Dummy source file: " + dummySourceFilePath);
132-
133-
URI cleanURI = new URI(dummySourceFilePath);
135+
URI cleanURI;
136+
try {
137+
cleanURI = new URI(dummySourceFilePath);
138+
} catch (URISyntaxException e) {
139+
messager.printMessage(Kind.ERROR, "Could not find the AndroidManifest.xml file, path to dummy source file cannot be parsed: " + dummySourceFilePath);
140+
return Option.absent();
141+
}
134142

135143
File dummyFile = new File(cleanURI);
136144

@@ -153,27 +161,36 @@ private File findManifestInParentsDirectories() throws IOException, URISyntaxExc
153161
}
154162

155163
if (!androidManifestFile.exists()) {
156-
throw new IllegalStateException("Could not find the AndroidManifest.xml file, going up from path [" + sourcesGenerationFolder.getAbsolutePath() + "] found using dummy file [" + dummySourceFilePath + "] (max atempts: " + MAX_PARENTS_FROM_SOURCE_FOLDER + ")");
164+
messager.printMessage(Kind.ERROR, "Could not find the AndroidManifest.xml file, going up from path [" + sourcesGenerationFolder.getAbsolutePath() + "] found using dummy file [" + dummySourceFilePath + "] (max atempts: " + MAX_PARENTS_FROM_SOURCE_FOLDER + ")");
165+
return Option.absent();
157166
} else {
158167
messager.printMessage(Kind.NOTE, "AndroidManifest.xml file found: " + androidManifestFile.toString());
159168
}
160169

161-
return androidManifestFile;
170+
return Option.of(androidManifestFile);
162171
}
163172

164-
private AndroidManifest parseThrowing(File androidManifestFile, boolean libraryProject) throws Exception {
173+
private Option<AndroidManifest> parse(File androidManifestFile, boolean libraryProject) {
165174

166175
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
167-
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
168-
Document doc = docBuilder.parse(androidManifestFile);
176+
177+
Document doc;
178+
try {
179+
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
180+
doc = docBuilder.parse(androidManifestFile);
181+
} catch (Exception e) {
182+
Messager messager = processingEnv.getMessager();
183+
messager.printMessage(Kind.ERROR, "Could not parse the AndroidManifest.xml file at path " + androidManifestFile);
184+
return Option.absent();
185+
}
169186

170187
Element documentElement = doc.getDocumentElement();
171188
documentElement.normalize();
172189

173190
String applicationPackage = documentElement.getAttribute("package");
174191

175192
if (libraryProject) {
176-
return AndroidManifest.createLibraryManifest(applicationPackage);
193+
return Option.of(AndroidManifest.createLibraryManifest(applicationPackage));
177194
}
178195

179196
NodeList applicationNodes = documentElement.getElementsByTagName("application");
@@ -214,7 +231,7 @@ private AndroidManifest parseThrowing(File androidManifestFile, boolean libraryP
214231
componentQualifiedNames.addAll(receiverQualifiedNames);
215232
componentQualifiedNames.addAll(providerQualifiedNames);
216233

217-
return AndroidManifest.createManifest(applicationPackage, applicationQualifiedName, componentQualifiedNames);
234+
return Option.of(AndroidManifest.createManifest(applicationPackage, applicationQualifiedName, componentQualifiedNames));
218235
}
219236

220237
private List<String> extractComponentNames(String applicationPackage, NodeList componentNodes) {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package org.androidannotations.helper;
2+
3+
/**
4+
* I'd love to use Guava's Optional, but we're trying to keep the dependency
5+
* level to a minimum.
6+
*/
7+
public class Option<T> {
8+
9+
private static final Option<?> ABSENT = new Option<Object>(null, false);
10+
11+
public static <T> Option<T> of(T value) {
12+
return new Option<T>(value, true);
13+
}
14+
15+
@SuppressWarnings("unchecked")
16+
public static <T> Option<T> absent() {
17+
return (Option<T>) ABSENT;
18+
}
19+
20+
private final T reference;
21+
22+
private final boolean isPresent;
23+
24+
private Option(T reference, boolean isPresent) {
25+
this.reference = reference;
26+
this.isPresent = isPresent;
27+
}
28+
29+
public boolean isPresent() {
30+
return isPresent;
31+
}
32+
33+
public boolean isAbsent() {
34+
return !isPresent;
35+
}
36+
37+
public T get() {
38+
if (!isPresent) {
39+
throw new IllegalStateException("value is absent");
40+
}
41+
return reference;
42+
}
43+
44+
public T getOr(T defaultValue) {
45+
if (isPresent) {
46+
return reference;
47+
} else {
48+
return defaultValue;
49+
}
50+
}
51+
52+
}

‎AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/rclass/AndroidRClassFinder.java‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@
1515
*/
1616
package org.androidannotations.rclass;
1717

18+
import javax.annotation.processing.Messager;
1819
import javax.annotation.processing.ProcessingEnvironment;
1920
import javax.lang.model.element.TypeElement;
21+
import javax.tools.Diagnostic.Kind;
22+
23+
import org.androidannotations.helper.Option;
2024

2125
public class AndroidRClassFinder {
2226

@@ -26,9 +30,13 @@ public AndroidRClassFinder(ProcessingEnvironment processingEnv) {
2630
this.processingEnv = processingEnv;
2731
}
2832

29-
public IRClass find() {
33+
public Option<IRClass> find() {
3034
TypeElement androidRType = processingEnv.getElementUtils().getTypeElement("android.R");
31-
return new RClass(androidRType);
35+
if (androidRType == null) {
36+
Messager messager = processingEnv.getMessager();
37+
messager.printMessage(Kind.ERROR, "The android.R class cannot be found");
38+
return Option.absent();
39+
}
40+
return Option.<IRClass> of(new RClass(androidRType));
3241
}
33-
3442
}

‎AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/rclass/IRClass.java‎

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,4 @@ public String rName() {
2828

2929
IRInnerClass get(Res res);
3030

31-
final IRClass EMPTY_R_CLASS = new IRClass() {
32-
@Override
33-
public IRInnerClass get(Res res) {
34-
return IRInnerClass.EMPTY_R_INNER_CLASS;
35-
}
36-
};
37-
3831
}

‎AndroidAnnotations/androidannotations/src/main/java/org/androidannotations/rclass/ProjectRClassFinder.java‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import javax.tools.Diagnostic.Kind;
2323

2424
import org.androidannotations.helper.AndroidManifest;
25+
import org.androidannotations.helper.Option;
2526

2627
public class ProjectRClassFinder {
2728

@@ -31,19 +32,18 @@ public ProjectRClassFinder(ProcessingEnvironment processingEnv) {
3132
this.processingEnv = processingEnv;
3233
}
3334

34-
public IRClass find(AndroidManifest manifest) {
35+
public Option<IRClass> find(AndroidManifest manifest) {
3536

3637
Elements elementUtils = processingEnv.getElementUtils();
3738
String rClass = manifest.getApplicationPackage() + ".R";
3839
TypeElement rType = elementUtils.getTypeElement(rClass);
3940

4041
if (rType == null) {
4142
Messager messager = processingEnv.getMessager();
42-
messager.printMessage(Kind.WARNING, "The AndroidManifest.xml file was found, but not the compiled R class: " + rClass);
43-
return IRClass.EMPTY_R_CLASS;
43+
messager.printMessage(Kind.ERROR, "The generated " + rClass + " class cannot be found");
44+
return Option.absent();
4445
}
4546

46-
return new RClass(rType);
47+
return Option.<IRClass> of(new RClass(rType));
4748
}
48-
4949
}

0 commit comments

Comments
 (0)