src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.processor/src/org/graalvm/compiler/processor/AbstractProcessor.java
changeset 50330 2cbc42a5764b
child 50609 bf414874c28f
equal deleted inserted replaced
50329:18fba780c1d1 50330:2cbc42a5764b
       
     1 /*
       
     2  * Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     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 org.graalvm.compiler.processor;
       
    24 
       
    25 import java.io.IOException;
       
    26 import java.io.OutputStreamWriter;
       
    27 import java.io.PrintWriter;
       
    28 import java.util.ArrayList;
       
    29 import java.util.HashMap;
       
    30 import java.util.List;
       
    31 import java.util.Map;
       
    32 import java.util.NoSuchElementException;
       
    33 import java.util.regex.Matcher;
       
    34 import java.util.regex.Pattern;
       
    35 
       
    36 import javax.annotation.processing.FilerException;
       
    37 import javax.annotation.processing.ProcessingEnvironment;
       
    38 import javax.lang.model.element.AnnotationMirror;
       
    39 import javax.lang.model.element.AnnotationValue;
       
    40 import javax.lang.model.element.Element;
       
    41 import javax.lang.model.element.ExecutableElement;
       
    42 import javax.lang.model.element.TypeElement;
       
    43 import javax.lang.model.type.TypeMirror;
       
    44 import javax.lang.model.util.ElementFilter;
       
    45 import javax.tools.Diagnostic.Kind;
       
    46 import javax.tools.FileObject;
       
    47 import javax.tools.StandardLocation;
       
    48 
       
    49 /**
       
    50  * {@link javax.annotation.processing.AbstractProcessor} subclass that provides extra functionality.
       
    51  */
       
    52 @SuppressFBWarnings(value = "NM_SAME_SIMPLE_NAME_AS_SUPERCLASS", //
       
    53                 reason = "We want this type to be found when someone is writing a new Graal annotation processor")
       
    54 public abstract class AbstractProcessor extends javax.annotation.processing.AbstractProcessor {
       
    55 
       
    56     /**
       
    57      * Gets the processing environment available to this processor.
       
    58      */
       
    59     public ProcessingEnvironment env() {
       
    60         return processingEnv;
       
    61     }
       
    62 
       
    63     private final Map<String, TypeElement> types = new HashMap<>();
       
    64 
       
    65     /**
       
    66      * Gets the {@link TypeMirror} for a given class name.
       
    67      *
       
    68      * @throws NoClassDefFoundError if the class cannot be resolved
       
    69      */
       
    70     public TypeMirror getType(String className) {
       
    71         return getTypeElement(className).asType();
       
    72     }
       
    73 
       
    74     /**
       
    75      * Gets the {@link TypeMirror} for a given class name.
       
    76      *
       
    77      * @rturn {@code null} if the class cannot be resolved
       
    78      */
       
    79     public TypeMirror getTypeOrNull(String className) {
       
    80         TypeElement element = getTypeElementOrNull(className);
       
    81         if (element == null) {
       
    82             return null;
       
    83         }
       
    84         return element.asType();
       
    85     }
       
    86 
       
    87     /**
       
    88      * Gets the {@link TypeElement} for a given class name.
       
    89      *
       
    90      * @throws NoClassDefFoundError if the class cannot be resolved
       
    91      */
       
    92     public TypeElement getTypeElement(String className) {
       
    93         TypeElement type = getTypeElementOrNull(className);
       
    94         if (type == null) {
       
    95             throw new NoClassDefFoundError(className);
       
    96         }
       
    97         return type;
       
    98     }
       
    99 
       
   100     /**
       
   101      * Gets the {@link TypeElement} for a given class name.
       
   102      *
       
   103      * @returns {@code null} if the class cannot be resolved
       
   104      */
       
   105     public TypeElement getTypeElementOrNull(String className) {
       
   106         TypeElement type = types.get(className);
       
   107         if (type == null) {
       
   108             type = processingEnv.getElementUtils().getTypeElement(className);
       
   109             if (type == null) {
       
   110                 return null;
       
   111             }
       
   112             types.put(className, type);
       
   113         }
       
   114         return type;
       
   115     }
       
   116 
       
   117     /**
       
   118      * Converts a given {@link TypeMirror} to a {@link TypeElement}.
       
   119      *
       
   120      * @throws ClassCastException if type cannot be converted to a {@link TypeElement}
       
   121      */
       
   122     public TypeElement asTypeElement(TypeMirror type) {
       
   123         Element element = processingEnv.getTypeUtils().asElement(type);
       
   124         if (element == null) {
       
   125             throw new ClassCastException(type + " cannot be converted to a " + TypeElement.class.getName());
       
   126         }
       
   127         return (TypeElement) element;
       
   128     }
       
   129 
       
   130     /**
       
   131      * Regular expression for a qualified class name that assumes package names start with lowercase
       
   132      * and non-package components start with uppercase.
       
   133      */
       
   134     private static final Pattern QUALIFIED_CLASS_NAME_RE = Pattern.compile("(?:[a-z]\\w*\\.)+([A-Z].*)");
       
   135 
       
   136     /**
       
   137      * Gets the non-package component of a qualified class name.
       
   138      *
       
   139      * @throws IllegalArgumentException if {@code className} does not match
       
   140      *             {@link #QUALIFIED_CLASS_NAME_RE}
       
   141      */
       
   142     public static String getSimpleName(String className) {
       
   143         Matcher m = QUALIFIED_CLASS_NAME_RE.matcher(className);
       
   144         if (m.matches()) {
       
   145             return m.group(1);
       
   146         }
       
   147         throw new IllegalArgumentException("Class name \"" + className + "\" does not match pattern " + QUALIFIED_CLASS_NAME_RE);
       
   148     }
       
   149 
       
   150     /**
       
   151      * Gets the package component of a qualified class name.
       
   152      *
       
   153      * @throws IllegalArgumentException if {@code className} does not match
       
   154      *             {@link #QUALIFIED_CLASS_NAME_RE}
       
   155      */
       
   156     public static String getPackageName(String className) {
       
   157         String simpleName = getSimpleName(className);
       
   158         return className.substring(0, className.length() - simpleName.length() - 1);
       
   159     }
       
   160 
       
   161     /**
       
   162      * Gets the annotation of type {@code annotationType} directly present on {@code element}.
       
   163      *
       
   164      * @return {@code null} if an annotation of type {@code annotationType} is not on
       
   165      *         {@code element}
       
   166      */
       
   167     public AnnotationMirror getAnnotation(Element element, TypeMirror annotationType) {
       
   168         List<AnnotationMirror> mirrors = getAnnotations(element, annotationType);
       
   169         return mirrors.isEmpty() ? null : mirrors.get(0);
       
   170     }
       
   171 
       
   172     /**
       
   173      * Gets all annotations directly present on {@code element}.
       
   174      */
       
   175     public List<AnnotationMirror> getAnnotations(Element element, TypeMirror typeMirror) {
       
   176         List<AnnotationMirror> result = new ArrayList<>();
       
   177         for (AnnotationMirror mirror : element.getAnnotationMirrors()) {
       
   178             if (processingEnv.getTypeUtils().isSameType(mirror.getAnnotationType(), typeMirror)) {
       
   179                 result.add(mirror);
       
   180             }
       
   181         }
       
   182         return result;
       
   183     }
       
   184 
       
   185     /**
       
   186      * Gets the value of the {@code name} element of {@code annotation} and converts it to a value
       
   187      * of type {@code type}.
       
   188      *
       
   189      * @param type the expected type of the element value. This must be a subclass of one of the
       
   190      *            types described by {@link AnnotationValue}.
       
   191      * @throws NoSuchElementException if {@code annotation} has no element named {@code name}
       
   192      * @throws ClassCastException if the value of the specified element cannot be converted to
       
   193      *             {@code type}
       
   194      */
       
   195     public static <T> T getAnnotationValue(AnnotationMirror annotation, String name, Class<T> type) {
       
   196         ExecutableElement valueMethod = null;
       
   197         for (ExecutableElement method : ElementFilter.methodsIn(annotation.getAnnotationType().asElement().getEnclosedElements())) {
       
   198             if (method.getSimpleName().toString().equals(name)) {
       
   199                 valueMethod = method;
       
   200                 break;
       
   201             }
       
   202         }
       
   203 
       
   204         if (valueMethod == null) {
       
   205             return null;
       
   206         }
       
   207 
       
   208         AnnotationValue value = annotation.getElementValues().get(valueMethod);
       
   209         if (value == null) {
       
   210             value = valueMethod.getDefaultValue();
       
   211         }
       
   212 
       
   213         return type.cast(value.getValue());
       
   214     }
       
   215 
       
   216     /**
       
   217      * Gets the value of the {@code name} array-typed element of {@code annotation} and converts it
       
   218      * to list of values of type {@code type}.
       
   219      *
       
   220      * @param componentType the expected component type of the element value. This must be a
       
   221      *            subclass of one of the types described by {@link AnnotationValue}.
       
   222      * @throws NoSuchElementException if {@code annotation} has no element named {@code name}
       
   223      * @throws ClassCastException if the value of the specified element is not an array whose
       
   224      *             components cannot be converted to {@code componentType}
       
   225      */
       
   226     @SuppressWarnings("unchecked")
       
   227     public static <T> List<T> getAnnotationValueList(AnnotationMirror annotation, String name, Class<T> componentType) {
       
   228         List<? extends AnnotationValue> values = getAnnotationValue(annotation, name, List.class);
       
   229         List<T> result = new ArrayList<>();
       
   230 
       
   231         if (values != null) {
       
   232             for (AnnotationValue value : values) {
       
   233                 result.add(componentType.cast(value.getValue()));
       
   234             }
       
   235         }
       
   236         return result;
       
   237     }
       
   238 
       
   239     /**
       
   240      * Creates a {@code META-INF/providers/<providerClassName>} file whose contents are a single
       
   241      * line containing {@code serviceClassName}.
       
   242      */
       
   243     public void createProviderFile(String providerClassName, String serviceClassName, Element... originatingElements) {
       
   244         assert originatingElements.length > 0;
       
   245         String filename = "META-INF/providers/" + providerClassName;
       
   246         try {
       
   247             FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", filename, originatingElements);
       
   248             PrintWriter writer = new PrintWriter(new OutputStreamWriter(file.openOutputStream(), "UTF-8"));
       
   249             writer.println(serviceClassName);
       
   250             writer.close();
       
   251         } catch (IOException e) {
       
   252             processingEnv.getMessager().printMessage(isBug367599(e) ? Kind.NOTE : Kind.ERROR, e.getMessage(), originatingElements[0]);
       
   253         }
       
   254     }
       
   255 
       
   256     /**
       
   257      * Determines if a given exception is (most likely) caused by
       
   258      * <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=367599">Bug 367599</a>.
       
   259      */
       
   260     private static boolean isBug367599(Throwable t) {
       
   261         if (t instanceof FilerException) {
       
   262             for (StackTraceElement ste : t.getStackTrace()) {
       
   263                 if (ste.toString().contains("org.eclipse.jdt.internal.apt.pluggable.core.filer.IdeFilerImpl.create")) {
       
   264                     // See: https://bugs.eclipse.org/bugs/show_bug.cgi?id=367599
       
   265                     return true;
       
   266                 }
       
   267             }
       
   268         }
       
   269         return t.getCause() != null && isBug367599(t.getCause());
       
   270     }
       
   271 }