src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.serviceprovider.processor/src/org/graalvm/compiler/serviceprovider/processor/ServiceProviderProcessor.java
changeset 50330 2cbc42a5764b
parent 48861 47f19ff9903c
child 50858 2d3e99a72541
equal deleted inserted replaced
50329:18fba780c1d1 50330:2cbc42a5764b
     1 /*
     1 /*
     2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     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
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.
     7  * published by the Free Software Foundation.
    20  * or visit www.oracle.com if you need additional information or have any
    20  * or visit www.oracle.com if you need additional information or have any
    21  * questions.
    21  * questions.
    22  */
    22  */
    23 package org.graalvm.compiler.serviceprovider.processor;
    23 package org.graalvm.compiler.serviceprovider.processor;
    24 
    24 
    25 import java.io.IOException;
       
    26 import java.io.OutputStreamWriter;
       
    27 import java.io.PrintWriter;
       
    28 import java.util.HashMap;
    25 import java.util.HashMap;
    29 import java.util.HashSet;
    26 import java.util.HashSet;
    30 import java.util.Map;
    27 import java.util.Map;
    31 import java.util.Map.Entry;
    28 import java.util.Map.Entry;
    32 import java.util.Set;
    29 import java.util.Set;
    33 
    30 
    34 import javax.annotation.processing.AbstractProcessor;
       
    35 import javax.annotation.processing.FilerException;
       
    36 import javax.annotation.processing.RoundEnvironment;
    31 import javax.annotation.processing.RoundEnvironment;
    37 import javax.annotation.processing.SupportedAnnotationTypes;
    32 import javax.annotation.processing.SupportedAnnotationTypes;
    38 import javax.lang.model.SourceVersion;
    33 import javax.lang.model.SourceVersion;
       
    34 import javax.lang.model.element.AnnotationMirror;
    39 import javax.lang.model.element.Element;
    35 import javax.lang.model.element.Element;
    40 import javax.lang.model.element.ElementKind;
    36 import javax.lang.model.element.ElementKind;
    41 import javax.lang.model.element.PackageElement;
    37 import javax.lang.model.element.PackageElement;
    42 import javax.lang.model.element.TypeElement;
    38 import javax.lang.model.element.TypeElement;
    43 import javax.lang.model.type.MirroredTypeException;
       
    44 import javax.lang.model.type.TypeMirror;
    39 import javax.lang.model.type.TypeMirror;
    45 import javax.tools.Diagnostic.Kind;
    40 import javax.tools.Diagnostic.Kind;
    46 import javax.tools.FileObject;
       
    47 import javax.tools.StandardLocation;
       
    48 
    41 
    49 import org.graalvm.compiler.serviceprovider.ServiceProvider;
    42 import org.graalvm.compiler.processor.AbstractProcessor;
    50 
    43 
    51 /**
    44 /**
    52  * Processes classes annotated with {@link ServiceProvider}. For a service defined by {@code S} and
    45  * Processes classes annotated with {@code ServiceProvider}. For a service defined by {@code S} and
    53  * a class {@code P} implementing the service, this processor generates the file
    46  * a class {@code P} implementing the service, this processor generates the file
    54  * {@code META-INF/providers/P} whose contents are a single line containing the fully qualified name
    47  * {@code META-INF/providers/P} whose contents are a single line containing the fully qualified name
    55  * of {@code S}.
    48  * of {@code S}.
    56  */
    49  */
    57 @SupportedAnnotationTypes("org.graalvm.compiler.serviceprovider.ServiceProvider")
    50 @SupportedAnnotationTypes("org.graalvm.compiler.serviceprovider.ServiceProvider")
    58 public class ServiceProviderProcessor extends AbstractProcessor {
    51 public class ServiceProviderProcessor extends AbstractProcessor {
    59 
    52 
       
    53     private static final String SERVICE_PROVIDER_CLASS_NAME = "org.graalvm.compiler.serviceprovider.ServiceProvider";
    60     private final Set<TypeElement> processed = new HashSet<>();
    54     private final Set<TypeElement> processed = new HashSet<>();
    61     private final Map<TypeElement, String> serviceProviders = new HashMap<>();
    55     private final Map<TypeElement, String> serviceProviders = new HashMap<>();
    62 
    56 
    63     @Override
    57     @Override
    64     public SourceVersion getSupportedSourceVersion() {
    58     public SourceVersion getSupportedSourceVersion() {
    79         if (processed.contains(serviceProvider)) {
    73         if (processed.contains(serviceProvider)) {
    80             return;
    74             return;
    81         }
    75         }
    82 
    76 
    83         processed.add(serviceProvider);
    77         processed.add(serviceProvider);
    84         ServiceProvider annotation = serviceProvider.getAnnotation(ServiceProvider.class);
    78         AnnotationMirror annotation = getAnnotation(serviceProvider, getType(SERVICE_PROVIDER_CLASS_NAME));
    85         if (annotation != null) {
    79         if (annotation != null) {
    86             try {
    80             TypeMirror service = getAnnotationValue(annotation, "value", TypeMirror.class);
    87                 annotation.value();
    81             if (verifyAnnotation(service, serviceProvider)) {
    88             } catch (MirroredTypeException ex) {
    82                 if (serviceProvider.getNestingKind().isNested()) {
    89                 TypeMirror service = ex.getTypeMirror();
    83                     /*
    90                 if (verifyAnnotation(service, serviceProvider)) {
    84                      * This is a simplifying constraint that means we don't have to process the
    91                     if (serviceProvider.getNestingKind().isNested()) {
    85                      * qualified name to insert '$' characters at the relevant positions.
    92                         /*
    86                      */
    93                          * This is a simplifying constraint that means we don't have to process the
    87                     String msg = String.format("Service provider class %s must be a top level class", serviceProvider.getSimpleName());
    94                          * qualified name to insert '$' characters at the relevant positions.
    88                     processingEnv.getMessager().printMessage(Kind.ERROR, msg, serviceProvider);
    95                          */
    89                 } else {
    96                         String msg = String.format("Service provider class %s must be a top level class", serviceProvider.getSimpleName());
    90                     /*
    97                         processingEnv.getMessager().printMessage(Kind.ERROR, msg, serviceProvider);
    91                      * Since the definition of the service class is not necessarily modifiable, we
    98                     } else {
    92                      * need to support a non-top-level service class and ensure its name is properly
    99                         /*
    93                      * expressed with '$' separating nesting levels instead of '.'.
   100                          * Since the definition of the service class is not necessarily modifiable,
    94                      */
   101                          * we need to support a non-top-level service class and ensure its name is
    95                     TypeElement serviceElement = (TypeElement) processingEnv.getTypeUtils().asElement(service);
   102                          * properly expressed with '$' separating nesting levels instead of '.'.
    96                     String serviceName = serviceElement.getSimpleName().toString();
   103                          */
    97                     Element enclosing = serviceElement.getEnclosingElement();
   104                         TypeElement serviceElement = (TypeElement) processingEnv.getTypeUtils().asElement(service);
    98                     while (enclosing != null) {
   105                         String serviceName = serviceElement.getSimpleName().toString();
    99                         final ElementKind kind = enclosing.getKind();
   106                         Element enclosing = serviceElement.getEnclosingElement();
   100                         if (kind == ElementKind.PACKAGE) {
   107                         while (enclosing != null) {
   101                             serviceName = ((PackageElement) enclosing).getQualifiedName().toString() + "." + serviceName;
   108                             final ElementKind kind = enclosing.getKind();
   102                             break;
   109                             if (kind == ElementKind.PACKAGE) {
   103                         } else if (kind == ElementKind.CLASS || kind == ElementKind.INTERFACE) {
   110                                 serviceName = ((PackageElement) enclosing).getQualifiedName().toString() + "." + serviceName;
   104                             serviceName = ((TypeElement) enclosing).getSimpleName().toString() + "$" + serviceName;
   111                                 break;
   105                             enclosing = enclosing.getEnclosingElement();
   112                             } else if (kind == ElementKind.CLASS || kind == ElementKind.INTERFACE) {
   106                         } else {
   113                                 serviceName = ((TypeElement) enclosing).getSimpleName().toString() + "$" + serviceName;
   107                             String msg = String.format("Cannot generate provider descriptor for service class %s as it is not nested in a package, class or interface",
   114                                 enclosing = enclosing.getEnclosingElement();
   108                                             serviceElement.getQualifiedName());
   115                             } else {
   109                             processingEnv.getMessager().printMessage(Kind.ERROR, msg, serviceProvider);
   116                                 String msg = String.format("Cannot generate provider descriptor for service class %s as it is not nested in a package, class or interface",
   110                             return;
   117                                                 serviceElement.getQualifiedName());
       
   118                                 processingEnv.getMessager().printMessage(Kind.ERROR, msg, serviceProvider);
       
   119                                 return;
       
   120                             }
       
   121                         }
   111                         }
   122                         serviceProviders.put(serviceProvider, serviceName);
       
   123                     }
   112                     }
       
   113                     serviceProviders.put(serviceProvider, serviceName);
   124                 }
   114                 }
   125             }
   115             }
   126         }
   116         }
   127     }
       
   128 
       
   129     private void writeProviderFile(TypeElement serviceProvider, String interfaceName) {
       
   130         String filename = "META-INF/providers/" + serviceProvider.getQualifiedName();
       
   131         try {
       
   132             FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", filename, serviceProvider);
       
   133             PrintWriter writer = new PrintWriter(new OutputStreamWriter(file.openOutputStream(), "UTF-8"));
       
   134             writer.println(interfaceName);
       
   135             writer.close();
       
   136         } catch (IOException e) {
       
   137             processingEnv.getMessager().printMessage(isBug367599(e) ? Kind.NOTE : Kind.ERROR, e.getMessage(), serviceProvider);
       
   138         }
       
   139     }
       
   140 
       
   141     /**
       
   142      * Determines if a given exception is (most likely) caused by
       
   143      * <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=367599">Bug 367599</a>.
       
   144      */
       
   145     private static boolean isBug367599(Throwable t) {
       
   146         if (t instanceof FilerException) {
       
   147             for (StackTraceElement ste : t.getStackTrace()) {
       
   148                 if (ste.toString().contains("org.eclipse.jdt.internal.apt.pluggable.core.filer.IdeFilerImpl.create")) {
       
   149                     // See: https://bugs.eclipse.org/bugs/show_bug.cgi?id=367599
       
   150                     return true;
       
   151                 }
       
   152             }
       
   153         }
       
   154         return t.getCause() != null && isBug367599(t.getCause());
       
   155     }
   117     }
   156 
   118 
   157     @Override
   119     @Override
   158     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
   120     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
   159         if (roundEnv.processingOver()) {
   121         if (roundEnv.processingOver()) {
   160             for (Entry<TypeElement, String> e : serviceProviders.entrySet()) {
   122             for (Entry<TypeElement, String> e : serviceProviders.entrySet()) {
   161                 writeProviderFile(e.getKey(), e.getValue());
   123                 createProviderFile(e.getKey().getQualifiedName().toString(), e.getValue(), e.getKey());
   162             }
   124             }
   163             serviceProviders.clear();
   125             serviceProviders.clear();
   164             return true;
   126             return true;
   165         }
   127         }
   166 
   128 
   167         for (Element element : roundEnv.getElementsAnnotatedWith(ServiceProvider.class)) {
   129         TypeElement serviceProviderTypeElement = getTypeElement(SERVICE_PROVIDER_CLASS_NAME);
       
   130         for (Element element : roundEnv.getElementsAnnotatedWith(serviceProviderTypeElement)) {
   168             assert element.getKind().isClass();
   131             assert element.getKind().isClass();
   169             processElement((TypeElement) element);
   132             processElement((TypeElement) element);
   170         }
   133         }
   171 
   134 
   172         return true;
   135         return true;