mirror of https://github.com/apache/maven.git
[MNG-7954] New dependency injection mechanism (#1393)
This commit is contained in:
parent
3f9fec2307
commit
a37cf3d37f
|
@ -52,8 +52,8 @@
|
||||||
<artifactId>maven-api-plugin</artifactId>
|
<artifactId>maven-api-plugin</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>jakarta.inject</groupId>
|
<groupId>org.apache.maven</groupId>
|
||||||
<artifactId>jakarta.inject-api</artifactId>
|
<artifactId>maven-api-di</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,6 @@ import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
import jakarta.inject.Scope;
|
|
||||||
|
|
||||||
import static java.lang.annotation.ElementType.METHOD;
|
import static java.lang.annotation.ElementType.METHOD;
|
||||||
import static java.lang.annotation.ElementType.TYPE;
|
import static java.lang.annotation.ElementType.TYPE;
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
@ -32,6 +30,8 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
* Indicates that the annotated bean has a lifespan limited to a given mojo execution,
|
* 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.
|
* which means each mojo execution will result in a different instance being injected.
|
||||||
*
|
*
|
||||||
|
* TODO: this is currently not implemented
|
||||||
|
*
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
@Scope
|
@Scope
|
||||||
|
|
|
@ -22,8 +22,6 @@ import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
import jakarta.inject.Scope;
|
|
||||||
|
|
||||||
import static java.lang.annotation.ElementType.METHOD;
|
import static java.lang.annotation.ElementType.METHOD;
|
||||||
import static java.lang.annotation.ElementType.TYPE;
|
import static java.lang.annotation.ElementType.TYPE;
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
@ -32,6 +30,8 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
* Indicates that annotated component should be instantiated before session execution starts
|
* Indicates that annotated component should be instantiated before session execution starts
|
||||||
* and discarded after session execution completes.
|
* and discarded after session execution completes.
|
||||||
*
|
*
|
||||||
|
* TODO: this is currently not implemented
|
||||||
|
*
|
||||||
* @since 4.0.0
|
* @since 4.0.0
|
||||||
*/
|
*/
|
||||||
@Scope
|
@Scope
|
||||||
|
|
|
@ -35,7 +35,7 @@ import org.apache.maven.api.annotations.Nonnull;
|
||||||
*/
|
*/
|
||||||
@Experimental
|
@Experimental
|
||||||
@Documented
|
@Documented
|
||||||
@Retention(RetentionPolicy.CLASS)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Inherited
|
@Inherited
|
||||||
public @interface Execute {
|
public @interface Execute {
|
||||||
|
|
|
@ -38,7 +38,7 @@ import org.apache.maven.api.annotations.Nonnull;
|
||||||
*/
|
*/
|
||||||
@Experimental
|
@Experimental
|
||||||
@Documented
|
@Documented
|
||||||
@Retention(RetentionPolicy.CLASS)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Inherited
|
@Inherited
|
||||||
public @interface Mojo {
|
public @interface Mojo {
|
||||||
|
|
|
@ -41,7 +41,7 @@ import org.apache.maven.api.annotations.Nonnull;
|
||||||
*/
|
*/
|
||||||
@Experimental
|
@Experimental
|
||||||
@Documented
|
@Documented
|
||||||
@Retention(RetentionPolicy.CLASS)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||||
@Inherited
|
@Inherited
|
||||||
public @interface Parameter {
|
public @interface Parameter {
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-api</artifactId>
|
||||||
|
<version>4.0.0-alpha-13-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>maven-api-di</artifactId>
|
||||||
|
<name>Maven 4 API :: Dependency Injection</name>
|
||||||
|
<description>Maven 4 API - Dependency Injection</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-api-meta</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
|
@ -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.api.di;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.*;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
@Target({FIELD, CONSTRUCTOR, METHOD})
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface Inject {}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* 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.api.di;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.*;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface Named {
|
||||||
|
String value() default "";
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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.api.di;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.METHOD;
|
||||||
|
import static java.lang.annotation.ElementType.TYPE;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
@Target({TYPE, METHOD})
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface Priority {
|
||||||
|
int value();
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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.api.di;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.METHOD;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used on a static method to provide a bean.
|
||||||
|
*/
|
||||||
|
@Target(METHOD)
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface Provides {}
|
|
@ -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.api.di;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
@Target(ANNOTATION_TYPE)
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface Qualifier {}
|
|
@ -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.api.di;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
@Target(ANNOTATION_TYPE)
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface Scope {}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* 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.api.di;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
@Scope
|
||||||
|
@Documented
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
public @interface Singleton {}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* 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.api.di;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.*;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
@Target({FIELD, METHOD, TYPE})
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface Typed {
|
||||||
|
Class<?>[] value() default {};
|
||||||
|
}
|
|
@ -33,6 +33,7 @@
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>maven-api-meta</module>
|
<module>maven-api-meta</module>
|
||||||
|
<module>maven-api-di</module>
|
||||||
<module>maven-api-xml</module>
|
<module>maven-api-xml</module>
|
||||||
<module>maven-api-model</module>
|
<module>maven-api-model</module>
|
||||||
<module>maven-api-plugin</module>
|
<module>maven-api-plugin</module>
|
||||||
|
|
|
@ -33,6 +33,10 @@ under the License.
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- Maven -->
|
<!-- Maven -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-di</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.maven</groupId>
|
<groupId>org.apache.maven</groupId>
|
||||||
<artifactId>maven-model</artifactId>
|
<artifactId>maven-model</artifactId>
|
||||||
|
|
|
@ -52,16 +52,26 @@ public class EnhancedComponentConfigurator extends BasicComponentConfigurator {
|
||||||
try {
|
try {
|
||||||
ClassRealmConverter.pushContextRealm(realm);
|
ClassRealmConverter.pushContextRealm(realm);
|
||||||
|
|
||||||
new EnhancedConfigurationConverter()
|
this.configureComponent(component, configuration, evaluator, (ClassLoader) realm, listener);
|
||||||
.processConfiguration(
|
|
||||||
converterLookup,
|
|
||||||
component,
|
|
||||||
realm, //
|
|
||||||
configuration,
|
|
||||||
evaluator,
|
|
||||||
listener);
|
|
||||||
} finally {
|
} finally {
|
||||||
ClassRealmConverter.popContextRealm();
|
ClassRealmConverter.popContextRealm();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void configureComponent(
|
||||||
|
Object component,
|
||||||
|
PlexusConfiguration configuration,
|
||||||
|
ExpressionEvaluator evaluator,
|
||||||
|
ClassLoader loader,
|
||||||
|
ConfigurationListener listener)
|
||||||
|
throws ComponentConfigurationException {
|
||||||
|
new EnhancedConfigurationConverter()
|
||||||
|
.processConfiguration(
|
||||||
|
converterLookup,
|
||||||
|
component,
|
||||||
|
loader, //
|
||||||
|
configuration,
|
||||||
|
evaluator,
|
||||||
|
listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class MojoExecutionScopeModule extends AbstractModule {
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
bindScope(MojoExecutionScoped.class, scope);
|
bindScope(MojoExecutionScoped.class, scope);
|
||||||
bindScope(org.apache.maven.api.di.MojoExecutionScoped.class, scope);
|
// bindScope(org.apache.maven.api.di.MojoExecutionScoped.class, scope);
|
||||||
bind(MojoExecutionScope.class).toInstance(scope);
|
bind(MojoExecutionScope.class).toInstance(scope);
|
||||||
bind(MavenProject.class)
|
bind(MavenProject.class)
|
||||||
.toProvider(MojoExecutionScope.seededKeyProvider())
|
.toProvider(MojoExecutionScope.seededKeyProvider())
|
||||||
|
|
|
@ -22,31 +22,25 @@ import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.*;
|
||||||
import java.io.File;
|
import java.lang.annotation.Annotation;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.jar.JarFile;
|
import java.util.jar.JarFile;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
|
|
||||||
import com.google.inject.AbstractModule;
|
|
||||||
import com.google.inject.name.Names;
|
|
||||||
import org.apache.maven.RepositoryUtils;
|
import org.apache.maven.RepositoryUtils;
|
||||||
|
import org.apache.maven.api.Project;
|
||||||
|
import org.apache.maven.api.Session;
|
||||||
import org.apache.maven.api.xml.XmlNode;
|
import org.apache.maven.api.xml.XmlNode;
|
||||||
import org.apache.maven.artifact.Artifact;
|
import org.apache.maven.artifact.Artifact;
|
||||||
import org.apache.maven.classrealm.ClassRealmManager;
|
import org.apache.maven.classrealm.ClassRealmManager;
|
||||||
|
import org.apache.maven.di.Injector;
|
||||||
import org.apache.maven.execution.MavenSession;
|
import org.apache.maven.execution.MavenSession;
|
||||||
import org.apache.maven.execution.scope.internal.MojoExecutionScope;
|
import org.apache.maven.execution.scope.internal.MojoExecutionScope;
|
||||||
import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
|
import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
|
||||||
|
import org.apache.maven.internal.impl.DefaultLog;
|
||||||
import org.apache.maven.internal.impl.DefaultMojoExecution;
|
import org.apache.maven.internal.impl.DefaultMojoExecution;
|
||||||
import org.apache.maven.internal.impl.InternalSession;
|
import org.apache.maven.internal.impl.InternalSession;
|
||||||
import org.apache.maven.internal.xml.XmlPlexusConfiguration;
|
import org.apache.maven.internal.xml.XmlPlexusConfiguration;
|
||||||
|
@ -429,29 +423,6 @@ public class DefaultMavenPluginManager implements MavenPluginManager {
|
||||||
((DefaultPlexusContainer) container)
|
((DefaultPlexusContainer) container)
|
||||||
.discoverComponents(
|
.discoverComponents(
|
||||||
pluginRealm,
|
pluginRealm,
|
||||||
new AbstractModule() {
|
|
||||||
@Override
|
|
||||||
protected void configure() {
|
|
||||||
if (pluginDescriptor != null) {
|
|
||||||
for (MojoDescriptor mojo : pluginDescriptor.getMojos()) {
|
|
||||||
if (mojo.isV4Api()) {
|
|
||||||
try {
|
|
||||||
mojo.setRealm(pluginRealm);
|
|
||||||
Class<?> cl = mojo.getImplementationClass();
|
|
||||||
if (cl == null) {
|
|
||||||
cl = pluginRealm.loadClass(mojo.getImplementation());
|
|
||||||
}
|
|
||||||
bind(org.apache.maven.api.plugin.Mojo.class)
|
|
||||||
.annotatedWith(Names.named(mojo.getId()))
|
|
||||||
.to((Class) cl);
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
throw new IllegalStateException("Unable to load mojo class", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new SessionScopeModule(container.lookup(SessionScope.class)),
|
new SessionScopeModule(container.lookup(SessionScope.class)),
|
||||||
new MojoExecutionScopeModule(container.lookup(MojoExecutionScope.class)),
|
new MojoExecutionScopeModule(container.lookup(MojoExecutionScope.class)),
|
||||||
new PluginConfigurationModule(plugin.getDelegate()));
|
new PluginConfigurationModule(plugin.getDelegate()));
|
||||||
|
@ -528,15 +499,101 @@ public class DefaultMavenPluginManager implements MavenPluginManager {
|
||||||
Thread.currentThread().setContextClassLoader(pluginRealm);
|
Thread.currentThread().setContextClassLoader(pluginRealm);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (mojoDescriptor.isV4Api()) {
|
||||||
|
return loadV4Mojo(mojoInterface, session, mojoExecution, mojoDescriptor, pluginDescriptor, pluginRealm);
|
||||||
|
} else {
|
||||||
|
return loadV3Mojo(mojoInterface, session, mojoExecution, mojoDescriptor, pluginDescriptor, pluginRealm);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Thread.currentThread().setContextClassLoader(oldClassLoader);
|
||||||
|
container.setLookupRealm(oldLookupRealm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> T loadV4Mojo(
|
||||||
|
Class<T> mojoInterface,
|
||||||
|
MavenSession session,
|
||||||
|
MojoExecution mojoExecution,
|
||||||
|
MojoDescriptor mojoDescriptor,
|
||||||
|
PluginDescriptor pluginDescriptor,
|
||||||
|
ClassRealm pluginRealm)
|
||||||
|
throws PluginContainerException, PluginConfigurationException {
|
||||||
|
T mojo;
|
||||||
|
|
||||||
|
InternalSession sessionV4 = InternalSession.from(session.getSession());
|
||||||
|
Project project = sessionV4.getProject(session.getCurrentProject());
|
||||||
|
org.apache.maven.api.MojoExecution execution = new DefaultMojoExecution(sessionV4, mojoExecution);
|
||||||
|
org.apache.maven.api.plugin.Log log = new DefaultLog(
|
||||||
|
LoggerFactory.getLogger(mojoExecution.getMojoDescriptor().getFullGoalName()));
|
||||||
|
try {
|
||||||
|
Set<String> classes = new HashSet<>();
|
||||||
|
try (InputStream is = pluginRealm.getResourceAsStream("META-INF/maven/org.apache.maven.api.di.Inject");
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(is)))) {
|
||||||
|
reader.lines().forEach(classes::add);
|
||||||
|
}
|
||||||
|
Injector injector = Injector.create();
|
||||||
|
// Add known classes
|
||||||
|
// TODO: get those from the existing plexus scopes ?
|
||||||
|
injector.bindInstance(Session.class, sessionV4);
|
||||||
|
injector.bindInstance(Project.class, project);
|
||||||
|
injector.bindInstance(org.apache.maven.api.MojoExecution.class, execution);
|
||||||
|
injector.bindInstance(org.apache.maven.api.plugin.Log.class, log);
|
||||||
|
// Add plugin classes
|
||||||
|
for (String className : classes) {
|
||||||
|
Class<?> clazz = pluginRealm.loadClass(className);
|
||||||
|
injector.bindImplicit(clazz);
|
||||||
|
}
|
||||||
|
mojo = mojoInterface.cast(injector.getInstance(mojoDescriptor.getImplementationClass()));
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new PluginContainerException(mojoDescriptor, pluginRealm, "Unable to lookup Mojo", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlNode dom = mojoExecution.getConfiguration() != null
|
||||||
|
? mojoExecution.getConfiguration().getDom()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
PlexusConfiguration pomConfiguration;
|
||||||
|
|
||||||
|
if (dom == null) {
|
||||||
|
pomConfiguration = new DefaultPlexusConfiguration("configuration");
|
||||||
|
} else {
|
||||||
|
pomConfiguration = XmlPlexusConfiguration.toPlexusConfiguration(dom);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpressionEvaluator expressionEvaluator =
|
||||||
|
new PluginParameterExpressionEvaluatorV4(sessionV4, project, execution);
|
||||||
|
|
||||||
|
for (MavenPluginConfigurationValidator validator : configurationValidators) {
|
||||||
|
validator.validate(session, mojoDescriptor, mojo.getClass(), pomConfiguration, expressionEvaluator);
|
||||||
|
}
|
||||||
|
|
||||||
|
populateMojoExecutionFields(
|
||||||
|
mojo,
|
||||||
|
mojoExecution.getExecutionId(),
|
||||||
|
mojoDescriptor,
|
||||||
|
pluginRealm,
|
||||||
|
pomConfiguration,
|
||||||
|
expressionEvaluator);
|
||||||
|
|
||||||
|
return mojo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> T loadV3Mojo(
|
||||||
|
Class<T> mojoInterface,
|
||||||
|
MavenSession session,
|
||||||
|
MojoExecution mojoExecution,
|
||||||
|
MojoDescriptor mojoDescriptor,
|
||||||
|
PluginDescriptor pluginDescriptor,
|
||||||
|
ClassRealm pluginRealm)
|
||||||
|
throws PluginContainerException, PluginConfigurationException {
|
||||||
T mojo;
|
T mojo;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mojo = container.lookup(mojoInterface, mojoDescriptor.getRoleHint());
|
mojo = container.lookup(mojoInterface, mojoDescriptor.getRoleHint());
|
||||||
} catch (ComponentLookupException e) {
|
} catch (ComponentLookupException e) {
|
||||||
Throwable cause = e.getCause();
|
Throwable cause = e.getCause();
|
||||||
while (cause != null
|
while (cause != null && !(cause instanceof LinkageError) && !(cause instanceof ClassNotFoundException)) {
|
||||||
&& !(cause instanceof LinkageError)
|
|
||||||
&& !(cause instanceof ClassNotFoundException)) {
|
|
||||||
cause = cause.getCause();
|
cause = cause.getCause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -609,16 +666,8 @@ public class DefaultMavenPluginManager implements MavenPluginManager {
|
||||||
pomConfiguration = XmlPlexusConfiguration.toPlexusConfiguration(dom);
|
pomConfiguration = XmlPlexusConfiguration.toPlexusConfiguration(dom);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExpressionEvaluator expressionEvaluator;
|
|
||||||
InternalSession sessionV4 = InternalSession.from(session.getSession());
|
InternalSession sessionV4 = InternalSession.from(session.getSession());
|
||||||
if (mojoDescriptor.isV4Api()) {
|
ExpressionEvaluator expressionEvaluator = new PluginParameterExpressionEvaluator(session, mojoExecution);
|
||||||
expressionEvaluator = new PluginParameterExpressionEvaluatorV4(
|
|
||||||
sessionV4,
|
|
||||||
sessionV4.getProject(session.getCurrentProject()),
|
|
||||||
new DefaultMojoExecution(sessionV4, mojoExecution));
|
|
||||||
} else {
|
|
||||||
expressionEvaluator = new PluginParameterExpressionEvaluator(session, mojoExecution);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (MavenPluginConfigurationValidator validator : configurationValidators) {
|
for (MavenPluginConfigurationValidator validator : configurationValidators) {
|
||||||
validator.validate(session, mojoDescriptor, mojo.getClass(), pomConfiguration, expressionEvaluator);
|
validator.validate(session, mojoDescriptor, mojo.getClass(), pomConfiguration, expressionEvaluator);
|
||||||
|
@ -633,10 +682,6 @@ public class DefaultMavenPluginManager implements MavenPluginManager {
|
||||||
expressionEvaluator);
|
expressionEvaluator);
|
||||||
|
|
||||||
return mojo;
|
return mojo;
|
||||||
} finally {
|
|
||||||
Thread.currentThread().setContextClassLoader(oldClassLoader);
|
|
||||||
container.setLookupRealm(oldLookupRealm);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void populateMojoExecutionFields(
|
private void populateMojoExecutionFields(
|
||||||
|
@ -892,4 +937,29 @@ public class DefaultMavenPluginManager implements MavenPluginManager {
|
||||||
pluginDependenciesResolver.resolvePlugin(extensionPlugin, null, null, repositories, session);
|
pluginDependenciesResolver.resolvePlugin(extensionPlugin, null, null, repositories, session);
|
||||||
return toMavenArtifacts(root);
|
return toMavenArtifacts(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class NamedImpl implements Named {
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
NamedImpl(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String value() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
|
public int hashCode() {
|
||||||
|
return 127 * "value".hashCode() ^ this.value.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return o instanceof Named && this.value.equals(((Named) o).value());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<? extends Annotation> annotationType() {
|
||||||
|
return Named.class;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ public class SessionScopeModule extends AbstractModule {
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
bindScope(SessionScoped.class, scope);
|
bindScope(SessionScoped.class, scope);
|
||||||
bindScope(org.apache.maven.api.di.SessionScoped.class, scope);
|
// bindScope(org.apache.maven.api.di.SessionScoped.class, scope);
|
||||||
bind(SessionScope.class).toInstance(scope);
|
bind(SessionScope.class).toInstance(scope);
|
||||||
|
|
||||||
bind(MavenSession.class)
|
bind(MavenSession.class)
|
||||||
|
|
|
@ -23,8 +23,8 @@ import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import com.google.inject.OutOfScopeException;
|
import com.google.inject.OutOfScopeException;
|
||||||
|
import org.apache.maven.SessionScoped;
|
||||||
import org.apache.maven.api.Session;
|
import org.apache.maven.api.Session;
|
||||||
import org.apache.maven.api.di.SessionScoped;
|
|
||||||
import org.apache.maven.session.scope.internal.SessionScope;
|
import org.apache.maven.session.scope.internal.SessionScope;
|
||||||
import org.codehaus.plexus.PlexusContainer;
|
import org.codehaus.plexus.PlexusContainer;
|
||||||
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
|
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven</artifactId>
|
||||||
|
<version>4.0.0-alpha-13-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>maven-di</artifactId>
|
||||||
|
<name>Maven Dependency Injection</name>
|
||||||
|
<description>Provides the implementation for the Dependency Injection mechanism in Maven</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-api-di</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-api-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-xml-impl</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.plexus</groupId>
|
||||||
|
<artifactId>plexus-xml</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
import org.apache.maven.di.impl.InjectorImpl;
|
||||||
|
|
||||||
|
public interface Injector {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Builder API
|
||||||
|
//
|
||||||
|
|
||||||
|
static Injector create() {
|
||||||
|
return new InjectorImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
Injector bindScope(Class<? extends Annotation> scopeAnnotation, Scope scope);
|
||||||
|
|
||||||
|
Injector bindImplicit(Class<?> cls);
|
||||||
|
|
||||||
|
<T> Injector bindInstance(Class<T> cls, T instance);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Bean access
|
||||||
|
//
|
||||||
|
|
||||||
|
<T> void injectInstance(T instance);
|
||||||
|
|
||||||
|
<T> T getInstance(Class<T> key);
|
||||||
|
|
||||||
|
<T> T getInstance(Key<T> key);
|
||||||
|
}
|
|
@ -0,0 +1,161 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.apache.maven.api.annotations.Nullable;
|
||||||
|
import org.apache.maven.di.impl.ReflectionUtils;
|
||||||
|
import org.apache.maven.di.impl.TypeUtils;
|
||||||
|
import org.apache.maven.di.impl.Types;
|
||||||
|
import org.apache.maven.di.impl.Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The key defines an identity of a binding. In any DI, a key is usually a type of the object along
|
||||||
|
* with some optional tag to distinguish between bindings which make objects of the same type.
|
||||||
|
* <p>
|
||||||
|
* In ActiveJ Inject, a key is also a type token - special abstract class that can store type information
|
||||||
|
* with the shortest syntax possible in Java.
|
||||||
|
* <p>
|
||||||
|
* For example, to create a key of type Map<String, List<Integer>>, you can just use
|
||||||
|
* this syntax: <code>new Key<Map<String, List<Integer>>>(){}</code>.
|
||||||
|
* <p>
|
||||||
|
* If your types are not known at compile time, you can use {@link Types#parameterizedType} to make a
|
||||||
|
* parameterized type and give it to a {@link #ofType Key.ofType} constructor.
|
||||||
|
*
|
||||||
|
* @param <T> binding type
|
||||||
|
*/
|
||||||
|
public abstract class Key<T> {
|
||||||
|
private final Type type;
|
||||||
|
private final @Nullable Object qualifier;
|
||||||
|
|
||||||
|
private int hash;
|
||||||
|
|
||||||
|
protected Key() {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Key(@Nullable Object qualifier) {
|
||||||
|
this.type = TypeUtils.simplifyType(getTypeParameter());
|
||||||
|
this.qualifier = qualifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Key(Type type, @Nullable Object qualifier) {
|
||||||
|
this.type = TypeUtils.simplifyType(type);
|
||||||
|
this.qualifier = qualifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final class KeyImpl<T> extends Key<T> {
|
||||||
|
KeyImpl(Type type, Object qualifier) {
|
||||||
|
super(type, qualifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Key<T> of(Class<T> type) {
|
||||||
|
return new KeyImpl<>(type, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Key<T> of(Class<T> type, @Nullable Object qualifier) {
|
||||||
|
return new KeyImpl<>(type, qualifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Key<T> ofType(Type type) {
|
||||||
|
return new KeyImpl<>(type, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Key<T> ofType(Type type, @Nullable Object qualifier) {
|
||||||
|
return new KeyImpl<>(type, qualifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Type getTypeParameter() {
|
||||||
|
// this cannot possibly fail so not even a check here
|
||||||
|
Type typeArgument = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
|
||||||
|
Object outerInstance = ReflectionUtils.getOuterClassInstance(this);
|
||||||
|
// // the outer instance is null in static context
|
||||||
|
return outerInstance != null
|
||||||
|
? Types.bind(typeArgument, Types.getAllTypeBindings(outerInstance.getClass()))
|
||||||
|
: typeArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A shortcut for <code>{@link Types#getRawType(Type)}(key.getType())</code>.
|
||||||
|
* Also casts the result to a properly parameterized class.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Class<T> getRawType() {
|
||||||
|
return (Class<T>) Types.getRawType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a type parameter of the underlying type wrapped as a key with no qualifier.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException when underlying type is not a parameterized one.
|
||||||
|
*/
|
||||||
|
public <U> Key<U> getTypeParameter(int index) {
|
||||||
|
if (type instanceof ParameterizedType) {
|
||||||
|
return new KeyImpl<>(((ParameterizedType) type).getActualTypeArguments()[index], null);
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Expected type from key " + getDisplayString() + " to be parameterized");
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable Object getQualifier() {
|
||||||
|
return qualifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an underlying type with display string formatting (package names stripped)
|
||||||
|
* and prepended qualifier display string if this key has a qualifier.
|
||||||
|
*/
|
||||||
|
public String getDisplayString() {
|
||||||
|
return (qualifier != null ? Utils.getDisplayString(qualifier) + " " : "")
|
||||||
|
+ ReflectionUtils.getDisplayName(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(o instanceof Key<?>)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Key<?> that = (Key<?>) o;
|
||||||
|
return type.equals(that.type) && Objects.equals(qualifier, that.qualifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int hashCode = hash;
|
||||||
|
if (hashCode == 0) {
|
||||||
|
hash = 31 * type.hashCode() + (qualifier == null ? 0 : qualifier.hashCode());
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return (qualifier != null ? qualifier + " " : "") + type.getTypeName();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public interface Scope {
|
||||||
|
|
||||||
|
<T> Supplier<T> scope(Key<T> key, Annotation scope, Supplier<T> unscoped);
|
||||||
|
}
|
|
@ -0,0 +1,188 @@
|
||||||
|
/*
|
||||||
|
* 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 java.lang.annotation.Annotation;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.apache.maven.di.Key;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.joining;
|
||||||
|
|
||||||
|
public abstract class Binding<T> {
|
||||||
|
private final Set<Key<?>> dependencies;
|
||||||
|
private Annotation scope;
|
||||||
|
private int priority;
|
||||||
|
private Key<?> originalKey;
|
||||||
|
|
||||||
|
protected Binding(Key<? extends T> originalKey, Set<Key<?>> dependencies) {
|
||||||
|
this(originalKey, dependencies, null, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Binding(Key<?> originalKey, Set<Key<?>> dependencies, Annotation scope, int priority) {
|
||||||
|
this.originalKey = originalKey;
|
||||||
|
this.dependencies = dependencies;
|
||||||
|
this.scope = scope;
|
||||||
|
this.priority = priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Binding<T> toInstance(T instance) {
|
||||||
|
return new BindingToInstance<>(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <R> Binding<R> to(TupleConstructorN<R> constructor, Class<?>[] types) {
|
||||||
|
return Binding.to(constructor, Stream.of(types).map(Key::of).toArray(Key<?>[]::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <R> Binding<R> to(TupleConstructorN<R> constructor, Key<?>[] dependencies) {
|
||||||
|
return to(constructor, dependencies, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <R> Binding<R> to(TupleConstructorN<R> constructor, Key<?>[] dependencies, int priority) {
|
||||||
|
return new BindingToConstructor<>(null, constructor, dependencies, priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
public Binding<T> scope(Annotation scope) {
|
||||||
|
this.scope = scope;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Binding<T> prioritize(int priority) {
|
||||||
|
this.priority = priority;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Binding<T> withKey(Key<?> key) {
|
||||||
|
this.originalKey = key;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Binding<T> initializeWith(BindingInitializer<T> bindingInitializer) {
|
||||||
|
return new Binding<T>(
|
||||||
|
this.originalKey,
|
||||||
|
Stream.of(this.dependencies, bindingInitializer.getDependencies())
|
||||||
|
.flatMap(Set::stream)
|
||||||
|
.collect(Collectors.toSet()),
|
||||||
|
this.scope,
|
||||||
|
this.priority) {
|
||||||
|
@Override
|
||||||
|
public Supplier<T> compile(Function<Key<?>, 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Binding.this.toString();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Supplier<T> compile(Function<Key<?>, Supplier<?>> compiler);
|
||||||
|
|
||||||
|
public Set<Key<?>> getDependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Annotation getScope() {
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayString() {
|
||||||
|
return dependencies.stream().map(Key::getDisplayString).collect(joining(", ", "[", "]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Key<?> getOriginalKey() {
|
||||||
|
return originalKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPriority() {
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Binding" + dependencies.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface TupleConstructorN<R> {
|
||||||
|
R create(Object... args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BindingToInstance<T> extends Binding<T> {
|
||||||
|
final T instance;
|
||||||
|
|
||||||
|
BindingToInstance(T instance) {
|
||||||
|
super(null, Collections.emptySet());
|
||||||
|
this.instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Supplier<T> compile(Function<Key<?>, Supplier<?>> compiler) {
|
||||||
|
return () -> instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "BindingToInstance[" + instance + "]" + getDependencies();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BindingToConstructor<T> extends Binding<T> {
|
||||||
|
final TupleConstructorN<T> constructor;
|
||||||
|
|
||||||
|
BindingToConstructor(
|
||||||
|
Key<? extends T> key, TupleConstructorN<T> constructor, Key<?>[] dependencies, int priority) {
|
||||||
|
super(key, new HashSet<>(Arrays.asList(dependencies)), null, priority);
|
||||||
|
this.constructor = constructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Supplier<T> compile(Function<Key<?>, Supplier<?>> compiler) {
|
||||||
|
return () -> {
|
||||||
|
Object[] args = getDependencies().stream()
|
||||||
|
.map(compiler)
|
||||||
|
.map(Supplier::get)
|
||||||
|
.toArray();
|
||||||
|
return constructor.create(args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "BindingToConstructor[" + constructor + "]" + getDependencies();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* 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 java.util.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
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;
|
||||||
|
|
||||||
|
protected BindingInitializer(Set<Key<?>> dependencies) {
|
||||||
|
this.dependencies = dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Key<?>> getDependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Consumer<T> compile(Function<Key<?>, Supplier<?>> compiler);
|
||||||
|
|
||||||
|
public static <T> BindingInitializer<T> combine(List<BindingInitializer<T>> bindingInitializers) {
|
||||||
|
Set<Key<?>> deps = bindingInitializers.stream()
|
||||||
|
.map(BindingInitializer::getDependencies)
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.collect(toSet());
|
||||||
|
return new BindingInitializer<T>(deps) {
|
||||||
|
@Override
|
||||||
|
public Consumer<T> compile(Function<Key<?>, Supplier<?>> compiler) {
|
||||||
|
return instance -> bindingInitializers.stream()
|
||||||
|
.map(bindingInitializer -> bindingInitializer.compile(compiler))
|
||||||
|
.forEach(i -> i.accept(instance));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* 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.Injector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A runtime exception that is thrown on startup when some static conditions fail
|
||||||
|
* (missing or cyclic dependencies, incorrect annotations etc.) or in runtime when
|
||||||
|
* you ask an {@link Injector} for an instance it does not have a {@link Binding binding} for.
|
||||||
|
*/
|
||||||
|
public final class DIException extends RuntimeException {
|
||||||
|
public DIException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DIException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,357 @@
|
||||||
|
/*
|
||||||
|
* 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 java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.lang.reflect.TypeVariable;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.Provides;
|
||||||
|
import org.apache.maven.api.di.Singleton;
|
||||||
|
import org.apache.maven.api.di.Typed;
|
||||||
|
import org.apache.maven.di.Injector;
|
||||||
|
import org.apache.maven.di.Key;
|
||||||
|
import org.apache.maven.di.Scope;
|
||||||
|
|
||||||
|
public class InjectorImpl implements Injector {
|
||||||
|
|
||||||
|
private final Map<Key<?>, Set<Binding<?>>> bindings = new HashMap<>();
|
||||||
|
private final Map<Class<? extends Annotation>, Scope> scopes = new HashMap<>();
|
||||||
|
|
||||||
|
public InjectorImpl() {
|
||||||
|
bindScope(Singleton.class, new SingletonScope());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getInstance(Class<T> key) {
|
||||||
|
return getInstance(Key.of(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getInstance(Key<T> key) {
|
||||||
|
return getCompiledBinding(key).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <T> void injectInstance(T instance) {
|
||||||
|
ReflectionUtils.generateInjectingInitializer(Key.of((Class<T>) instance.getClass()))
|
||||||
|
.compile(this::getCompiledBinding)
|
||||||
|
.accept(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Injector bindScope(Class<? extends Annotation> scopeAnnotation, Scope scope) {
|
||||||
|
if (scopes.put(scopeAnnotation, scope) != null) {
|
||||||
|
throw new DIException(
|
||||||
|
"Cannot rebind scope annotation class to a different implementation: " + scopeAnnotation);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <U> Injector bindInstance(Class<U> clazz, U instance) {
|
||||||
|
Key<?> key = Key.of(clazz, ReflectionUtils.qualifierOf(clazz));
|
||||||
|
Binding<U> binding = Binding.toInstance(instance);
|
||||||
|
return doBind(key, binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Injector bindImplicit(Class<?> clazz) {
|
||||||
|
Key<?> key = Key.of(clazz, ReflectionUtils.qualifierOf(clazz));
|
||||||
|
Binding<?> binding = ReflectionUtils.generateImplicitBinding(key);
|
||||||
|
return doBind(key, binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Injector doBind(Key<?> key, Binding<?> binding) {
|
||||||
|
doBindImplicit(key, binding);
|
||||||
|
Class<?> cls = key.getRawType().getSuperclass();
|
||||||
|
while (cls != Object.class && cls != null) {
|
||||||
|
key = Key.of(cls, key.getQualifier());
|
||||||
|
doBindImplicit(key, binding);
|
||||||
|
cls = cls.getSuperclass();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <U> Injector bind(Key<U> key, Binding<U> b) {
|
||||||
|
Set<Binding<?>> bindingSet = bindings.computeIfAbsent(key, $ -> new HashSet<>());
|
||||||
|
bindingSet.add(b);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
|
private <T> Set<Binding<T>> getBindings(Key<T> key) {
|
||||||
|
return (Set) bindings.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <Q> Supplier<Q> getCompiledBinding(Key<Q> key) {
|
||||||
|
Set<Binding<Q>> res = getBindings(key);
|
||||||
|
if (res != null) {
|
||||||
|
List<Binding<Q>> bindingList = new ArrayList<>(res);
|
||||||
|
Comparator<Binding<Q>> comparing = Comparator.comparing(Binding::getPriority);
|
||||||
|
bindingList.sort(comparing.reversed());
|
||||||
|
Binding<Q> binding = bindingList.get(0);
|
||||||
|
return compile(binding);
|
||||||
|
}
|
||||||
|
if (key.getRawType() == List.class) {
|
||||||
|
Set<Binding<Object>> res2 = getBindings(key.getTypeParameter(0));
|
||||||
|
if (res2 != null) {
|
||||||
|
List<Supplier<Object>> bindingList =
|
||||||
|
res2.stream().map(this::compile).collect(Collectors.toList());
|
||||||
|
//noinspection unchecked
|
||||||
|
return () -> (Q) new WrappingList<>(bindingList, Supplier::get);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (key.getRawType() == Map.class) {
|
||||||
|
Key<?> k = key.getTypeParameter(0);
|
||||||
|
Key<Object> v = key.getTypeParameter(1);
|
||||||
|
Set<Binding<Object>> res2 = getBindings(v);
|
||||||
|
if (k.getRawType() == String.class && res2 != null) {
|
||||||
|
Map<String, Supplier<Object>> map = res2.stream()
|
||||||
|
.filter(b -> b.getOriginalKey().getQualifier() == null
|
||||||
|
|| b.getOriginalKey().getQualifier() instanceof String)
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
b -> (String) b.getOriginalKey().getQualifier(), this::compile));
|
||||||
|
//noinspection unchecked
|
||||||
|
return (() -> (Q) new WrappingMap<>(map, Supplier::get));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new DIException("No binding to construct an instance for key "
|
||||||
|
+ key.getDisplayString() + ". Existing bindings:\n"
|
||||||
|
+ bindings.keySet().stream().map(Key::toString).collect(Collectors.joining("\n - ", " - ", "")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <Q> Supplier<Q> compile(Binding<Q> binding) {
|
||||||
|
Supplier<Q> compiled = binding.compile(this::getCompiledBinding);
|
||||||
|
if (binding.getScope() != null) {
|
||||||
|
Scope scope = scopes.entrySet().stream()
|
||||||
|
.filter(e -> e.getKey().isInstance(binding.getScope()))
|
||||||
|
.map(Map.Entry::getValue)
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(() -> new DIException("Scope not bound for annotation "
|
||||||
|
+ binding.getScope().getClass()));
|
||||||
|
compiled = scope.scope((Key<Q>) binding.getOriginalKey(), binding.getScope(), compiled);
|
||||||
|
}
|
||||||
|
return compiled;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void doBindImplicit(Key<?> key, Binding<?> binding) {
|
||||||
|
if (binding != null) {
|
||||||
|
// For non-explicit bindings, also bind all their base classes and interfaces according to the @Type
|
||||||
|
Set<Key<?>> toBind = new HashSet<>();
|
||||||
|
Deque<Key<?>> todo = new ArrayDeque<>();
|
||||||
|
todo.add(key);
|
||||||
|
|
||||||
|
Set<Class<?>> types;
|
||||||
|
Typed typed = key.getRawType().getAnnotation(Typed.class);
|
||||||
|
if (typed != null) {
|
||||||
|
Class<?>[] typesArray = typed.value();
|
||||||
|
if (typesArray == null || typesArray.length == 0) {
|
||||||
|
types = new HashSet<>(Arrays.asList(key.getRawType().getInterfaces()));
|
||||||
|
types.add(Object.class);
|
||||||
|
} else {
|
||||||
|
types = new HashSet<>(Arrays.asList(typesArray));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
types = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Key<?>> done = new HashSet<>();
|
||||||
|
while (!todo.isEmpty()) {
|
||||||
|
Key<?> type = todo.remove();
|
||||||
|
if (done.add(type)) {
|
||||||
|
Class<?> cls = Types.getRawType(type.getType());
|
||||||
|
Type[] interfaces = cls.getGenericInterfaces();
|
||||||
|
Arrays.stream(interfaces)
|
||||||
|
.map(t -> Key.ofType(t, key.getQualifier()))
|
||||||
|
.forEach(todo::add);
|
||||||
|
Type supercls = cls.getGenericSuperclass();
|
||||||
|
if (supercls != null) {
|
||||||
|
todo.add(Key.ofType(supercls, key.getQualifier()));
|
||||||
|
}
|
||||||
|
if (types == null || types.contains(cls)) {
|
||||||
|
toBind.add(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Also bind without the qualifier
|
||||||
|
if (key.getQualifier() != null) {
|
||||||
|
new HashSet<>(toBind).forEach(k -> toBind.add(Key.ofType(k.getType())));
|
||||||
|
}
|
||||||
|
toBind.forEach((k -> bind((Key<Object>) k, (Binding<Object>) binding)));
|
||||||
|
}
|
||||||
|
// Bind inner classes
|
||||||
|
for (Class<?> inner : key.getRawType().getDeclaredClasses()) {
|
||||||
|
bindImplicit(inner);
|
||||||
|
}
|
||||||
|
// Bind inner providers
|
||||||
|
for (Method method : key.getRawType().getDeclaredMethods()) {
|
||||||
|
if (method.isAnnotationPresent(Provides.class)) {
|
||||||
|
Object qualifier = ReflectionUtils.qualifierOf(method);
|
||||||
|
Annotation scope = ReflectionUtils.scopeOf(method);
|
||||||
|
|
||||||
|
TypeVariable<Method>[] methodTypeParameters = method.getTypeParameters();
|
||||||
|
if (methodTypeParameters.length != 0) {
|
||||||
|
throw new DIException("Parameterized method are not supported " + method);
|
||||||
|
}
|
||||||
|
Map<TypeVariable<?>, Type> mapping = new HashMap<>();
|
||||||
|
for (TypeVariable<Method> methodTypeParameter : methodTypeParameters) {
|
||||||
|
mapping.put(methodTypeParameter, methodTypeParameter);
|
||||||
|
}
|
||||||
|
mapping.putAll(Types.getAllTypeBindings(key.getRawType()));
|
||||||
|
|
||||||
|
Type returnType = Types.bind(method.getGenericReturnType(), mapping);
|
||||||
|
Key<Object> rkey = Key.ofType(returnType, qualifier);
|
||||||
|
|
||||||
|
Set<Class<?>> types;
|
||||||
|
Typed typed = method.getAnnotation(Typed.class);
|
||||||
|
if (typed != null) {
|
||||||
|
Class<?>[] typesArray = typed.value();
|
||||||
|
if (typesArray == null || typesArray.length == 0) {
|
||||||
|
types = new HashSet<>(Arrays.asList(rkey.getRawType().getInterfaces()));
|
||||||
|
types.add(Object.class);
|
||||||
|
} else {
|
||||||
|
types = new HashSet<>(Arrays.asList(typesArray));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
types = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Key<?>> toBind = new HashSet<>();
|
||||||
|
Deque<Key<?>> todo = new ArrayDeque<>();
|
||||||
|
todo.add(rkey);
|
||||||
|
|
||||||
|
Set<Key<?>> done = new HashSet<>();
|
||||||
|
while (!todo.isEmpty()) {
|
||||||
|
Key<?> type = todo.remove();
|
||||||
|
if (done.add(type)) {
|
||||||
|
Class<?> cls = Types.getRawType(type.getType());
|
||||||
|
Type[] interfaces = cls.getGenericInterfaces();
|
||||||
|
Arrays.stream(interfaces)
|
||||||
|
.map(t -> Key.ofType(t, qualifier))
|
||||||
|
.forEach(todo::add);
|
||||||
|
Type supercls = cls.getGenericSuperclass();
|
||||||
|
if (supercls != null) {
|
||||||
|
todo.add(Key.ofType(supercls, qualifier));
|
||||||
|
}
|
||||||
|
if (types == null || types.contains(cls)) {
|
||||||
|
toBind.add(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Also bind without the qualifier
|
||||||
|
if (qualifier != null) {
|
||||||
|
new HashSet<>(toBind).forEach(k -> toBind.add(Key.ofType(k.getType())));
|
||||||
|
}
|
||||||
|
|
||||||
|
Binding<Object> bind = ReflectionUtils.bindingFromMethod(method).scope(scope);
|
||||||
|
toBind.forEach((k -> bind((Key<Object>) k, bind)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class WrappingMap<K, V, T> extends AbstractMap<K, V> {
|
||||||
|
|
||||||
|
private final Map<K, T> delegate;
|
||||||
|
private final Function<T, V> mapper;
|
||||||
|
|
||||||
|
WrappingMap(Map<K, T> delegate, Function<T, V> mapper) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
this.mapper = mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("NullableProblems")
|
||||||
|
@Override
|
||||||
|
public Set<Entry<K, V>> entrySet() {
|
||||||
|
return new AbstractSet<Entry<K, V>>() {
|
||||||
|
@Override
|
||||||
|
public Iterator<Entry<K, V>> iterator() {
|
||||||
|
Iterator<Entry<K, T>> it = delegate.entrySet().iterator();
|
||||||
|
return new Iterator<Entry<K, V>>() {
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return it.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Entry<K, V> next() {
|
||||||
|
Entry<K, T> n = it.next();
|
||||||
|
return new SimpleImmutableEntry<>(n.getKey(), mapper.apply(n.getValue()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return delegate.size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class WrappingList<Q, T> extends AbstractList<Q> {
|
||||||
|
|
||||||
|
private final List<T> delegate;
|
||||||
|
private final Function<T, Q> mapper;
|
||||||
|
|
||||||
|
WrappingList(List<T> delegate, Function<T, Q> mapper) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
this.mapper = mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Q get(int index) {
|
||||||
|
return mapper.apply(delegate.get(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return delegate.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingletonScope implements Scope {
|
||||||
|
Map<Key<?>, java.util.function.Supplier<?>> cache = new HashMap<>();
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <T> java.util.function.Supplier<T> scope(
|
||||||
|
Key<T> key, Annotation scope, java.util.function.Supplier<T> unscoped) {
|
||||||
|
return (java.util.function.Supplier<T>)
|
||||||
|
cache.computeIfAbsent(key, k -> new java.util.function.Supplier<T>() {
|
||||||
|
volatile T instance;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() {
|
||||||
|
if (instance == null) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = unscoped.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,381 @@
|
||||||
|
/*
|
||||||
|
* 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 java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.apache.maven.api.annotations.Nullable;
|
||||||
|
import org.apache.maven.api.di.*;
|
||||||
|
import org.apache.maven.di.Key;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
|
public final class ReflectionUtils {
|
||||||
|
private static final String IDENT = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*";
|
||||||
|
|
||||||
|
private static final Pattern PACKAGE = Pattern.compile("(?:" + IDENT + "\\.)*");
|
||||||
|
private static final Pattern PACKAGE_AND_PARENT = Pattern.compile(PACKAGE.pattern() + "(?:" + IDENT + "\\$\\d*)?");
|
||||||
|
private static final Pattern ARRAY_SIGNATURE = Pattern.compile("\\[L(.*?);");
|
||||||
|
|
||||||
|
public static String getDisplayName(Type type) {
|
||||||
|
Class<?> raw = Types.getRawType(type);
|
||||||
|
String typeName;
|
||||||
|
if (raw.isAnonymousClass()) {
|
||||||
|
Type superclass = raw.getGenericSuperclass();
|
||||||
|
typeName = "? extends " + superclass.getTypeName();
|
||||||
|
} else {
|
||||||
|
typeName = type.getTypeName();
|
||||||
|
}
|
||||||
|
|
||||||
|
return PACKAGE_AND_PARENT
|
||||||
|
.matcher(ARRAY_SIGNATURE.matcher(typeName).replaceAll("$1[]"))
|
||||||
|
.replaceAll("");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable Object getOuterClassInstance(Object innerClassInstance) {
|
||||||
|
if (innerClassInstance == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Class<?> cls = innerClassInstance.getClass();
|
||||||
|
Class<?> enclosingClass = cls.getEnclosingClass();
|
||||||
|
if (enclosingClass == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (Field field : cls.getDeclaredFields()) {
|
||||||
|
if (!field.isSynthetic() || !field.getName().startsWith("this$") || field.getType() != enclosingClass) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
field.setAccessible(true);
|
||||||
|
try {
|
||||||
|
return field.get(innerClassInstance);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable Object qualifierOf(AnnotatedElement annotatedElement) {
|
||||||
|
Object qualifier = null;
|
||||||
|
for (Annotation annotation : annotatedElement.getDeclaredAnnotations()) {
|
||||||
|
if (annotation.annotationType().isAnnotationPresent(Qualifier.class)) {
|
||||||
|
if (qualifier != null) {
|
||||||
|
throw new DIException("More than one qualifier annotation on " + annotatedElement);
|
||||||
|
}
|
||||||
|
if (annotation instanceof Named) {
|
||||||
|
qualifier = ((Named) annotation).value();
|
||||||
|
} else {
|
||||||
|
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||||
|
qualifier = Utils.isMarker(annotationType) ? annotationType : annotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return qualifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable Annotation scopeOf(AnnotatedElement annotatedElement) {
|
||||||
|
Annotation scope = null;
|
||||||
|
for (Annotation annotation : annotatedElement.getDeclaredAnnotations()) {
|
||||||
|
if (annotation.annotationType().isAnnotationPresent(org.apache.maven.api.di.Scope.class)) {
|
||||||
|
if (scope != null) {
|
||||||
|
throw new DIException("More than one scope annotation on " + annotatedElement);
|
||||||
|
}
|
||||||
|
scope = annotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Key<T> keyOf(@Nullable Type container, Type type, AnnotatedElement annotatedElement) {
|
||||||
|
return Key.ofType(
|
||||||
|
container != null ? Types.bind(type, Types.getAllTypeBindings(container)) : type,
|
||||||
|
qualifierOf(annotatedElement));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends AnnotatedElement & Member> List<T> getAnnotatedElements(
|
||||||
|
Class<?> cls,
|
||||||
|
Class<? extends Annotation> annotationType,
|
||||||
|
Function<Class<?>, T[]> extractor,
|
||||||
|
boolean allowStatic) {
|
||||||
|
List<T> result = new ArrayList<>();
|
||||||
|
while (cls != null) {
|
||||||
|
for (T element : extractor.apply(cls)) {
|
||||||
|
if (element.isAnnotationPresent(annotationType)) {
|
||||||
|
if (!allowStatic && Modifier.isStatic(element.getModifiers())) {
|
||||||
|
throw new DIException(
|
||||||
|
"@" + annotationType.getSimpleName() + " annotation is not allowed on " + element);
|
||||||
|
}
|
||||||
|
result.add(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cls = cls.getSuperclass();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> @Nullable Binding<T> generateImplicitBinding(Key<T> key) {
|
||||||
|
Binding<T> binding = generateConstructorBinding(key);
|
||||||
|
if (binding != null) {
|
||||||
|
Annotation scope = scopeOf(key.getRawType());
|
||||||
|
if (scope != null) {
|
||||||
|
binding = binding.scope(scope);
|
||||||
|
}
|
||||||
|
binding = binding.initializeWith(generateInjectingInitializer(key));
|
||||||
|
}
|
||||||
|
return binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> @Nullable Binding<T> generateConstructorBinding(Key<T> key) {
|
||||||
|
Class<?> cls = key.getRawType();
|
||||||
|
|
||||||
|
Annotation classInjectAnnotation = Stream.of(cls.getAnnotations())
|
||||||
|
.filter(a -> a.annotationType().isAnnotationPresent(Qualifier.class))
|
||||||
|
.findAny()
|
||||||
|
.orElse(null);
|
||||||
|
List<Constructor<?>> constructors = Arrays.asList(cls.getDeclaredConstructors());
|
||||||
|
List<Constructor<?>> injectConstructors = constructors.stream()
|
||||||
|
.filter(c -> c.isAnnotationPresent(Inject.class))
|
||||||
|
.collect(toList());
|
||||||
|
|
||||||
|
List<Method> factoryMethods = Arrays.stream(cls.getDeclaredMethods())
|
||||||
|
.filter(method -> method.getReturnType() == cls && Modifier.isStatic(method.getModifiers()))
|
||||||
|
.collect(toList());
|
||||||
|
List<Method> injectFactoryMethods = factoryMethods.stream()
|
||||||
|
.filter(method -> method.isAnnotationPresent(Inject.class))
|
||||||
|
.collect(toList());
|
||||||
|
|
||||||
|
if (classInjectAnnotation != null) {
|
||||||
|
if (!injectConstructors.isEmpty()) {
|
||||||
|
throw failedImplicitBinding(key, "inject annotation on class with inject constructor");
|
||||||
|
}
|
||||||
|
if (!factoryMethods.isEmpty()) {
|
||||||
|
throw failedImplicitBinding(key, "inject annotation on class with factory method");
|
||||||
|
}
|
||||||
|
if (constructors.isEmpty()) {
|
||||||
|
throw failedImplicitBinding(key, "inject annotation on interface");
|
||||||
|
}
|
||||||
|
if (constructors.size() > 1) {
|
||||||
|
throw failedImplicitBinding(key, "inject annotation on class with multiple constructors");
|
||||||
|
}
|
||||||
|
Constructor<T> declaredConstructor =
|
||||||
|
(Constructor<T>) constructors.iterator().next();
|
||||||
|
|
||||||
|
Class<?> enclosingClass = cls.getEnclosingClass();
|
||||||
|
if (enclosingClass != null
|
||||||
|
&& !Modifier.isStatic(cls.getModifiers())
|
||||||
|
&& declaredConstructor.getParameterCount() != 1) {
|
||||||
|
throw failedImplicitBinding(
|
||||||
|
key,
|
||||||
|
"inject annotation on local class that closes over outside variables and/or has no default constructor");
|
||||||
|
}
|
||||||
|
return bindingFromConstructor(key, declaredConstructor);
|
||||||
|
}
|
||||||
|
if (!injectConstructors.isEmpty()) {
|
||||||
|
if (injectConstructors.size() > 1) {
|
||||||
|
throw failedImplicitBinding(key, "more than one inject constructor");
|
||||||
|
}
|
||||||
|
if (!injectFactoryMethods.isEmpty()) {
|
||||||
|
throw failedImplicitBinding(key, "both inject constructor and inject factory method are present");
|
||||||
|
}
|
||||||
|
return bindingFromConstructor(
|
||||||
|
key, (Constructor<T>) injectConstructors.iterator().next());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!injectFactoryMethods.isEmpty()) {
|
||||||
|
if (injectFactoryMethods.size() > 1) {
|
||||||
|
throw failedImplicitBinding(key, "more than one inject factory method");
|
||||||
|
}
|
||||||
|
return bindingFromMethod(injectFactoryMethods.iterator().next());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DIException failedImplicitBinding(Key<?> requestedKey, String message) {
|
||||||
|
return new DIException(
|
||||||
|
"Failed to generate implicit binding for " + requestedKey.getDisplayString() + ", " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> BindingInitializer<T> generateInjectingInitializer(Key<T> container) {
|
||||||
|
Class<T> rawType = container.getRawType();
|
||||||
|
List<BindingInitializer<T>> initializers = Stream.concat(
|
||||||
|
getAnnotatedElements(rawType, Inject.class, Class::getDeclaredFields, false).stream()
|
||||||
|
.map(field -> fieldInjector(container, field)),
|
||||||
|
getAnnotatedElements(rawType, Inject.class, Class::getDeclaredMethods, true).stream()
|
||||||
|
.filter(method -> !Modifier.isStatic(
|
||||||
|
method.getModifiers())) // we allow them and just filter out to allow
|
||||||
|
// static factory methods
|
||||||
|
.map(method -> methodInjector(container, method)))
|
||||||
|
.collect(toList());
|
||||||
|
return BindingInitializer.combine(initializers);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
@Override
|
||||||
|
public Consumer<T> compile(Function<Key<?>, Supplier<?>> compiler) {
|
||||||
|
Supplier<?> binding = compiler.apply(key);
|
||||||
|
return (T instance) -> {
|
||||||
|
Object arg = binding.get();
|
||||||
|
try {
|
||||||
|
field.set(instance, arg);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new DIException("Not allowed to set injectable field " + field, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> BindingInitializer<T> methodInjector(Key<T> container, Method method) {
|
||||||
|
method.setAccessible(true);
|
||||||
|
Key<?>[] dependencies = toDependencies(container.getType(), method);
|
||||||
|
return new BindingInitializer<T>(new HashSet<>(Arrays.asList(dependencies))) {
|
||||||
|
@Override
|
||||||
|
public Consumer<T> compile(Function<Key<?>, Supplier<?>> compiler) {
|
||||||
|
return instance -> {
|
||||||
|
Object[] args = getDependencies().stream()
|
||||||
|
.map(compiler)
|
||||||
|
.map(Supplier::get)
|
||||||
|
.toArray();
|
||||||
|
try {
|
||||||
|
method.invoke(instance, args);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new DIException("Not allowed to call injectable method " + method, e);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw new DIException("Failed to call injectable method " + method, e.getCause());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Key<?>[] toDependencies(@Nullable Type container, Executable executable) {
|
||||||
|
Key<?>[] 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);
|
||||||
|
System.arraycopy(keys, 0, nkeys, 1, keys.length);
|
||||||
|
return nkeys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Key<?>[] toArgDependencies(@Nullable Type container, Executable executable) {
|
||||||
|
Parameter[] parameters = executable.getParameters();
|
||||||
|
Key<?>[] dependencies = new Key<?>[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);
|
||||||
|
}
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> Binding<T> bindingFromMethod(Method method) {
|
||||||
|
method.setAccessible(true);
|
||||||
|
Binding<T> binding = Binding.to(
|
||||||
|
args -> {
|
||||||
|
try {
|
||||||
|
Object instance;
|
||||||
|
Object[] params;
|
||||||
|
if (Modifier.isStatic(method.getModifiers())) {
|
||||||
|
instance = null;
|
||||||
|
params = args;
|
||||||
|
} else {
|
||||||
|
instance = args[0];
|
||||||
|
params = Arrays.copyOfRange(args, 1, args.length);
|
||||||
|
}
|
||||||
|
T result = (T) method.invoke(instance, params);
|
||||||
|
if (result == null) {
|
||||||
|
throw new NullPointerException(
|
||||||
|
"@Provides method must return non-null result, method " + method);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new DIException("Not allowed to call method " + method, e);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw new DIException("Failed to call method " + method, e.getCause());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
toDependencies(method.getDeclaringClass(), method));
|
||||||
|
|
||||||
|
Priority priority = method.getAnnotation(Priority.class);
|
||||||
|
if (priority != null) {
|
||||||
|
binding = binding.prioritize(priority.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
return binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Binding<T> bindingFromConstructor(Key<T> key, Constructor<T> constructor) {
|
||||||
|
constructor.setAccessible(true);
|
||||||
|
|
||||||
|
Key<?>[] dependencies = toDependencies(key.getType(), constructor);
|
||||||
|
|
||||||
|
Binding<T> binding = Binding.to(
|
||||||
|
args -> {
|
||||||
|
try {
|
||||||
|
return constructor.newInstance(args);
|
||||||
|
} catch (InstantiationException e) {
|
||||||
|
throw new DIException(
|
||||||
|
"Cannot instantiate object from the constructor " + constructor
|
||||||
|
+ " to provide requested key " + key,
|
||||||
|
e);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new DIException(
|
||||||
|
"Not allowed to call constructor " + constructor + " to provide requested key " + key,
|
||||||
|
e);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw new DIException(
|
||||||
|
"Failed to call constructor " + constructor + " to provide requested key " + key,
|
||||||
|
e.getCause());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dependencies);
|
||||||
|
|
||||||
|
Priority priority = constructor.getDeclaringClass().getAnnotation(Priority.class);
|
||||||
|
if (priority != null) {
|
||||||
|
binding = binding.prioritize(priority.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
return binding.withKey(key);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,379 @@
|
||||||
|
/*
|
||||||
|
* 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 java.lang.reflect.*;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class contains reflection utilities to work with Java types.
|
||||||
|
* Its main use is for method {@link Types#parameterizedType Types.parameterized}.
|
||||||
|
* However, just like with {@link ReflectionUtils}, other type utility
|
||||||
|
* methods are pretty clean too, so they are left public.
|
||||||
|
*/
|
||||||
|
public final class TypeUtils {
|
||||||
|
|
||||||
|
public static boolean isInheritedFrom(Type type, Type from, Map<Type, Type> dejaVu) {
|
||||||
|
if (from == Object.class) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (matches(type, from, dejaVu) || matches(from, type, dejaVu)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(type instanceof Class || type instanceof ParameterizedType || type instanceof GenericArrayType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Class<?> rawType = Types.getRawType(type);
|
||||||
|
|
||||||
|
Type superclass = rawType.getGenericSuperclass();
|
||||||
|
if (superclass != null && isInheritedFrom(superclass, from, dejaVu)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return Arrays.stream(rawType.getGenericInterfaces()).anyMatch(iface -> isInheritedFrom(iface, from, dejaVu));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean matches(Type strict, Type pattern) {
|
||||||
|
return matches(strict, pattern, new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean matches(Type strict, Type pattern, Map<Type, Type> dejaVu) {
|
||||||
|
if (strict.equals(pattern) || dejaVu.get(strict) == pattern) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
dejaVu.put(strict, pattern);
|
||||||
|
try {
|
||||||
|
if (pattern instanceof WildcardType) {
|
||||||
|
WildcardType wildcard = (WildcardType) pattern;
|
||||||
|
return Arrays.stream(wildcard.getUpperBounds())
|
||||||
|
.allMatch(bound -> isInheritedFrom(strict, bound, dejaVu))
|
||||||
|
&& Arrays.stream(wildcard.getLowerBounds())
|
||||||
|
.allMatch(bound -> isInheritedFrom(bound, strict, dejaVu));
|
||||||
|
}
|
||||||
|
if (pattern instanceof TypeVariable<?>) {
|
||||||
|
TypeVariable<?> typevar = (TypeVariable<?>) pattern;
|
||||||
|
return Arrays.stream(typevar.getBounds()).allMatch(bound -> isInheritedFrom(strict, bound, dejaVu));
|
||||||
|
}
|
||||||
|
if (strict instanceof GenericArrayType && pattern instanceof GenericArrayType) {
|
||||||
|
return matches(
|
||||||
|
((GenericArrayType) strict).getGenericComponentType(),
|
||||||
|
((GenericArrayType) pattern).getGenericComponentType(),
|
||||||
|
dejaVu);
|
||||||
|
}
|
||||||
|
if (!(strict instanceof ParameterizedType) || !(pattern instanceof ParameterizedType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ParameterizedType parameterizedStrict = (ParameterizedType) strict;
|
||||||
|
ParameterizedType parameterizedPattern = (ParameterizedType) pattern;
|
||||||
|
if (parameterizedPattern.getOwnerType() != null) {
|
||||||
|
if (parameterizedStrict.getOwnerType() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!matches(parameterizedPattern.getOwnerType(), parameterizedStrict.getOwnerType(), dejaVu)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!matches(parameterizedPattern.getRawType(), parameterizedStrict.getRawType(), dejaVu)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type[] strictParams = parameterizedStrict.getActualTypeArguments();
|
||||||
|
Type[] patternParams = parameterizedPattern.getActualTypeArguments();
|
||||||
|
if (strictParams.length != patternParams.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < strictParams.length; i++) {
|
||||||
|
if (!matches(strictParams[i], patternParams[i], dejaVu)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} finally {
|
||||||
|
dejaVu.remove(strict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean contains(Type type, Type sub) {
|
||||||
|
if (type.equals(sub)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (type instanceof GenericArrayType) {
|
||||||
|
return contains(((GenericArrayType) type).getGenericComponentType(), sub);
|
||||||
|
}
|
||||||
|
if (!(type instanceof ParameterizedType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ParameterizedType parameterized = (ParameterizedType) type;
|
||||||
|
if (contains(parameterized.getRawType(), sub)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (parameterized.getOwnerType() != null && contains(parameterized.getOwnerType(), sub)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return Arrays.stream(parameterized.getActualTypeArguments()).anyMatch(argument -> contains(argument, sub));
|
||||||
|
}
|
||||||
|
|
||||||
|
// pattern = Map<K, List<V>>
|
||||||
|
// real = Map<String, List<Integer>>
|
||||||
|
//
|
||||||
|
// result = {K -> String, V -> Integer}
|
||||||
|
public static Map<TypeVariable<?>, Type> extractMatchingGenerics(Type pattern, Type real) {
|
||||||
|
Map<TypeVariable<?>, Type> result = new HashMap<>();
|
||||||
|
extractMatchingGenerics(pattern, real, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void extractMatchingGenerics(Type pattern, Type real, Map<TypeVariable<?>, Type> result) {
|
||||||
|
if (pattern instanceof TypeVariable) {
|
||||||
|
result.put((TypeVariable<?>) pattern, real);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (pattern.equals(real)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (pattern instanceof GenericArrayType && real instanceof GenericArrayType) {
|
||||||
|
extractMatchingGenerics(
|
||||||
|
((GenericArrayType) pattern).getGenericComponentType(),
|
||||||
|
((GenericArrayType) real).getGenericComponentType(),
|
||||||
|
result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!(pattern instanceof ParameterizedType) || !(real instanceof ParameterizedType)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ParameterizedType parameterizedPattern = (ParameterizedType) pattern;
|
||||||
|
ParameterizedType parameterizedReal = (ParameterizedType) real;
|
||||||
|
if (!parameterizedPattern.getRawType().equals(parameterizedReal.getRawType())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
extractMatchingGenerics(parameterizedPattern.getRawType(), parameterizedReal.getRawType(), result);
|
||||||
|
if (!Objects.equals(parameterizedPattern.getOwnerType(), parameterizedReal.getOwnerType())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (parameterizedPattern.getOwnerType() != null) {
|
||||||
|
extractMatchingGenerics(parameterizedPattern.getOwnerType(), parameterizedReal.getOwnerType(), result);
|
||||||
|
}
|
||||||
|
Type[] patternTypeArgs = parameterizedPattern.getActualTypeArguments();
|
||||||
|
Type[] realTypeArgs = parameterizedReal.getActualTypeArguments();
|
||||||
|
if (patternTypeArgs.length != realTypeArgs.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < patternTypeArgs.length; i++) {
|
||||||
|
extractMatchingGenerics(patternTypeArgs[i], realTypeArgs[i], result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Type simplifyType(Type original) {
|
||||||
|
if (original instanceof Class) {
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (original instanceof GenericArrayType) {
|
||||||
|
Type componentType = ((GenericArrayType) original).getGenericComponentType();
|
||||||
|
Type repackedComponentType = simplifyType(componentType);
|
||||||
|
if (componentType != repackedComponentType) {
|
||||||
|
return Types.genericArrayType(repackedComponentType);
|
||||||
|
}
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (original instanceof ParameterizedType) {
|
||||||
|
ParameterizedType parameterizedType = (ParameterizedType) original;
|
||||||
|
Type[] typeArguments = parameterizedType.getActualTypeArguments();
|
||||||
|
Type[] repackedTypeArguments = simplifyTypes(typeArguments);
|
||||||
|
|
||||||
|
if (isAllObjects(repackedTypeArguments)) {
|
||||||
|
return parameterizedType.getRawType();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeArguments != repackedTypeArguments) {
|
||||||
|
return Types.parameterizedType(
|
||||||
|
parameterizedType.getOwnerType(), parameterizedType.getRawType(), repackedTypeArguments);
|
||||||
|
}
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (original instanceof TypeVariable) {
|
||||||
|
throw new IllegalArgumentException("Key should not contain a type variable: " + original);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (original instanceof WildcardType) {
|
||||||
|
WildcardType wildcardType = (WildcardType) original;
|
||||||
|
Type[] upperBounds = wildcardType.getUpperBounds();
|
||||||
|
if (upperBounds.length == 1) {
|
||||||
|
Type upperBound = upperBounds[0];
|
||||||
|
if (upperBound != Object.class) {
|
||||||
|
return simplifyType(upperBound);
|
||||||
|
}
|
||||||
|
} else if (upperBounds.length > 1) {
|
||||||
|
throw new IllegalArgumentException("Multiple upper bounds not supported: " + original);
|
||||||
|
}
|
||||||
|
|
||||||
|
Type[] lowerBounds = wildcardType.getLowerBounds();
|
||||||
|
if (lowerBounds.length == 1) {
|
||||||
|
return simplifyType(lowerBounds[0]);
|
||||||
|
} else if (lowerBounds.length > 1) {
|
||||||
|
throw new IllegalArgumentException("Multiple lower bounds not supported: " + original);
|
||||||
|
}
|
||||||
|
return Object.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Type[] simplifyTypes(Type[] original) {
|
||||||
|
int length = original.length;
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
Type typeArgument = original[i];
|
||||||
|
Type repackTypeArgument = simplifyType(typeArgument);
|
||||||
|
if (repackTypeArgument != typeArgument) {
|
||||||
|
Type[] repackedTypeArguments = new Type[length];
|
||||||
|
System.arraycopy(original, 0, repackedTypeArguments, 0, i);
|
||||||
|
repackedTypeArguments[i++] = repackTypeArgument;
|
||||||
|
for (; i < length; i++) {
|
||||||
|
repackedTypeArguments[i] = simplifyType(original[i]);
|
||||||
|
}
|
||||||
|
return repackedTypeArguments;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isAllObjects(Type[] types) {
|
||||||
|
for (Type type : types) {
|
||||||
|
if (type != Object.class) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether a {@code from} type is assignable to {@code to} type
|
||||||
|
*
|
||||||
|
* @param to a 'to' type that should be checked for possible assignment
|
||||||
|
* @param from a 'from' type that should be checked for possible assignment
|
||||||
|
* @return whether an object of type {@code from} is assignable to an object of type {@code to}
|
||||||
|
*/
|
||||||
|
public static boolean isAssignable(Type to, Type from) {
|
||||||
|
// shortcut
|
||||||
|
if (to instanceof Class && from instanceof Class) {
|
||||||
|
return ((Class<?>) to).isAssignableFrom((Class<?>) from);
|
||||||
|
}
|
||||||
|
return isAssignable(to, from, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
toUppers = wildcardTo.getUpperBounds();
|
||||||
|
toLowers = wildcardTo.getLowerBounds();
|
||||||
|
} else {
|
||||||
|
toUppers = new Type[] {to};
|
||||||
|
toLowers = strict ? toUppers : Types.NO_TYPES;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type[] fromUppers, fromLowers;
|
||||||
|
if (from instanceof WildcardType) {
|
||||||
|
WildcardType wildcardTo = (WildcardType) to;
|
||||||
|
fromUppers = wildcardTo.getUpperBounds();
|
||||||
|
fromLowers = wildcardTo.getLowerBounds();
|
||||||
|
} else {
|
||||||
|
fromUppers = new Type[] {from};
|
||||||
|
fromLowers = strict ? fromUppers : Types.NO_TYPES;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Type toUpper : toUppers) {
|
||||||
|
for (Type fromUpper : fromUppers) {
|
||||||
|
if (!isAssignable(toUpper, fromUpper, false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (toLowers.length == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (fromLowers.length == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (Type toLower : toLowers) {
|
||||||
|
for (Type fromLower : fromLowers) {
|
||||||
|
if (!isAssignable(fromLower, toLower, false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (to instanceof GenericArrayType) {
|
||||||
|
to = Types.getRawType(to);
|
||||||
|
}
|
||||||
|
if (from instanceof GenericArrayType) {
|
||||||
|
from = Types.getRawType(from);
|
||||||
|
}
|
||||||
|
if (!strict && to instanceof Class) {
|
||||||
|
return ((Class<?>) to).isAssignableFrom(Types.getRawType(from));
|
||||||
|
}
|
||||||
|
Class<?> toRawClazz = Types.getRawType(to);
|
||||||
|
Type[] toTypeArguments = Types.getActualTypeArguments(to);
|
||||||
|
return isAssignable(toRawClazz, toTypeArguments, from, strict);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isAssignable(Class<?> toRawClazz, Type[] toTypeArguments, Type from, boolean strict) {
|
||||||
|
Class<?> fromRawClazz = Types.getRawType(from);
|
||||||
|
if (strict && !toRawClazz.equals(fromRawClazz)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!strict && !toRawClazz.isAssignableFrom(fromRawClazz)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (toRawClazz.isArray()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Type[] fromTypeArguments = Types.getActualTypeArguments(from);
|
||||||
|
if (toRawClazz == fromRawClazz) {
|
||||||
|
if (toTypeArguments.length > fromTypeArguments.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < toTypeArguments.length; i++) {
|
||||||
|
if (!isAssignable(toTypeArguments[i], fromTypeArguments[i], true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Map<TypeVariable<?>, Type> typeBindings = Types.getTypeBindings(from);
|
||||||
|
for (Type anInterface : fromRawClazz.getGenericInterfaces()) {
|
||||||
|
if (isAssignable(
|
||||||
|
toRawClazz,
|
||||||
|
toTypeArguments,
|
||||||
|
Types.bind(anInterface, key -> typeBindings.getOrDefault(key, Types.wildcardTypeAny())),
|
||||||
|
false)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type superclass = fromRawClazz.getGenericSuperclass();
|
||||||
|
return superclass != null
|
||||||
|
&& isAssignable(toRawClazz, toTypeArguments, Types.bind(superclass, typeBindings), false);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,471 @@
|
||||||
|
/*
|
||||||
|
* 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 java.lang.reflect.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.apache.maven.api.annotations.Nullable;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.joining;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Various helper methods for type processing
|
||||||
|
*/
|
||||||
|
public class Types {
|
||||||
|
public static final Type[] NO_TYPES = new Type[0];
|
||||||
|
public static final WildcardType WILDCARD_TYPE_ANY = new WildcardTypeImpl(new Type[] {Object.class}, new Type[0]);
|
||||||
|
private static final Map<Type, Map<TypeVariable<?>, Type>> TYPE_BINDINGS_CACHE = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a raw {@link Class} for a given {@link Type}.
|
||||||
|
* <p>
|
||||||
|
* A type can be any of {@link Class}, {@link ParameterizedType}, {@link WildcardType},
|
||||||
|
* {@link GenericArrayType} or {@link TypeVariable}
|
||||||
|
*/
|
||||||
|
public static Class<?> getRawType(Type type) {
|
||||||
|
if (type instanceof Class) {
|
||||||
|
return (Class<?>) type;
|
||||||
|
} else if (type instanceof ParameterizedType) {
|
||||||
|
return (Class<?>) ((ParameterizedType) type).getRawType();
|
||||||
|
} else if (type instanceof WildcardType) {
|
||||||
|
Type[] upperBounds = ((WildcardType) type).getUpperBounds();
|
||||||
|
return getRawType(getUppermostType(upperBounds));
|
||||||
|
} else if (type instanceof GenericArrayType) {
|
||||||
|
Class<?> rawComponentType = getRawType(((GenericArrayType) type).getGenericComponentType());
|
||||||
|
try {
|
||||||
|
return Class.forName("[L" + rawComponentType.getName() + ";");
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
} else if (type instanceof TypeVariable) {
|
||||||
|
return getRawType(getUppermostType(((TypeVariable<?>) type).getBounds()));
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unsupported type: " + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the most common type among given types
|
||||||
|
*/
|
||||||
|
public static Type getUppermostType(Type[] types) {
|
||||||
|
Type result = types[0];
|
||||||
|
for (int i = 1; i < types.length; i++) {
|
||||||
|
Type type = types[i];
|
||||||
|
if (TypeUtils.isAssignable(type, result)) {
|
||||||
|
result = type;
|
||||||
|
continue;
|
||||||
|
} else if (TypeUtils.isAssignable(result, type)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unrelated types: " + result + " , " + type);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of actual type arguments for a given {@link Type}
|
||||||
|
*
|
||||||
|
* @param type type whose actual type arguments should be retrieved
|
||||||
|
* @return an array of actual type arguments for a given {@link Type}
|
||||||
|
*/
|
||||||
|
public static Type[] getActualTypeArguments(Type type) {
|
||||||
|
if (type instanceof Class) {
|
||||||
|
return ((Class<?>) type).isArray() ? new Type[] {((Class<?>) type).getComponentType()} : NO_TYPES;
|
||||||
|
} else if (type instanceof ParameterizedType) {
|
||||||
|
return ((ParameterizedType) type).getActualTypeArguments();
|
||||||
|
} else if (type instanceof GenericArrayType) {
|
||||||
|
return new Type[] {((GenericArrayType) type).getGenericComponentType()};
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unsupported type: " + type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a map of type bindings for a given {@link Type}
|
||||||
|
*/
|
||||||
|
public static Map<TypeVariable<?>, Type> getTypeBindings(Type type) {
|
||||||
|
Type[] typeArguments = getActualTypeArguments(type);
|
||||||
|
if (typeArguments.length == 0) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
TypeVariable<?>[] typeVariables = getRawType(type).getTypeParameters();
|
||||||
|
Map<TypeVariable<?>, Type> map = new HashMap<>();
|
||||||
|
for (int i = 0; i < typeVariables.length; i++) {
|
||||||
|
map.put(typeVariables[i], typeArguments[i]);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a map of all type bindings for a given {@link Type}.
|
||||||
|
* Includes type bindings from a whole class hierarchy
|
||||||
|
*/
|
||||||
|
public static Map<TypeVariable<?>, Type> getAllTypeBindings(Type type) {
|
||||||
|
return TYPE_BINDINGS_CACHE.computeIfAbsent(type, t -> {
|
||||||
|
Map<TypeVariable<?>, Type> mapping = new HashMap<>();
|
||||||
|
getAllTypeBindingsImpl(t, mapping);
|
||||||
|
return mapping;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void getAllTypeBindingsImpl(Type type, Map<TypeVariable<?>, Type> mapping) {
|
||||||
|
Class<?> cls = getRawType(type);
|
||||||
|
|
||||||
|
if (type instanceof ParameterizedType) {
|
||||||
|
Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
|
||||||
|
if (typeArguments.length != 0) {
|
||||||
|
TypeVariable<? extends Class<?>>[] typeVariables = cls.getTypeParameters();
|
||||||
|
for (int i = 0; i < typeArguments.length; i++) {
|
||||||
|
Type typeArgument = typeArguments[i];
|
||||||
|
mapping.put(
|
||||||
|
typeVariables[i],
|
||||||
|
typeArgument instanceof TypeVariable
|
||||||
|
? Objects.requireNonNull(mapping.get(typeArgument))
|
||||||
|
: typeArgument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Type superclass = cls.getGenericSuperclass();
|
||||||
|
if (superclass != null) {
|
||||||
|
getAllTypeBindingsImpl(superclass, mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Type anInterface : cls.getGenericInterfaces()) {
|
||||||
|
getAllTypeBindingsImpl(anInterface, mapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds a given type with actual type arguments
|
||||||
|
*
|
||||||
|
* @param type a type to be bound
|
||||||
|
* @param bindings a map of actual types
|
||||||
|
*/
|
||||||
|
public static Type bind(Type type, Map<TypeVariable<?>, Type> bindings) {
|
||||||
|
return bind(type, bindings::get);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds a given type with actual type arguments
|
||||||
|
*
|
||||||
|
* @param type a type to be bound
|
||||||
|
* @param bindings a lookup function for actual types
|
||||||
|
*/
|
||||||
|
public static Type bind(Type type, Function<TypeVariable<?>, Type> bindings) {
|
||||||
|
if (type instanceof Class) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
if (type instanceof TypeVariable<?>) {
|
||||||
|
TypeVariable<?> typeVariable = (TypeVariable<?>) type;
|
||||||
|
Type actualType = bindings.apply(typeVariable);
|
||||||
|
if (actualType == null) {
|
||||||
|
throw new IllegalArgumentException("Type variable not found: " + typeVariable + " ( "
|
||||||
|
+ typeVariable.getGenericDeclaration() + " ) ");
|
||||||
|
}
|
||||||
|
return actualType;
|
||||||
|
}
|
||||||
|
if (type instanceof ParameterizedType) {
|
||||||
|
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||||
|
Type[] typeArguments = parameterizedType.getActualTypeArguments();
|
||||||
|
Type[] typeArguments2 = new Type[typeArguments.length];
|
||||||
|
for (int i = 0; i < typeArguments.length; i++) {
|
||||||
|
typeArguments2[i] = bind(typeArguments[i], bindings);
|
||||||
|
}
|
||||||
|
return new ParameterizedTypeImpl(
|
||||||
|
parameterizedType.getOwnerType(), parameterizedType.getRawType(), typeArguments2);
|
||||||
|
}
|
||||||
|
if (type instanceof GenericArrayType) {
|
||||||
|
Type componentType = ((GenericArrayType) type).getGenericComponentType();
|
||||||
|
return new GenericArrayTypeImpl(bind(componentType, bindings));
|
||||||
|
}
|
||||||
|
if (type instanceof WildcardType) {
|
||||||
|
WildcardType wildcardType = (WildcardType) type;
|
||||||
|
Type[] upperBounds = wildcardType.getUpperBounds();
|
||||||
|
Type[] upperBounds2 = new Type[upperBounds.length];
|
||||||
|
for (int i = 0; i < upperBounds.length; i++) {
|
||||||
|
upperBounds2[i] = bind(upperBounds[i], bindings);
|
||||||
|
}
|
||||||
|
Type[] lowerBounds = wildcardType.getLowerBounds();
|
||||||
|
Type[] lowerBounds2 = new Type[lowerBounds.length];
|
||||||
|
for (int i = 0; i < lowerBounds.length; i++) {
|
||||||
|
lowerBounds2[i] = bind(lowerBounds[i], bindings);
|
||||||
|
}
|
||||||
|
return new WildcardTypeImpl(upperBounds2, lowerBounds2);
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unsupported type: " + type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link ParameterizedType}
|
||||||
|
*
|
||||||
|
* @param ownerType an owner type
|
||||||
|
* @param rawType a type to be parameterized
|
||||||
|
* @param parameters parameter types
|
||||||
|
* @return an instance of {@link ParameterizedType}
|
||||||
|
*/
|
||||||
|
public static ParameterizedType parameterizedType(@Nullable Type ownerType, Type rawType, Type[] parameters) {
|
||||||
|
return new ParameterizedTypeImpl(ownerType, rawType, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link ParameterizedType}
|
||||||
|
*
|
||||||
|
* @see #parameterizedType(Type, Type, Type[])
|
||||||
|
*/
|
||||||
|
public static ParameterizedType parameterizedType(Class<?> rawType, Type... parameters) {
|
||||||
|
return new ParameterizedTypeImpl(null, rawType, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class ParameterizedTypeImpl implements ParameterizedType {
|
||||||
|
private final @Nullable Type ownerType;
|
||||||
|
private final Type rawType;
|
||||||
|
private final Type[] actualTypeArguments;
|
||||||
|
|
||||||
|
ParameterizedTypeImpl(@Nullable Type ownerType, Type rawType, Type[] actualTypeArguments) {
|
||||||
|
this.ownerType = ownerType;
|
||||||
|
this.rawType = rawType;
|
||||||
|
this.actualTypeArguments = actualTypeArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getRawType() {
|
||||||
|
return rawType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type[] getActualTypeArguments() {
|
||||||
|
return actualTypeArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Type getOwnerType() {
|
||||||
|
return ownerType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(ownerType) ^ Arrays.hashCode(actualTypeArguments) ^ rawType.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (!(other instanceof ParameterizedType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ParameterizedType that = (ParameterizedType) other;
|
||||||
|
return this.getRawType().equals(that.getRawType())
|
||||||
|
&& Objects.equals(this.getOwnerType(), that.getOwnerType())
|
||||||
|
&& Arrays.equals(this.getActualTypeArguments(), that.getActualTypeArguments());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return rawType.getTypeName()
|
||||||
|
+ Arrays.stream(actualTypeArguments).map(Types::toString).collect(joining(", ", "<", ">"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link WildcardType} bound by upper and lower bounds
|
||||||
|
*
|
||||||
|
* @param upperBounds a wildcard upper bound types
|
||||||
|
* @param lowerBounds a wildcard lower bound types
|
||||||
|
* @return an instance of {@link WildcardType}
|
||||||
|
*/
|
||||||
|
public static WildcardType wildcardType(Type[] upperBounds, Type[] lowerBounds) {
|
||||||
|
return new WildcardTypeImpl(upperBounds, lowerBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an instance of {@link WildcardType} that matches any type
|
||||||
|
* <p>
|
||||||
|
* E.g. {@code <?>}
|
||||||
|
*
|
||||||
|
* @see #wildcardType(Type[], Type[])
|
||||||
|
*/
|
||||||
|
public static WildcardType wildcardTypeAny() {
|
||||||
|
return WILDCARD_TYPE_ANY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link WildcardType} bound by a single upper bound
|
||||||
|
* <p>
|
||||||
|
* E.g. {@code <? extends UpperBound>}
|
||||||
|
*
|
||||||
|
* @param upperBound a wildcard upper bound type
|
||||||
|
* @return an instance of {@link WildcardType}
|
||||||
|
* @see #wildcardType(Type[], Type[])
|
||||||
|
*/
|
||||||
|
public static WildcardType wildcardTypeExtends(Type upperBound) {
|
||||||
|
return new WildcardTypeImpl(new Type[] {upperBound}, NO_TYPES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link WildcardType} bound by a single lower bound
|
||||||
|
* <p>
|
||||||
|
* E.g. {@code <? super LowerBound>}
|
||||||
|
*
|
||||||
|
* @param lowerBound a wildcard lower bound type
|
||||||
|
* @return an instance of {@link WildcardType}
|
||||||
|
* @see #wildcardType(Type[], Type[])
|
||||||
|
*/
|
||||||
|
public static WildcardType wildcardTypeSuper(Type lowerBound) {
|
||||||
|
return new WildcardTypeImpl(NO_TYPES, new Type[] {lowerBound});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WildcardTypeImpl implements WildcardType {
|
||||||
|
private final Type[] upperBounds;
|
||||||
|
private final Type[] lowerBounds;
|
||||||
|
|
||||||
|
WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
|
||||||
|
this.upperBounds = upperBounds;
|
||||||
|
this.lowerBounds = lowerBounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type[] getUpperBounds() {
|
||||||
|
return upperBounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type[] getLowerBounds() {
|
||||||
|
return lowerBounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Arrays.hashCode(upperBounds) ^ Arrays.hashCode(lowerBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (!(other instanceof WildcardType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
WildcardType that = (WildcardType) other;
|
||||||
|
return Arrays.equals(this.getUpperBounds(), that.getUpperBounds())
|
||||||
|
&& Arrays.equals(this.getLowerBounds(), that.getLowerBounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "?"
|
||||||
|
+ (upperBounds.length == 0
|
||||||
|
? ""
|
||||||
|
: " extends "
|
||||||
|
+ Arrays.stream(upperBounds)
|
||||||
|
.map(Types::toString)
|
||||||
|
.collect(joining(" & ")))
|
||||||
|
+ (lowerBounds.length == 0
|
||||||
|
? ""
|
||||||
|
: " super "
|
||||||
|
+ Arrays.stream(lowerBounds)
|
||||||
|
.map(Types::toString)
|
||||||
|
.collect(joining(" & ")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link GenericArrayType} with a given component type
|
||||||
|
* <p>
|
||||||
|
* Same as {@code T[]}
|
||||||
|
*
|
||||||
|
* @param componentType a component type of generic array
|
||||||
|
* @return an instance of {@link GenericArrayType}
|
||||||
|
* @see #wildcardType(Type[], Type[])
|
||||||
|
*/
|
||||||
|
public static GenericArrayType genericArrayType(Type componentType) {
|
||||||
|
return new GenericArrayTypeImpl(componentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class GenericArrayTypeImpl implements GenericArrayType {
|
||||||
|
private final Type componentType;
|
||||||
|
|
||||||
|
GenericArrayTypeImpl(Type componentType) {
|
||||||
|
this.componentType = componentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getGenericComponentType() {
|
||||||
|
return componentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return componentType.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (!(other instanceof GenericArrayType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
GenericArrayType that = (GenericArrayType) other;
|
||||||
|
return this.getGenericComponentType().equals(that.getGenericComponentType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Types.toString(componentType) + "[]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toString(Type type) {
|
||||||
|
return type instanceof Class ? ((Class<?>) type).getName() : type.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a simple name for a given {@link Type}
|
||||||
|
*
|
||||||
|
* @see Class#getSimpleName()
|
||||||
|
*/
|
||||||
|
public static String getSimpleName(Type type) {
|
||||||
|
if (type instanceof Class) {
|
||||||
|
return ((Class<?>) type).getSimpleName();
|
||||||
|
} else if (type instanceof ParameterizedType) {
|
||||||
|
return Arrays.stream(((ParameterizedType) type).getActualTypeArguments())
|
||||||
|
.map(Types::getSimpleName)
|
||||||
|
.collect(joining(",", "<", ">"));
|
||||||
|
} else if (type instanceof WildcardType) {
|
||||||
|
WildcardType wildcardType = (WildcardType) type;
|
||||||
|
Type[] upperBounds = wildcardType.getUpperBounds();
|
||||||
|
Type[] lowerBounds = wildcardType.getLowerBounds();
|
||||||
|
return "?"
|
||||||
|
+ (upperBounds.length == 0
|
||||||
|
? ""
|
||||||
|
: " extends "
|
||||||
|
+ Arrays.stream(upperBounds)
|
||||||
|
.map(Types::getSimpleName)
|
||||||
|
.collect(joining(" & ")))
|
||||||
|
+ (lowerBounds.length == 0
|
||||||
|
? ""
|
||||||
|
: " super "
|
||||||
|
+ Arrays.stream(lowerBounds)
|
||||||
|
.map(Types::getSimpleName)
|
||||||
|
.collect(joining(" & ")));
|
||||||
|
} else if (type instanceof GenericArrayType) {
|
||||||
|
return Types.getSimpleName(((GenericArrayType) type).getGenericComponentType()) + "[]";
|
||||||
|
}
|
||||||
|
|
||||||
|
return type.getTypeName();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* 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 java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
import org.apache.maven.api.annotations.Nullable;
|
||||||
|
|
||||||
|
public final class Utils {
|
||||||
|
|
||||||
|
public static String getDisplayString(Class<? extends Annotation> annotationType, @Nullable Annotation annotation) {
|
||||||
|
if (annotation == null) {
|
||||||
|
return "@" + ReflectionUtils.getDisplayName(annotationType);
|
||||||
|
}
|
||||||
|
String typeName = annotationType.getName();
|
||||||
|
String str = annotation.toString();
|
||||||
|
return str.startsWith("@" + typeName)
|
||||||
|
? "@" + ReflectionUtils.getDisplayName(annotationType) + str.substring(typeName.length() + 1)
|
||||||
|
: str;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getDisplayString(Object object) {
|
||||||
|
if (object instanceof Class && ((Class<?>) object).isAnnotation()) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return getDisplayString((Class<? extends Annotation>) object, null);
|
||||||
|
}
|
||||||
|
if (object instanceof Annotation) {
|
||||||
|
Annotation annotation = (Annotation) object;
|
||||||
|
return getDisplayString(annotation.annotationType(), annotation);
|
||||||
|
}
|
||||||
|
return object.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isMarker(Class<? extends Annotation> annotationType) {
|
||||||
|
return annotationType.getDeclaredMethods().length == 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
/*
|
||||||
|
* 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.processor;
|
||||||
|
|
||||||
|
import javax.annotation.processing.Completion;
|
||||||
|
import javax.annotation.processing.ProcessingEnvironment;
|
||||||
|
import javax.annotation.processing.Processor;
|
||||||
|
import javax.annotation.processing.RoundEnvironment;
|
||||||
|
import javax.lang.model.SourceVersion;
|
||||||
|
import javax.lang.model.element.*;
|
||||||
|
import javax.tools.Diagnostic;
|
||||||
|
import javax.tools.StandardLocation;
|
||||||
|
import javax.xml.stream.XMLStreamReader;
|
||||||
|
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import com.ctc.wstx.stax.WstxInputFactory;
|
||||||
|
import org.apache.maven.api.di.Qualifier;
|
||||||
|
import org.apache.maven.api.xml.XmlNode;
|
||||||
|
import org.apache.maven.internal.xml.XmlNodeBuilder;
|
||||||
|
|
||||||
|
public class IndexAnnotationProcessor implements Processor {
|
||||||
|
|
||||||
|
private ProcessingEnvironment environment;
|
||||||
|
private Set<String> index = new TreeSet<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(ProcessingEnvironment processingEnv) {
|
||||||
|
this.environment = processingEnv;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||||
|
for (TypeElement annotation : annotations) {
|
||||||
|
if (annotation.getAnnotation(Qualifier.class) != null) {
|
||||||
|
for (Element elem : roundEnv.getElementsAnnotatedWith(annotation)) {
|
||||||
|
if (elem.getKind().isClass()) {
|
||||||
|
addClassToIndex(environment
|
||||||
|
.getElementUtils()
|
||||||
|
.getBinaryName((TypeElement) elem)
|
||||||
|
.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Element elem : roundEnv.getElementsAnnotatedWith(org.apache.maven.api.plugin.annotations.Mojo.class)) {
|
||||||
|
PackageElement packageElement = environment.getElementUtils().getPackageOf(elem);
|
||||||
|
String packageName = packageElement.getQualifiedName().toString();
|
||||||
|
String generatorClassName = elem.getSimpleName().toString() + "Factory";
|
||||||
|
|
||||||
|
String mojoName = elem.getAnnotation(org.apache.maven.api.plugin.annotations.Mojo.class)
|
||||||
|
.name();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Reader reader = environment
|
||||||
|
.getFiler()
|
||||||
|
.getResource(StandardLocation.CLASS_OUTPUT, "", "META-INF/maven/plugin.xml")
|
||||||
|
.openReader(true);
|
||||||
|
XMLStreamReader parser = WstxInputFactory.newFactory().createXMLStreamReader(reader);
|
||||||
|
XmlNode plugin = XmlNodeBuilder.build(parser, null);
|
||||||
|
String groupId = plugin.getChild("groupId").getValue();
|
||||||
|
String artifactId = plugin.getChild("artifactId").getValue();
|
||||||
|
String version = plugin.getChild("version").getValue();
|
||||||
|
|
||||||
|
Writer file = environment
|
||||||
|
.getFiler()
|
||||||
|
.createSourceFile(packageName + "." + generatorClassName)
|
||||||
|
.openWriter();
|
||||||
|
file.write("package " + packageName + ";\n");
|
||||||
|
file.write("public class " + generatorClassName + " {\n");
|
||||||
|
file.write(" @org.apache.maven.api.di.Named(\"" + groupId + ":" + artifactId + ":" + version + ":"
|
||||||
|
+ mojoName + "\")\n");
|
||||||
|
file.write(" @org.apache.maven.api.di.Provides\n");
|
||||||
|
file.write(" public static " + ((TypeElement) elem).getQualifiedName() + " create() {\n");
|
||||||
|
file.write(" return new " + ((TypeElement) elem).getQualifiedName() + "();\n");
|
||||||
|
file.write(" }\n");
|
||||||
|
file.write("}\n");
|
||||||
|
file.flush();
|
||||||
|
file.close();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.getLogger(IndexAnnotationProcessor.class.getName()).log(Level.SEVERE, null, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
addClassToIndex(packageName + "." + generatorClassName);
|
||||||
|
}
|
||||||
|
if (roundEnv.processingOver()) {
|
||||||
|
flushIndex();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addClassToIndex(String className) {
|
||||||
|
index.add(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void flushIndex() {
|
||||||
|
try (BufferedWriter writer = new BufferedWriter(environment
|
||||||
|
.getFiler()
|
||||||
|
.createResource(StandardLocation.CLASS_OUTPUT, "", "META-INF/maven/org.apache.maven.api.di.Inject")
|
||||||
|
.openWriter())) {
|
||||||
|
for (String line : index) {
|
||||||
|
writer.write(line);
|
||||||
|
writer.newLine();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
environment.getMessager().printMessage(Diagnostic.Kind.WARNING, e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<? extends Completion> getCompletions(
|
||||||
|
Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getSupportedAnnotationTypes() {
|
||||||
|
return Collections.singleton("*");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getSupportedOptions() {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SourceVersion getSupportedSourceVersion() {
|
||||||
|
return SourceVersion.latestSupported();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
org.apache.maven.di.processor.IndexAnnotationProcessor
|
|
@ -0,0 +1,277 @@
|
||||||
|
/*
|
||||||
|
* 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 java.lang.annotation.Retention;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import org.apache.maven.api.di.*;
|
||||||
|
import org.apache.maven.di.Injector;
|
||||||
|
import org.apache.maven.di.Key;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public class DITest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void markerQualifierTest() {
|
||||||
|
Injector injector = Injector.create().bindImplicit(QualifierTest.class);
|
||||||
|
QualifierTest.MyMojo mojo = injector.getInstance(QualifierTest.MyMojo.class);
|
||||||
|
assertNotNull(mojo);
|
||||||
|
assertInstanceOf(QualifierTest.MyQualifiedServiceImpl.class, mojo.service);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class QualifierTest {
|
||||||
|
@Qualifier
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@interface MyQualifier {}
|
||||||
|
|
||||||
|
interface MyService {}
|
||||||
|
|
||||||
|
@Named
|
||||||
|
@Priority(10)
|
||||||
|
static class MyNamedServiceImpl implements MyService {}
|
||||||
|
|
||||||
|
@MyQualifier
|
||||||
|
static class MyQualifiedServiceImpl implements MyService {}
|
||||||
|
|
||||||
|
@Named
|
||||||
|
static class MyMojo {
|
||||||
|
@Inject
|
||||||
|
@MyQualifier
|
||||||
|
MyService service;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void priorityTest() {
|
||||||
|
Injector injector = Injector.create().bindImplicit(PriorityTest.class);
|
||||||
|
PriorityTest.MyMojo mojo = injector.getInstance(PriorityTest.MyMojo.class);
|
||||||
|
assertNotNull(mojo);
|
||||||
|
assertInstanceOf(PriorityTest.MyPriorityServiceImpl.class, mojo.service);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class PriorityTest {
|
||||||
|
|
||||||
|
interface MyService {}
|
||||||
|
|
||||||
|
@Named
|
||||||
|
static class MyServiceImpl implements MyService {}
|
||||||
|
|
||||||
|
@Named
|
||||||
|
@Priority(10)
|
||||||
|
static class MyPriorityServiceImpl implements MyService {}
|
||||||
|
|
||||||
|
@Named
|
||||||
|
static class MyMojo {
|
||||||
|
@Inject
|
||||||
|
MyService service;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void mojoTest() {
|
||||||
|
Injector injector = Injector.create().bindImplicit(MojoTest.class);
|
||||||
|
MojoTest.MyMojo mojo = injector.getInstance(MojoTest.MyMojo.class);
|
||||||
|
assertNotNull(mojo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
static class MojoTest {
|
||||||
|
@Qualifier
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@interface Mojo {}
|
||||||
|
|
||||||
|
interface MyService {}
|
||||||
|
|
||||||
|
@Named
|
||||||
|
static class MyServiceImpl implements MyService {}
|
||||||
|
|
||||||
|
@Mojo
|
||||||
|
static class MyMojo {
|
||||||
|
@Inject
|
||||||
|
MyService service;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void typedTest() {
|
||||||
|
Injector injector =
|
||||||
|
Injector.create().bindImplicit(TypedTest.MyServiceImpl.class).bindImplicit(TypedTest.MyMojo.class);
|
||||||
|
TypedTest.MyMojo mojo = injector.getInstance(TypedTest.MyMojo.class);
|
||||||
|
assertNotNull(mojo);
|
||||||
|
assertNotNull(mojo.service);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
static class TypedTest {
|
||||||
|
|
||||||
|
interface MyService {}
|
||||||
|
|
||||||
|
@Named
|
||||||
|
@Typed
|
||||||
|
static class MyServiceImpl implements MyService {}
|
||||||
|
|
||||||
|
@Named
|
||||||
|
static class MyMojo {
|
||||||
|
@Inject
|
||||||
|
MyService service;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void bindInterfacesTest() {
|
||||||
|
Injector injector = Injector.create().bindImplicit(BindInterfaces.class);
|
||||||
|
BindInterfaces.TestInterface<String> inst =
|
||||||
|
injector.getInstance(new Key<BindInterfaces.TestInterface<String>>() {});
|
||||||
|
assertNotNull(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class BindInterfaces {
|
||||||
|
|
||||||
|
interface TestInterface<T> {
|
||||||
|
T getObj();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Named
|
||||||
|
static class ClassImpl implements TestInterface<String> {
|
||||||
|
@Override
|
||||||
|
public String getObj() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Named
|
||||||
|
@Typed
|
||||||
|
static class TypedClassImpl implements TestInterface<String> {
|
||||||
|
@Override
|
||||||
|
public String getObj() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void injectListTest() {
|
||||||
|
Injector injector = Injector.create().bindImplicit(InjectList.class);
|
||||||
|
List<InjectList.MyService> services = injector.getInstance(new Key<List<InjectList.MyService>>() {});
|
||||||
|
assertNotNull(services);
|
||||||
|
assertEquals(2, services.size());
|
||||||
|
|
||||||
|
assertNotNull(services.get(0));
|
||||||
|
assertInstanceOf(InjectList.MyService.class, services.get(0));
|
||||||
|
assertNotNull(services.get(1));
|
||||||
|
assertInstanceOf(InjectList.MyService.class, services.get(1));
|
||||||
|
assertNotSame(services.get(0).getClass(), services.get(1).getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
static class InjectList {
|
||||||
|
|
||||||
|
interface MyService {}
|
||||||
|
|
||||||
|
@Named("foo")
|
||||||
|
static class MyServiceImpl implements MyService {}
|
||||||
|
|
||||||
|
@Named("bar")
|
||||||
|
static class AnotherServiceImpl implements MyService {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void injectMapTest() {
|
||||||
|
Injector injector = Injector.create().bindImplicit(InjectMap.class);
|
||||||
|
Map<String, InjectMap.MyService> services =
|
||||||
|
injector.getInstance(new Key<Map<String, InjectMap.MyService>>() {});
|
||||||
|
assertNotNull(services);
|
||||||
|
assertEquals(2, services.size());
|
||||||
|
|
||||||
|
List<Map.Entry<String, InjectMap.MyService>> entries = new ArrayList<>(services.entrySet());
|
||||||
|
assertNotNull(entries.get(0));
|
||||||
|
assertInstanceOf(InjectMap.MyService.class, entries.get(0).getValue());
|
||||||
|
assertInstanceOf(String.class, entries.get(0).getKey());
|
||||||
|
assertNotNull(entries.get(1));
|
||||||
|
assertInstanceOf(String.class, entries.get(1).getKey());
|
||||||
|
assertInstanceOf(InjectMap.MyService.class, entries.get(1).getValue());
|
||||||
|
assertNotEquals(entries.get(0).getKey(), entries.get(1).getKey());
|
||||||
|
assertNotSame(
|
||||||
|
entries.get(0).getValue().getClass(), entries.get(1).getValue().getClass());
|
||||||
|
|
||||||
|
InjectMap.MyMojo mojo = injector.getInstance(InjectMap.MyMojo.class);
|
||||||
|
assertNotNull(mojo);
|
||||||
|
assertNotNull(mojo.services);
|
||||||
|
assertEquals(2, mojo.services.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
static class InjectMap {
|
||||||
|
|
||||||
|
interface MyService {}
|
||||||
|
|
||||||
|
@Named("foo")
|
||||||
|
static class MyServiceImpl implements MyService {}
|
||||||
|
|
||||||
|
@Named("bar")
|
||||||
|
static class AnotherServiceImpl implements MyService {}
|
||||||
|
|
||||||
|
@Named
|
||||||
|
static class MyMojo {
|
||||||
|
@Inject
|
||||||
|
Map<String, MyService> services;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSingleton() {
|
||||||
|
Injector injector = Injector.create()
|
||||||
|
.bindImplicit(SingletonContainer.Bean1.class)
|
||||||
|
.bindImplicit(SingletonContainer.Bean2.class);
|
||||||
|
|
||||||
|
SingletonContainer.Bean1 b1a = injector.getInstance(SingletonContainer.Bean1.class);
|
||||||
|
assertNotNull(b1a);
|
||||||
|
SingletonContainer.Bean1 b1b = injector.getInstance(SingletonContainer.Bean1.class);
|
||||||
|
assertNotNull(b1b);
|
||||||
|
assertEquals(b1a.num, b1b.num);
|
||||||
|
|
||||||
|
SingletonContainer.Bean2 b2a = injector.getInstance(SingletonContainer.Bean2.class);
|
||||||
|
assertNotNull(b2a);
|
||||||
|
SingletonContainer.Bean2 b2b = injector.getInstance(SingletonContainer.Bean2.class);
|
||||||
|
assertNotNull(b2b);
|
||||||
|
assertNotEquals(b2a.num, b2b.num);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class SingletonContainer {
|
||||||
|
private static final AtomicInteger bean1 = new AtomicInteger();
|
||||||
|
private static final AtomicInteger bean2 = new AtomicInteger();
|
||||||
|
|
||||||
|
@Named
|
||||||
|
@Singleton
|
||||||
|
static class Bean1 {
|
||||||
|
int num = bean1.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Named
|
||||||
|
static class Bean2 {
|
||||||
|
int num = bean2.incrementAndGet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* 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 java.lang.reflect.AnnotatedParameterizedType;
|
||||||
|
import java.lang.reflect.AnnotatedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A type token for defining complex types (annotated, parameterized)
|
||||||
|
* <p>
|
||||||
|
* Usage example:
|
||||||
|
* <p>
|
||||||
|
* {@code Type listOfStringsType = new TypeT<List<String>>(){}.getType()}
|
||||||
|
*
|
||||||
|
* @param <T> actual type
|
||||||
|
*/
|
||||||
|
public abstract class TypeT<T> {
|
||||||
|
private final AnnotatedType annotatedType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new type token. A type argument {@link T} <b>must</b> be specified.
|
||||||
|
* A typical usage is:
|
||||||
|
* <p>
|
||||||
|
* {@code TypeT<List<Integer>> integerListTypeT = new TypeT<List<Integer>>(){};}
|
||||||
|
*
|
||||||
|
* @throws AssertionError if a {@link TypeT} is created with a raw type
|
||||||
|
*/
|
||||||
|
protected TypeT() {
|
||||||
|
this.annotatedType = getSuperclassTypeParameter(this.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AnnotatedType getSuperclassTypeParameter(Class<?> subclass) {
|
||||||
|
AnnotatedType superclass = subclass.getAnnotatedSuperclass();
|
||||||
|
if (superclass instanceof AnnotatedParameterizedType) {
|
||||||
|
return ((AnnotatedParameterizedType) superclass).getAnnotatedActualTypeArguments()[0];
|
||||||
|
}
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an {@link AnnotatedType} of a {@link T}
|
||||||
|
*/
|
||||||
|
public final AnnotatedType getAnnotatedType() {
|
||||||
|
return annotatedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@link Type} of a {@link T}
|
||||||
|
*/
|
||||||
|
public final Type getType() {
|
||||||
|
return annotatedType.getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a raw type (e.g {@link Class}) of a {@link T}
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public final Class<T> getRawType() {
|
||||||
|
return (Class<T>) Types.getRawType(annotatedType.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String toString() {
|
||||||
|
return annotatedType.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,157 @@
|
||||||
|
/*
|
||||||
|
* 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 java.lang.reflect.Type;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.apache.maven.di.impl.TypeUtils.simplifyType;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
public class TypeUtilsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSimplifyType() {
|
||||||
|
{
|
||||||
|
Type type = Integer.class;
|
||||||
|
assertEquals(type, simplifyType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Type type = new TypeT<Set<Integer>>() {}.getType();
|
||||||
|
assertEquals(type, simplifyType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Type type = new TypeT<Set<Set<Set<Integer>>>>() {}.getType();
|
||||||
|
assertEquals(type, simplifyType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Type type = new TypeT<Set<? extends Integer>>() {}.getType();
|
||||||
|
Type expected = new TypeT<Set<Integer>>() {}.getType();
|
||||||
|
assertEquals(expected, simplifyType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Type type = new TypeT<Set<? extends Set<? extends Set<? extends Integer>>>>() {}.getType();
|
||||||
|
Type expected = new TypeT<Set<Set<Set<Integer>>>>() {}.getType();
|
||||||
|
assertEquals(expected, simplifyType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Type type = new TypeT<Set<Set<? extends Set<Integer>>>>() {}.getType();
|
||||||
|
Type expected = new TypeT<Set<Set<Set<Integer>>>>() {}.getType();
|
||||||
|
assertEquals(expected, simplifyType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Type type = new TypeT<Set<? super Integer>>() {}.getType();
|
||||||
|
Type expected = new TypeT<Set<Integer>>() {}.getType();
|
||||||
|
assertEquals(expected, simplifyType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Type type = new TypeT<Set<? super Set<? super Set<? super Integer>>>>() {}.getType();
|
||||||
|
Type expected = new TypeT<Set<Set<Set<Integer>>>>() {}.getType();
|
||||||
|
assertEquals(expected, simplifyType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Type type = new TypeT<Set<Set<? super Set<Integer>>>>() {}.getType();
|
||||||
|
Type expected = new TypeT<Set<Set<Set<Integer>>>>() {}.getType();
|
||||||
|
assertEquals(expected, simplifyType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Type type = new TypeT<Set<? extends Set<? super Set<? extends Integer>>>>() {}.getType();
|
||||||
|
Type expected = new TypeT<Set<Set<Set<Integer>>>>() {}.getType();
|
||||||
|
assertEquals(expected, simplifyType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Type type = new TypeT<Set<? extends Integer>[]>() {}.getType();
|
||||||
|
Type expected = new TypeT<Set<Integer>[]>() {}.getType();
|
||||||
|
assertEquals(expected, simplifyType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Type type = new TypeT<Set<? super Integer>[]>() {}.getType();
|
||||||
|
Type expected = new TypeT<Set<Integer>[]>() {}.getType();
|
||||||
|
assertEquals(expected, simplifyType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Type type = new TypeT<TestInterface<? extends Integer, ? extends Integer>>() {}.getType();
|
||||||
|
Type expected = new TypeT<TestInterface<Integer, Integer>>() {}.getType();
|
||||||
|
assertEquals(expected, simplifyType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Type type = new TypeT<TestInterface<Integer, Integer>>() {}.getType();
|
||||||
|
Type expected = new TypeT<TestInterface<Integer, Integer>>() {}.getType();
|
||||||
|
assertEquals(expected, simplifyType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Type type = new TypeT<TestClass<?, ?, ?, ?, ?, ?, ?, ?, ?>>() {}.getType();
|
||||||
|
Type expected = TestClass.class;
|
||||||
|
assertEquals(expected, simplifyType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Type type = new TypeT<TestClass<?, ?, ?, Object, ?, ?, ?, ?, ?>>() {}.getType();
|
||||||
|
Type expected = TestClass.class;
|
||||||
|
assertEquals(expected, simplifyType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//noinspection TypeParameterExplicitlyExtendsObject
|
||||||
|
Type type = new TypeT<
|
||||||
|
TestClass<
|
||||||
|
Integer,
|
||||||
|
? extends Integer,
|
||||||
|
? super Integer,
|
||||||
|
Object,
|
||||||
|
? extends Object,
|
||||||
|
? super Object,
|
||||||
|
?,
|
||||||
|
Set<? extends TestInterface<Integer, ? extends Integer>>,
|
||||||
|
Set<? super TestInterface<Integer, ? super Integer>>>>() {}.getType();
|
||||||
|
Type expected = new TypeT<
|
||||||
|
TestClass<
|
||||||
|
Integer,
|
||||||
|
Integer,
|
||||||
|
Integer,
|
||||||
|
Object,
|
||||||
|
Object,
|
||||||
|
Object,
|
||||||
|
Object,
|
||||||
|
Set<TestInterface<Integer, Integer>>,
|
||||||
|
Set<TestInterface<Integer, Integer>>>>() {}.getType();
|
||||||
|
assertEquals(expected, simplifyType(type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class TestClass<A, B, C, D, E, F, G, H, I> {}
|
||||||
|
|
||||||
|
interface TestInterface<A, B extends Integer> {}
|
||||||
|
}
|
11
pom.xml
11
pom.xml
|
@ -105,6 +105,7 @@ under the License.
|
||||||
<module>maven-model</module>
|
<module>maven-model</module>
|
||||||
<module>maven-model-builder</module>
|
<module>maven-model-builder</module>
|
||||||
<module>api</module>
|
<module>api</module>
|
||||||
|
<module>maven-di</module>
|
||||||
<module>maven-xml-impl</module>
|
<module>maven-xml-impl</module>
|
||||||
<module>maven-core</module>
|
<module>maven-core</module>
|
||||||
<module>maven-settings</module>
|
<module>maven-settings</module>
|
||||||
|
@ -257,6 +258,16 @@ under the License.
|
||||||
<artifactId>maven-api-xml</artifactId>
|
<artifactId>maven-api-xml</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-api-di</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven</groupId>
|
||||||
|
<artifactId>maven-di</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.maven</groupId>
|
<groupId>org.apache.maven</groupId>
|
||||||
<artifactId>maven-model-builder</artifactId>
|
<artifactId>maven-model-builder</artifactId>
|
||||||
|
|
Loading…
Reference in New Issue