< prev index next >

src/jdk.vm.ci/share/classes/jdk.vm.ci.options.processor/src/jdk/vm/ci/options/processor/OptionProcessor.java

Print this page




   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package jdk.vm.ci.options.processor;
  24 
  25 import java.io.*;
  26 import java.util.*;
  27 
  28 import javax.annotation.processing.*;
  29 import javax.lang.model.*;
  30 import javax.lang.model.element.*;
  31 import javax.lang.model.type.*;
  32 import javax.lang.model.util.*;



















  33 import javax.tools.Diagnostic.Kind;

  34 
  35 import jdk.vm.ci.options.*;
  36 
  37 import javax.tools.*;

  38 
  39 /**
  40  * Processes static fields annotated with {@link Option}. An {@link OptionDescriptors}
  41  * implementation is generated for each top level class containing at least one such field. The name
  42  * of the generated class for top level class {@code com.foo.Bar} is
  43  * {@code com.foo.Bar_OptionDescriptors}.
  44  */
  45 @SupportedAnnotationTypes({"jdk.vm.ci.options.Option"})
  46 public class OptionProcessor extends AbstractProcessor {
  47 
  48     @Override
  49     public SourceVersion getSupportedSourceVersion() {
  50         return SourceVersion.latest();
  51     }
  52 
  53     private final Set<Element> processed = new HashSet<>();
  54 
  55     private void processElement(Element element, OptionsInfo info) {
  56 
  57         if (!element.getModifiers().contains(Modifier.STATIC)) {


  88             return;
  89         }
  90 
  91         String help = annotation.help();
  92         if (help.length() != 0) {
  93             char firstChar = help.charAt(0);
  94             if (!Character.isUpperCase(firstChar)) {
  95                 processingEnv.getMessager().printMessage(Kind.ERROR, "Option help text must start with upper case letter", element);
  96                 return;
  97             }
  98         }
  99 
 100         String optionName = annotation.name();
 101         if (optionName.equals("")) {
 102             optionName = fieldName;
 103         }
 104 
 105         DeclaredType declaredOptionValueType = declaredFieldType;
 106         while (!types.isSameType(types.erasure(declaredOptionValueType), types.erasure(optionValueType))) {
 107             List<? extends TypeMirror> directSupertypes = types.directSupertypes(declaredFieldType);
 108             assert!directSupertypes.isEmpty();
 109             declaredOptionValueType = (DeclaredType) directSupertypes.get(0);
 110         }
 111 
 112         assert!declaredOptionValueType.getTypeArguments().isEmpty();
 113         String optionType = declaredOptionValueType.getTypeArguments().get(0).toString();
 114         if (optionType.startsWith("java.lang.")) {
 115             optionType = optionType.substring("java.lang.".length());
 116         }
 117 
 118         Element enclosing = element.getEnclosingElement();
 119         String declaringClass = "";
 120         String separator = "";
 121         Set<Element> originatingElementsList = info.originatingElements;
 122         originatingElementsList.add(field);
 123         while (enclosing != null) {
 124             if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) {
 125                 if (enclosing.getModifiers().contains(Modifier.PRIVATE)) {
 126                     String msg = String.format("Option field cannot be declared in a private %s %s", enclosing.getKind().name().toLowerCase(), enclosing);
 127                     processingEnv.getMessager().printMessage(Kind.ERROR, msg, element);
 128                     return;
 129                 }
 130                 originatingElementsList.add(enclosing);
 131                 declaringClass = enclosing.getSimpleName() + separator + declaringClass;
 132                 separator = ".";


 177                 out.println("        if (value.equals(\"" + info.options.get(0).name + "\")) {");
 178             } else {
 179                 out.println("        switch (value) {");
 180             }
 181             for (OptionInfo option : info.options) {
 182                 String name = option.name;
 183                 String optionValue;
 184                 if (option.field.getModifiers().contains(Modifier.PRIVATE)) {
 185                     needPrivateFieldAccessor = true;
 186                     optionValue = "field(" + option.declaringClass + ".class, \"" + option.field.getSimpleName() + "\")";
 187                 } else {
 188                     optionValue = option.declaringClass + "." + option.field.getSimpleName();
 189                 }
 190                 String type = option.type;
 191                 String help = option.help;
 192                 String declaringClass = option.declaringClass;
 193                 Name fieldName = option.field.getSimpleName();
 194                 if (info.options.size() == 1) {
 195                     out.printf("            return %s.create(\"%s\", %s.class, \"%s\", %s.class, \"%s\", %s);\n", desc, name, type, help, declaringClass, fieldName, optionValue);
 196                 } else {
 197                     out.printf("            case \"" + name + "\": return %s.create(\"%s\", %s.class, \"%s\", %s.class, \"%s\", %s);\n", desc, name, type, help, declaringClass, fieldName,
 198                                     optionValue);
 199                 }
 200             }
 201             out.println("        }");
 202             out.println("        // CheckStyle: resume line length check");
 203             out.println("        return null;");
 204             out.println("    }");
 205             out.println();
 206             out.println("    @Override");
 207             out.println("    public Iterator<" + desc + "> iterator() {");
 208             out.println("        // CheckStyle: stop line length check");
 209             out.println("        List<" + desc + "> options = Arrays.asList(");
 210             for (OptionInfo option : info.options) {
 211                 String optionValue;
 212                 if (option.field.getModifiers().contains(Modifier.PRIVATE)) {
 213                     needPrivateFieldAccessor = true;
 214                     optionValue = "field(" + option.declaringClass + ".class, \"" + option.field.getSimpleName() + "\")";
 215                 } else {
 216                     optionValue = option.declaringClass + "." + option.field.getSimpleName();
 217                 }
 218                 String name = option.name;


 224                 out.printf("            %s.create(\"%s\", %s.class, \"%s\", %s.class, \"%s\", %s)%s\n", desc, name, type, help, declaringClass, fieldName, optionValue, comma);
 225                 i++;
 226             }
 227             out.println("        );");
 228             out.println("        // CheckStyle: resume line length check");
 229             out.println("        return options.iterator();");
 230             out.println("    }");
 231             if (needPrivateFieldAccessor) {
 232                 out.println("    private static " + OptionValue.class.getSimpleName() + "<?> field(Class<?> declaringClass, String fieldName) {");
 233                 out.println("        try {");
 234                 out.println("            java.lang.reflect.Field field = declaringClass.getDeclaredField(fieldName);");
 235                 out.println("            field.setAccessible(true);");
 236                 out.println("            return (" + OptionValue.class.getSimpleName() + "<?>) field.get(null);");
 237                 out.println("        } catch (Exception e) {");
 238                 out.println("            throw (InternalError) new InternalError().initCause(e);");
 239                 out.println("        }");
 240                 out.println("    }");
 241             }
 242             out.println("}");
 243         }
 244 
 245         try {
 246             createOptionsFile(pkg, topDeclaringClass.toString(), originatingElements);
 247         } catch (IOException e) {
 248             processingEnv.getMessager().printMessage(Kind.ERROR, e.getMessage(), info.topDeclaringType);
 249         }
 250     }
 251 
 252     private void createOptionsFile(String pkg, String relativeName, Element... originatingElements) throws IOException {
 253         String filename = "META-INF/jvmci.options/" + pkg + "." + relativeName;
 254         FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", filename, originatingElements);
 255         PrintWriter writer = new PrintWriter(new OutputStreamWriter(file.openOutputStream(), "UTF-8"));
 256         writer.close();
 257     }
 258 
 259     protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) {
 260         try {
 261             // Ensure Unix line endings to comply with code style guide checked by Checkstyle
 262             JavaFileObject sourceFile = filer.createSourceFile(pkg + "." + relativeName, originatingElements);
 263             return new PrintWriter(sourceFile.openWriter()) {
 264 
 265                 @Override
 266                 public void println() {
 267                     print("\n");
 268                 }
 269             };
 270         } catch (IOException e) {
 271             throw new RuntimeException(e);
 272         }
 273     }
 274 
 275     static class OptionInfo implements Comparable<OptionInfo> {
 276 




   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package jdk.vm.ci.options.processor;
  24 
  25 import java.io.IOException;
  26 import java.io.PrintWriter;
  27 import java.util.ArrayList;
  28 import java.util.Collections;
  29 import java.util.HashMap;
  30 import java.util.HashSet;
  31 import java.util.List;
  32 import java.util.Map;
  33 import java.util.Set;
  34 
  35 import javax.annotation.processing.AbstractProcessor;
  36 import javax.annotation.processing.Filer;
  37 import javax.annotation.processing.RoundEnvironment;
  38 import javax.annotation.processing.SupportedAnnotationTypes;
  39 import javax.lang.model.SourceVersion;
  40 import javax.lang.model.element.Element;
  41 import javax.lang.model.element.ElementKind;
  42 import javax.lang.model.element.Modifier;
  43 import javax.lang.model.element.Name;
  44 import javax.lang.model.element.PackageElement;
  45 import javax.lang.model.element.TypeElement;
  46 import javax.lang.model.element.VariableElement;
  47 import javax.lang.model.type.DeclaredType;
  48 import javax.lang.model.type.TypeKind;
  49 import javax.lang.model.type.TypeMirror;
  50 import javax.lang.model.util.Elements;
  51 import javax.lang.model.util.Types;
  52 import javax.tools.Diagnostic.Kind;
  53 import javax.tools.JavaFileObject;
  54 
  55 import jdk.vm.ci.options.Option;
  56 import jdk.vm.ci.options.OptionDescriptor;
  57 import jdk.vm.ci.options.OptionDescriptors;
  58 import jdk.vm.ci.options.OptionValue;
  59 
  60 /**
  61  * Processes static fields annotated with {@link Option}. An {@link OptionDescriptors}
  62  * implementation is generated for each top level class containing at least one such field. The name
  63  * of the generated class for top level class {@code com.foo.Bar} is
  64  * {@code com.foo.Bar_OptionDescriptors}.
  65  */
  66 @SupportedAnnotationTypes({"jdk.vm.ci.options.Option"})
  67 public class OptionProcessor extends AbstractProcessor {
  68 
  69     @Override
  70     public SourceVersion getSupportedSourceVersion() {
  71         return SourceVersion.latest();
  72     }
  73 
  74     private final Set<Element> processed = new HashSet<>();
  75 
  76     private void processElement(Element element, OptionsInfo info) {
  77 
  78         if (!element.getModifiers().contains(Modifier.STATIC)) {


 109             return;
 110         }
 111 
 112         String help = annotation.help();
 113         if (help.length() != 0) {
 114             char firstChar = help.charAt(0);
 115             if (!Character.isUpperCase(firstChar)) {
 116                 processingEnv.getMessager().printMessage(Kind.ERROR, "Option help text must start with upper case letter", element);
 117                 return;
 118             }
 119         }
 120 
 121         String optionName = annotation.name();
 122         if (optionName.equals("")) {
 123             optionName = fieldName;
 124         }
 125 
 126         DeclaredType declaredOptionValueType = declaredFieldType;
 127         while (!types.isSameType(types.erasure(declaredOptionValueType), types.erasure(optionValueType))) {
 128             List<? extends TypeMirror> directSupertypes = types.directSupertypes(declaredFieldType);
 129             assert !directSupertypes.isEmpty();
 130             declaredOptionValueType = (DeclaredType) directSupertypes.get(0);
 131         }
 132 
 133         assert !declaredOptionValueType.getTypeArguments().isEmpty();
 134         String optionType = declaredOptionValueType.getTypeArguments().get(0).toString();
 135         if (optionType.startsWith("java.lang.")) {
 136             optionType = optionType.substring("java.lang.".length());
 137         }
 138 
 139         Element enclosing = element.getEnclosingElement();
 140         String declaringClass = "";
 141         String separator = "";
 142         Set<Element> originatingElementsList = info.originatingElements;
 143         originatingElementsList.add(field);
 144         while (enclosing != null) {
 145             if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) {
 146                 if (enclosing.getModifiers().contains(Modifier.PRIVATE)) {
 147                     String msg = String.format("Option field cannot be declared in a private %s %s", enclosing.getKind().name().toLowerCase(), enclosing);
 148                     processingEnv.getMessager().printMessage(Kind.ERROR, msg, element);
 149                     return;
 150                 }
 151                 originatingElementsList.add(enclosing);
 152                 declaringClass = enclosing.getSimpleName() + separator + declaringClass;
 153                 separator = ".";


 198                 out.println("        if (value.equals(\"" + info.options.get(0).name + "\")) {");
 199             } else {
 200                 out.println("        switch (value) {");
 201             }
 202             for (OptionInfo option : info.options) {
 203                 String name = option.name;
 204                 String optionValue;
 205                 if (option.field.getModifiers().contains(Modifier.PRIVATE)) {
 206                     needPrivateFieldAccessor = true;
 207                     optionValue = "field(" + option.declaringClass + ".class, \"" + option.field.getSimpleName() + "\")";
 208                 } else {
 209                     optionValue = option.declaringClass + "." + option.field.getSimpleName();
 210                 }
 211                 String type = option.type;
 212                 String help = option.help;
 213                 String declaringClass = option.declaringClass;
 214                 Name fieldName = option.field.getSimpleName();
 215                 if (info.options.size() == 1) {
 216                     out.printf("            return %s.create(\"%s\", %s.class, \"%s\", %s.class, \"%s\", %s);\n", desc, name, type, help, declaringClass, fieldName, optionValue);
 217                 } else {
 218                     out.printf("            case \"" + name + "\": return %s.create(\"%s\", %s.class, \"%s\", %s.class, \"%s\", %s);\n", desc, name, type, help, declaringClass, fieldName, optionValue);

 219                 }
 220             }
 221             out.println("        }");
 222             out.println("        // CheckStyle: resume line length check");
 223             out.println("        return null;");
 224             out.println("    }");
 225             out.println();
 226             out.println("    @Override");
 227             out.println("    public Iterator<" + desc + "> iterator() {");
 228             out.println("        // CheckStyle: stop line length check");
 229             out.println("        List<" + desc + "> options = Arrays.asList(");
 230             for (OptionInfo option : info.options) {
 231                 String optionValue;
 232                 if (option.field.getModifiers().contains(Modifier.PRIVATE)) {
 233                     needPrivateFieldAccessor = true;
 234                     optionValue = "field(" + option.declaringClass + ".class, \"" + option.field.getSimpleName() + "\")";
 235                 } else {
 236                     optionValue = option.declaringClass + "." + option.field.getSimpleName();
 237                 }
 238                 String name = option.name;


 244                 out.printf("            %s.create(\"%s\", %s.class, \"%s\", %s.class, \"%s\", %s)%s\n", desc, name, type, help, declaringClass, fieldName, optionValue, comma);
 245                 i++;
 246             }
 247             out.println("        );");
 248             out.println("        // CheckStyle: resume line length check");
 249             out.println("        return options.iterator();");
 250             out.println("    }");
 251             if (needPrivateFieldAccessor) {
 252                 out.println("    private static " + OptionValue.class.getSimpleName() + "<?> field(Class<?> declaringClass, String fieldName) {");
 253                 out.println("        try {");
 254                 out.println("            java.lang.reflect.Field field = declaringClass.getDeclaredField(fieldName);");
 255                 out.println("            field.setAccessible(true);");
 256                 out.println("            return (" + OptionValue.class.getSimpleName() + "<?>) field.get(null);");
 257                 out.println("        } catch (Exception e) {");
 258                 out.println("            throw (InternalError) new InternalError().initCause(e);");
 259                 out.println("        }");
 260                 out.println("    }");
 261             }
 262             out.println("}");
 263         }













 264     }
 265 
 266     protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) {
 267         try {
 268             // Ensure Unix line endings to comply with code style guide checked by Checkstyle
 269             JavaFileObject sourceFile = filer.createSourceFile(pkg + "." + relativeName, originatingElements);
 270             return new PrintWriter(sourceFile.openWriter()) {
 271 
 272                 @Override
 273                 public void println() {
 274                     print("\n");
 275                 }
 276             };
 277         } catch (IOException e) {
 278             throw new RuntimeException(e);
 279         }
 280     }
 281 
 282     static class OptionInfo implements Comparable<OptionInfo> {
 283 


< prev index next >