aboutsummaryrefslogtreecommitdiff
path: root/service
diff options
context:
space:
mode:
authorZac Sweers <zac.sweers@gmail.com>2017-08-29 08:27:52 -0400
committerRon Shapiro <shapiro.rd@gmail.com>2017-12-11 16:55:53 -0500
commitc02f8396f410b6f89872bd23e2adb50e0832528a (patch)
tree7fd9c4ca3ebe3be99956e959b2f924e23ce38c02 /service
parent259c27224b22813e2940d6e92ddf9c45cebd3108 (diff)
downloadauto-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')
-rw-r--r--service/pom.xml2
-rw-r--r--service/src/main/java/com/google/auto/service/AutoService.java4
-rw-r--r--service/src/main/java/com/google/auto/service/processor/AutoServiceProcessor.java76
-rw-r--r--service/src/test/java/com/google/auto/service/processor/AutoServiceProcessorTest.java34
-rw-r--r--service/src/test/resources/META-INF/services/test.AnotherServiceMulti1
-rw-r--r--service/src/test/resources/META-INF/services/test.SomeServiceMulti1
-rw-r--r--service/src/test/resources/test/MultiServiceProvider.java21
-rw-r--r--service/src/test/resources/test/NoServices.java21
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 {}