diff options
author | Zac Sweers <zac.sweers@gmail.com> | 2017-08-29 08:27:52 -0400 |
---|---|---|
committer | Ron Shapiro <shapiro.rd@gmail.com> | 2017-12-11 16:55:53 -0500 |
commit | c02f8396f410b6f89872bd23e2adb50e0832528a (patch) | |
tree | 7fd9c4ca3ebe3be99956e959b2f924e23ce38c02 /service | |
parent | 259c27224b22813e2940d6e92ddf9c45cebd3108 (diff) | |
download | auto-c02f8396f410b6f89872bd23e2adb50e0832528a.tar.gz |
Allow @AutoService to accept multiple service providers
Fixes https://github.com/google/auto/issues/488
Closes https://github.com/google/auto/issues/548
RELNOTES=Allow @AutoService to accept multiple service providers
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=178621520
Diffstat (limited to 'service')
8 files changed, 112 insertions, 48 deletions
diff --git a/service/pom.xml b/service/pom.xml index 7104cb8d..b8b4f5e9 100644 --- a/service/pom.xml +++ b/service/pom.xml @@ -79,6 +79,8 @@ <configuration> <!-- disable processing because the definition in META-INF/services breaks javac --> <compilerArgument>-proc:none</compilerArgument> + <source>1.8</source> + <target>1.8</target> </configuration> </plugin> </plugins> diff --git a/service/src/main/java/com/google/auto/service/AutoService.java b/service/src/main/java/com/google/auto/service/AutoService.java index f7911169..5fd44a65 100644 --- a/service/src/main/java/com/google/auto/service/AutoService.java +++ b/service/src/main/java/com/google/auto/service/AutoService.java @@ -40,6 +40,6 @@ import java.lang.annotation.Target; @Retention(SOURCE) @Target(TYPE) public @interface AutoService { - /** Returns the interface implemented by this service provider. */ - Class<?> value(); + /** Returns the interfaces implemented by this service provider. */ + Class<?>[] value(); } diff --git a/service/src/main/java/com/google/auto/service/processor/AutoServiceProcessor.java b/service/src/main/java/com/google/auto/service/processor/AutoServiceProcessor.java index 4a0ab055..71c14174 100644 --- a/service/src/main/java/com/google/auto/service/processor/AutoServiceProcessor.java +++ b/service/src/main/java/com/google/auto/service/processor/AutoServiceProcessor.java @@ -15,9 +15,13 @@ */ package com.google.auto.service.processor; +import static com.google.auto.common.AnnotationMirrors.getAnnotationValue; import static com.google.auto.common.MoreElements.getAnnotationMirror; +import static com.google.common.collect.ImmutableSet.toImmutableSet; +import com.google.auto.common.MoreTypes; import com.google.auto.service.AutoService; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; @@ -27,7 +31,7 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.HashSet; -import java.util.Map; +import java.util.List; import java.util.Set; import java.util.SortedSet; import javax.annotation.processing.AbstractProcessor; @@ -38,7 +42,6 @@ import javax.lang.model.SourceVersion; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; @@ -58,6 +61,9 @@ import javax.tools.StandardLocation; @SupportedOptions({ "debug", "verify" }) public class AutoServiceProcessor extends AbstractProcessor { + @VisibleForTesting + static final String MISSING_SERVICES_ERROR = "No service interfaces provided for element!"; + /** * Maps the class names of service provider interfaces to the * class names of the concrete classes which implement them. @@ -127,26 +133,27 @@ public class AutoServiceProcessor extends AbstractProcessor { for (Element e : elements) { // TODO(gak): check for error trees? TypeElement providerImplementer = (TypeElement) e; - AnnotationMirror providerAnnotation = getAnnotationMirror(e, AutoService.class).get(); - DeclaredType providerInterface = getProviderInterface(providerAnnotation); - TypeElement providerType = (TypeElement) providerInterface.asElement(); - - log("provider interface: " + providerType.getQualifiedName()); - log("provider implementer: " + providerImplementer.getQualifiedName()); - - if (!checkImplementer(providerImplementer, providerType)) { - String message = "ServiceProviders must implement their service provider interface. " - + providerImplementer.getQualifiedName() + " does not implement " - + providerType.getQualifiedName(); - error(message, e, providerAnnotation); + AnnotationMirror annotationMirror = getAnnotationMirror(e, AutoService.class).get(); + Set<DeclaredType> providerInterfaces = getValueFieldOfClasses(annotationMirror); + if (providerInterfaces.isEmpty()) { + error(MISSING_SERVICES_ERROR, e, annotationMirror); + continue; + } + for (DeclaredType providerInterface : providerInterfaces) { + TypeElement providerType = MoreTypes.asTypeElement(providerInterface); + + log("provider interface: " + providerType.getQualifiedName()); + log("provider implementer: " + providerImplementer.getQualifiedName()); + + if (checkImplementer(providerImplementer, providerType)) { + providers.put(getBinaryName(providerType), getBinaryName(providerImplementer)); + } else { + String message = "ServiceProviders must implement their service provider interface. " + + providerImplementer.getQualifiedName() + " does not implement " + + providerType.getQualifiedName(); + error(message, e, annotationMirror); + } } - - String providerTypeName = getBinaryName(providerType); - String providerImplementerName = getBinaryName(providerImplementer); - log("provider interface binary name: " + providerTypeName); - log("provider implementer binary name: " + providerImplementerName); - - providers.put(providerTypeName, providerImplementerName); } } @@ -243,23 +250,16 @@ public class AutoServiceProcessor extends AbstractProcessor { return getBinaryNameImpl(typeElement, typeElement.getSimpleName() + "$" + className); } - private DeclaredType getProviderInterface(AnnotationMirror providerAnnotation) { - - // The very simplest of way of doing this, is also unfortunately unworkable. - // We'd like to do: - // ServiceProvider provider = e.getAnnotation(ServiceProvider.class); - // Class<?> providerInterface = provider.value(); - // - // but unfortunately we can't load the arbitrary class at annotation - // processing time. So, instead, we have to use the mirror to get at the - // value (much more painful). - - Map<? extends ExecutableElement, ? extends AnnotationValue> valueIndex = - providerAnnotation.getElementValues(); - log("annotation values: " + valueIndex); - - AnnotationValue value = valueIndex.values().iterator().next(); - return (DeclaredType) value.getValue(); + /** + * Returns the contents of a {@code Class[]}-typed "value" field in a given {@code annotationMirror}. + */ + private ImmutableSet<DeclaredType> getValueFieldOfClasses(AnnotationMirror annotationMirror) { + @SuppressWarnings("unchecked") + List<AnnotationValue> values = + ((List<AnnotationValue>) getAnnotationValue(annotationMirror, "value").getValue()); + // TODO(ronshapiro): class literals may not always be declared types, i.e. int.class, + // int[].class + return values.stream().map(value -> (DeclaredType) value.getValue()).collect(toImmutableSet()); } private void log(String msg) { diff --git a/service/src/test/java/com/google/auto/service/processor/AutoServiceProcessorTest.java b/service/src/test/java/com/google/auto/service/processor/AutoServiceProcessorTest.java index 533373db..6b5e1cfa 100644 --- a/service/src/test/java/com/google/auto/service/processor/AutoServiceProcessorTest.java +++ b/service/src/test/java/com/google/auto/service/processor/AutoServiceProcessorTest.java @@ -15,12 +15,10 @@ */ package com.google.auto.service.processor; -import static com.google.common.truth.Truth.assert_; -import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources; +import static com.google.auto.service.processor.AutoServiceProcessor.MISSING_SERVICES_ERROR; +import static com.google.testing.compile.JavaSourcesSubject.assertThat; -import com.google.auto.service.processor.AutoServiceProcessor; import com.google.testing.compile.JavaFileObjects; -import java.util.Arrays; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -32,18 +30,38 @@ import org.junit.runners.JUnit4; public class AutoServiceProcessorTest { @Test public void autoService() { - assert_().about(javaSources()) - .that(Arrays.asList( + assertThat( JavaFileObjects.forResource("test/SomeService.java"), JavaFileObjects.forResource("test/SomeServiceProvider1.java"), JavaFileObjects.forResource("test/SomeServiceProvider2.java"), JavaFileObjects.forResource("test/Enclosing.java"), JavaFileObjects.forResource("test/AnotherService.java"), - JavaFileObjects.forResource("test/AnotherServiceProvider.java"))) + JavaFileObjects.forResource("test/AnotherServiceProvider.java")) .processedWith(new AutoServiceProcessor()) .compilesWithoutError() .and().generatesFiles( JavaFileObjects.forResource("META-INF/services/test.SomeService"), JavaFileObjects.forResource("META-INF/services/test.AnotherService")); } -}
\ No newline at end of file + + @Test + public void multiService() { + assertThat( + JavaFileObjects.forResource("test/SomeService.java"), + JavaFileObjects.forResource("test/AnotherService.java"), + JavaFileObjects.forResource("test/MultiServiceProvider.java")) + .processedWith(new AutoServiceProcessor()) + .compilesWithoutError() + .and().generatesFiles( + JavaFileObjects.forResource("META-INF/services/test.SomeServiceMulti"), + JavaFileObjects.forResource("META-INF/services/test.AnotherServiceMulti")); + } + + @Test + public void badMultiService() { + assertThat(JavaFileObjects.forResource("test/NoServices.java")) + .processedWith(new AutoServiceProcessor()) + .failsToCompile() + .withErrorContaining(MISSING_SERVICES_ERROR); + } +} diff --git a/service/src/test/resources/META-INF/services/test.AnotherServiceMulti b/service/src/test/resources/META-INF/services/test.AnotherServiceMulti new file mode 100644 index 00000000..f6ef36ac --- /dev/null +++ b/service/src/test/resources/META-INF/services/test.AnotherServiceMulti @@ -0,0 +1 @@ +test.MultiServiceProvider diff --git a/service/src/test/resources/META-INF/services/test.SomeServiceMulti b/service/src/test/resources/META-INF/services/test.SomeServiceMulti new file mode 100644 index 00000000..f6ef36ac --- /dev/null +++ b/service/src/test/resources/META-INF/services/test.SomeServiceMulti @@ -0,0 +1 @@ +test.MultiServiceProvider diff --git a/service/src/test/resources/test/MultiServiceProvider.java b/service/src/test/resources/test/MultiServiceProvider.java new file mode 100644 index 00000000..804904bf --- /dev/null +++ b/service/src/test/resources/test/MultiServiceProvider.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package test; + +import com.google.auto.service.AutoService; + +@AutoService({SomeService.class, AnotherService.class}) +public class MultiServiceProvider implements SomeService, AnotherService {} diff --git a/service/src/test/resources/test/NoServices.java b/service/src/test/resources/test/NoServices.java new file mode 100644 index 00000000..3687eb55 --- /dev/null +++ b/service/src/test/resources/test/NoServices.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2008 Google, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package test; + +import com.google.auto.service.AutoService; + +@AutoService({}) +public class NoServices {} |