com.google.testing.compile
compile-testing
diff --git a/factory/src/main/java/com/google/auto/factory/Provided.java b/factory/src/main/java/com/google/auto/factory/Provided.java
index 226a16f48c..18029af557 100644
--- a/factory/src/main/java/com/google/auto/factory/Provided.java
+++ b/factory/src/main/java/com/google/auto/factory/Provided.java
@@ -20,8 +20,13 @@
import java.lang.annotation.Target;
/**
- * An annotation to be applied to parameters that should be provided by an
- * {@linkplain javax.inject.Inject injected} {@link javax.inject.Provider} in a generated factory.
+ * An annotation to be applied to parameters that should be provided by an injected {@code Provider}
+ * in a generated factory.
+ *
+ * The {@code @Inject} and {@code Provider} classes come from either the legacy package {@code
+ * javax.inject} or the updated package {@code jakarta.inject}. {@code jakarta.inject} is used if it
+ * is on the classpath. Compile with {@code -Acom.google.auto.factory.InjectApi=javax} if you want
+ * to use {@code javax.inject} even when {@code jakarta.inject} is available.
*
* @author Gregory Kick
*/
diff --git a/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryProcessor.java b/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryProcessor.java
index 0654eed10e..05f631898c 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryProcessor.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryProcessor.java
@@ -41,11 +41,13 @@
import java.util.List;
import java.util.Optional;
import java.util.Set;
+import java.util.function.Consumer;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedOptions;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
@@ -60,6 +62,7 @@
import javax.tools.Diagnostic.Kind;
import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType;
+import org.checkerframework.checker.nullness.qual.Nullable;
/**
* The annotation processor that generates factories for {@link AutoFactory} annotations.
@@ -68,13 +71,25 @@
*/
@IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.ISOLATING)
@AutoService(Processor.class)
+@SupportedOptions(AutoFactoryProcessor.INJECT_API_OPTION)
public final class AutoFactoryProcessor extends AbstractProcessor {
+ static final String INJECT_API_OPTION = "com.google.auto.factory.InjectApi";
+
+ private static final ImmutableSet INJECT_APIS = ImmutableSet.of("jakarta", "javax");
+
private FactoryDescriptorGenerator factoryDescriptorGenerator;
private AutoFactoryDeclaration.Factory declarationFactory;
private ProvidedChecker providedChecker;
private Messager messager;
private Elements elements;
private Types types;
+ private InjectApi injectApi;
+
+ /**
+ * If non-null, we will call this whenever the {@link #process} method is called, giving it one of
+ * the {@code @AutoFactory} elements, and do nothing else.
+ */
+ private @Nullable Consumer<@Nullable Element> errorFunction;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
@@ -82,14 +97,40 @@ public synchronized void init(ProcessingEnvironment processingEnv) {
elements = processingEnv.getElementUtils();
types = processingEnv.getTypeUtils();
messager = processingEnv.getMessager();
+ String api = processingEnv.getOptions().get(INJECT_API_OPTION);
+ if (api != null && !INJECT_APIS.contains(api)) {
+ messager.printMessage(
+ Kind.ERROR,
+ "Usage: -A"
+ + INJECT_API_OPTION
+ + "=, where is "
+ + String.join(" or ", INJECT_APIS));
+ errorFunction = unused -> {};
+ return;
+ }
+ try {
+ injectApi = InjectApi.from(elements, api);
+ } catch (IllegalStateException e) {
+ errorFunction = element -> {
+ messager.printMessage(Kind.ERROR, e.getMessage(), element);
+ errorFunction = unused -> {}; // Only print the error once.
+ };
+ return;
+ }
providedChecker = new ProvidedChecker(messager);
declarationFactory = new AutoFactoryDeclaration.Factory(elements, messager);
factoryDescriptorGenerator =
- new FactoryDescriptorGenerator(messager, types, declarationFactory);
+ new FactoryDescriptorGenerator(messager, types, declarationFactory, injectApi);
}
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (errorFunction != null) {
+ Set extends Element> elements = roundEnv.getElementsAnnotatedWith(AutoFactory.class);
+ Element anElement = elements.isEmpty() ? null : elements.iterator().next();
+ errorFunction.accept(anElement);
+ return false;
+ }
try {
doProcess(roundEnv);
} catch (Throwable e) {
@@ -141,7 +182,8 @@ private void doProcess(RoundEnvironment roundEnv) {
indexedMethodsBuilder.build();
ImmutableSetMultimap factoriesBeingCreated =
simpleNamesToNames(indexedMethods.keySet());
- FactoryWriter factoryWriter = new FactoryWriter(processingEnv, factoriesBeingCreated);
+ FactoryWriter factoryWriter =
+ new FactoryWriter(processingEnv, injectApi, factoriesBeingCreated);
indexedMethods
.asMap()
@@ -216,7 +258,10 @@ private ImmutableSet implementationMethods(
Elements2.getExecutableElementAsMemberOf(types, implementationMethod, supertype);
ImmutableSet passedParameters =
Parameter.forParameterList(
- implementationMethod.getParameters(), methodType.getParameterTypes(), types);
+ implementationMethod.getParameters(),
+ methodType.getParameterTypes(),
+ types,
+ injectApi);
implementationMethodsBuilder.add(
ImplementationMethodDescriptor.builder()
.name(implementationMethod.getSimpleName().toString())
@@ -270,7 +315,9 @@ private void checkAnnotationsToApply(Element annotation) {
Set seenAnnotations = new HashSet<>();
for (ExecutableElement annotationMember : methodsIn(annotation.getEnclosedElements())) {
TypeMirror memberType = annotationMember.getReturnType();
- boolean isAnnotation = memberType.getKind().equals(DECLARED) && asElement(memberType).getKind().equals(ANNOTATION_TYPE);
+ boolean isAnnotation =
+ memberType.getKind().equals(DECLARED)
+ && asElement(memberType).getKind().equals(ANNOTATION_TYPE);
if (!isAnnotation && !memberType.getKind().equals(ERROR)) {
messager.printMessage(
Kind.ERROR,
diff --git a/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptorGenerator.java b/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptorGenerator.java
index 70a21ea263..584b342eb8 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptorGenerator.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptorGenerator.java
@@ -52,12 +52,17 @@ final class FactoryDescriptorGenerator {
private final Messager messager;
private final Types types;
private final AutoFactoryDeclaration.Factory declarationFactory;
+ private final InjectApi injectApi;
FactoryDescriptorGenerator(
- Messager messager, Types types, AutoFactoryDeclaration.Factory declarationFactory) {
+ Messager messager,
+ Types types,
+ AutoFactoryDeclaration.Factory declarationFactory,
+ InjectApi injectApi) {
this.messager = messager;
this.types = types;
this.declarationFactory = declarationFactory;
+ this.injectApi = injectApi;
}
ImmutableSet generateDescriptor(Element element) {
@@ -132,16 +137,17 @@ FactoryMethodDescriptor generateDescriptorForConstructor(
// The map returned by partitioningBy always has entries for both key values but our
// null-checker isn't yet smart enough to know that.
ImmutableSet providedParameters =
- Parameter.forParameterList(requireNonNull(parameterMap.get(true)), types);
+ Parameter.forParameterList(requireNonNull(parameterMap.get(true)), types, injectApi);
ImmutableSet passedParameters =
- Parameter.forParameterList(requireNonNull(parameterMap.get(false)), types);
+ Parameter.forParameterList(requireNonNull(parameterMap.get(false)), types, injectApi);
return FactoryMethodDescriptor.builder(declaration)
.name("create")
.returnType(classElement.asType())
.publicMethod(classElement.getModifiers().contains(PUBLIC))
.providedParameters(providedParameters)
.passedParameters(passedParameters)
- .creationParameters(Parameter.forParameterList(constructor.getParameters(), types))
+ .creationParameters(
+ Parameter.forParameterList(constructor.getParameters(), types, injectApi))
.isVarArgs(constructor.isVarArgs())
.exceptions(constructor.getThrownTypes())
.overridingMethod(false)
diff --git a/factory/src/main/java/com/google/auto/factory/processor/FactoryWriter.java b/factory/src/main/java/com/google/auto/factory/processor/FactoryWriter.java
index a887c57715..c0a7959133 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/FactoryWriter.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/FactoryWriter.java
@@ -49,8 +49,6 @@
import java.util.List;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
-import javax.inject.Inject;
-import javax.inject.Provider;
import javax.lang.model.SourceVersion;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
@@ -60,6 +58,7 @@
final class FactoryWriter {
+ private final InjectApi injectApi;
private final Filer filer;
private final Elements elements;
private final SourceVersion sourceVersion;
@@ -67,7 +66,9 @@ final class FactoryWriter {
FactoryWriter(
ProcessingEnvironment processingEnv,
+ InjectApi injectApi,
ImmutableSetMultimap factoriesBeingCreated) {
+ this.injectApi = injectApi;
this.filer = processingEnv.getFiler();
this.elements = processingEnv.getElementUtils();
this.sourceVersion = processingEnv.getSourceVersion();
@@ -118,7 +119,8 @@ private static void addFactoryTypeParameters(
private void addConstructorAndProviderFields(
TypeSpec.Builder factory, FactoryDescriptor descriptor) {
- MethodSpec.Builder constructor = constructorBuilder().addAnnotation(Inject.class);
+ MethodSpec.Builder constructor =
+ constructorBuilder().addAnnotation(ClassName.get(injectApi.inject()));
if (descriptor.publicType()) {
constructor.addModifiers(PUBLIC);
}
@@ -127,7 +129,8 @@ private void addConstructorAndProviderFields(
for (ProviderField provider : providerFields) {
++argumentNumber;
TypeName typeName = resolveTypeName(provider.key().type().get()).box();
- TypeName providerType = ParameterizedTypeName.get(ClassName.get(Provider.class), typeName);
+ TypeName providerType =
+ ParameterizedTypeName.get(ClassName.get(injectApi.provider()), typeName);
factory.addField(providerType, provider.name(), PRIVATE, FINAL);
if (provider.key().qualifier().isPresent()) {
// only qualify the constructor parameter
@@ -181,7 +184,7 @@ private void addFactoryMethods(
} else {
ProviderField provider = requireNonNull(descriptor.providers().get(parameter.key()));
argument = CodeBlock.of(provider.name());
- if (parameter.isProvider()) {
+ if (injectApi.isProvider(parameter.type().get())) {
// Providers are checked for nullness in the Factory's constructor.
checkNotNull = false;
} else {
diff --git a/factory/src/main/java/com/google/auto/factory/processor/InjectApi.java b/factory/src/main/java/com/google/auto/factory/processor/InjectApi.java
new file mode 100644
index 0000000000..d1689fed25
--- /dev/null
+++ b/factory/src/main/java/com/google/auto/factory/processor/InjectApi.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2023 Google LLC
+ *
+ * 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 com.google.auto.factory.processor;
+
+import static com.google.auto.common.MoreStreams.toImmutableMap;
+import static java.util.stream.Collectors.joining;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.util.AbstractMap.SimpleEntry;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/** Encapsulates the choice of {@code jakarta.inject} or {@code javax.inject}. */
+@AutoValue
+abstract class InjectApi {
+ abstract TypeElement inject();
+
+ abstract TypeElement provider();
+
+ abstract TypeElement qualifier();
+
+ private static final ImmutableList PREFIXES_IN_ORDER =
+ ImmutableList.of("jakarta.inject.", "javax.inject.");
+
+ static InjectApi from(Elements elementUtils, @Nullable String apiPrefix) {
+ ImmutableList apiPackages =
+ (apiPrefix == null) ? PREFIXES_IN_ORDER : ImmutableList.of(apiPrefix + ".inject.");
+ for (String apiPackage : apiPackages) {
+ ImmutableMap apiMap = apiMap(elementUtils, apiPackage);
+ TypeElement inject = apiMap.get("Inject");
+ TypeElement provider = apiMap.get("Provider");
+ TypeElement qualifier = apiMap.get("Qualifier");
+ if (inject != null && provider != null && qualifier != null) {
+ return new AutoValue_InjectApi(inject, provider, qualifier);
+ }
+ }
+ String classes = "{" + String.join(",", API_CLASSES) + "}";
+ String missing = apiPackages.stream().sorted().map(s -> s + classes).collect(joining(" or "));
+ throw new IllegalStateException("Class path for AutoFactory class must include " + missing);
+ }
+
+ /** True if {@code type} is a {@code Provider}. */
+ boolean isProvider(TypeMirror type) {
+ return type.getKind().equals(TypeKind.DECLARED)
+ && MoreTypes.asTypeElement(type).equals(provider());
+ }
+
+ private static ImmutableMap apiMap(
+ Elements elementUtils, String apiPackage) {
+ return API_CLASSES.stream()
+ .map(name -> new SimpleEntry<>(name, elementUtils.getTypeElement(apiPackage + name)))
+ .filter(entry -> entry.getValue() != null)
+ .collect(toImmutableMap(SimpleEntry::getKey, SimpleEntry::getValue));
+ }
+
+ private static final ImmutableSet API_CLASSES =
+ ImmutableSet.of("Inject", "Provider", "Qualifier");
+}
diff --git a/factory/src/main/java/com/google/auto/factory/processor/Key.java b/factory/src/main/java/com/google/auto/factory/processor/Key.java
index 6dc7644554..8347ce95f2 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/Key.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/Key.java
@@ -16,7 +16,6 @@
package com.google.auto.factory.processor;
import static com.google.auto.common.MoreElements.isAnnotationPresent;
-import static com.google.auto.factory.processor.Mirrors.isProvider;
import static com.google.auto.factory.processor.Mirrors.unwrapOptionalEquivalence;
import static com.google.auto.factory.processor.Mirrors.wrapOptionalInEquivalence;
@@ -26,7 +25,6 @@
import com.google.common.base.Equivalence;
import java.util.Collection;
import java.util.Optional;
-import javax.inject.Qualifier;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
@@ -37,7 +35,6 @@
* @author Gregory Kick
*/
@AutoValue
-// TODO(ronshapiro): reuse dagger.model.Key?
abstract class Key {
abstract Equivalence.Wrapper type();
@@ -49,7 +46,7 @@ Optional qualifier() {
}
/**
- * Constructs a key based on the type {@code type} and any {@link Qualifier}s in {@code
+ * Constructs a key based on the type {@code type} and any {@code Qualifier}s in {@code
* annotations}.
*
* If {@code type} is a {@code Provider}, the returned {@link Key}'s {@link #type()} is
@@ -57,6 +54,7 @@ Optional qualifier() {
* corresponding {@linkplain Types#boxedClass(PrimitiveType) boxed type}.
*
* For example:
+ *
*
* Input type | {@code Key.type()}
* |
---|
{@code String} | {@code String}
@@ -64,18 +62,19 @@ Optional qualifier() {
* {@code int} | {@code Integer}
* | |
*/
- static Key create(TypeMirror type, Collection annotations, Types types) {
+ static Key create(
+ TypeMirror type, Collection annotations, Types types, InjectApi injectApi) {
// TODO(gak): check for only one qualifier rather than using the first
Optional qualifier =
annotations.stream()
.filter(
annotation ->
isAnnotationPresent(
- annotation.getAnnotationType().asElement(), Qualifier.class))
+ annotation.getAnnotationType().asElement(), injectApi.qualifier()))
.findFirst();
TypeMirror keyType =
- isProvider(type)
+ injectApi.isProvider(type)
? MoreTypes.asDeclared(type).getTypeArguments().get(0)
: boxedType(type, types);
return new AutoValue_Key(
diff --git a/factory/src/main/java/com/google/auto/factory/processor/Mirrors.java b/factory/src/main/java/com/google/auto/factory/processor/Mirrors.java
index 5739511ecf..c81623d469 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/Mirrors.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/Mirrors.java
@@ -15,14 +15,12 @@
*/
package com.google.auto.factory.processor;
-import com.google.auto.common.MoreTypes;
import com.google.common.base.Equivalence;
import com.google.common.collect.ImmutableMap;
import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
-import javax.inject.Provider;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
@@ -30,7 +28,6 @@
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.SimpleElementVisitor6;
final class Mirrors {
@@ -53,11 +50,6 @@ public Name visitType(TypeElement e, Void p) {
null);
}
- /** {@code true} if {@code type} is a {@link Provider}. */
- static boolean isProvider(TypeMirror type) {
- return MoreTypes.isType(type) && MoreTypes.isTypeOf(Provider.class, type);
- }
-
/**
* Returns an annotation value map with {@link String} keys instead of {@link ExecutableElement}
* instances.
diff --git a/factory/src/main/java/com/google/auto/factory/processor/Parameter.java b/factory/src/main/java/com/google/auto/factory/processor/Parameter.java
index bb586a1cf3..16a419a8c3 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/Parameter.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/Parameter.java
@@ -34,7 +34,6 @@
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
-import javax.inject.Provider;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
@@ -49,15 +48,11 @@
abstract class Parameter {
/**
- * The original type of the parameter, while {@code key().type()} erases the wrapped {@link
+ * The original type of the parameter, while {@code key().type()} erases the wrapped {@code
* Provider}, if any.
*/
abstract Equivalence.Wrapper type();
- boolean isProvider() {
- return Mirrors.isProvider(type().get());
- }
-
boolean isPrimitive() {
return type().get().getKind().isPrimitive();
}
@@ -79,15 +74,16 @@ ImmutableList annotations() {
Optional nullable() {
return unwrapOptionalEquivalence(nullableWrapper());
}
+
private static Parameter forVariableElement(
- VariableElement variable, TypeMirror type, Types types) {
+ VariableElement variable, TypeMirror type, Types types, InjectApi injectApi) {
ImmutableList allAnnotations =
Stream.of(variable.getAnnotationMirrors(), type.getAnnotationMirrors())
.flatMap(List::stream)
.collect(toImmutableList());
Optional nullable =
allAnnotations.stream().filter(Parameter::isNullable).findFirst();
- Key key = Key.create(type, allAnnotations, types);
+ Key key = Key.create(type, allAnnotations, types, injectApi);
ImmutableSet> typeAnnotationWrappers =
type.getAnnotationMirrors().stream()
@@ -120,12 +116,14 @@ private static boolean isNullable(AnnotationMirror annotation) {
static ImmutableSet forParameterList(
List extends VariableElement> variables,
List extends TypeMirror> variableTypes,
- Types types) {
+ Types types,
+ InjectApi injectApi) {
checkArgument(variables.size() == variableTypes.size());
ImmutableSet.Builder builder = ImmutableSet.builder();
Set names = Sets.newHashSetWithExpectedSize(variables.size());
for (int i = 0; i < variables.size(); i++) {
- Parameter parameter = forVariableElement(variables.get(i), variableTypes.get(i), types);
+ Parameter parameter =
+ forVariableElement(variables.get(i), variableTypes.get(i), types, injectApi);
checkArgument(names.add(parameter.name()), "Duplicate parameter name: %s", parameter.name());
builder.add(parameter);
}
@@ -135,11 +133,11 @@ static ImmutableSet forParameterList(
}
static ImmutableSet forParameterList(
- List extends VariableElement> variables, Types types) {
+ List extends VariableElement> variables, Types types, InjectApi injectApi) {
List variableTypes = Lists.newArrayListWithExpectedSize(variables.size());
for (VariableElement var : variables) {
variableTypes.add(var.asType());
}
- return forParameterList(variables, variableTypes, types);
+ return forParameterList(variables, variableTypes, types, injectApi);
}
}
diff --git a/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorNegativeTest.java b/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorNegativeTest.java
index 4998aa57e6..c9907b057d 100644
--- a/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorNegativeTest.java
+++ b/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorNegativeTest.java
@@ -15,12 +15,17 @@
*/
package com.google.auto.factory.processor;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.testing.compile.CompilationSubject.assertThat;
+import com.google.common.collect.ImmutableList;
import com.google.testing.compile.Compilation;
import com.google.testing.compile.Compiler;
import com.google.testing.compile.JavaFileObjects;
+import java.io.File;
+import java.net.URL;
import javax.tools.JavaFileObject;
+import javax.tools.StandardLocation;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -178,4 +183,46 @@ public void annotationsToApplyNotAnnotations() {
.inFile(file)
.onLineContaining("andWhatIsThis");
}
+
+ @Test
+ public void noInjectApi() throws Exception {
+ URL autoFactoryUrl =
+ Class.forName("com.google.auto.factory.AutoFactory")
+ .getProtectionDomain()
+ .getCodeSource()
+ .getLocation();
+ assertThat(autoFactoryUrl.getProtocol()).isEqualTo("file");
+ File autoFactoryFile = new File(autoFactoryUrl.getPath());
+ Compiler compiler =
+ Compiler.javac()
+ .withProcessors(new AutoFactoryProcessor())
+ .withClasspath(ImmutableList.of(autoFactoryFile));
+ JavaFileObject file = JavaFileObjects.forResource("good/SimpleClass.java");
+ Compilation compilation = compiler.compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "Class path for AutoFactory class must include"
+ + " jakarta.inject.{Inject,Provider,Qualifier} or"
+ + " javax.inject.{Inject,Provider,Qualifier}");
+ assertThat(compilation).hadErrorCount(1);
+ }
+
+ /**
+ * AutoFactoryProcessor shouldn't complain about the absence of {@code javax.inject} if there are
+ * no {@code @AutoFactory} classes being compiled. Its {@code init} will be called and will see
+ * the problem, but will say nothing.
+ */
+ @Test
+ public void noInjectApiButNoAutoFactoryEither() {
+ Compiler compiler =
+ Compiler.javac()
+ .withProcessors(new AutoFactoryProcessor())
+ .withClasspath(ImmutableList.of());
+ JavaFileObject file =
+ JavaFileObjects.forSourceString("test.Foo", "package test; public class Foo {}");
+ Compilation compilation = compiler.compile(file);
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation).generatedFile(StandardLocation.CLASS_OUTPUT, "test", "Foo.class");
+ }
}
diff --git a/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorTest.java b/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorTest.java
index 845ea58a42..14026b17d6 100644
--- a/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorTest.java
+++ b/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorTest.java
@@ -63,16 +63,36 @@ public AutoFactoryProcessorTest(@TestParameter Config config) {
private enum InjectPackage {
JAVAX,
+ JAKARTA
}
+ /**
+ * Each test configuration specifies whether javax or jakarta or both are on the classpath, which
+ * one is expected to be chosen, and any {@code -A} options.
+ */
private enum Config {
- JAVAX_ONLY_ON_CLASSPATH(ImmutableList.of(InjectPackage.JAVAX), InjectPackage.JAVAX);
-
- /** Config that is used for negative tests, and to update the golden files. */
- static final Config DEFAULT = JAVAX_ONLY_ON_CLASSPATH;
+ JAVAX_ONLY_ON_CLASSPATH(ImmutableList.of(InjectPackage.JAVAX), InjectPackage.JAVAX),
+ JAKARTA_ONLY_ON_CLASSPATH(ImmutableList.of(InjectPackage.JAKARTA), InjectPackage.JAKARTA),
+ BOTH_ON_CLASSPATH(
+ ImmutableList.of(InjectPackage.JAVAX, InjectPackage.JAKARTA),
+ InjectPackage.JAKARTA),
+ EXPLICIT_JAVAX(
+ ImmutableList.of(InjectPackage.JAVAX, InjectPackage.JAKARTA),
+ InjectPackage.JAVAX,
+ ImmutableList.of("-A" + AutoFactoryProcessor.INJECT_API_OPTION + "=javax")),
+ EXPLICIT_JAKARTA(
+ ImmutableList.of(InjectPackage.JAVAX, InjectPackage.JAKARTA),
+ InjectPackage.JAKARTA,
+ ImmutableList.of("-A" + AutoFactoryProcessor.INJECT_API_OPTION + "=jakarta"));
+
+ /**
+ * Config that is used for negative tests, and to update the golden files. Since those files use
+ * {@code javax.inject}, we need a config that specifies that package.
+ */
+ static final Config DEFAULT = EXPLICIT_JAVAX;
final ImmutableList packagesOnClasspath;
- final InjectPackage unusedExpectedPackage;
+ final InjectPackage expectedPackage;
final ImmutableList options;
Config(ImmutableList packagesOnClasspath, InjectPackage expectedPackage) {
@@ -84,7 +104,7 @@ private enum Config {
InjectPackage expectedPackage,
ImmutableList options) {
this.packagesOnClasspath = packagesOnClasspath;
- this.unusedExpectedPackage = expectedPackage;
+ this.expectedPackage = expectedPackage;
this.options = options;
}
@@ -95,6 +115,7 @@ private enum Config {
fileForClass("javax.annotation.Nullable"),
fileForClass("org.checkerframework.checker.nullness.compatqual.NullableType"));
static final File JAVAX_CLASSPATH = fileForClass("javax.inject.Provider");
+ static final File JAKARTA_CLASSPATH = fileForClass("jakarta.inject.Provider");
static File fileForClass(String className) {
Class> c;
@@ -114,6 +135,9 @@ ImmutableList classpath() {
if (packagesOnClasspath.contains(InjectPackage.JAVAX)) {
classpathBuilder.add(JAVAX_CLASSPATH);
}
+ if (packagesOnClasspath.contains(InjectPackage.JAKARTA)) {
+ classpathBuilder.add(JAKARTA_CLASSPATH);
+ }
return classpathBuilder.build();
}
@@ -184,6 +208,9 @@ private JavaFileObject goldenFile(String resourceName) {
try {
URL resourceUrl = Resources.getResource(resourceName);
String source = Resources.toString(resourceUrl, UTF_8);
+ if (config.expectedPackage.equals(InjectPackage.JAKARTA)) {
+ source = source.replace("javax.inject", "jakarta.inject");
+ }
String className = resourceName.replaceFirst("\\.java$", "").replace('/', '.');
return JavaFileObjects.forSourceString(className, source);
} catch (IOException e) {
@@ -369,57 +396,6 @@ public void mixedDepsImplementingInterfaces() {
"expected/MixedDepsImplementingInterfacesFactory.java"));
}
- @Test
- public void failsWithMixedFinals() {
- JavaFileObject file = JavaFileObjects.forResource("bad/MixedFinals.java");
- Compilation compilation = config.javac().compile(file);
- assertThat(compilation).failed();
- assertThat(compilation)
- .hadErrorContaining(
- "Cannot mix allowSubclasses=true and allowSubclasses=false in one factory.")
- .inFile(file)
- .onLine(24);
- assertThat(compilation)
- .hadErrorContaining(
- "Cannot mix allowSubclasses=true and allowSubclasses=false in one factory.")
- .inFile(file)
- .onLine(27);
- }
-
- @Test
- public void providedButNoAutoFactory() {
- JavaFileObject file = JavaFileObjects.forResource("bad/ProvidedButNoAutoFactory.java");
- Compilation compilation = config.javac().compile(file);
- assertThat(compilation).failed();
- assertThat(compilation)
- .hadErrorContaining(
- "@Provided may only be applied to constructors requesting an auto-factory")
- .inFile(file)
- .onLineContaining("@Provided");
- }
-
- @Test
- public void providedOnMethodParameter() {
- JavaFileObject file = JavaFileObjects.forResource("bad/ProvidedOnMethodParameter.java");
- Compilation compilation = config.javac().compile(file);
- assertThat(compilation).failed();
- assertThat(compilation)
- .hadErrorContaining("@Provided may only be applied to constructor parameters")
- .inFile(file)
- .onLineContaining("@Provided");
- }
-
- @Test
- public void invalidCustomName() {
- JavaFileObject file = JavaFileObjects.forResource("bad/InvalidCustomName.java");
- Compilation compilation = config.javac().compile(file);
- assertThat(compilation).failed();
- assertThat(compilation)
- .hadErrorContaining("\"SillyFactory!\" is not a valid Java identifier")
- .inFile(file)
- .onLineContaining("SillyFactory!");
- }
-
@Test
public void factoryExtendingAbstractClass() {
goldenTest(
@@ -438,21 +414,6 @@ public void factoryWithConstructorThrowsClauseExtendingAbstractClass() {
"expected/FactoryExtendingAbstractClassThrowsFactory.java"));
}
- @Test
- public void factoryExtendingAbstractClass_withConstructorParams() {
- JavaFileObject file =
- JavaFileObjects.forResource("bad/FactoryExtendingAbstractClassWithConstructorParams.java");
- Compilation compilation = config.javac().compile(file);
- assertThat(compilation).failed();
- assertThat(compilation)
- .hadErrorContaining(
- "tests.FactoryExtendingAbstractClassWithConstructorParams.AbstractFactory is not a"
- + " valid supertype for a factory. Factory supertypes must have a no-arg"
- + " constructor.")
- .inFile(file)
- .onLineContaining("@AutoFactory");
- }
-
@Test
public void factoryExtendingAbstractClass_multipleConstructors() {
goldenTest(
@@ -460,45 +421,6 @@ public void factoryExtendingAbstractClass_multipleConstructors() {
ImmutableMap.of());
}
- @Test
- public void factoryExtendingInterface() {
- JavaFileObject file = JavaFileObjects.forResource("bad/InterfaceSupertype.java");
- Compilation compilation = config.javac().compile(file);
- assertThat(compilation).failed();
- assertThat(compilation)
- .hadErrorContaining(
- "java.lang.Runnable is not a valid supertype for a factory. Supertypes must be"
- + " non-final classes.")
- .inFile(file)
- .onLineContaining("@AutoFactory");
- }
-
- @Test
- public void factoryExtendingEnum() {
- JavaFileObject file = JavaFileObjects.forResource("bad/EnumSupertype.java");
- Compilation compilation = config.javac().compile(file);
- assertThat(compilation).failed();
- assertThat(compilation)
- .hadErrorContaining(
- "java.util.concurrent.TimeUnit is not a valid supertype for a factory. Supertypes must"
- + " be non-final classes.")
- .inFile(file)
- .onLineContaining("@AutoFactory");
- }
-
- @Test
- public void factoryExtendingFinalClass() {
- JavaFileObject file = JavaFileObjects.forResource("bad/FinalSupertype.java");
- Compilation compilation = config.javac().compile(file);
- assertThat(compilation).failed();
- assertThat(compilation)
- .hadErrorContaining(
- "java.lang.Boolean is not a valid supertype for a factory. Supertypes must be"
- + " non-final classes.")
- .inFile(file)
- .onLineContaining("@AutoFactory");
- }
-
@Test
public void factoryImplementingGenericInterfaceExtension() {
goldenTest(
@@ -684,6 +606,9 @@ private void rewriteImports(List sourceLines) {
? "import javax.annotation.Generated;"
: line);
}
+ if (config.expectedPackage.equals(InjectPackage.JAKARTA)) {
+ importLines.replaceAll(line -> line.replace("javax.inject", "jakarta.inject"));
+ }
Collections.sort(importLines);
}
}