20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24
25 package org.graalvm.compiler.options.processor;
26
27 import java.io.BufferedReader;
28 import java.io.IOException;
29 import java.io.InputStreamReader;
30 import java.io.PrintWriter;
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38
39 import javax.annotation.processing.Filer;
40 import javax.annotation.processing.RoundEnvironment;
41 import javax.annotation.processing.SupportedAnnotationTypes;
42 import javax.lang.model.SourceVersion;
43 import javax.lang.model.element.AnnotationMirror;
44 import javax.lang.model.element.Element;
45 import javax.lang.model.element.ElementKind;
46 import javax.lang.model.element.Modifier;
47 import javax.lang.model.element.Name;
48 import javax.lang.model.element.PackageElement;
49 import javax.lang.model.element.TypeElement;
50 import javax.lang.model.element.VariableElement;
51 import javax.lang.model.type.DeclaredType;
52 import javax.lang.model.type.TypeKind;
53 import javax.lang.model.type.TypeMirror;
54 import javax.lang.model.util.Types;
55 import javax.tools.Diagnostic.Kind;
56 import javax.tools.FileObject;
57 import javax.tools.JavaFileObject;
58 import javax.tools.StandardLocation;
59
60 import org.graalvm.compiler.processor.AbstractProcessor;
61
62 /**
63 * Processes static fields annotated with {@code Option}. An {@code OptionDescriptors}
64 * implementation is generated for each top level class containing at least one such field. The name
65 * of the generated class for top level class {@code com.foo.Bar} is
66 * {@code com.foo.Bar_OptionDescriptors}.
67 */
139 DeclaredType declaredOptionKeyType = declaredFieldType;
140 while (!types.isSameType(types.erasure(declaredOptionKeyType), types.erasure(optionKeyTypeMirror))) {
141 List<? extends TypeMirror> directSupertypes = types.directSupertypes(declaredFieldType);
142 assert !directSupertypes.isEmpty();
143 declaredOptionKeyType = (DeclaredType) directSupertypes.get(0);
144 }
145
146 assert !declaredOptionKeyType.getTypeArguments().isEmpty();
147 String optionType = declaredOptionKeyType.getTypeArguments().get(0).toString();
148 if (optionType.startsWith("java.lang.")) {
149 optionType = optionType.substring("java.lang.".length());
150 }
151
152 Element enclosing = element.getEnclosingElement();
153 String declaringClass = "";
154 String separator = "";
155 Set<Element> originatingElementsList = info.originatingElements;
156 originatingElementsList.add(field);
157 PackageElement enclosingPackage = null;
158 while (enclosing != null) {
159 if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) {
160 if (enclosing.getModifiers().contains(Modifier.PRIVATE)) {
161 String msg = String.format("Option field cannot be declared in a private %s %s", enclosing.getKind().name().toLowerCase(), enclosing);
162 processingEnv.getMessager().printMessage(Kind.ERROR, msg, element);
163 return;
164 }
165 originatingElementsList.add(enclosing);
166 declaringClass = enclosing.getSimpleName() + separator + declaringClass;
167 separator = ".";
168 } else if (enclosing.getKind() == ElementKind.PACKAGE) {
169 enclosingPackage = (PackageElement) enclosing;
170 }
171 enclosing = enclosing.getEnclosingElement();
172 }
173 if (enclosingPackage == null) {
174 processingEnv.getMessager().printMessage(Kind.ERROR, "Option field cannot be declared in the unnamed package", element);
175 return;
176 }
177 List<String> helpValue = getAnnotationValueList(annotation, "help", String.class);
178 String help = "";
179 List<String> extraHelp = new ArrayList<>();
180
181 if (helpValue.size() == 1) {
182 help = helpValue.get(0);
183 if (help.startsWith("file:")) {
184 String path = help.substring("file:".length());
185 Filer filer = processingEnv.getFiler();
186 try {
187 FileObject file;
188 try {
189 file = filer.getResource(StandardLocation.SOURCE_PATH, enclosingPackage.getQualifiedName(), path);
204 }
205 } catch (IOException e) {
206 String msg = String.format("Error reading %s containing the help text for option field: %s", path, e);
207 processingEnv.getMessager().printMessage(Kind.ERROR, msg, element);
208 return;
209 }
210 }
211 } else if (helpValue.size() > 1) {
212 help = helpValue.get(0);
213 extraHelp = helpValue.subList(1, helpValue.size());
214 }
215 if (help.length() != 0) {
216 char firstChar = help.charAt(0);
217 if (!Character.isUpperCase(firstChar)) {
218 processingEnv.getMessager().printMessage(Kind.ERROR, "Option help text must start with an upper case letter", element);
219 return;
220 }
221 }
222
223 String optionTypeName = getAnnotationValue(annotation, "type", VariableElement.class).getSimpleName().toString();
224 info.options.add(new OptionInfo(optionName, optionTypeName, help, extraHelp, optionType, declaringClass, field));
225 }
226
227 private void createFiles(OptionsInfo info) {
228 String pkg = ((PackageElement) info.topDeclaringType.getEnclosingElement()).getQualifiedName().toString();
229 Name topDeclaringClass = info.topDeclaringType.getSimpleName();
230 Element[] originatingElements = info.originatingElements.toArray(new Element[info.originatingElements.size()]);
231
232 createOptionsDescriptorsFile(info, pkg, topDeclaringClass, originatingElements);
233 }
234
235 private void createOptionsDescriptorsFile(OptionsInfo info, String pkg, Name topDeclaringClass, Element[] originatingElements) {
236 String optionsClassName = topDeclaringClass + "_" + getSimpleName(OPTION_DESCRIPTORS_CLASS_NAME);
237
238 Filer filer = processingEnv.getFiler();
239 try (PrintWriter out = createSourceFile(pkg, optionsClassName, filer, originatingElements)) {
240
241 out.println("// CheckStyle: stop header check");
242 out.println("// CheckStyle: stop line length check");
243 out.println("// GENERATED CONTENT - DO NOT EDIT");
244 out.println("// Source: " + topDeclaringClass + ".java");
245 out.println("package " + pkg + ";");
246 out.println("");
247 out.println("import java.util.*;");
248 out.println("import " + getPackageName(OPTION_DESCRIPTORS_CLASS_NAME) + ".*;");
249 out.println("import " + OPTION_TYPE_CLASS_NAME + ";");
250 out.println("");
251 out.println("public class " + optionsClassName + " implements " + getSimpleName(OPTION_DESCRIPTORS_CLASS_NAME) + " {");
252
253 String desc = getSimpleName(OPTION_DESCRIPTOR_CLASS_NAME);
254
255 Collections.sort(info.options);
256
257 out.println(" @Override");
258 out.println(" public OptionDescriptor get(String value) {");
259 out.println(" switch (value) {");
260 out.println(" // CheckStyle: stop line length check");
261 for (OptionInfo option : info.options) {
262 String name = option.name;
263 String optionField;
264 if (option.field.getModifiers().contains(Modifier.PRIVATE)) {
265 throw new InternalError();
266 } else {
267 optionField = option.declaringClass + "." + option.field.getSimpleName();
268 }
269 out.println(" case \"" + name + "\": {");
270 String optionType = option.optionType;
271 String type = option.type;
272 String help = option.help;
273 List<String> extraHelp = option.extraHelp;
274 String declaringClass = option.declaringClass;
275 Name fieldName = option.field.getSimpleName();
276 out.printf(" return " + desc + ".create(\n");
277 out.printf(" /*name*/ \"%s\",\n", name);
278 out.printf(" /*optionType*/ %s.%s,\n", getSimpleName(OPTION_TYPE_CLASS_NAME), optionType);
279 out.printf(" /*optionValueType*/ %s.class,\n", type);
280 out.printf(" /*help*/ \"%s\",\n", help);
281 if (extraHelp.size() != 0) {
282 out.printf(" /*extraHelp*/ new String[] {\n");
283 for (String line : extraHelp) {
284 out.printf(" \"%s\",\n", line.replace("\\", "\\\\").replace("\"", "\\\""));
285 }
286 out.printf(" },\n");
287 }
288 out.printf(" /*declaringClass*/ %s.class,\n", declaringClass);
289 out.printf(" /*fieldName*/ \"%s\",\n", fieldName);
290 out.printf(" /*option*/ %s);\n", optionField);
291 out.println(" }");
292 }
293 out.println(" // CheckStyle: resume line length check");
294 out.println(" }");
295 out.println(" return null;");
302 out.println(" @Override");
303 out.println(" public boolean hasNext() {");
304 out.println(" return i < " + info.options.size() + ";");
305 out.println(" }");
306 out.println(" @Override");
307 out.println(" public OptionDescriptor next() {");
308 out.println(" switch (i++) {");
309 for (int i = 0; i < info.options.size(); i++) {
310 OptionInfo option = info.options.get(i);
311 out.println(" case " + i + ": return get(\"" + option.name + "\");");
312 }
313 out.println(" }");
314 out.println(" throw new NoSuchElementException();");
315 out.println(" }");
316 out.println(" };");
317 out.println(" }");
318 out.println("}");
319 }
320 }
321
322 protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) {
323 try {
324 // Ensure Unix line endings to comply with code style guide checked by Checkstyle
325 JavaFileObject sourceFile = filer.createSourceFile(pkg + "." + relativeName, originatingElements);
326 return new PrintWriter(sourceFile.openWriter()) {
327
328 @Override
329 public void println() {
330 print("\n");
331 }
332 };
333 } catch (IOException e) {
334 throw new RuntimeException(e);
335 }
336 }
337
338 static class OptionInfo implements Comparable<OptionInfo> {
339
340 final String name;
341 final String optionType;
342 final String help;
343 final List<String> extraHelp;
344 final String type;
345 final String declaringClass;
346 final VariableElement field;
347
348 OptionInfo(String name, String optionType, String help, List<String> extraHelp, String type, String declaringClass, VariableElement field) {
349 this.name = name;
350 this.optionType = optionType;
351 this.help = help;
352 this.extraHelp = extraHelp;
353 this.type = type;
354 this.declaringClass = declaringClass;
355 this.field = field;
356 }
357
358 @Override
359 public int compareTo(OptionInfo other) {
360 return name.compareTo(other.name);
361 }
362
363 @Override
364 public String toString() {
365 return declaringClass + "." + field;
366 }
367 }
368
369 static class OptionsInfo {
370
371 final Element topDeclaringType;
372 final List<OptionInfo> options = new ArrayList<>();
373 final Set<Element> originatingElements = new HashSet<>();
374
375 OptionsInfo(Element topDeclaringType) {
376 this.topDeclaringType = topDeclaringType;
377 }
378 }
379
380 private static Element topDeclaringType(Element element) {
381 Element enclosing = element.getEnclosingElement();
382 if (enclosing == null || enclosing.getKind() == ElementKind.PACKAGE) {
383 assert element.getKind() == ElementKind.CLASS || element.getKind() == ElementKind.INTERFACE;
384 return element;
385 }
386 return topDeclaringType(enclosing);
387 }
388
389 @Override
390 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
391 if (roundEnv.processingOver()) {
392 return true;
393 }
394
395 TypeElement optionTypeElement = getTypeElement(OPTION_CLASS_NAME);
396
397 optionTypeMirror = optionTypeElement.asType();
398 optionKeyTypeMirror = getTypeElement(OPTION_KEY_CLASS_NAME).asType();
399
400 Map<Element, OptionsInfo> map = new HashMap<>();
401 for (Element element : roundEnv.getElementsAnnotatedWith(optionTypeElement)) {
402 if (!processed.contains(element)) {
403 processed.add(element);
404 Element topDeclaringType = topDeclaringType(element);
405 OptionsInfo options = map.get(topDeclaringType);
406 if (options == null) {
407 options = new OptionsInfo(topDeclaringType);
408 map.put(topDeclaringType, options);
409 }
410 if (!element.getEnclosingElement().getSimpleName().toString().endsWith("Options")) {
411 processingEnv.getMessager().printMessage(Kind.ERROR, "Option declaring classes must have a name that ends with 'Options'", element.getEnclosingElement());
412 }
413 processElement(element, options);
414 }
415 }
416
417 boolean ok = true;
418 Map<String, OptionInfo> uniqueness = new HashMap<>();
419 for (OptionsInfo info : map.values()) {
420 for (OptionInfo option : info.options) {
421 OptionInfo conflict = uniqueness.put(option.name, option);
422 if (conflict != null) {
423 processingEnv.getMessager().printMessage(Kind.ERROR, "Duplicate option names for " + option + " and " + conflict, option.field);
424 ok = false;
425 }
426 }
427 }
428
429 if (ok) {
430 for (OptionsInfo info : map.values()) {
431 createFiles(info);
432 }
433 }
434
435 return true;
436 }
437 }
|
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24
25 package org.graalvm.compiler.options.processor;
26
27 import java.io.BufferedReader;
28 import java.io.IOException;
29 import java.io.InputStreamReader;
30 import java.io.PrintWriter;
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38
39 import javax.annotation.processing.Filer;
40 import javax.annotation.processing.ProcessingEnvironment;
41 import javax.annotation.processing.RoundEnvironment;
42 import javax.annotation.processing.SupportedAnnotationTypes;
43 import javax.lang.model.SourceVersion;
44 import javax.lang.model.element.AnnotationMirror;
45 import javax.lang.model.element.Element;
46 import javax.lang.model.element.ElementKind;
47 import javax.lang.model.element.Modifier;
48 import javax.lang.model.element.PackageElement;
49 import javax.lang.model.element.TypeElement;
50 import javax.lang.model.element.VariableElement;
51 import javax.lang.model.type.DeclaredType;
52 import javax.lang.model.type.TypeKind;
53 import javax.lang.model.type.TypeMirror;
54 import javax.lang.model.util.Types;
55 import javax.tools.Diagnostic.Kind;
56 import javax.tools.FileObject;
57 import javax.tools.JavaFileObject;
58 import javax.tools.StandardLocation;
59
60 import org.graalvm.compiler.processor.AbstractProcessor;
61
62 /**
63 * Processes static fields annotated with {@code Option}. An {@code OptionDescriptors}
64 * implementation is generated for each top level class containing at least one such field. The name
65 * of the generated class for top level class {@code com.foo.Bar} is
66 * {@code com.foo.Bar_OptionDescriptors}.
67 */
139 DeclaredType declaredOptionKeyType = declaredFieldType;
140 while (!types.isSameType(types.erasure(declaredOptionKeyType), types.erasure(optionKeyTypeMirror))) {
141 List<? extends TypeMirror> directSupertypes = types.directSupertypes(declaredFieldType);
142 assert !directSupertypes.isEmpty();
143 declaredOptionKeyType = (DeclaredType) directSupertypes.get(0);
144 }
145
146 assert !declaredOptionKeyType.getTypeArguments().isEmpty();
147 String optionType = declaredOptionKeyType.getTypeArguments().get(0).toString();
148 if (optionType.startsWith("java.lang.")) {
149 optionType = optionType.substring("java.lang.".length());
150 }
151
152 Element enclosing = element.getEnclosingElement();
153 String declaringClass = "";
154 String separator = "";
155 Set<Element> originatingElementsList = info.originatingElements;
156 originatingElementsList.add(field);
157 PackageElement enclosingPackage = null;
158 while (enclosing != null) {
159 if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE || enclosing.getKind() == ElementKind.ENUM) {
160 if (enclosing.getModifiers().contains(Modifier.PRIVATE)) {
161 String msg = String.format("Option field cannot be declared in a private %s %s", enclosing.getKind().name().toLowerCase(), enclosing);
162 processingEnv.getMessager().printMessage(Kind.ERROR, msg, element);
163 return;
164 }
165 originatingElementsList.add(enclosing);
166 declaringClass = enclosing.getSimpleName() + separator + declaringClass;
167 separator = ".";
168 } else if (enclosing.getKind() == ElementKind.PACKAGE) {
169 enclosingPackage = (PackageElement) enclosing;
170 break;
171 } else {
172 processingEnv.getMessager().printMessage(Kind.ERROR, "Unexpected enclosing element kind: " + enclosing.getKind(), element);
173 return;
174 }
175 enclosing = enclosing.getEnclosingElement();
176 }
177 if (enclosingPackage == null) {
178 processingEnv.getMessager().printMessage(Kind.ERROR, "Option field cannot be declared in the unnamed package", element);
179 return;
180 }
181 List<String> helpValue = getAnnotationValueList(annotation, "help", String.class);
182 String help = "";
183 List<String> extraHelp = new ArrayList<>();
184
185 if (helpValue.size() == 1) {
186 help = helpValue.get(0);
187 if (help.startsWith("file:")) {
188 String path = help.substring("file:".length());
189 Filer filer = processingEnv.getFiler();
190 try {
191 FileObject file;
192 try {
193 file = filer.getResource(StandardLocation.SOURCE_PATH, enclosingPackage.getQualifiedName(), path);
208 }
209 } catch (IOException e) {
210 String msg = String.format("Error reading %s containing the help text for option field: %s", path, e);
211 processingEnv.getMessager().printMessage(Kind.ERROR, msg, element);
212 return;
213 }
214 }
215 } else if (helpValue.size() > 1) {
216 help = helpValue.get(0);
217 extraHelp = helpValue.subList(1, helpValue.size());
218 }
219 if (help.length() != 0) {
220 char firstChar = help.charAt(0);
221 if (!Character.isUpperCase(firstChar)) {
222 processingEnv.getMessager().printMessage(Kind.ERROR, "Option help text must start with an upper case letter", element);
223 return;
224 }
225 }
226
227 String optionTypeName = getAnnotationValue(annotation, "type", VariableElement.class).getSimpleName().toString();
228 info.options.add(new OptionInfo(optionName, optionTypeName, help, extraHelp, optionType, declaringClass, field.getSimpleName().toString()));
229 }
230
231 public static void createOptionsDescriptorsFile(ProcessingEnvironment processingEnv, OptionsInfo info) {
232 Element[] originatingElements = info.originatingElements.toArray(new Element[info.originatingElements.size()]);
233 String optionsDescriptorsClassName = info.className + "_" + getSimpleName(OPTION_DESCRIPTORS_CLASS_NAME);
234
235 Filer filer = processingEnv.getFiler();
236 try (PrintWriter out = createSourceFile(info.packageName, optionsDescriptorsClassName, filer, originatingElements)) {
237
238 out.println("// CheckStyle: stop header check");
239 out.println("// CheckStyle: stop line length check");
240 out.println("// GENERATED CONTENT - DO NOT EDIT");
241 out.println("// Source: " + info.className + ".java");
242 out.println("package " + info.packageName + ";");
243 out.println("");
244 out.println("import java.util.*;");
245 out.println("import " + getPackageName(OPTION_DESCRIPTORS_CLASS_NAME) + ".*;");
246 out.println("import " + OPTION_TYPE_CLASS_NAME + ";");
247 out.println("");
248 out.println("public class " + optionsDescriptorsClassName + " implements " + getSimpleName(OPTION_DESCRIPTORS_CLASS_NAME) + " {");
249
250 String desc = getSimpleName(OPTION_DESCRIPTOR_CLASS_NAME);
251
252 Collections.sort(info.options);
253
254 out.println(" @Override");
255 out.println(" public OptionDescriptor get(String value) {");
256 out.println(" switch (value) {");
257 out.println(" // CheckStyle: stop line length check");
258 for (OptionInfo option : info.options) {
259 String name = option.name;
260 String optionField = option.declaringClass + "." + option.field;
261 out.println(" case \"" + name + "\": {");
262 String optionType = option.optionType;
263 String type = option.type;
264 String help = option.help;
265 List<String> extraHelp = option.extraHelp;
266 String declaringClass = option.declaringClass;
267 String fieldName = option.field;
268 out.printf(" return " + desc + ".create(\n");
269 out.printf(" /*name*/ \"%s\",\n", name);
270 out.printf(" /*optionType*/ %s.%s,\n", getSimpleName(OPTION_TYPE_CLASS_NAME), optionType);
271 out.printf(" /*optionValueType*/ %s.class,\n", type);
272 out.printf(" /*help*/ \"%s\",\n", help);
273 if (extraHelp.size() != 0) {
274 out.printf(" /*extraHelp*/ new String[] {\n");
275 for (String line : extraHelp) {
276 out.printf(" \"%s\",\n", line.replace("\\", "\\\\").replace("\"", "\\\""));
277 }
278 out.printf(" },\n");
279 }
280 out.printf(" /*declaringClass*/ %s.class,\n", declaringClass);
281 out.printf(" /*fieldName*/ \"%s\",\n", fieldName);
282 out.printf(" /*option*/ %s);\n", optionField);
283 out.println(" }");
284 }
285 out.println(" // CheckStyle: resume line length check");
286 out.println(" }");
287 out.println(" return null;");
294 out.println(" @Override");
295 out.println(" public boolean hasNext() {");
296 out.println(" return i < " + info.options.size() + ";");
297 out.println(" }");
298 out.println(" @Override");
299 out.println(" public OptionDescriptor next() {");
300 out.println(" switch (i++) {");
301 for (int i = 0; i < info.options.size(); i++) {
302 OptionInfo option = info.options.get(i);
303 out.println(" case " + i + ": return get(\"" + option.name + "\");");
304 }
305 out.println(" }");
306 out.println(" throw new NoSuchElementException();");
307 out.println(" }");
308 out.println(" };");
309 out.println(" }");
310 out.println("}");
311 }
312 }
313
314 public static PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) {
315 try {
316 // Ensure Unix line endings to comply with code style guide checked by Checkstyle
317 String className = pkg + "." + relativeName;
318 JavaFileObject sourceFile = filer.createSourceFile(className, originatingElements);
319 return new PrintWriter(sourceFile.openWriter()) {
320
321 @Override
322 public void println() {
323 print("\n");
324 }
325 };
326 } catch (IOException e) {
327 throw new RuntimeException(e);
328 }
329 }
330
331 public static class OptionInfo implements Comparable<OptionInfo> {
332
333 public final String name;
334 public final String optionType;
335 public final String help;
336 public final List<String> extraHelp;
337 public final String type;
338 public final String declaringClass;
339 public final String field;
340
341 public OptionInfo(String name, String optionType, String help, List<String> extraHelp, String type, String declaringClass, String field) {
342 this.name = name;
343 this.optionType = optionType;
344 this.help = help;
345 this.extraHelp = extraHelp;
346 this.type = type;
347 this.declaringClass = declaringClass;
348 this.field = field;
349 }
350
351 @Override
352 public int compareTo(OptionInfo other) {
353 return name.compareTo(other.name);
354 }
355
356 @Override
357 public String toString() {
358 return declaringClass + "." + field;
359 }
360 }
361
362 public static class OptionsInfo {
363
364 public final String packageName;
365 public final String className;
366 public final List<OptionInfo> options = new ArrayList<>();
367 public final Set<Element> originatingElements = new HashSet<>();
368
369 public OptionsInfo(String packageName, String className) {
370 this.packageName = packageName;
371 this.className = className;
372 }
373 }
374
375 private static Element topDeclaringType(Element element) {
376 Element enclosing = element.getEnclosingElement();
377 if (enclosing == null || enclosing.getKind() == ElementKind.PACKAGE) {
378 assert element.getKind() == ElementKind.CLASS || element.getKind() == ElementKind.INTERFACE;
379 return element;
380 }
381 return topDeclaringType(enclosing);
382 }
383
384 @Override
385 public boolean doProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
386 if (roundEnv.processingOver()) {
387 return true;
388 }
389
390 TypeElement optionTypeElement = getTypeElement(OPTION_CLASS_NAME);
391
392 optionTypeMirror = optionTypeElement.asType();
393 optionKeyTypeMirror = getTypeElement(OPTION_KEY_CLASS_NAME).asType();
394
395 Map<Element, OptionsInfo> map = new HashMap<>();
396 for (Element element : roundEnv.getElementsAnnotatedWith(optionTypeElement)) {
397 if (!processed.contains(element)) {
398 processed.add(element);
399 Element topDeclaringType = topDeclaringType(element);
400 OptionsInfo options = map.get(topDeclaringType);
401 if (options == null) {
402 String pkg = ((PackageElement) topDeclaringType.getEnclosingElement()).getQualifiedName().toString();
403 String topDeclaringClass = topDeclaringType.getSimpleName().toString();
404 options = new OptionsInfo(pkg, topDeclaringClass);
405 map.put(topDeclaringType, options);
406 }
407 if (!element.getEnclosingElement().getSimpleName().toString().endsWith("Options")) {
408 processingEnv.getMessager().printMessage(Kind.ERROR, "Option declaring classes must have a name that ends with 'Options'", element.getEnclosingElement());
409 }
410 processElement(element, options);
411 }
412 }
413
414 boolean ok = true;
415 Map<String, OptionInfo> uniqueness = new HashMap<>();
416 for (Map.Entry<Element, OptionsInfo> e : map.entrySet()) {
417 OptionsInfo info = e.getValue();
418 for (OptionInfo option : info.options) {
419 OptionInfo conflict = uniqueness.put(option.name, option);
420 if (conflict != null) {
421 processingEnv.getMessager().printMessage(Kind.ERROR, "Duplicate option names for " + option + " and " + conflict, e.getKey());
422 ok = false;
423 }
424 }
425 }
426
427 if (ok) {
428 for (OptionsInfo info : map.values()) {
429 createOptionsDescriptorsFile(processingEnv, info);
430 }
431 }
432
433 return true;
434 }
435 }
|