DI improvements (#1717)
* Code cleanup
* Provide build path with causes when an exception occur
* Fix toString to display meaningful info
* Javadoc
* Add @NonNull and @Overrides annotations
* Support for @Nullable on fields and parameters
diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/MojoExecutionScoped.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/MojoExecutionScoped.java
index a3fec5c..729ec5e 100644
--- a/api/maven-api-di/src/main/java/org/apache/maven/api/di/MojoExecutionScoped.java
+++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/MojoExecutionScoped.java
@@ -29,6 +29,13 @@
/**
* Indicates that the annotated bean has a lifespan limited to a given mojo execution,
* which means each mojo execution will result in a different instance being injected.
+ * <p>
+ * The following objects will be bound to the mojo execution scope:
+ * <ul>
+ * <li>{@code org.apache.maven.api.MojoExecution}</li>
+ * <li>{@code org.apache.maven.api.Project}</li>
+ * <li>{@code org.apache.maven.api.plugin.Log}</li>
+ * </ul>
*
* @since 4.0.0
*/
diff --git a/api/maven-api-di/src/main/java/org/apache/maven/api/di/SessionScoped.java b/api/maven-api-di/src/main/java/org/apache/maven/api/di/SessionScoped.java
index eae72f8..06c1049 100644
--- a/api/maven-api-di/src/main/java/org/apache/maven/api/di/SessionScoped.java
+++ b/api/maven-api-di/src/main/java/org/apache/maven/api/di/SessionScoped.java
@@ -29,6 +29,8 @@
/**
* Indicates that annotated component should be instantiated before session execution starts
* and discarded after session execution completes.
+ * <p>
+ * A {@code org.apache.maven.api.Session} object is available in the scope of this annotation.
*
* @since 4.0.0
*/
diff --git a/api/maven-api-meta/src/main/java/org/apache/maven/api/annotations/Nullable.java b/api/maven-api-meta/src/main/java/org/apache/maven/api/annotations/Nullable.java
index 32e4bd7..df2517c 100644
--- a/api/maven-api-meta/src/main/java/org/apache/maven/api/annotations/Nullable.java
+++ b/api/maven-api-meta/src/main/java/org/apache/maven/api/annotations/Nullable.java
@@ -30,5 +30,5 @@
*/
@Experimental
@Documented
-@Retention(RetentionPolicy.CLASS)
+@Retention(RetentionPolicy.RUNTIME)
public @interface Nullable {}
diff --git a/maven-core/src/main/java/org/apache/maven/internal/impl/SisuDiBridgeModule.java b/maven-core/src/main/java/org/apache/maven/internal/impl/SisuDiBridgeModule.java
index ab12ee5..eb6789c 100644
--- a/maven-core/src/main/java/org/apache/maven/internal/impl/SisuDiBridgeModule.java
+++ b/maven-core/src/main/java/org/apache/maven/internal/impl/SisuDiBridgeModule.java
@@ -48,6 +48,7 @@
import org.apache.maven.di.Key;
import org.apache.maven.di.impl.Binding;
import org.apache.maven.di.impl.DIException;
+import org.apache.maven.di.impl.Dependency;
import org.apache.maven.di.impl.InjectorImpl;
import org.apache.maven.execution.scope.internal.MojoExecutionScope;
import org.apache.maven.session.scope.internal.SessionScope;
@@ -66,7 +67,8 @@ protected void configure() {
injector = new InjectorImpl() {
@Override
- public <Q> Supplier<Q> getCompiledBinding(Key<Q> key) {
+ public <Q> Supplier<Q> getCompiledBinding(Dependency<Q> dep) {
+ Key<Q> key = dep.key();
Set<Binding<Q>> res = getBindings(key);
if (res != null && !res.isEmpty()) {
List<Binding<Q>> bindingList = new ArrayList<>(res);
@@ -119,6 +121,9 @@ public <Q> Supplier<Q> getCompiledBinding(Key<Q> key) {
// ignore
e.printStackTrace();
}
+ if (dep.optional()) {
+ return () -> null;
+ }
throw new DIException("No binding to construct an instance for key "
+ key.getDisplayString() + ". Existing bindings:\n"
+ getBoundKeys().stream()
diff --git a/maven-di/src/main/java/org/apache/maven/di/Injector.java b/maven-di/src/main/java/org/apache/maven/di/Injector.java
index 9ccc1b6..d4eea98 100644
--- a/maven-di/src/main/java/org/apache/maven/di/Injector.java
+++ b/maven-di/src/main/java/org/apache/maven/di/Injector.java
@@ -21,6 +21,7 @@
import java.lang.annotation.Annotation;
import java.util.function.Supplier;
+import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.di.impl.InjectorImpl;
public interface Injector {
@@ -29,18 +30,24 @@ public interface Injector {
// Builder API
//
+ @Nonnull
static Injector create() {
return new InjectorImpl();
}
+ @Nonnull
Injector discover(ClassLoader classLoader);
+ @Nonnull
Injector bindScope(Class<? extends Annotation> scopeAnnotation, Scope scope);
+ @Nonnull
Injector bindScope(Class<? extends Annotation> scopeAnnotation, Supplier<Scope> scope);
+ @Nonnull
Injector bindImplicit(Class<?> cls);
+ @Nonnull
<T> Injector bindInstance(Class<T> cls, T instance);
//
diff --git a/maven-di/src/main/java/org/apache/maven/di/impl/Binding.java b/maven-di/src/main/java/org/apache/maven/di/impl/Binding.java
index 97e650a..204c07a 100644
--- a/maven-di/src/main/java/org/apache/maven/di/impl/Binding.java
+++ b/maven-di/src/main/java/org/apache/maven/di/impl/Binding.java
@@ -34,16 +34,16 @@
import static java.util.stream.Collectors.joining;
public abstract class Binding<T> {
- private final Set<Key<?>> dependencies;
+ private final Set<Dependency<?>> dependencies;
private Annotation scope;
private int priority;
private Key<?> originalKey;
- protected Binding(Key<? extends T> originalKey, Set<Key<?>> dependencies) {
+ protected Binding(Key<? extends T> originalKey, Set<Dependency<?>> dependencies) {
this(originalKey, dependencies, null, 0);
}
- protected Binding(Key<?> originalKey, Set<Key<?>> dependencies, Annotation scope, int priority) {
+ protected Binding(Key<?> originalKey, Set<Dependency<?>> dependencies, Annotation scope, int priority) {
this.originalKey = originalKey;
this.dependencies = dependencies;
this.scope = scope;
@@ -56,15 +56,18 @@ public static <T> Binding<T> toInstance(T instance) {
public static <R> Binding<R> to(Key<R> originalKey, TupleConstructorN<R> constructor, Class<?>[] types) {
return Binding.to(
- originalKey, constructor, Stream.of(types).map(Key::of).toArray(Key<?>[]::new));
+ originalKey,
+ constructor,
+ Stream.of(types).map(c -> new Dependency<>(Key.of(c), false)).toArray(Dependency<?>[]::new));
}
- public static <R> Binding<R> to(Key<R> originalKey, TupleConstructorN<R> constructor, Key<?>[] dependencies) {
+ public static <R> Binding<R> to(
+ Key<R> originalKey, TupleConstructorN<R> constructor, Dependency<?>[] dependencies) {
return to(originalKey, constructor, dependencies, 0);
}
public static <R> Binding<R> to(
- Key<R> originalKey, TupleConstructorN<R> constructor, Key<?>[] dependencies, int priority) {
+ Key<R> originalKey, TupleConstructorN<R> constructor, Dependency<?>[] dependencies, int priority) {
return new BindingToConstructor<>(originalKey, constructor, dependencies, priority);
}
@@ -94,13 +97,17 @@ public Binding<T> initializeWith(BindingInitializer<T> bindingInitializer) {
this.scope,
this.priority) {
@Override
- public Supplier<T> compile(Function<Key<?>, Supplier<?>> compiler) {
+ public Supplier<T> compile(Function<Dependency<?>, Supplier<?>> compiler) {
final Supplier<T> compiledBinding = Binding.this.compile(compiler);
final Consumer<T> consumer = bindingInitializer.compile(compiler);
return () -> {
- T instance = compiledBinding.get();
- consumer.accept(instance);
- return instance;
+ try {
+ T instance = compiledBinding.get();
+ consumer.accept(instance);
+ return instance;
+ } catch (DIException e) {
+ throw new DIException("Error while initializing binding " + Binding.this, e);
+ }
};
}
@@ -111,9 +118,9 @@ public String toString() {
};
}
- public abstract Supplier<T> compile(Function<Key<?>, Supplier<?>> compiler);
+ public abstract Supplier<T> compile(Function<Dependency<?>, Supplier<?>> compiler);
- public Set<Key<?>> getDependencies() {
+ public Set<Dependency<?>> getDependencies() {
return dependencies;
}
@@ -122,7 +129,7 @@ public Annotation getScope() {
}
public String getDisplayString() {
- return dependencies.stream().map(Key::getDisplayString).collect(joining(", ", "[", "]"));
+ return dependencies.stream().map(Dependency::getDisplayString).collect(joining(", ", "[", "]"));
}
public Key<?> getOriginalKey() {
@@ -152,7 +159,7 @@ public BindingToInstance(T instance) {
}
@Override
- public Supplier<T> compile(Function<Key<?>, Supplier<?>> compiler) {
+ public Supplier<T> compile(Function<Dependency<?>, Supplier<?>> compiler) {
return () -> instance;
}
@@ -164,17 +171,17 @@ public String toString() {
public static class BindingToConstructor<T> extends Binding<T> {
final TupleConstructorN<T> constructor;
- final Key<?>[] args;
+ final Dependency<?>[] args;
BindingToConstructor(
- Key<? extends T> key, TupleConstructorN<T> constructor, Key<?>[] dependencies, int priority) {
+ Key<? extends T> key, TupleConstructorN<T> constructor, Dependency<?>[] dependencies, int priority) {
super(key, new HashSet<>(Arrays.asList(dependencies)), null, priority);
this.constructor = constructor;
this.args = dependencies;
}
@Override
- public Supplier<T> compile(Function<Key<?>, Supplier<?>> compiler) {
+ public Supplier<T> compile(Function<Dependency<?>, Supplier<?>> compiler) {
return () -> {
Object[] args =
Stream.of(this.args).map(compiler).map(Supplier::get).toArray();
@@ -184,7 +191,7 @@ public Supplier<T> compile(Function<Key<?>, Supplier<?>> compiler) {
@Override
public String toString() {
- return "BindingToConstructor[" + constructor + "]" + getDependencies();
+ return "BindingToConstructor[" + getOriginalKey() + "]" + getDependencies();
}
}
}
diff --git a/maven-di/src/main/java/org/apache/maven/di/impl/BindingInitializer.java b/maven-di/src/main/java/org/apache/maven/di/impl/BindingInitializer.java
index a123f30..ccf5530 100644
--- a/maven-di/src/main/java/org/apache/maven/di/impl/BindingInitializer.java
+++ b/maven-di/src/main/java/org/apache/maven/di/impl/BindingInitializer.java
@@ -25,32 +25,30 @@
import java.util.function.Function;
import java.util.function.Supplier;
-import org.apache.maven.di.Key;
-
import static java.util.stream.Collectors.toSet;
public abstract class BindingInitializer<T> {
- private final Set<Key<?>> dependencies;
+ private final Set<Dependency<?>> dependencies;
- protected BindingInitializer(Set<Key<?>> dependencies) {
+ protected BindingInitializer(Set<Dependency<?>> dependencies) {
this.dependencies = dependencies;
}
- public Set<Key<?>> getDependencies() {
+ public Set<Dependency<?>> getDependencies() {
return dependencies;
}
- public abstract Consumer<T> compile(Function<Key<?>, Supplier<?>> compiler);
+ public abstract Consumer<T> compile(Function<Dependency<?>, Supplier<?>> compiler);
public static <T> BindingInitializer<T> combine(List<BindingInitializer<T>> bindingInitializers) {
- Set<Key<?>> deps = bindingInitializers.stream()
+ Set<Dependency<?>> deps = bindingInitializers.stream()
.map(BindingInitializer::getDependencies)
.flatMap(Collection::stream)
.collect(toSet());
- return new BindingInitializer<T>(deps) {
+ return new BindingInitializer<>(deps) {
@Override
- public Consumer<T> compile(Function<Key<?>, Supplier<?>> compiler) {
+ public Consumer<T> compile(Function<Dependency<?>, Supplier<?>> compiler) {
return instance -> bindingInitializers.stream()
.map(bindingInitializer -> bindingInitializer.compile(compiler))
.forEach(i -> i.accept(instance));
diff --git a/maven-di/src/main/java/org/apache/maven/di/impl/Dependency.java b/maven-di/src/main/java/org/apache/maven/di/impl/Dependency.java
new file mode 100644
index 0000000..4dc9052
--- /dev/null
+++ b/maven-di/src/main/java/org/apache/maven/di/impl/Dependency.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.maven.di.impl;
+
+import org.apache.maven.di.Key;
+
+public record Dependency<T>(Key<T> key, boolean optional) {
+ public String getDisplayString() {
+ String s = key.getDisplayString();
+ if (optional) {
+ s = "?" + s;
+ }
+ return s;
+ }
+}
diff --git a/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java b/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java
index 143f521..1e047f5 100644
--- a/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java
+++ b/maven-di/src/main/java/org/apache/maven/di/impl/InjectorImpl.java
@@ -64,12 +64,14 @@ public InjectorImpl() {
bindScope(Singleton.class, new SingletonScope());
}
+ @Override
public <T> T getInstance(Class<T> key) {
return getInstance(Key.of(key));
}
+ @Override
public <T> T getInstance(Key<T> key) {
- return getCompiledBinding(key).get();
+ return getCompiledBinding(new Dependency<>(key, false)).get();
}
@SuppressWarnings("unchecked")
@@ -88,7 +90,7 @@ public Injector discover(ClassLoader classLoader) {
try (InputStream is = enumeration.nextElement().openStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(is)))) {
for (String line :
- reader.lines().filter(l -> !l.startsWith("#")).collect(Collectors.toList())) {
+ reader.lines().filter(l -> !l.startsWith("#")).toList()) {
Class<?> clazz = classLoader.loadClass(line);
bindImplicit(clazz);
}
@@ -100,10 +102,12 @@ public Injector discover(ClassLoader classLoader) {
return this;
}
+ @Override
public Injector bindScope(Class<? extends Annotation> scopeAnnotation, Scope scope) {
return bindScope(scopeAnnotation, () -> scope);
}
+ @Override
public Injector bindScope(Class<? extends Annotation> scopeAnnotation, Supplier<Scope> scope) {
if (scopes.put(scopeAnnotation, scope) != null) {
throw new DIException(
@@ -112,6 +116,7 @@ public Injector bindScope(Class<? extends Annotation> scopeAnnotation, Supplier<
return this;
}
+ @Override
public <U> Injector bindInstance(Class<U> clazz, U instance) {
Key<?> key = Key.of(clazz, ReflectionUtils.qualifierOf(clazz));
Binding<U> binding = Binding.toInstance(instance);
@@ -133,7 +138,7 @@ public Injector bindImplicit(Class<?> clazz) {
return this;
}
- private LinkedHashSet<Key<?>> current = new LinkedHashSet<>();
+ private final LinkedHashSet<Key<?>> current = new LinkedHashSet<>();
private Injector doBind(Key<?> key, Binding<?> binding) {
if (!current.add(key)) {
@@ -175,7 +180,8 @@ public Map<Key<?>, Set<Binding<?>>> getBindings() {
return bindings;
}
- public <Q> Supplier<Q> getCompiledBinding(Key<Q> key) {
+ public <Q> Supplier<Q> getCompiledBinding(Dependency<Q> dep) {
+ Key<Q> key = dep.key();
Set<Binding<Q>> res = getBindings(key);
if (res != null && !res.isEmpty()) {
List<Binding<Q>> bindingList = new ArrayList<>(res);
@@ -211,6 +217,9 @@ public <Q> Supplier<Q> getCompiledBinding(Key<Q> key) {
return () -> (Q) map(map);
}
}
+ if (dep.optional()) {
+ return () -> null;
+ }
throw new DIException("No binding to construct an instance for key "
+ key.getDisplayString() + ". Existing bindings:\n"
+ getBoundKeys().stream()
@@ -312,14 +321,13 @@ private static class WrappingMap<K, V, T> extends AbstractMap<K, V> {
this.mapper = mapper;
}
- @SuppressWarnings("NullableProblems")
@Override
public Set<Entry<K, V>> entrySet() {
- return new AbstractSet<Entry<K, V>>() {
+ return new AbstractSet<>() {
@Override
public Iterator<Entry<K, V>> iterator() {
Iterator<Entry<K, T>> it = delegate.entrySet().iterator();
- return new Iterator<Entry<K, V>>() {
+ return new Iterator<>() {
@Override
public boolean hasNext() {
return it.hasNext();
diff --git a/maven-di/src/main/java/org/apache/maven/di/impl/ReflectionUtils.java b/maven-di/src/main/java/org/apache/maven/di/impl/ReflectionUtils.java
index 4389138..3d49a28 100644
--- a/maven-di/src/main/java/org/apache/maven/di/impl/ReflectionUtils.java
+++ b/maven-di/src/main/java/org/apache/maven/di/impl/ReflectionUtils.java
@@ -171,14 +171,14 @@ public static <T extends AnnotatedElement & Member> List<T> getAnnotatedElements
List<Constructor<?>> constructors = Arrays.asList(cls.getDeclaredConstructors());
List<Constructor<?>> injectConstructors = constructors.stream()
.filter(c -> c.isAnnotationPresent(Inject.class))
- .collect(toList());
+ .toList();
List<Method> factoryMethods = Arrays.stream(cls.getDeclaredMethods())
.filter(method -> method.getReturnType() == cls && Modifier.isStatic(method.getModifiers()))
- .collect(toList());
+ .toList();
List<Method> injectFactoryMethods = factoryMethods.stream()
.filter(method -> method.isAnnotationPresent(Inject.class))
- .collect(toList());
+ .toList();
if (!injectConstructors.isEmpty()) {
if (injectConstructors.size() > 1) {
@@ -240,10 +240,12 @@ public static <T> BindingInitializer<T> generateInjectingInitializer(Key<T> cont
public static <T> BindingInitializer<T> fieldInjector(Key<T> container, Field field) {
field.setAccessible(true);
Key<Object> key = keyOf(container.getType(), field.getGenericType(), field);
- return new BindingInitializer<T>(Collections.singleton(key)) {
+ boolean optional = field.isAnnotationPresent(Nullable.class);
+ Dependency<Object> dep = new Dependency<>(key, optional);
+ return new BindingInitializer<T>(Collections.singleton(dep)) {
@Override
- public Consumer<T> compile(Function<Key<?>, Supplier<?>> compiler) {
- Supplier<?> binding = compiler.apply(key);
+ public Consumer<T> compile(Function<Dependency<?>, Supplier<?>> compiler) {
+ Supplier<?> binding = compiler.apply(dep);
return (T instance) -> {
Object arg = binding.get();
try {
@@ -258,10 +260,10 @@ public Consumer<T> compile(Function<Key<?>, Supplier<?>> compiler) {
public static <T> BindingInitializer<T> methodInjector(Key<T> container, Method method) {
method.setAccessible(true);
- Key<?>[] dependencies = toDependencies(container.getType(), method);
+ Dependency<?>[] dependencies = toDependencies(container.getType(), method);
return new BindingInitializer<T>(new HashSet<>(Arrays.asList(dependencies))) {
@Override
- public Consumer<T> compile(Function<Key<?>, Supplier<?>> compiler) {
+ public Consumer<T> compile(Function<Dependency<?>, Supplier<?>> compiler) {
return instance -> {
Object[] args = getDependencies().stream()
.map(compiler)
@@ -279,35 +281,31 @@ public Consumer<T> compile(Function<Key<?>, Supplier<?>> compiler) {
};
}
- public static Key<?>[] toDependencies(@Nullable Type container, Executable executable) {
- Key<?>[] keys = toArgDependencies(container, executable);
+ public static Dependency<?>[] toDependencies(@Nullable Type container, Executable executable) {
+ Dependency<?>[] keys = toArgDependencies(container, executable);
if (executable instanceof Constructor || Modifier.isStatic(executable.getModifiers())) {
return keys;
} else {
- Key<?>[] nkeys = new Key[keys.length + 1];
- nkeys[0] = Key.ofType(container);
+ Dependency<?>[] nkeys = new Dependency[keys.length + 1];
+ nkeys[0] = new Dependency<>(Key.ofType(container), false);
System.arraycopy(keys, 0, nkeys, 1, keys.length);
return nkeys;
}
}
- private static Key<?>[] toArgDependencies(@Nullable Type container, Executable executable) {
+ private static Dependency<?>[] toArgDependencies(@Nullable Type container, Executable executable) {
Parameter[] parameters = executable.getParameters();
- Key<?>[] dependencies = new Key<?>[parameters.length];
+ Dependency<?>[] dependencies = new Dependency<?>[parameters.length];
if (parameters.length == 0) {
return dependencies;
}
- Type type = parameters[0].getParameterizedType();
- Parameter parameter = parameters[0];
- dependencies[0] = keyOf(container, type, parameter);
-
Type[] genericParameterTypes = executable.getGenericParameterTypes();
- boolean hasImplicitDependency = genericParameterTypes.length != parameters.length;
- for (int i = 1; i < dependencies.length; i++) {
- type = genericParameterTypes[hasImplicitDependency ? i - 1 : i];
- parameter = parameters[i];
- dependencies[i] = keyOf(container, type, parameter);
+ for (int i = 0; i < dependencies.length; i++) {
+ Type type = genericParameterTypes[i];
+ Parameter parameter = parameters[i];
+ boolean optional = parameter.isAnnotationPresent(Nullable.class);
+ dependencies[i] = new Dependency<>(keyOf(container, type, parameter), optional);
}
return dependencies;
}
@@ -353,7 +351,7 @@ public static <T> Binding<T> bindingFromMethod(Method method) {
public static <T> Binding<T> bindingFromConstructor(Key<T> key, Constructor<T> constructor) {
constructor.setAccessible(true);
- Key<?>[] dependencies = toDependencies(key.getType(), constructor);
+ Dependency<?>[] dependencies = toDependencies(key.getType(), constructor);
Binding<T> binding = Binding.to(
key,
diff --git a/maven-di/src/main/java/org/apache/maven/di/impl/Types.java b/maven-di/src/main/java/org/apache/maven/di/impl/Types.java
index 735057b..b4830bd 100644
--- a/maven-di/src/main/java/org/apache/maven/di/impl/Types.java
+++ b/maven-di/src/main/java/org/apache/maven/di/impl/Types.java
@@ -186,8 +186,7 @@ public static Type bind(Type type, Function<TypeVariable<?>, Type> bindings) {
if (type instanceof Class) {
return type;
}
- if (type instanceof TypeVariable<?>) {
- TypeVariable<?> typeVariable = (TypeVariable<?>) type;
+ if (type instanceof TypeVariable<?> typeVariable) {
Type actualType = bindings.apply(typeVariable);
if (actualType == null) {
throw new TypeNotBoundException("Type variable not found: " + typeVariable + " ( "
@@ -195,8 +194,7 @@ public static Type bind(Type type, Function<TypeVariable<?>, Type> bindings) {
}
return actualType;
}
- if (type instanceof ParameterizedType) {
- ParameterizedType parameterizedType = (ParameterizedType) type;
+ if (type instanceof ParameterizedType parameterizedType) {
Type[] typeArguments = parameterizedType.getActualTypeArguments();
Type[] typeArguments2 = new Type[typeArguments.length];
for (int i = 0; i < typeArguments.length; i++) {
@@ -209,8 +207,7 @@ public static Type bind(Type type, Function<TypeVariable<?>, Type> bindings) {
Type componentType = ((GenericArrayType) type).getGenericComponentType();
return new GenericArrayTypeImpl(bind(componentType, bindings));
}
- if (type instanceof WildcardType) {
- WildcardType wildcardType = (WildcardType) type;
+ if (type instanceof WildcardType wildcardType) {
Type[] upperBounds = wildcardType.getUpperBounds();
Type[] upperBounds2 = new Type[upperBounds.length];
for (int i = 0; i < upperBounds.length; i++) {
@@ -309,8 +306,7 @@ public static Type simplifyType(Type original) {
return original;
}
- if (original instanceof ParameterizedType) {
- ParameterizedType parameterizedType = (ParameterizedType) original;
+ if (original instanceof ParameterizedType parameterizedType) {
Type[] typeArguments = parameterizedType.getActualTypeArguments();
Type[] repackedTypeArguments = simplifyTypes(typeArguments);
@@ -329,8 +325,7 @@ public static Type simplifyType(Type original) {
throw new IllegalArgumentException("Key should not contain a type variable: " + original);
}
- if (original instanceof WildcardType) {
- WildcardType wildcardType = (WildcardType) original;
+ if (original instanceof WildcardType wildcardType) {
Type[] upperBounds = wildcardType.getUpperBounds();
if (upperBounds.length == 1) {
Type upperBound = upperBounds[0];
@@ -398,8 +393,7 @@ public static boolean isAssignable(Type to, Type from) {
private static boolean isAssignable(Type to, Type from, boolean strict) {
if (to instanceof WildcardType || from instanceof WildcardType) {
Type[] toUppers, toLowers;
- if (to instanceof WildcardType) {
- WildcardType wildcardTo = (WildcardType) to;
+ if (to instanceof WildcardType wildcardTo) {
toUppers = wildcardTo.getUpperBounds();
toLowers = wildcardTo.getLowerBounds();
} else {
@@ -408,10 +402,9 @@ private static boolean isAssignable(Type to, Type from, boolean strict) {
}
Type[] fromUppers, fromLowers;
- if (from instanceof WildcardType) {
- WildcardType wildcardTo = (WildcardType) to;
- fromUppers = wildcardTo.getUpperBounds();
- fromLowers = wildcardTo.getLowerBounds();
+ if (from instanceof WildcardType wildcardFrom) {
+ fromUppers = wildcardFrom.getUpperBounds();
+ fromLowers = wildcardFrom.getLowerBounds();
} else {
fromUppers = new Type[] {from};
fromLowers = strict ? fromUppers : NO_TYPES;
@@ -523,10 +516,9 @@ public int hashCode() {
@Override
public boolean equals(Object other) {
- if (!(other instanceof ParameterizedType)) {
+ if (!(other instanceof ParameterizedType that)) {
return false;
}
- ParameterizedType that = (ParameterizedType) other;
return this.getRawType().equals(that.getRawType())
&& Objects.equals(this.getOwnerType(), that.getOwnerType())
&& Arrays.equals(this.getActualTypeArguments(), that.getActualTypeArguments());
@@ -613,10 +605,9 @@ public int hashCode() {
@Override
public boolean equals(Object other) {
- if (!(other instanceof WildcardType)) {
+ if (!(other instanceof WildcardType that)) {
return false;
}
- WildcardType that = (WildcardType) other;
return Arrays.equals(this.getUpperBounds(), that.getUpperBounds())
&& Arrays.equals(this.getLowerBounds(), that.getLowerBounds());
}
@@ -671,10 +662,9 @@ public int hashCode() {
@Override
public boolean equals(Object other) {
- if (!(other instanceof GenericArrayType)) {
+ if (!(other instanceof GenericArrayType that)) {
return false;
}
- GenericArrayType that = (GenericArrayType) other;
return this.getGenericComponentType().equals(that.getGenericComponentType());
}
@@ -700,8 +690,7 @@ public static String getSimpleName(Type type) {
return Arrays.stream(((ParameterizedType) type).getActualTypeArguments())
.map(Types::getSimpleName)
.collect(joining(",", "<", ">"));
- } else if (type instanceof WildcardType) {
- WildcardType wildcardType = (WildcardType) type;
+ } else if (type instanceof WildcardType wildcardType) {
Type[] upperBounds = wildcardType.getUpperBounds();
Type[] lowerBounds = wildcardType.getLowerBounds();
return "?"
diff --git a/maven-di/src/test/java/org/apache/maven/di/impl/InjectorImplTest.java b/maven-di/src/test/java/org/apache/maven/di/impl/InjectorImplTest.java
index b732fe8..0d22acf 100644
--- a/maven-di/src/test/java/org/apache/maven/di/impl/InjectorImplTest.java
+++ b/maven-di/src/test/java/org/apache/maven/di/impl/InjectorImplTest.java
@@ -25,6 +25,7 @@
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.maven.api.annotations.Nullable;
import org.apache.maven.api.di.Inject;
import org.apache.maven.api.di.Named;
import org.apache.maven.api.di.Priority;
@@ -42,6 +43,7 @@
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertNull;
@SuppressWarnings("unused")
public class InjectorImplTest {
@@ -328,4 +330,49 @@ static class Another {}
@Named
static class Third {}
}
+
+ @Test
+ void testNullableOnField() {
+ Injector injector = Injector.create().bindImplicit(NullableOnField.class);
+ NullableOnField.MyMojo mojo = injector.getInstance(NullableOnField.MyMojo.class);
+ assertNotNull(mojo);
+ assertNull(mojo.service);
+ }
+
+ static class NullableOnField {
+
+ @Named
+ interface MyService {}
+
+ @Named
+ static class MyMojo {
+ @Inject
+ @Nullable
+ MyService service;
+ }
+ }
+
+ @Test
+ void testNullableOnConstructor() {
+ Injector injector = Injector.create().bindImplicit(NullableOnConstructor.class);
+ NullableOnConstructor.MyMojo mojo = injector.getInstance(NullableOnConstructor.MyMojo.class);
+ assertNotNull(mojo);
+ assertNull(mojo.service);
+ }
+
+ static class NullableOnConstructor {
+
+ @Named
+ interface MyService {}
+
+ @Named
+ static class MyMojo {
+ private final MyService service;
+
+ @Inject
+ public MyMojo(@Nullable MyService service) {
+ this.service = service;
+ }
+ }
+ }
}