├── settings.gradle ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── gradle-mvn-push.gradle ├── kotlin-guice ├── gradle.properties ├── build.gradle └── src │ ├── jmh │ ├── java │ │ └── dev │ │ │ └── misfitlabs │ │ │ └── kotlinguice4 │ │ │ └── benchmarks │ │ │ ├── Complex.java │ │ │ ├── Simple.java │ │ │ ├── ComplexImpl.java │ │ │ ├── SimpleImpl.java │ │ │ ├── StringComplexImpl.java │ │ │ └── JavaInjectorBenchmark.java │ └── kotlin │ │ └── dev │ │ └── misfitlabs │ │ └── kotlinguice4 │ │ └── benchmarks │ │ ├── KotlinInjectorBenchmark.kt │ │ └── KotlinStandardInjectorBenchmark.kt │ ├── main │ ├── kotlin │ │ └── dev │ │ │ └── misfitlabs │ │ │ └── kotlinguice4 │ │ │ ├── multibindings │ │ │ ├── TypeAndValue.kt │ │ │ ├── KotlinLinkedBindingBuilderImpl.kt │ │ │ ├── KotlinMultimapModule.kt │ │ │ ├── KotlinTypes.kt │ │ │ ├── MapBindingSelection.kt │ │ │ ├── KotlinMultibindingsScanner.kt │ │ │ ├── KotlinOptionalBinder.kt │ │ │ ├── KotlinMultibinder.kt │ │ │ └── KotlinMapBinder.kt │ │ │ ├── TypeLiteral.kt │ │ │ ├── binder │ │ │ ├── KotlinAnnotatedElementBuilder.kt │ │ │ ├── Extensions.kt │ │ │ ├── KotlinScopedBindingBuilder.kt │ │ │ ├── KotlinAnnotatedBindingBuilder.kt │ │ │ └── KotlinLinkedBindingBuilder.kt │ │ │ ├── internal │ │ │ └── KotlinBindingBuilder.kt │ │ │ ├── Key.kt │ │ │ ├── KotlinPrivateBinder.kt │ │ │ ├── KotlinBinder.kt │ │ │ ├── InjectorExtensions.kt │ │ │ ├── KotlinPrivateModule.kt │ │ │ └── KotlinModule.kt │ └── java │ │ └── dev │ │ └── misfitlabs │ │ └── kotlinguice4 │ │ └── multibindings │ │ ├── Element.java │ │ ├── MapKeys.java │ │ └── RealElement.java │ └── test │ └── kotlin │ └── dev │ └── misfitlabs │ └── kotlinguice4 │ ├── TypeLiteralSpec.kt │ ├── multibindings │ ├── TestClasses.kt │ └── KotlinMultibinderSpec.kt │ ├── TestClasses.kt │ ├── KeySpec.kt │ ├── KotlinBinderSpec.kt │ ├── InjectorExtensionsSpec.kt │ ├── KotlinPrivateBinderSpec.kt │ └── KotlinModuleSpec.kt ├── .gitignore ├── .travis.yml ├── .github └── CONTRIBUTING.md ├── gradle.properties ├── doc └── adr │ ├── 0001-record-architecture-decisions.md │ └── 0002-use-packaging-for-major-version-interoperability.md ├── RELEASING.md ├── CHANGELOG.md ├── gradlew.bat ├── README.md ├── gradlew └── LICENSE.txt /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'kotlin-guice-parent' 2 | 3 | include 'kotlin-guice' 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/misfitlabsdev/kotlin-guice/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /kotlin-guice/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=kotlin-guice 2 | POM_NAME=Guice DSL extensions for Kotlin 3 | POM_PACKAGING=jar 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Sep 12 16:31:13 CDT 2019 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .settings 4 | eclipsebin 5 | 6 | bin 7 | gen 8 | build 9 | out 10 | lib 11 | 12 | target 13 | pom.xml.* 14 | release.properties 15 | 16 | .gradle 17 | 18 | .idea 19 | *.iml 20 | *.ipr 21 | *.iws 22 | classes 23 | 24 | obj 25 | 26 | .DS_Store 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - oraclejdk8 5 | 6 | addons: 7 | apt: 8 | packages: 9 | - oracle-java8-installer # Updates JDK 8 to the latest available. 10 | 11 | branches: 12 | except: 13 | - gh-pages 14 | 15 | notifications: 16 | email: false 17 | 18 | sudo: false 19 | 20 | cache: 21 | directories: 22 | - $HOME/.m2 23 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | If you would like to contribute code you can do so through GitHub by forking 5 | the repository and sending a pull request. 6 | 7 | When submitting code, please make every effort to follow existing conventions 8 | and style in order to keep the code as readable as possible. Please also make 9 | sure your code compiles by running `./gradlew clean build`. Ktlint failures 10 | during compilation indicate errors in your style and should be corrected. 11 | -------------------------------------------------------------------------------- /kotlin-guice/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | compile "com.google.inject:guice" 3 | compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8" 4 | compile "org.jetbrains.kotlin:kotlin-reflect" 5 | 6 | testCompile 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0' 7 | testCompile 'org.amshove.kluent:kluent' 8 | testCompile 'org.spekframework.spek2:spek-dsl-jvm' 9 | testCompile 'org.spekframework.spek2:spek-runner-junit5' 10 | testCompile 'org.junit.platform:junit-platform-runner' 11 | } 12 | 13 | apply from: rootProject.file('gradle/gradle-mvn-push.gradle') 14 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | GROUP=dev.misfitlabs.kotlinguice4 2 | VERSION_NAME=3.1.0-SNAPSHOT 3 | 4 | POM_DESCRIPTION=Guice DSL extensions for Kotlin 5 | 6 | POM_URL=https://github.com/misfitlabsdev/kotlin-guice/ 7 | POM_SCM_URL=https://github.com/misfitlabsdev/kotlin-guice/ 8 | POM_SCM_CONNECTION=scm:git:git://github.com/misfitlabsdev/kotlin-guice.git 9 | POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/misfitlabsdev/kotlin-guice.git 10 | 11 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 12 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt 13 | POM_LICENCE_DIST=repo 14 | 15 | POM_DEVELOPER_ID=johnlcox 16 | POM_DEVELOPER_NAME=John Leacox 17 | -------------------------------------------------------------------------------- /kotlin-guice/src/jmh/java/dev/misfitlabs/kotlinguice4/benchmarks/Complex.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4.benchmarks; 19 | 20 | /** 21 | * @author John Leacox 22 | */ 23 | public interface Complex { 24 | T value(); 25 | } 26 | -------------------------------------------------------------------------------- /kotlin-guice/src/jmh/java/dev/misfitlabs/kotlinguice4/benchmarks/Simple.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4.benchmarks; 19 | 20 | /** 21 | * @author John Leacox 22 | */ 23 | public interface Simple { 24 | String value(); 25 | } 26 | -------------------------------------------------------------------------------- /doc/adr/0001-record-architecture-decisions.md: -------------------------------------------------------------------------------- 1 | # ADR 1: Record Architecture Decisions 2 | 3 | Date: 2017-09-03 4 | 5 | ## Status 6 | 7 | Accepted 8 | 9 | ## Context 10 | 11 | Even though this is a small library project that may not have any decisions that qualify as a decision that `addresses a significant requirement`, there is still value in having historical documentation of certain design decisions. Historical documentation of such decisions allows others to see -- and ourselves to remember -- the reasoning behind why decisions were made. 12 | 13 | ## Decision 14 | 15 | We will use Architecture Decision Records, as described by Michael Nygard. 16 | 17 | http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions 18 | 19 | ## Consequences 20 | 21 | In addition to the consequences documented by Michael Nygard in the article from above, we will also have a place to lookup historical decisions both for ourselves and for others that want to know why a decision was made. 22 | -------------------------------------------------------------------------------- /kotlin-guice/src/jmh/java/dev/misfitlabs/kotlinguice4/benchmarks/ComplexImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4.benchmarks; 19 | 20 | /** 21 | * @author John Leacox 22 | */ 23 | public class ComplexImpl implements Complex { 24 | @Override 25 | public T value() { 26 | return null; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /kotlin-guice/src/jmh/java/dev/misfitlabs/kotlinguice4/benchmarks/SimpleImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4.benchmarks; 19 | 20 | /** 21 | * @author John Leacox 22 | */ 23 | public class SimpleImpl implements Simple { 24 | @Override 25 | public String value() { 26 | return "Impl of Simple"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /kotlin-guice/src/jmh/java/dev/misfitlabs/kotlinguice4/benchmarks/StringComplexImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4.benchmarks; 19 | 20 | /** 21 | * @author John Leacox 22 | */ 23 | public class StringComplexImpl implements Complex { 24 | @Override 25 | public String value() { 26 | return "String Impl of Complex"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/multibindings/TypeAndValue.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package dev.misfitlabs.kotlinguice4.multibindings 18 | 19 | import com.google.inject.TypeLiteral 20 | 21 | /** 22 | * A data class for holding both a [TypeLiteral] and a value. 23 | * 24 | * @author John Leacox 25 | * @since 1.0 26 | */ 27 | internal data class TypeAndValue(val type: TypeLiteral, val value: T) 28 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/TypeLiteral.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4 19 | 20 | import com.google.inject.TypeLiteral 21 | 22 | /** 23 | * Creates a new [TypeLiteral] for the specified generic type [T]. 24 | * 25 | * @see TypeLiteral 26 | * @author John Leacox 27 | * @since 1.0 28 | */ 29 | inline fun typeLiteral() = object : TypeLiteral() {} 30 | -------------------------------------------------------------------------------- /RELEASING.md: -------------------------------------------------------------------------------- 1 | Releasing 2 | ======== 3 | 4 | 1. Change the version in `gradle.properties` to a non-SNAPSHOT version. 5 | 2. Update the `CHANGELOG.md` for the impending release. 6 | 3. Update the `README.md` with the new version. 7 | 4. `git commit -am "Prepare for release X.Y.Z."` (where X.Y.Z is the new version) 8 | 5. `git tag -a X.Y.Z -m "Version X.Y.Z"` (where X.Y.Z is the new version) 9 | 6. `./gradlew clean uploadArchives` 10 | 7. Update the `gradle.properties` to the next SNAPSHOT version. 11 | 8. `git commit -am "Prepare next development version."` 12 | 9. `git push && git push --tags` 13 | 10. Visit [Sonatype Nexus](https://oss.sonatype.org/) and promote the artifact. 14 | 15 | If step 6 or 7 fails, drop the Sonatype repo, fix the problem, commit, and start again at step 5. 16 | 17 | 18 | Prerequisites 19 | ------------- 20 | 21 | In `~/.gradle/gradle.properties`, set the following: 22 | 23 | * `SONATYPE_NEXUS_USERNAME` - Sonatype username for releasing to `dev.misfitlabs`. 24 | * `SONATYPE_NEXUS_PASSWORD` - Sonatype password for releasing to `dev.misfitlabs`. 25 | * `signing.keyId` - GPG Key ID to use for signing. 26 | * `signing.password` - GPG Key password. 27 | * `signing.secretKeyRingFile` = GPG secret key file. 28 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/multibindings/KotlinLinkedBindingBuilderImpl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package dev.misfitlabs.kotlinguice4.multibindings 18 | 19 | import com.google.inject.binder.LinkedBindingBuilder 20 | import dev.misfitlabs.kotlinguice4.binder.KotlinLinkedBindingBuilder 21 | 22 | /** 23 | * An internal concrete implementation of [KotlinLinkedBindingBuilder] for usage in the multibinding 24 | * factory methods. 25 | * 26 | * @author John Leacox 27 | * @since 1.0 28 | */ 29 | internal class KotlinLinkedBindingBuilderImpl(delegate: LinkedBindingBuilder) : 30 | KotlinLinkedBindingBuilder(delegate) 31 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/java/dev/misfitlabs/kotlinguice4/multibindings/Element.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2008 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 18 | package dev.misfitlabs.kotlinguice4.multibindings; 19 | 20 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 21 | 22 | import com.google.inject.BindingAnnotation; 23 | 24 | import java.lang.annotation.Retention; 25 | 26 | /** 27 | * An internal binding annotation applied to each element in a multibinding. 28 | * All elements are assigned a globally-unique id to allow different modules 29 | * to contribute multibindings independently. 30 | * 31 | * @author jessewilson@google.com (Jesse Wilson) 32 | */ 33 | @Retention(RUNTIME) @BindingAnnotation 34 | @interface Element { 35 | 36 | enum Type { 37 | MAPBINDER, 38 | MULTIBINDER, 39 | OPTIONALBINDER; 40 | } 41 | 42 | String setName(); 43 | int uniqueId(); 44 | Type type(); 45 | String keyType(); 46 | } 47 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/binder/KotlinAnnotatedElementBuilder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4.binder 19 | 20 | import com.google.inject.binder.AnnotatedElementBuilder 21 | 22 | /** 23 | * An extension of [AnnotatedElementBuilder] that enhances the binding DSL to allow binding using 24 | * reified type parameters. 25 | * 26 | * @param delegate The underlying Guice builder to be extended 27 | * @see com.google.inject.Binder 28 | * @author John Leacox 29 | * @since 1.0 30 | */ 31 | class KotlinAnnotatedElementBuilder(val delegate: AnnotatedElementBuilder) : 32 | AnnotatedElementBuilder by delegate { 33 | /** Binds with the annotation specified by the type parameter. */ 34 | inline fun annotatedWith() { 35 | delegate.annotatedWith(TAnn::class.java) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/binder/Extensions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4.binder 19 | 20 | import com.google.inject.binder.AnnotatedConstantBindingBuilder 21 | import com.google.inject.binder.ConstantBindingBuilder 22 | 23 | /** 24 | * Binds a constant to the given class using a type parameter. 25 | * 26 | * @see com.google.inject.Binder 27 | * @author John Leacox 28 | * @since 1.0 29 | */ 30 | inline fun ConstantBindingBuilder.to() { 31 | this.to(T::class.java) 32 | } 33 | 34 | /** 35 | * Binds a constant with an annotation using a type parameter. 36 | * 37 | * @see com.google.inject.Binder 38 | * @author John Leacox 39 | * @since 1.0 40 | */ 41 | inline fun AnnotatedConstantBindingBuilder.annotatedWith(): 42 | ConstantBindingBuilder { 43 | return this.annotatedWith(TAnn::class.java) 44 | } 45 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/binder/KotlinScopedBindingBuilder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4.binder 19 | 20 | import com.google.inject.binder.ScopedBindingBuilder 21 | 22 | /** 23 | * An extension of [ScopedBindingBuilder] that enhances the binding DSL to allow binding using reified 24 | * type parameters. 25 | * 26 | * @param self The underlying Guice builder to be extended 27 | * @see com.google.inject.Binder 28 | * @author John Leacox 29 | * @since 1.0 30 | */ 31 | abstract class KotlinScopedBindingBuilder(private val self: ScopedBindingBuilder) : 32 | ScopedBindingBuilder by self { 33 | /** The underlying Guice builder that is being extended. */ 34 | open val delegate = self 35 | 36 | /** Places the binding into the scope specified by the annotation type parameter. */ 37 | inline fun `in`() = delegate.`in`(TAnn::class.java) 38 | } 39 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/internal/KotlinBindingBuilder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4.internal 19 | 20 | import com.google.inject.binder.AnnotatedBindingBuilder 21 | import dev.misfitlabs.kotlinguice4.binder.KotlinAnnotatedBindingBuilder 22 | import dev.misfitlabs.kotlinguice4.binder.KotlinLinkedBindingBuilder 23 | 24 | /** 25 | * An internal implementation of the Kotlin enhanced binding builders. 26 | * 27 | * @suppress 28 | * @param self The underlying Guice builder to be extended 29 | * @author John Leacox 30 | * @since 1.0 31 | */ 32 | @Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE") 33 | class KotlinBindingBuilder(private val self: AnnotatedBindingBuilder) : 34 | KotlinAnnotatedBindingBuilder(self), AnnotatedBindingBuilder by self { 35 | override fun annotatedWith(annotation: Annotation): KotlinLinkedBindingBuilder { 36 | return super.annotatedWith(annotation) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/multibindings/KotlinMultimapModule.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package dev.misfitlabs.kotlinguice4.multibindings 18 | 19 | import dev.misfitlabs.kotlinguice4.KotlinModule 20 | 21 | /** 22 | * A module for binding the kotlin multimap types. 23 | * 24 | * @author John Leacox 25 | * @since 1.0 26 | */ 27 | internal class KotlinMultimapModule(private val bindingSelection: MapBindingSelection) : 28 | KotlinModule() { 29 | override fun configure() { 30 | bind(bindingSelection.multimapKey).to(bindingSelection.mutableMultimapKey) 31 | bind(bindingSelection.providerSetMultimapKey) 32 | .to(bindingSelection.mutableProviderSetMultimapKey) 33 | bind(bindingSelection.jakartaProviderSetMultimapKey) 34 | .to(bindingSelection.mutableJakartaProviderSetMultimapKey) 35 | bind(bindingSelection.providerCollectionMultimapKey) 36 | .to(bindingSelection.mutableProviderCollectionMultimapKey) 37 | bind(bindingSelection.jakartaProviderCollectionMultimapKey) 38 | .to(bindingSelection.mutableJakartaProviderCollectionMultimapKey) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /kotlin-guice/src/test/kotlin/dev/misfitlabs/kotlinguice4/TypeLiteralSpec.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4 19 | 20 | import com.google.inject.TypeLiteral 21 | import java.util.concurrent.Callable 22 | import org.amshove.kluent.shouldEqual 23 | import org.spekframework.spek2.Spek 24 | import org.spekframework.spek2.style.specification.describe 25 | 26 | /** 27 | * @author John Leacox 28 | */ 29 | object TypeLiteralSpec : Spek({ 30 | describe("#typeLiteral") { 31 | it("creates a TypeLiteral from a simple type") { 32 | val simpleTypeLiteral = typeLiteral() 33 | simpleTypeLiteral shouldEqual TypeLiteral.get(A::class.java) 34 | } 35 | 36 | it("creates a TypeLiteral from a complex type") { 37 | val complexTypeLiteral = typeLiteral>>() 38 | complexTypeLiteral shouldEqual object : TypeLiteral>>() {} 39 | } 40 | 41 | it("creates a TypeLiteral from array types") { 42 | val arrayTypeLiteral = typeLiteral>() 43 | arrayTypeLiteral shouldEqual object : TypeLiteral>() {} 44 | } 45 | } 46 | }) 47 | -------------------------------------------------------------------------------- /doc/adr/0002-use-packaging-for-major-version-interoperability.md: -------------------------------------------------------------------------------- 1 | # ADR 2: Use Package Name and GroupId for Major Version Interoperability 2 | 3 | Date: 2017-09-03 4 | 5 | ## Status 6 | 7 | Accepted 8 | 9 | ## Context 10 | 11 | Major version updates to libraries often include breaking API changes. Upgrading an application to a new major version of a library can be difficult since other libraries may also have dependencies on the updated library. 12 | 13 | For this library, `kotlin-guice`, the API tracks heavily to the Guice API. When Guice 14 | makes a major version upgrade, this library will also likely need to make a corresponding major version upgrade. 15 | 16 | ## Decision 17 | 18 | We will use the Java package and Maven group ID to allow interoperability of major versions of this library. As described by Jake Wharton in the blog post below, we will accomplish this in three ways. 19 | 20 | 1. The Java package name will include the version number. 21 | 1. The library name will be part of the group ID. 22 | 1. The group ID will include the version number. 23 | 24 | Since this library is meant mostly as an extension to the corresponding Guice libraries, we will use the Guice major version. If we need to make a major version change to `kotlin-guice` within a single version of Guice, then we may have to include both version numbers. Until such a situation arises, we do not have to make that decision, but one possible option is `com.authzee.kotlinguice4_2`. 25 | 26 | http://jakewharton.com/java-interoperability-policy-for-major-version-updates/ 27 | 28 | ## Consequences 29 | 30 | The group ID and package of `kotlin-guice` will be a bit more verbose, but will also provide clarity as to what version of Guice they are intended to be used with. When Guice makes a major version upgrade, `kotlin-guice` can make a corresponding major version change, while still being interoperable with the previous version. 31 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | ## [3.1.0] - _In Development_ 5 | 6 | ## [3.0.0] - _2023-06-26_ 7 | 8 | ### Changed 9 | Upgrade to Guice 7.0.0 10 | Move from javax to jakarta namespace 11 | 12 | ## [1.6.0] - _2022-02-12_ 13 | 14 | ### Added 15 | * Add annotation instance support for `KotlinMapBinder`. 16 | 17 | ### Changed 18 | * Upgrade to Guice 5.1.0 19 | 20 | ## [1.5.0] - _2021-05-08_ 21 | 22 | ### Changed 23 | * Upgrade to Guice 5.0.1 24 | 25 | ## [1.4.1] - _2019-11-02_ 26 | 27 | ### Fixed 28 | * Fix `kotlinBinder` holding a cached reference to old `Binder` instances in `KotlinModule`. 29 | 30 | ## [1.4.0] - _2019-07-27_ 31 | 32 | ### Changed 33 | * Upgrade to Guice 4.2.2. 34 | * Move multibinding files into the main kotlin-guice module. 35 | * Switch to Keep a Changelog style for Changelog moving forward - https://keepachangelog.com/en/1.0.0/ 36 | * Rename groupId and packages to `dev.misfitlabs`. 37 | * Upgrade tests from Spek to Spek2. 38 | 39 | ### Removed 40 | * Remove kotlin-guice-multibindings module to match the removal of guice-multibindings in Guice 4.2. 41 | 42 | ## Version 1.3.0 43 | 44 | _2019-04-25_ 45 | 46 | * Add delegating builders to binder skipSources. 47 | * Add binding support for annotation instances for kotlin-based DSL. 48 | 49 | ## Version 1.2.0 50 | 51 | _2018-10-17_ 52 | 53 | * Actually replace dependency on JRE8 with JDK8. 54 | 55 | ## Version 1.1.0 56 | 57 | _2018-10-11_ 58 | 59 | * Replace dependency on JRE8 with JDK8. 60 | 61 | ## Version 1.0.0 62 | 63 | _2017-09-05_ 64 | 65 | * Initial release. 66 | 67 | [Unreleased]: https://github.com/misfitlabsdev/kotlin-guice/compare/3.0.0...HEAD 68 | [3.0.0]: https://github.com/misfitlabsdev/kotlin-guice/compare/1.6.0...3.0.0 69 | [1.6.0]: https://github.com/misfitlabsdev/kotlin-guice/compare/1.5.0...1.6.0 70 | [1.5.0]: https://github.com/misfitlabsdev/kotlin-guice/compare/1.4.1...1.5.0 71 | [1.4.1]: https://github.com/misfitlabsdev/kotlin-guice/compare/1.4.0...1.4.1 72 | [1.4.0]: https://github.com/misfitlabsdev/kotlin-guice/compare/1.3.0...1.4.0 73 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/binder/KotlinAnnotatedBindingBuilder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4.binder 19 | 20 | import com.google.inject.binder.AnnotatedBindingBuilder 21 | 22 | /** 23 | * An extension of [AnnotatedBindingBuilder] that enhances the binding DSL to allow binding using 24 | * reified type parameters. 25 | * 26 | * @param self The underlying Guice builder to be extended 27 | * @see com.google.inject.Binder 28 | * @author John Leacox 29 | * @since 1.0 30 | */ 31 | @Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE") 32 | abstract class KotlinAnnotatedBindingBuilder(private val self: AnnotatedBindingBuilder) : 33 | KotlinLinkedBindingBuilder(self), AnnotatedBindingBuilder by self { 34 | /** The underlying Guice builder that is being extended. */ 35 | override val delegate = self 36 | 37 | /** Binds with the annotation specified by the type parameter. */ 38 | inline fun annotatedWith(): KotlinLinkedBindingBuilder { 39 | delegate.annotatedWith(TAnn::class.java) 40 | return this 41 | } 42 | 43 | /** Binds with the specified annotation. */ 44 | override fun annotatedWith(annotation: Annotation): KotlinLinkedBindingBuilder { 45 | delegate.annotatedWith(annotation) 46 | return this 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/Key.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4 19 | 20 | import com.google.inject.Key 21 | import java.lang.reflect.Type 22 | 23 | /** 24 | * Gets a key for an injection of type [T]. 25 | * 26 | * @author John Leacox 27 | * @since 1.0 28 | */ 29 | inline fun key(): Key = Key.get(typeLiteral()) 30 | 31 | /** 32 | * Gets a key for an injection of type [T] with annotation type [TAnn]. 33 | * 34 | * @author John Leacox 35 | * @since 1.0 36 | */ 37 | inline fun annotatedKey(): Key = Key.get(typeLiteral(), TAnn::class.java) 38 | 39 | /** 40 | * Gets a key for an injection of type [T] and the specified annotation. 41 | * 42 | * @author John Leacox 43 | * @since 1.0 44 | */ 45 | inline fun annotatedKey(annotation: Annotation): Key = Key.get(typeLiteral(), annotation) 46 | 47 | /** 48 | * Gets a key for an injection of the specified type and annotation type [TAnn]. 49 | * 50 | * @author John Leacox 51 | * @since 1.0 52 | */ 53 | inline fun annotatedKey(type: Type): Key<*> = Key.get(type, TAnn::class.java) 54 | 55 | /** 56 | * Returns a new key of the specified type [T] with the same annotation as this key. 57 | * 58 | * @author John Leacox 59 | * @since 1.0 60 | */ 61 | inline fun Key<*>.ofType(): Key = this.ofType(typeLiteral()) 62 | -------------------------------------------------------------------------------- /kotlin-guice/src/test/kotlin/dev/misfitlabs/kotlinguice4/multibindings/TestClasses.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4.multibindings 19 | 20 | import com.google.inject.BindingAnnotation 21 | import jakarta.inject.Inject 22 | import java.util.concurrent.Callable 23 | 24 | interface A { 25 | fun get(): String 26 | } 27 | 28 | class ACallable : Callable { 29 | override fun call(): A { 30 | return AImpl() 31 | } 32 | } 33 | 34 | class TCallable : Callable { 35 | override fun call(): T? { 36 | return null 37 | } 38 | } 39 | 40 | class BCallable : Callable { 41 | override fun call(): B { 42 | return B() 43 | } 44 | } 45 | 46 | open class AImpl : A { 47 | override fun get(): String { 48 | return "Impl of A" 49 | } 50 | } 51 | 52 | @Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FUNCTION) 53 | @Retention(AnnotationRetention.RUNTIME) 54 | @BindingAnnotation 55 | annotation class Annotated 56 | 57 | open class B : AImpl() { 58 | override fun get(): String { 59 | return "This is B" 60 | } 61 | } 62 | 63 | // Nested out parameters don't play well with Java. It results in ? extends ? extends... 64 | // To avoid this issue and allow Guice (written in Java) to work well with these 65 | // we must suppress the wildcards. 66 | @JvmSuppressWildcards 67 | class SetContainer @Inject constructor(val set: Set) 68 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/binder/KotlinLinkedBindingBuilder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4.binder 19 | 20 | import com.google.inject.binder.LinkedBindingBuilder 21 | import dev.misfitlabs.kotlinguice4.typeLiteral 22 | import jakarta.inject.Provider 23 | 24 | /** 25 | * An extension of [LinkedBindingBuilder] that enhances the binding DSL to allow binding using reified 26 | * type parameters. 27 | * 28 | * @param self The underlying Guice builder to be extended 29 | * @see com.google.inject.Binder 30 | * @author John Leacox 31 | * @since 1.0 32 | */ 33 | @Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE") 34 | abstract class KotlinLinkedBindingBuilder(private val self: LinkedBindingBuilder) : 35 | KotlinScopedBindingBuilder(self), LinkedBindingBuilder by self { 36 | /** The underlying Guice builder that is being extended. */ 37 | override val delegate = self 38 | 39 | /** Binds to the implementation class specified by the type parameter. */ 40 | inline fun to(): KotlinScopedBindingBuilder { 41 | delegate.to(typeLiteral()) 42 | return this 43 | } 44 | 45 | /** Binds to the provider class specified by the type parameter. */ 46 | inline fun > toProvider(): KotlinScopedBindingBuilder { 47 | delegate.toProvider(typeLiteral()) 48 | return this 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/java/dev/misfitlabs/kotlinguice4/multibindings/MapKeys.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package dev.misfitlabs.kotlinguice4.multibindings; 18 | 19 | import com.google.inject.TypeLiteral; 20 | import com.google.inject.multibindings.MapKey; 21 | import java.lang.annotation.Annotation; 22 | import java.lang.reflect.InvocationTargetException; 23 | import java.lang.reflect.Method; 24 | 25 | /** 26 | * Static utility methods for working with {@link MapKey} annotations. 27 | * 28 | * @author John Leacox 29 | * @since 1.0 30 | */ 31 | final class MapKeys { 32 | private MapKeys() { 33 | } 34 | 35 | /** 36 | * Returns the type and value of the map key annotation. 37 | * 38 | * @throws IllegalStateException if the map key value cannot be determined 39 | */ 40 | @SuppressWarnings("unchecked") 41 | static TypeAndValue typeAndValueOfMapKey(Annotation mapKeyAnnotation) { 42 | if (!mapKeyAnnotation.annotationType().getAnnotation(MapKey.class).unwrapValue()) { 43 | return new TypeAndValue(TypeLiteral.get(mapKeyAnnotation.annotationType()), mapKeyAnnotation); 44 | } else { 45 | try { 46 | Method valueMethod = mapKeyAnnotation.annotationType().getDeclaredMethod("value"); 47 | valueMethod.setAccessible(true); 48 | TypeLiteral returnType = 49 | TypeLiteral.get(mapKeyAnnotation.annotationType()).getReturnType(valueMethod); 50 | return new TypeAndValue(returnType, valueMethod.invoke(mapKeyAnnotation)); 51 | } catch (NoSuchMethodException | SecurityException | IllegalAccessException 52 | | InvocationTargetException e) { 53 | throw new IllegalStateException(e); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/KotlinPrivateBinder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4 19 | 20 | import com.google.inject.PrivateBinder 21 | import dev.misfitlabs.kotlinguice4.binder.KotlinAnnotatedElementBuilder 22 | 23 | /** 24 | * A wrapper of [PrivateBinder] that enhances the binding DSL to allow binding using reified type 25 | * parameters. 26 | * 27 | * By using this class instead of [PrivateBinder] you can replace the following DSL lines: 28 | * ``` 29 | * bind(Service::class.java).to(ServiceImpl::class.java).`in`(Singleton::class.java) 30 | * bind(object : TypeLiteral>() {}) 31 | * .to(CreditCardPaymentService::class.java) 32 | * 33 | * expose(object : TypeLiteral>() {}) 34 | * ``` 35 | * with 36 | * ``` 37 | * bind().to().`in`() 38 | * bind>().to() 39 | * 40 | * expose>() 41 | * ``` 42 | * 43 | * @see PrivateBinder 44 | * @author Brian van de Boogaard 45 | * @since 1.0 46 | */ 47 | @Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE") 48 | class KotlinPrivateBinder(override val delegate: PrivateBinder) : 49 | KotlinBinder(delegate), PrivateBinder by delegate { 50 | /** 51 | * Makes a binding for [T] available to the enclosing environment. Use 52 | * [KotlinAnnotatedElementBuilder.annotatedWith] to expose [T] with a binding annotation. 53 | */ 54 | inline fun expose(): KotlinAnnotatedElementBuilder { 55 | return KotlinAnnotatedElementBuilder(delegate.expose(typeLiteral())) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/multibindings/KotlinTypes.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package dev.misfitlabs.kotlinguice4.multibindings 18 | 19 | import com.google.inject.util.Types 20 | import java.lang.reflect.ParameterizedType 21 | import java.lang.reflect.Type 22 | 23 | /** 24 | * Utility methods for building kotlin specific types. 25 | * 26 | * @author John Leacox 27 | * @since 1.0 28 | */ 29 | internal object KotlinTypes { 30 | internal fun collectionOf(elementType: Type): ParameterizedType { 31 | return Types.newParameterizedType( 32 | kotlin.collections.Collection::class.java, 33 | Types.subtypeOf(elementType)) 34 | } 35 | 36 | internal fun mutableCollectionOf(elementType: Type): ParameterizedType { 37 | return Types.newParameterizedType( 38 | kotlin.collections.MutableCollection::class.java, 39 | elementType) 40 | } 41 | 42 | internal fun setOf(elementType: Type): ParameterizedType { 43 | return Types.newParameterizedType( 44 | kotlin.collections.Set::class.java, 45 | Types.subtypeOf(elementType)) 46 | } 47 | 48 | internal fun mutableSetOf(elementType: Type): ParameterizedType { 49 | return Types.newParameterizedType( 50 | kotlin.collections.MutableSet::class.java, 51 | elementType) 52 | } 53 | 54 | internal fun mapOf(keyType: Type, valueType: Type): ParameterizedType { 55 | return Types.newParameterizedType( 56 | Map::class.java, 57 | keyType, 58 | Types.subtypeOf(valueType)) 59 | } 60 | 61 | internal fun mutableMapOf(keyType: Type, valueType: Type): ParameterizedType { 62 | return Types.newParameterizedType( 63 | MutableMap::class.java, 64 | keyType, 65 | valueType) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /kotlin-guice/src/test/kotlin/dev/misfitlabs/kotlinguice4/TestClasses.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4 19 | 20 | import com.google.inject.BindingAnnotation 21 | import com.google.inject.Key 22 | import com.google.inject.Provider 23 | import com.google.inject.Scope 24 | import com.google.inject.ScopeAnnotation 25 | import jakarta.inject.Inject 26 | import java.util.concurrent.Callable 27 | 28 | interface A { 29 | fun get(): String 30 | } 31 | 32 | class AContainer @Inject constructor(val a: A) 33 | 34 | class ACallable : Callable { 35 | override fun call(): A { 36 | return AImpl() 37 | } 38 | } 39 | 40 | class TCallable : Callable { 41 | override fun call(): T? { 42 | return null 43 | } 44 | } 45 | 46 | open class AImpl : A { 47 | override fun get(): String { 48 | return "Impl of A" 49 | } 50 | } 51 | 52 | @Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FUNCTION) 53 | @Retention(AnnotationRetention.RUNTIME) 54 | @BindingAnnotation 55 | annotation class Annotated 56 | 57 | open class B : AImpl() { 58 | override fun get(): String { 59 | return "This is B" 60 | } 61 | } 62 | 63 | @Retention(AnnotationRetention.RUNTIME) 64 | @ScopeAnnotation 65 | annotation class TestScoped 66 | 67 | @Suppress("UNCHECKED_CAST") 68 | class TestScope : Scope { 69 | private var scopedObjectsMap = HashMap, Any>() 70 | 71 | override fun scope(key: Key, unscoped: Provider): Provider { 72 | return Provider { scopedObjectsMap.getOrPut(key) { unscoped.get() as Any } as T } 73 | } 74 | 75 | fun reset() { 76 | scopedObjectsMap = HashMap, Any>() 77 | } 78 | } 79 | 80 | class BProvider : Provider { 81 | override fun get(): B { 82 | return B() 83 | } 84 | } 85 | 86 | class TProvider : Provider { 87 | override fun get(): T? { 88 | return null 89 | } 90 | } 91 | 92 | object StaticInjectionObj { 93 | @Inject var staticInjectionSite: String? = null 94 | 95 | fun reset() { 96 | staticInjectionSite = null 97 | } 98 | } 99 | 100 | class MembersInjection { 101 | @Inject var memberInjectionSite: String? = null 102 | @Inject @Annotated var annotatedMemberInjectionSite: String? = null 103 | } 104 | -------------------------------------------------------------------------------- /kotlin-guice/src/jmh/kotlin/dev/misfitlabs/kotlinguice4/benchmarks/KotlinInjectorBenchmark.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4.benchmarks 19 | 20 | import com.google.inject.Guice 21 | import dev.misfitlabs.kotlinguice4.KotlinModule 22 | import dev.misfitlabs.kotlinguice4.getInstance 23 | import java.util.concurrent.TimeUnit 24 | import org.openjdk.jmh.annotations.Benchmark 25 | import org.openjdk.jmh.annotations.BenchmarkMode 26 | import org.openjdk.jmh.annotations.CompilerControl 27 | import org.openjdk.jmh.annotations.Fork 28 | import org.openjdk.jmh.annotations.Measurement 29 | import org.openjdk.jmh.annotations.Mode 30 | import org.openjdk.jmh.annotations.OutputTimeUnit 31 | import org.openjdk.jmh.annotations.Scope 32 | import org.openjdk.jmh.annotations.State 33 | import org.openjdk.jmh.annotations.Warmup 34 | 35 | /** 36 | * Benchmarks showing the performance of Guice bindings from Kotlin when using the kotlin-guice 37 | * library extensions. 38 | * 39 | * @author John Leacox 40 | */ 41 | @Fork(1) 42 | @Warmup(iterations = 10) 43 | @Measurement(iterations = 10) 44 | @State(Scope.Benchmark) 45 | @BenchmarkMode(Mode.AverageTime) 46 | @OutputTimeUnit(TimeUnit.MILLISECONDS) 47 | @CompilerControl(CompilerControl.Mode.DONT_INLINE) 48 | open class KotlinInjectorBenchmark { 49 | @Benchmark fun getSimpleInstance() { 50 | val injector = Guice.createInjector(object : KotlinModule() { 51 | override fun configure() { 52 | bind().to() 53 | } 54 | }) 55 | 56 | val instance = injector.getInstance() 57 | instance.value() 58 | } 59 | 60 | @Benchmark fun getComplexIterableInstance() { 61 | val injector = Guice.createInjector(object : KotlinModule() { 62 | override fun configure() { 63 | bind>>().to>>() 64 | } 65 | }) 66 | 67 | val instance = injector.getInstance>>() 68 | instance.value() 69 | } 70 | 71 | @Benchmark fun getComplexStringInstance() { 72 | val injector = Guice.createInjector(object : KotlinModule() { 73 | override fun configure() { 74 | bind>().to() 75 | } 76 | }) 77 | 78 | val instance = injector.getInstance>() 79 | instance.value() 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/KotlinBinder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4 19 | 20 | import com.google.inject.Binder 21 | import com.google.inject.MembersInjector 22 | import com.google.inject.Provider 23 | import com.google.inject.Scope 24 | import dev.misfitlabs.kotlinguice4.binder.KotlinAnnotatedBindingBuilder 25 | import dev.misfitlabs.kotlinguice4.internal.KotlinBindingBuilder 26 | 27 | /** 28 | * A wrapper of [Binder] that enhances the binding DSL to allow binding using reified type 29 | * parameters. 30 | * 31 | * By using this class instead of [Binder] you can replace the following DSL lines: 32 | * ``` 33 | * bind(Service::class.java).to(ServiceImpl::class.java).`in`(Singleton::class.java) 34 | * bind(object : TypeLiteral>() {}) 35 | * .to(CreditCardPaymentService::class.java) 36 | * ``` 37 | * with 38 | * ``` 39 | * bind().to().`in`() 40 | * bind>().to() 41 | * ``` 42 | * 43 | * @see Binder 44 | * @author John Leacox 45 | * @since 1.0 46 | */ 47 | open class KotlinBinder(open val delegate: Binder) : Binder by delegate { 48 | /** 49 | * Binds a [Scope] to an [Annotation] using an annotation type parameter. 50 | * 51 | * @see Binder 52 | */ 53 | inline fun bindScope(scope: Scope) { 54 | bindScope(TAnn::class.java, scope) 55 | } 56 | 57 | /** 58 | * Binds using a type parameter. 59 | * 60 | * @see Binder 61 | */ 62 | inline fun bind(): KotlinAnnotatedBindingBuilder { 63 | return KotlinBindingBuilder(bind(typeLiteral())) 64 | } 65 | 66 | /** 67 | * Requests static injection using a type parameter. 68 | * 69 | * @see Binder 70 | */ 71 | inline fun requestStaticInjection() { 72 | requestStaticInjection(T::class.java) 73 | } 74 | 75 | /** 76 | * Gets a provider using a type parameter. 77 | * 78 | * @see Binder 79 | */ 80 | inline fun getProvider(): Provider { 81 | return getProvider(key()) 82 | } 83 | 84 | /** 85 | * Gets a members injector using a type parameter. 86 | * 87 | * @see Binder 88 | */ 89 | inline fun getMembersInjector(): MembersInjector { 90 | return getMembersInjector(typeLiteral()) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem http://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /kotlin-guice/src/jmh/kotlin/dev/misfitlabs/kotlinguice4/benchmarks/KotlinStandardInjectorBenchmark.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4.benchmarks 19 | 20 | import com.google.inject.AbstractModule 21 | import com.google.inject.Guice 22 | import com.google.inject.Key 23 | import com.google.inject.TypeLiteral 24 | import java.util.concurrent.TimeUnit 25 | import org.openjdk.jmh.annotations.Benchmark 26 | import org.openjdk.jmh.annotations.BenchmarkMode 27 | import org.openjdk.jmh.annotations.CompilerControl 28 | import org.openjdk.jmh.annotations.Fork 29 | import org.openjdk.jmh.annotations.Measurement 30 | import org.openjdk.jmh.annotations.Mode 31 | import org.openjdk.jmh.annotations.OutputTimeUnit 32 | import org.openjdk.jmh.annotations.Scope 33 | import org.openjdk.jmh.annotations.State 34 | import org.openjdk.jmh.annotations.Warmup 35 | 36 | /** 37 | * Benchmarks showing the performance of Guice bindings from Kotlin without using the kotlin-guice 38 | * library extensions. 39 | * 40 | * @author John Leacox 41 | */ 42 | @Fork(1) 43 | @Warmup(iterations = 10) 44 | @Measurement(iterations = 10) 45 | @State(Scope.Benchmark) 46 | @BenchmarkMode(Mode.AverageTime) 47 | @OutputTimeUnit(TimeUnit.MILLISECONDS) 48 | @CompilerControl(CompilerControl.Mode.DONT_INLINE) 49 | open class KotlinStandardInjectorBenchmark { 50 | @Benchmark fun getSimpleInstance() { 51 | val injector = Guice.createInjector(object : AbstractModule() { 52 | override fun configure() { 53 | bind(Simple::class.java).to(SimpleImpl::class.java) 54 | } 55 | }) 56 | 57 | val instance = injector.getInstance(Simple::class.java) 58 | instance.value() 59 | } 60 | 61 | @Benchmark fun getComplexIterableInstance() { 62 | val injector = Guice.createInjector(object : AbstractModule() { 63 | override fun configure() { 64 | bind(object : TypeLiteral>>() {}) 65 | .to(object : TypeLiteral>>() {}) 66 | } 67 | }) 68 | 69 | val instance = injector 70 | .getInstance(Key.get(object : TypeLiteral>>() {})) 71 | instance.value() 72 | } 73 | 74 | @Benchmark fun getComplexStringInstance() { 75 | val injector = Guice.createInjector(object : AbstractModule() { 76 | override fun configure() { 77 | bind(object : TypeLiteral>() {}).to(StringComplexImpl::class.java) 78 | } 79 | }) 80 | 81 | val instance = injector.getInstance(Key.get(object : TypeLiteral>() {})) 82 | instance.value() 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/InjectorExtensions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4 19 | 20 | import com.google.inject.Binding 21 | import com.google.inject.Injector 22 | import com.google.inject.MembersInjector 23 | import com.google.inject.Provider 24 | 25 | /** 26 | * Returns the member injector used to inject dependencies into methods and fields on instances of 27 | * the given type [T]. 28 | * 29 | * @param T the type to get the [MembersInjector] for 30 | * @see com.google.inject.Injector.getMembersInjector 31 | * @author John Leacox 32 | * @since 1.0 33 | */ 34 | inline fun Injector.getMembersInjector(): MembersInjector { 35 | return getMembersInjector(typeLiteral()) 36 | } 37 | 38 | /** 39 | * Returns the binding for the given type [T]. 40 | * 41 | * This method is part of the Guice SPI and is intended for use by tools and extensions. 42 | * 43 | * @param T the type to get the [Binding] for 44 | * @throws com.google.inject.ConfigurationException if this injector cannot find or create the 45 | * binding 46 | * @see com.google.inject.Injector.getBinding 47 | * @author John Leacox 48 | * @since 1.0 49 | */ 50 | inline fun Injector.getBinding(): Binding { 51 | return getBinding(key()) 52 | } 53 | 54 | /** 55 | * Returns all explicit bindings for type [T]. 56 | * 57 | * This method is part of the Guice SPI and is intended for use by tools and extensions. 58 | * 59 | * @param T the type to get the [Bindings][Binding] for 60 | * @see com.google.inject.Injector.findBindingsByType 61 | * @author John Leacox 62 | * @since 1.0 63 | */ 64 | inline fun Injector.findBindingsByType(): List> { 65 | return findBindingsByType(typeLiteral()) 66 | } 67 | 68 | /** 69 | * Returns the provider used to obtain instances of the given type [T]. 70 | * 71 | * @param T the type to get the [Provider] for 72 | * @throws com.google.inject.ConfigurationException if this injector cannot find or create the 73 | * provider 74 | * @see com.google.inject.Injector.getProvider 75 | * @author John Leacox 76 | * @since 1.0 77 | */ 78 | inline fun Injector.getProvider(): Provider { 79 | return getProvider(key()) 80 | } 81 | 82 | /** 83 | * Returns the appropriate instance for the given injection type [T]. 84 | * 85 | * @param T the type to get an instance of 86 | * @throws com.google.inject.ConfigurationException if this injector cannot find or create the 87 | * instance 88 | * @see com.google.inject.Injector.getInstance 89 | * @author John Leacox 90 | * @since 1.0 91 | */ 92 | inline fun Injector.getInstance(): T { 93 | return getInstance(key()) 94 | } 95 | -------------------------------------------------------------------------------- /kotlin-guice/src/jmh/java/dev/misfitlabs/kotlinguice4/benchmarks/JavaInjectorBenchmark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4.benchmarks; 19 | 20 | import com.google.inject.AbstractModule; 21 | import com.google.inject.Guice; 22 | import com.google.inject.Injector; 23 | import com.google.inject.Key; 24 | import com.google.inject.TypeLiteral; 25 | import java.util.concurrent.TimeUnit; 26 | import org.openjdk.jmh.annotations.Benchmark; 27 | import org.openjdk.jmh.annotations.BenchmarkMode; 28 | import org.openjdk.jmh.annotations.CompilerControl; 29 | import org.openjdk.jmh.annotations.Fork; 30 | import org.openjdk.jmh.annotations.Measurement; 31 | import org.openjdk.jmh.annotations.Mode; 32 | import org.openjdk.jmh.annotations.OutputTimeUnit; 33 | import org.openjdk.jmh.annotations.Scope; 34 | import org.openjdk.jmh.annotations.State; 35 | import org.openjdk.jmh.annotations.Warmup; 36 | 37 | /** 38 | * Benchmarks showing the performance of Guice bindings from Java for comparison with the kotlin 39 | * benchmarks. 40 | * 41 | * @author John Leacox 42 | */ 43 | @Fork(1) 44 | @Warmup(iterations = 10) 45 | @Measurement(iterations = 10) 46 | @State(Scope.Benchmark) 47 | @BenchmarkMode(Mode.AverageTime) 48 | @OutputTimeUnit(TimeUnit.MILLISECONDS) 49 | @CompilerControl(CompilerControl.Mode.DONT_INLINE) 50 | public class JavaInjectorBenchmark { 51 | @Benchmark 52 | public void getSimpleInstance() { 53 | Injector injector = Guice.createInjector(new AbstractModule() { 54 | @Override 55 | protected void configure() { 56 | bind(Simple.class).to(SimpleImpl.class); 57 | } 58 | }); 59 | 60 | Simple instance = injector.getInstance(Simple.class); 61 | instance.value(); 62 | } 63 | 64 | @Benchmark 65 | public void getComplexIterableInstance() { 66 | Injector injector = Guice.createInjector(new AbstractModule() { 67 | @Override 68 | protected void configure() { 69 | bind(new TypeLiteral>>() { 70 | }) 71 | .to(new TypeLiteral>>() { 72 | }); 73 | } 74 | }); 75 | 76 | Complex> instance = injector 77 | .getInstance(Key.get(new TypeLiteral>>() { 78 | })); 79 | instance.value(); 80 | } 81 | 82 | @Benchmark 83 | public void getComplexStringInstance() { 84 | Injector injector = Guice.createInjector(new AbstractModule() { 85 | @Override 86 | protected void configure() { 87 | bind(new TypeLiteral>() { 88 | }) 89 | .to(StringComplexImpl.class); 90 | } 91 | }); 92 | 93 | Complex instance = injector 94 | .getInstance(Key.get(new TypeLiteral>() { 95 | })); 96 | instance.value(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/java/dev/misfitlabs/kotlinguice4/multibindings/RealElement.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2008 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package dev.misfitlabs.kotlinguice4.multibindings; 18 | 19 | import com.google.inject.Key; 20 | import com.google.inject.internal.Annotations; 21 | 22 | import java.lang.annotation.Annotation; 23 | import java.util.concurrent.atomic.AtomicInteger; 24 | 25 | /** An implementation of Element. */ 26 | // TODO(cgruber): Use AutoAnnotation when available, here & wherever else is makes sense. 27 | class RealElement implements Element { 28 | private static final AtomicInteger nextUniqueId = new AtomicInteger(1); 29 | 30 | private final int uniqueId; 31 | private final String setName; 32 | private final Element.Type type; 33 | private final String keyType; 34 | 35 | RealElement(String setName, Element.Type type, String keyType) { 36 | this(setName, type, keyType, nextUniqueId.incrementAndGet()); 37 | } 38 | 39 | RealElement(String setName, Element.Type type, String keyType, int uniqueId) { 40 | this.uniqueId = uniqueId; 41 | this.setName = setName; 42 | this.type = type; 43 | this.keyType = keyType; 44 | } 45 | 46 | @Override 47 | public String setName() { 48 | return setName; 49 | } 50 | 51 | @Override 52 | public int uniqueId() { 53 | return uniqueId; 54 | } 55 | 56 | @Override 57 | public Element.Type type() { 58 | return type; 59 | } 60 | 61 | @Override 62 | public String keyType() { 63 | return keyType; 64 | } 65 | 66 | @Override 67 | public Class annotationType() { 68 | return Element.class; 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | return "@" + Element.class.getName() + "(setName=" + setName 74 | + ",uniqueId=" + uniqueId + ", type=" + type + ", keyType=" + keyType + ")"; 75 | } 76 | 77 | @Override 78 | public boolean equals(Object o) { 79 | return o instanceof Element 80 | && ((Element) o).setName().equals(setName()) 81 | && ((Element) o).uniqueId() == uniqueId() 82 | && ((Element) o).type() == type() 83 | && ((Element) o).keyType().equals(keyType()); 84 | } 85 | 86 | @Override 87 | public int hashCode() { 88 | return ((127 * "setName".hashCode()) ^ setName.hashCode()) 89 | + ((127 * "uniqueId".hashCode()) ^ uniqueId) 90 | + ((127 * "type".hashCode()) ^ type.hashCode()) 91 | + ((127 * "keyType".hashCode()) ^ keyType.hashCode()); 92 | } 93 | 94 | /** 95 | * Returns the name the binding should use. This is based on the annotation. 96 | * If the annotation has an instance and is not a marker annotation, 97 | * we ask the annotation for its toString. If it was a marker annotation 98 | * or just an annotation type, we use the annotation's name. Otherwise, 99 | * the name is the empty string. 100 | */ 101 | static String nameOf(Key key) { 102 | Annotation annotation = key.getAnnotation(); 103 | Class annotationType = key.getAnnotationType(); 104 | if (annotation != null && !Annotations.isMarker(annotationType)) { 105 | return key.getAnnotation().toString(); 106 | } else if (key.getAnnotationType() != null) { 107 | return "@" + key.getAnnotationType().getName(); 108 | } else { 109 | return ""; 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /kotlin-guice/src/test/kotlin/dev/misfitlabs/kotlinguice4/KeySpec.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4 19 | 20 | import com.google.inject.Key 21 | import java.lang.reflect.Type 22 | import java.util.concurrent.Callable 23 | import org.amshove.kluent.shouldEqual 24 | import org.spekframework.spek2.Spek 25 | import org.spekframework.spek2.style.specification.describe 26 | 27 | /** 28 | * @author John Leacox 29 | */ 30 | object KeySpec : Spek({ 31 | describe("#key") { 32 | it("creates a Key from a simple type") { 33 | val key = key() 34 | key shouldEqual Key.get(A::class.java) 35 | } 36 | 37 | it("creates a Key from a complex type") { 38 | val key = key>>() 39 | key shouldEqual Key.get(typeLiteral>>()) 40 | } 41 | } 42 | 43 | describe("#annotatedKey") { 44 | it("creates a Key from a simple type and annotation type") { 45 | val key = annotatedKey() 46 | key shouldEqual Key.get(A::class.java, Annotated::class.java) 47 | } 48 | 49 | it("creates a Key from a complex type and annotation type") { 50 | val key = annotatedKey>, Annotated>() 51 | key shouldEqual Key.get(typeLiteral>>(), Annotated::class.java) 52 | } 53 | 54 | it("creates a Key from a simple type and annotation instance") { 55 | val annotationInstance = MembersInjection::class.java 56 | .getDeclaredField("annotatedMemberInjectionSite") 57 | .getAnnotation(Annotated::class.java) 58 | 59 | val key = annotatedKey(annotationInstance) 60 | key shouldEqual Key.get(A::class.java, annotationInstance) 61 | } 62 | 63 | it("creates a Key from a complex type and annotation instance") { 64 | val annotationInstance = MembersInjection::class.java 65 | .getDeclaredField("annotatedMemberInjectionSite") 66 | .getAnnotation(Annotated::class.java) 67 | 68 | val key = annotatedKey>>(annotationInstance) 69 | key shouldEqual Key.get(typeLiteral>>(), annotationInstance) 70 | } 71 | 72 | it("creates a Key from a java.lang.reflect.Type and annotation type") { 73 | val stringType: Type = String::class.java 74 | 75 | val key = annotatedKey(stringType) 76 | key shouldEqual Key.get(stringType, Annotated::class.java) 77 | } 78 | } 79 | 80 | describe("#ofType") { 81 | it("creates a Key from a simple type with the same annotation as the current key") { 82 | val key = annotatedKey() 83 | 84 | val newKey = key.ofType() 85 | newKey.annotation shouldEqual key.annotation 86 | newKey shouldEqual Key.get(String::class.java, Annotated::class.java) 87 | } 88 | 89 | it("creates a Key from a complex type with the same annotation as the current key") { 90 | val key = annotatedKey>, Annotated>() 91 | 92 | val newKey = key.ofType>>() 93 | newKey.annotation shouldEqual key.annotation 94 | newKey shouldEqual Key.get(typeLiteral>>(), Annotated::class.java) 95 | } 96 | } 97 | }) 98 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/KotlinPrivateModule.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4 19 | 20 | import com.google.inject.MembersInjector 21 | import com.google.inject.PrivateModule 22 | import com.google.inject.Provider 23 | import com.google.inject.Scope 24 | import dev.misfitlabs.kotlinguice4.binder.KotlinAnnotatedBindingBuilder 25 | import dev.misfitlabs.kotlinguice4.binder.KotlinAnnotatedElementBuilder 26 | import dev.misfitlabs.kotlinguice4.binder.KotlinLinkedBindingBuilder 27 | import dev.misfitlabs.kotlinguice4.binder.KotlinScopedBindingBuilder 28 | import dev.misfitlabs.kotlinguice4.internal.KotlinBindingBuilder 29 | 30 | /** 31 | * An extension of [KotlinPrivateModule] that enhances the binding DSL to allow binding using reified 32 | * type parameters. 33 | * 34 | * By using this class instead of [KotlinPrivateModule] you can replace: 35 | * ``` 36 | * class MyModule : KotlinPrivateModule() { 37 | * override fun configure() { 38 | * bind(Service::class.java).to(ServiceImpl::class.java).`in`(Singleton::class.java) 39 | * bind(object : TypeLiteral>() {}) 40 | * .to(CreditCardPaymentService::class.java) 41 | * 42 | * expose(object : TypeLiteral>() {} 43 | * } 44 | * } 45 | * ``` 46 | * with 47 | * ``` 48 | * class MyModule : KotlinPrivateModule() { 49 | * override fun configure() { 50 | * bind().to().`in`() 51 | * bind>().to() 52 | * 53 | * expose>() 54 | * } 55 | * } 56 | * ``` 57 | * 58 | * @see KotlinPrivateBinder 59 | * @see PrivateModule 60 | * @author Brian van de Boogaard 61 | * @since 1.0 62 | */ 63 | abstract class KotlinPrivateModule : PrivateModule() { 64 | /** Gets direct access to the underlying [KotlinPrivateBinder]. */ 65 | protected val kotlinBinder: KotlinPrivateBinder by lazy { 66 | KotlinPrivateBinder(binder().skipSources( 67 | KotlinAnnotatedBindingBuilder::class.java, 68 | KotlinAnnotatedElementBuilder::class.java, 69 | KotlinBinder::class.java, 70 | KotlinBindingBuilder::class.java, 71 | KotlinLinkedBindingBuilder::class.java, 72 | KotlinScopedBindingBuilder::class.java 73 | )) 74 | } 75 | 76 | /** Makes the binding for [T] available to enclosing modules and the injector. */ 77 | protected inline fun expose(): KotlinAnnotatedElementBuilder { 78 | return kotlinBinder.expose() 79 | } 80 | 81 | // Everything below is copied from KotlinModule. 82 | 83 | /** @see KotlinPrivateBinder.bindScope */ 84 | protected inline fun bindScope(scope: Scope) { 85 | kotlinBinder.bindScope(scope) 86 | } 87 | 88 | /** @see KotlinPrivateBinder.bind */ 89 | protected inline fun bind(): KotlinAnnotatedBindingBuilder { 90 | return kotlinBinder.bind() 91 | } 92 | 93 | /** @see KotlinPrivateBinder.requestStaticInjection */ 94 | protected inline fun requestStaticInjection() { 95 | kotlinBinder.requestStaticInjection() 96 | } 97 | 98 | /** @see PrivateModule.requireBinding */ 99 | protected inline fun requireBinding() { 100 | requireBinding(key()) 101 | } 102 | 103 | /** @see KotlinPrivateBinder.getProvider */ 104 | protected inline fun getProvider(): Provider { 105 | return kotlinBinder.getProvider() 106 | } 107 | 108 | /** @see KotlinPrivateBinder.getMembersInjector */ 109 | protected inline fun getMembersInjector(): MembersInjector { 110 | return kotlinBinder.getMembersInjector() 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | kotlin-guice 2 | ============ 3 | 4 | Guice extensions for Kotlin. This provides extension wrappers and extension methods for providing a better Guice DSL experience from Kotlin. It takes advantage of reified types to reduce class references like `bind(MyResource::class.java)` to `bind()`. 5 | 6 | ## Download 7 | 8 | Download the latest JAR via Maven: 9 | 10 | ```xml 11 | 12 | dev.misfitlabs.kotlinguice4 13 | kotlin-guice 14 | 3.0.0 15 | 16 | ``` 17 | 18 | or Gradle: 19 | 20 | ```gradle 21 | compile 'dev.misfitlabs.kotlinguice4:kotlin-guice:3.0.0' 22 | ``` 23 | 24 | ## Getting Started 25 | 26 | ### KotlinModule 27 | 28 | Use `KotlinModule` for Guice modules instead of `AbstractModule` to take advantage of the enhanced Kotlin Guice DSL. 29 | 30 | ```kotlin 31 | import dev.misfitlabs.kotlinguice4.KotlinModule 32 | 33 | class MyModule : KotlinModule() { 34 | override fun configure() { 35 | bind().to().`in`() 36 | bind>().to() 37 | bind().annotatedWith().to() 38 | } 39 | } 40 | ``` 41 | 42 | The `KotlinPrivateModule` can also be used if only some bindings need to be exposed. 43 | 44 | ```kotlin 45 | import dev.misfitlabs.kotlinguice4.KotlinPrivateModule 46 | 47 | class MyPrivateModule : KotlinPrivateModule() { 48 | override fun configure() { 49 | bind().to().`in`() 50 | bind>().to() 51 | bind().annotatedWith().to() 52 | 53 | expose>() 54 | } 55 | } 56 | ``` 57 | 58 | ### Injector 59 | 60 | The Guice injector has been enhanced with extension methods to make direct use of the injector better from Kotlin. 61 | 62 | ```kotlin 63 | import dev.misfitlabs.kotlinguice4.annotatedKey 64 | import dev.misfitlabs.kotlinguice4.getInstance 65 | 66 | fun main(args: Array) { 67 | val injector = Guice.createInjector(MyModule(), MyPrivateModule()) 68 | 69 | val paymentService = injector.getInstance>() 70 | 71 | // Use the annotatedKey to get an annotated instance 72 | val payPalProcessor = injector.getInstance(annotatedKey()) 73 | } 74 | ``` 75 | 76 | ### Key and TypeLiteral 77 | 78 | Package level functions are included to enhance creating `Key` and `TypeLiteral` instances from kotlin. 79 | 80 | ```kotlin 81 | import dev.misfitlabs.kotlinguice4.annotatedKey 82 | import dev.misfitlabs.kotlinguice4.key 83 | import dev.misfitlabs.kotlinguice4.typeLiteral 84 | 85 | val key = key() 86 | val annotatedKey = annotatedKey() 87 | val sameAnnotatedDifferentKey = annotatedKey.getType() 88 | 89 | val listType = typeLiteral>() 90 | ``` 91 | 92 | ### Multibindings 93 | 94 | As of version 1.4.1 the kotlin-guice-multibindings module is gone and the functionality has been merged into kotlin-guice. 95 | 96 | #### Usage 97 | 98 | ```kotlin 99 | val multibinder = KotlinMultibinder.newSetBinder(kotlinBinder) 100 | multibinder.addBinding().to() 101 | 102 | val mapbinder = KotlinMapBinder.newMapBinder(kotlinBinder) 103 | mapbinder.addBinding("twix").to() 104 | ``` 105 | 106 | With Guice 4.2+, scanning for methods with the multibinding annotations `ProvidesIntoSet`, `ProvidesIntoMap`, and `ProvidesIntoOptional` is enabled by default. However, the default scanner only provides bindings for Java collection types. In order to get bindings for Kotlin collection types, install the `KotlinMultibindingsScanner`. 107 | 108 | ```kotlin 109 | install(KotlinMultibindingsScanner.asModule()) 110 | ``` 111 | 112 | ## License 113 | 114 | Licensed under the Apache License, Version 2.0 (the "License"); 115 | you may not use this file except in compliance with the License. 116 | You may obtain a copy of the License at 117 | 118 | http://www.apache.org/licenses/LICENSE-2.0 119 | 120 | Unless required by applicable law or agreed to in writing, software 121 | distributed under the License is distributed on an "AS IS" BASIS, 122 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 123 | See the License for the specific language governing permissions and 124 | limitations under the License. 125 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/multibindings/MapBindingSelection.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package dev.misfitlabs.kotlinguice4.multibindings 18 | 19 | import com.google.inject.Key 20 | import com.google.inject.TypeLiteral 21 | 22 | /** 23 | * A container of the binding keys necessary for map bindings. 24 | * 25 | * @author John Leacox 26 | * @since 1.0 27 | */ 28 | @Suppress("HasPlatformType") 29 | internal class MapBindingSelection( 30 | private val keyType: TypeLiteral, 31 | private val valueType: TypeLiteral, 32 | val mapKey: Key> 33 | ) { 34 | val providerMapKey by lazy { 35 | mapKey.ofType(mapOfProviderOf(keyType, valueType)) 36 | } 37 | val mutableProviderMapKey by lazy { mapKey.ofType(mutableMapOfProviderOf(keyType, valueType)) } 38 | 39 | val jakartaProviderMapKey by lazy { 40 | mapKey.ofType(mapOfJakartaProviderOf(keyType, valueType)) 41 | } 42 | val mutableJakartaProviderMapKey by lazy { 43 | mapKey.ofType(mutableMapOfJakartaProviderOf(keyType, valueType)) 44 | } 45 | 46 | val setOfEntryOfJakartaProviderKey by lazy { 47 | mapKey.ofType(setOf(entryOfJakartaProviderOf(keyType, valueType))) 48 | } 49 | 50 | val mutableSetOfEntryOfJakartaProviderKey by lazy { 51 | mapKey.ofType(mutableSetOf(entryOfJakartaProviderOf(keyType, valueType))) 52 | } 53 | 54 | val collectionOfProviderOfEntryOfProviderKey by lazy { 55 | mapKey.ofType(collectionOf(providerOf(entryOfProviderOf(keyType, valueType)))) 56 | } 57 | val mutableCollectionOfProviderOfEntryOfProviderKey by lazy { 58 | mapKey.ofType(mutableCollectionOf(providerOf(entryOfProviderOf(keyType, valueType)))) 59 | } 60 | 61 | val collectionOfJakartaProviderOfEntryOfProviderKey by lazy { 62 | mapKey.ofType(collectionOf(jakartaProviderOf(entryOfProviderOf(keyType, valueType)))) 63 | } 64 | val mutableCollectionOfJakartaProviderOfEntryOfProviderKey by lazy { 65 | mapKey.ofType(mutableCollectionOf(jakartaProviderOf(entryOfProviderOf(keyType, valueType)))) 66 | } 67 | 68 | val multimapKey by lazy { mapKey.ofType(mapOf(keyType, setOf(valueType))) } 69 | val mutableMultimapKey by lazy { 70 | mapKey.ofType(mutableMapOf(keyType, mutableSetOf(valueType))) 71 | } 72 | 73 | val providerSetMultimapKey by lazy { 74 | mapKey.ofType(mapOfSetOfProviderOf(keyType, valueType)) 75 | } 76 | val mutableProviderSetMultimapKey by lazy { 77 | mapKey.ofType(mutableMapOfMutableSetOfProviderOf(keyType, valueType)) 78 | } 79 | 80 | val jakartaProviderSetMultimapKey by lazy { 81 | mapKey.ofType(mapOfSetOfJakartaProviderOf(keyType, valueType)) 82 | } 83 | val mutableJakartaProviderSetMultimapKey by lazy { 84 | mapKey.ofType(mutableMapOfMutableSetOfJakartaProviderOf(keyType, valueType)) 85 | } 86 | 87 | val providerCollectionMultimapKey by lazy { 88 | mapKey.ofType(mapOfCollectionOfProviderOf(keyType, valueType)) 89 | } 90 | val mutableProviderCollectionMultimapKey by lazy { 91 | mapKey.ofType(mutableMapOfMutableCollectionOfProviderOf(keyType, valueType)) 92 | } 93 | 94 | val jakartaProviderCollectionMultimapKey by lazy { 95 | mapKey.ofType(mapOfCollectionOfJakartaProviderOf(keyType, valueType)) 96 | } 97 | val mutableJakartaProviderCollectionMultimapKey by lazy { 98 | mapKey.ofType(mutableMapOfMutableCollectionOfJakartaProviderOf(keyType, valueType)) 99 | } 100 | 101 | override fun equals(other: Any?): Boolean { 102 | if (this === other) return true 103 | if (other?.javaClass != javaClass) return false 104 | 105 | other as MapBindingSelection<*, *> 106 | 107 | if (mapKey != other.mapKey) return false 108 | 109 | return true 110 | } 111 | 112 | override fun hashCode(): Int { 113 | return mapKey.hashCode() 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/KotlinModule.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4 19 | 20 | import com.google.inject.AbstractModule 21 | import com.google.inject.Binder 22 | import com.google.inject.MembersInjector 23 | import com.google.inject.Provider 24 | import com.google.inject.Scope 25 | import dev.misfitlabs.kotlinguice4.binder.KotlinAnnotatedBindingBuilder 26 | import dev.misfitlabs.kotlinguice4.binder.KotlinAnnotatedElementBuilder 27 | import dev.misfitlabs.kotlinguice4.binder.KotlinLinkedBindingBuilder 28 | import dev.misfitlabs.kotlinguice4.binder.KotlinScopedBindingBuilder 29 | import dev.misfitlabs.kotlinguice4.internal.KotlinBindingBuilder 30 | import kotlin.reflect.KProperty 31 | 32 | /** 33 | * An extension of [AbstractModule] that enhances the binding DSL to allow binding using reified 34 | * type parameters. 35 | * 36 | * By using this class instead of [AbstractModule] you can replace: 37 | * ``` 38 | * class MyModule : AbstractModule() { 39 | * override fun configure() { 40 | * bind(Service::class.java).to(ServiceImpl::class.java).`in`(Singleton::class.java) 41 | * bind(object : TypeLiteral>() {}) 42 | * .to(CreditCardPaymentService::class.java) 43 | * } 44 | * } 45 | * ``` 46 | * with 47 | * ``` 48 | * class MyModule : KotlinModule() { 49 | * override fun configure() { 50 | * bind().to().`in`() 51 | * bind>().to() 52 | * } 53 | * } 54 | * ``` 55 | * 56 | * @see KotlinBinder 57 | * @see AbstractModule 58 | * @author John Leacox 59 | * @since 1.0 60 | */ 61 | abstract class KotlinModule : AbstractModule() { 62 | private class KotlinLazyBinder(private val delegateBinder: () -> Binder) { 63 | private val classesToSkip = arrayOf( 64 | KotlinAnnotatedBindingBuilder::class.java, 65 | KotlinAnnotatedElementBuilder::class.java, 66 | KotlinBinder::class.java, 67 | KotlinBindingBuilder::class.java, 68 | KotlinLinkedBindingBuilder::class.java, 69 | KotlinScopedBindingBuilder::class.java 70 | ) 71 | 72 | var lazyBinder = lazyInit() 73 | var currentBinder: Binder? = null 74 | 75 | operator fun getValue(thisRef: Any?, property: KProperty<*>): KotlinBinder { 76 | if (currentBinder != delegateBinder()) { 77 | currentBinder = delegateBinder() 78 | lazyBinder = lazyInit() 79 | } 80 | return lazyBinder.value 81 | } 82 | 83 | private fun lazyInit() = lazy { KotlinBinder(delegateBinder().skipSources(*classesToSkip)) } 84 | } 85 | 86 | /** Gets direct access to the underlying [KotlinBinder]. */ 87 | protected val kotlinBinder: KotlinBinder by KotlinLazyBinder { this.binder() } 88 | 89 | /** @see KotlinBinder.bindScope */ 90 | protected inline fun bindScope(scope: Scope) { 91 | kotlinBinder.bindScope(scope) 92 | } 93 | 94 | /** @see KotlinBinder.bind */ 95 | protected inline fun bind(): KotlinAnnotatedBindingBuilder { 96 | return kotlinBinder.bind() 97 | } 98 | 99 | /** @see KotlinBinder.requestStaticInjection */ 100 | protected inline fun requestStaticInjection() { 101 | kotlinBinder.requestStaticInjection() 102 | } 103 | 104 | /** @see AbstractModule.requireBinding */ 105 | protected inline fun requireBinding() { 106 | requireBinding(key()) 107 | } 108 | 109 | /** @see KotlinBinder.getProvider */ 110 | protected inline fun getProvider(): Provider { 111 | return kotlinBinder.getProvider() 112 | } 113 | 114 | /** @see KotlinBinder.getMembersInjector */ 115 | protected inline fun getMembersInjector(): MembersInjector { 116 | return kotlinBinder.getMembersInjector() 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/multibindings/KotlinMultibindingsScanner.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package dev.misfitlabs.kotlinguice4.multibindings 18 | 19 | import com.google.common.collect.ImmutableSet 20 | import com.google.inject.AbstractModule 21 | import com.google.inject.Binder 22 | import com.google.inject.Key 23 | import com.google.inject.Module 24 | import com.google.inject.multibindings.MapKey 25 | import com.google.inject.multibindings.ProvidesIntoMap 26 | import com.google.inject.multibindings.ProvidesIntoOptional 27 | import com.google.inject.multibindings.ProvidesIntoSet 28 | import com.google.inject.spi.InjectionPoint 29 | import com.google.inject.spi.ModuleAnnotatedMethodScanner 30 | import java.lang.reflect.Method 31 | import kotlin.reflect.full.findAnnotation 32 | 33 | /** 34 | * Scans modules for methods annotated for multibindings, mapbindings, and optional bindings. 35 | * 36 | * This scanner provides additional bindings for Kotlin collection types. As of Guice 4.2 the 37 | * multibinding method scanner is installed by default, but it only works for Java collection 38 | * types. Install this additional Kotlin multibindings scanner to allow multibindings with 39 | * Kotlin collection types. 40 | * 41 | * @author John Leacox 42 | * @since 1.0 43 | */ 44 | object KotlinMultibindingsScanner { 45 | /** 46 | * Returns the scanner as a module. 47 | * 48 | * When installed it will scan modules for methods annotated for multibindings, mapbindings, 49 | * and optional bindings and provide them via Kotlin collection types. 50 | */ 51 | fun asModule(): Module { 52 | return object : AbstractModule() { 53 | override fun configure() { 54 | binder().scanModulesForAnnotatedMethods(KotlinMultibindingsMethodScanner.INSTANCE) 55 | } 56 | } 57 | } 58 | } 59 | 60 | private class KotlinMultibindingsMethodScanner : ModuleAnnotatedMethodScanner() { 61 | companion object { 62 | val INSTANCE: KotlinMultibindingsMethodScanner by lazy { 63 | KotlinMultibindingsMethodScanner() 64 | } 65 | } 66 | 67 | override fun annotationClasses(): MutableSet> { 68 | return ImmutableSet.of( 69 | ProvidesIntoSet::class.java, 70 | ProvidesIntoMap::class.java, 71 | ProvidesIntoOptional::class.java) 72 | } 73 | 74 | override fun prepareMethod( 75 | binder: Binder, 76 | annotation: Annotation, 77 | key: Key, 78 | injectionPoint: InjectionPoint 79 | ): Key { 80 | val method = injectionPoint.member as Method 81 | val mapKeyAnnotation = findMapKeyAnnotation(method) 82 | 83 | return when (annotation) { 84 | is ProvidesIntoSet -> KotlinMultibinder.newRealSetBinder(binder, key) 85 | .getKeyForNewItem() 86 | is ProvidesIntoMap -> { 87 | if (mapKeyAnnotation == null) { 88 | return key 89 | } 90 | val typeAndValue: TypeAndValue = MapKeys 91 | .typeAndValueOfMapKey(mapKeyAnnotation) 92 | val keyType = typeAndValue.type 93 | return KotlinMapBinder.newRealMapBinder(binder, keyType, key) 94 | .getKeyForNewValue(typeAndValue.value) 95 | } 96 | is ProvidesIntoOptional -> { 97 | return when (annotation.value) { 98 | ProvidesIntoOptional.Type.DEFAULT -> KotlinOptionalBinder 99 | .newRealOptionalBinder(binder, key).getKeyForDefaultBinding() 100 | ProvidesIntoOptional.Type.ACTUAL -> KotlinOptionalBinder 101 | .newRealOptionalBinder(binder, key).getKeyForActualBinding() 102 | } 103 | } 104 | else -> throw IllegalStateException("Invalid annotation: $annotation") 105 | } 106 | } 107 | 108 | private fun findMapKeyAnnotation(method: Method): Annotation? { 109 | var foundAnnotation: Annotation? = null 110 | for (annotation in method.annotations) { 111 | val mapKey = annotation.annotationClass.findAnnotation() 112 | if (mapKey != null) { 113 | if (foundAnnotation != null) { 114 | return null 115 | } 116 | if (mapKey.unwrapValue) { 117 | try { 118 | val valueMethod = annotation.annotationClass.java.getDeclaredMethod("value") 119 | if (valueMethod.returnType.isArray) { 120 | return null 121 | } 122 | } catch (invalid: NoSuchMethodException) { 123 | return null 124 | } 125 | } 126 | foundAnnotation = annotation 127 | } 128 | } 129 | return foundAnnotation 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/multibindings/KotlinOptionalBinder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package dev.misfitlabs.kotlinguice4.multibindings 18 | 19 | import com.google.inject.Binder 20 | import com.google.inject.Key 21 | import com.google.inject.multibindings.OptionalBinder 22 | import dev.misfitlabs.kotlinguice4.annotatedKey 23 | import dev.misfitlabs.kotlinguice4.binder.KotlinLinkedBindingBuilder 24 | import dev.misfitlabs.kotlinguice4.key 25 | 26 | /** 27 | * A wrapper of [OptionalBinder] that enhances the binding DSL to allow binding using reified type 28 | * parameters. 29 | * 30 | * By using this class instead of [OptionalBinder] you can replace the following lines: 31 | * ``` 32 | * val optionalBinder = OptionalBinder.newOptionalBinder(binder(), Renamer::class.java) 33 | * optionalBinder.setBinding().to(ReplacingRenamer::class.java); 34 | * ``` 35 | * with 36 | * ``` 37 | * val optionalBinder = OptionalBinder.newOptionalBinder(kotlinBinder) 38 | * optionalBinder.setBinding().to() 39 | * ``` 40 | * 41 | * @see OptionalBinder 42 | * @author John Leacox 43 | * @since 1.0 44 | */ 45 | interface KotlinOptionalBinder { 46 | /** 47 | * Returns a binding builder used to set the default value. 48 | * 49 | * @see OptionalBinder.setDefault 50 | */ 51 | fun setDefault(): KotlinLinkedBindingBuilder 52 | 53 | /** 54 | * Returns a binding builder used to set the actual value. 55 | * 56 | * @see OptionalBinder.setDefault 57 | */ 58 | fun setBinding(): KotlinLinkedBindingBuilder 59 | 60 | companion object { 61 | /** 62 | * Returns a new optional binder for configuring default and actual values of [T]. 63 | */ 64 | inline fun newOptionalBinder(binder: Binder): KotlinOptionalBinder { 65 | return newRealOptionalBinder(binder, key()) 66 | } 67 | 68 | /** 69 | * Returns a new optional binder for configuring default and actual values of [T] that is 70 | * bound with [TAnn]. 71 | */ 72 | inline fun 73 | newAnnotatedOptionalBinder(binder: Binder): KotlinOptionalBinder { 74 | return newRealOptionalBinder(binder, annotatedKey()) 75 | } 76 | 77 | @PublishedApi internal fun newRealOptionalBinder( 78 | binder: Binder, 79 | optionalKey: Key 80 | ): RealKotlinOptionalBinder { 81 | val skippingBinder = binder.skipSources(RealKotlinOptionalBinder::class.java, 82 | KotlinOptionalBinder::class.java, 83 | Companion::class.java) 84 | val optionalBinder = OptionalBinder.newOptionalBinder(skippingBinder, optionalKey) 85 | return RealKotlinOptionalBinder(optionalBinder, optionalKey) 86 | } 87 | } 88 | } 89 | 90 | internal class RealKotlinOptionalBinder( 91 | private val delegate: OptionalBinder, 92 | private val optionalKey: Key 93 | ) : KotlinOptionalBinder { 94 | private val elementType = optionalKey.typeLiteral 95 | private val name = RealElement.nameOf(optionalKey) 96 | 97 | override fun setDefault(): KotlinLinkedBindingBuilder { 98 | return KotlinLinkedBindingBuilderImpl(delegate.setDefault()) 99 | } 100 | 101 | override fun setBinding(): KotlinLinkedBindingBuilder { 102 | return KotlinLinkedBindingBuilderImpl(delegate.setBinding()) 103 | } 104 | 105 | fun getKeyForDefaultBinding(): Key { 106 | return Key.get(optionalKey.typeLiteral, 107 | RealElement(defaultNameOf(optionalKey), Element.Type.OPTIONALBINDER, "")) 108 | } 109 | 110 | fun getKeyForActualBinding(): Key { 111 | return Key.get(optionalKey.typeLiteral, 112 | RealElement(actualNameOf(optionalKey), Element.Type.OPTIONALBINDER, "")) 113 | } 114 | 115 | private fun defaultNameOf(key: Key<*>): String { 116 | val value = RealElement.nameOf(key) 117 | return "@Default" + if (value.isEmpty()) "" else "(value=$value)" 118 | } 119 | 120 | private fun actualNameOf(key: Key<*>): String { 121 | val value = RealElement.nameOf(key) 122 | return "@Actual" + if (value.isEmpty()) "" else "(value=$value)" 123 | } 124 | 125 | // Prevents the module from being installed multiple times. 126 | override fun equals(other: Any?): Boolean { 127 | if (this === other) return true 128 | if (other?.javaClass != javaClass) return false 129 | 130 | other as RealKotlinOptionalBinder<*> 131 | 132 | if (optionalKey != other.optionalKey) return false 133 | 134 | return true 135 | } 136 | 137 | override fun hashCode(): Int { 138 | return optionalKey.hashCode() 139 | } 140 | 141 | override fun toString(): String { 142 | return (if (name.isEmpty()) "" else name + " ") + 143 | "KotlinOptionalBinder<" + elementType + ">" 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin, switch paths to Windows format before running java 129 | if $cygwin ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=$((i+1)) 158 | done 159 | case $i in 160 | (0) set -- ;; 161 | (1) set -- "$args0" ;; 162 | (2) set -- "$args0" "$args1" ;; 163 | (3) set -- "$args0" "$args1" "$args2" ;; 164 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=$(save "$@") 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 184 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 185 | cd "$(dirname "$0")" 186 | fi 187 | 188 | exec "$JAVACMD" "$@" 189 | -------------------------------------------------------------------------------- /gradle/gradle-mvn-push.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Chris Banes 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | apply plugin: 'maven' 18 | apply plugin: 'signing' 19 | 20 | version = VERSION_NAME 21 | group = GROUP 22 | 23 | def isReleaseBuild() { 24 | return VERSION_NAME.contains("SNAPSHOT") == false 25 | } 26 | 27 | def getReleaseRepositoryUrl() { 28 | return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL : 29 | "https://oss.sonatype.org/service/local/staging/deploy/maven2/" 30 | } 31 | 32 | def getSnapshotRepositoryUrl() { 33 | return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL : 34 | "https://oss.sonatype.org/content/repositories/snapshots/" 35 | } 36 | 37 | def getRepositoryUsername() { 38 | return hasProperty('SONATYPE_NEXUS_USERNAME') ? SONATYPE_NEXUS_USERNAME : "" 39 | } 40 | 41 | def getRepositoryPassword() { 42 | return hasProperty('SONATYPE_NEXUS_PASSWORD') ? SONATYPE_NEXUS_PASSWORD : "" 43 | } 44 | 45 | afterEvaluate { project -> 46 | uploadArchives { 47 | repositories { 48 | mavenDeployer { 49 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 50 | 51 | pom.groupId = GROUP 52 | pom.artifactId = POM_ARTIFACT_ID 53 | pom.version = VERSION_NAME 54 | 55 | repository(url: getReleaseRepositoryUrl()) { 56 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 57 | } 58 | snapshotRepository(url: getSnapshotRepositoryUrl()) { 59 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 60 | } 61 | 62 | pom.project { 63 | name POM_NAME 64 | packaging POM_PACKAGING 65 | description POM_DESCRIPTION 66 | url POM_URL 67 | 68 | scm { 69 | url POM_SCM_URL 70 | connection POM_SCM_CONNECTION 71 | developerConnection POM_SCM_DEV_CONNECTION 72 | } 73 | 74 | licenses { 75 | license { 76 | name POM_LICENCE_NAME 77 | url POM_LICENCE_URL 78 | distribution POM_LICENCE_DIST 79 | } 80 | } 81 | 82 | developers { 83 | developer { 84 | id POM_DEVELOPER_ID 85 | name POM_DEVELOPER_NAME 86 | } 87 | } 88 | } 89 | } 90 | } 91 | } 92 | 93 | signing { 94 | required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } 95 | sign configurations.archives 96 | } 97 | 98 | if (project.getPlugins().hasPlugin('com.android.application') || 99 | project.getPlugins().hasPlugin('com.android.library')) { 100 | task install(type: Upload, dependsOn: assemble) { 101 | repositories.mavenInstaller { 102 | configuration = configurations.archives 103 | 104 | pom.groupId = GROUP 105 | pom.artifactId = POM_ARTIFACT_ID 106 | pom.version = VERSION_NAME 107 | 108 | pom.project { 109 | name POM_NAME 110 | packaging POM_PACKAGING 111 | description POM_DESCRIPTION 112 | url POM_URL 113 | 114 | scm { 115 | url POM_SCM_URL 116 | connection POM_SCM_CONNECTION 117 | developerConnection POM_SCM_DEV_CONNECTION 118 | } 119 | 120 | licenses { 121 | license { 122 | name POM_LICENCE_NAME 123 | url POM_LICENCE_URL 124 | distribution POM_LICENCE_DIST 125 | } 126 | } 127 | 128 | developers { 129 | developer { 130 | id POM_DEVELOPER_ID 131 | name POM_DEVELOPER_NAME 132 | } 133 | } 134 | } 135 | } 136 | } 137 | 138 | task androidJavadocs(type: Javadoc) { 139 | source = android.sourceSets.main.java.source 140 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 141 | } 142 | 143 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { 144 | classifier = 'javadoc' 145 | from androidJavadocs.destinationDir 146 | } 147 | 148 | task androidSourcesJar(type: Jar) { 149 | classifier = 'sources' 150 | from android.sourceSets.main.java.source 151 | } 152 | } else { 153 | install { 154 | repositories.mavenInstaller { 155 | pom.groupId = GROUP 156 | pom.artifactId = POM_ARTIFACT_ID 157 | pom.version = VERSION_NAME 158 | 159 | pom.project { 160 | name POM_NAME 161 | packaging POM_PACKAGING 162 | description POM_DESCRIPTION 163 | url POM_URL 164 | 165 | scm { 166 | url POM_SCM_URL 167 | connection POM_SCM_CONNECTION 168 | developerConnection POM_SCM_DEV_CONNECTION 169 | } 170 | 171 | licenses { 172 | license { 173 | name POM_LICENCE_NAME 174 | url POM_LICENCE_URL 175 | distribution POM_LICENCE_DIST 176 | } 177 | } 178 | 179 | developers { 180 | developer { 181 | id POM_DEVELOPER_ID 182 | name POM_DEVELOPER_NAME 183 | } 184 | } 185 | } 186 | } 187 | } 188 | 189 | task sourcesJar(type: Jar, dependsOn: classes) { 190 | classifier = 'sources' 191 | from sourceSets.main.allSource 192 | } 193 | 194 | task javadocJar(type: Jar, dependsOn: javadoc) { 195 | classifier = 'javadoc' 196 | from javadoc.destinationDir 197 | } 198 | } 199 | 200 | if (JavaVersion.current().isJava8Compatible()) { 201 | allprojects { 202 | tasks.withType(Javadoc) { 203 | options.addStringOption('Xdoclint:none', '-quiet') 204 | } 205 | } 206 | } 207 | 208 | artifacts { 209 | if (project.getPlugins().hasPlugin('com.android.application') || 210 | project.getPlugins().hasPlugin('com.android.library')) { 211 | archives androidSourcesJar 212 | archives androidJavadocsJar 213 | } else { 214 | archives sourcesJar 215 | archives javadocJar 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /kotlin-guice/src/test/kotlin/dev/misfitlabs/kotlinguice4/KotlinBinderSpec.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4 19 | 20 | import com.google.inject.Guice 21 | import com.google.inject.Key 22 | import dev.misfitlabs.kotlinguice4.binder.annotatedWith 23 | import dev.misfitlabs.kotlinguice4.binder.to 24 | import jakarta.inject.Inject 25 | import jakarta.inject.Singleton 26 | import java.util.concurrent.Callable 27 | import org.amshove.kluent.shouldBe 28 | import org.amshove.kluent.shouldBeInstanceOf 29 | import org.amshove.kluent.shouldEqual 30 | import org.amshove.kluent.shouldNotBe 31 | import org.spekframework.spek2.Spek 32 | import org.spekframework.spek2.style.specification.describe 33 | 34 | /** 35 | * @author John Leacox 36 | */ 37 | object KotlinBinderSpec : Spek({ 38 | beforeEachTest { 39 | StaticInjectionObj.reset() 40 | } 41 | 42 | describe("KotlinBinder") { 43 | describe("#bindScope") { 44 | it("binds a custom scope using a scope annotation type parameter") { 45 | val scope = TestScope() 46 | val injector = Guice.createInjector(object : KotlinModule() { 47 | override fun configure() { 48 | kotlinBinder.bindScope(scope) 49 | kotlinBinder.bind().to().`in`() 50 | } 51 | }) 52 | 53 | val a = injector.getInstance(A::class.java) 54 | a shouldBe injector.getInstance(A::class.java) 55 | 56 | scope.reset() 57 | 58 | a shouldNotBe injector.getInstance(A::class.java) 59 | } 60 | } 61 | 62 | describe("#bind") { 63 | it("binds source using a type parameter") { 64 | val injector = Guice.createInjector(object : KotlinModule() { 65 | override fun configure() { 66 | kotlinBinder.bind().to(AImpl::class.java) 67 | } 68 | }) 69 | 70 | val a = injector.getInstance(A::class.java) 71 | 72 | a.get() shouldEqual "Impl of A" 73 | } 74 | 75 | it("binds a complex source using a type parameter") { 76 | val injector = Guice.createInjector(object : KotlinModule() { 77 | override fun configure() { 78 | kotlinBinder.bind>().to(ACallable::class.java) 79 | } 80 | }) 81 | 82 | val a = injector.getInstance(key>()) 83 | a.call().get() shouldEqual "Impl of A" 84 | } 85 | 86 | it("binds to a target using a type parameter") { 87 | val injector = Guice.createInjector(object : KotlinModule() { 88 | override fun configure() { 89 | kotlinBinder.bind().to() 90 | } 91 | }) 92 | 93 | val a = injector.getInstance(A::class.java) 94 | 95 | a.get() shouldEqual "Impl of A" 96 | } 97 | 98 | it("binds to a complex target using a type parameter") { 99 | val injector = Guice.createInjector(object : KotlinModule() { 100 | override fun configure() { 101 | kotlinBinder.bind>().to>() 102 | } 103 | }) 104 | 105 | val callable = injector.getInstance(key>()) 106 | callable.call() shouldEqual null 107 | } 108 | 109 | it("binds with an annotation using a type parameter") { 110 | val injector = Guice.createInjector(object : KotlinModule() { 111 | override fun configure() { 112 | kotlinBinder.bind().to() 113 | kotlinBinder.bind().annotatedWith().to() 114 | } 115 | }) 116 | 117 | val a = injector.getInstance(Key.get(A::class.java, Annotated::class.java)) 118 | 119 | a.get() shouldEqual "Impl of A" 120 | } 121 | 122 | it("binds to a provider using a type parameter") { 123 | val injector = Guice.createInjector(object : KotlinModule() { 124 | override fun configure() { 125 | kotlinBinder.bind().toProvider() 126 | } 127 | }) 128 | 129 | val a = injector.getInstance(A::class.java) 130 | 131 | a shouldBeInstanceOf B::class.java 132 | } 133 | 134 | it("binds to a complex provider using a type parameter") { 135 | val injector = Guice.createInjector(object : KotlinModule() { 136 | override fun configure() { 137 | kotlinBinder.bind>().toProvider>>() 138 | } 139 | }) 140 | 141 | injector.getInstance(key>()) 142 | } 143 | 144 | it("binds in a scope") { 145 | val injector = Guice.createInjector(object : KotlinModule() { 146 | override fun configure() { 147 | kotlinBinder.bind().to().`in`() 148 | } 149 | }) 150 | 151 | val a = injector.getInstance(A::class.java) 152 | a shouldBe injector.getInstance(A::class.java) 153 | } 154 | } 155 | 156 | describe("#bindConstant") { 157 | it("binds to a target using a type parameter and annotation") { 158 | class ClassWithConstant @Inject constructor(@Annotated val constant: Class) 159 | 160 | val injector = Guice.createInjector(object : KotlinModule() { 161 | override fun configure() { 162 | kotlinBinder.bindConstant().annotatedWith().to>() 163 | } 164 | }) 165 | 166 | val classWithConstant = injector.getInstance(ClassWithConstant::class.java) 167 | classWithConstant.constant shouldEqual Iterator::class.java 168 | } 169 | } 170 | 171 | describe("#requestStaticInjection") { 172 | it("injects static fields") { 173 | Guice.createInjector(object : KotlinModule() { 174 | override fun configure() { 175 | kotlinBinder.bind().toInstance("Statically Injected") 176 | kotlinBinder.requestStaticInjection() 177 | } 178 | }) 179 | 180 | StaticInjectionObj.staticInjectionSite shouldEqual "Statically Injected" 181 | } 182 | } 183 | 184 | describe("#getProvider") { 185 | it("get a provider for a simple type") { 186 | Guice.createInjector(object : KotlinModule() { 187 | override fun configure() { 188 | kotlinBinder.bind().to() 189 | val provider = kotlinBinder.getProvider() 190 | provider.toString() shouldEqual "Provider" 191 | } 192 | }) 193 | } 194 | 195 | it("get a provider for an annotated key") { 196 | Guice.createInjector(object : KotlinModule() { 197 | override fun configure() { 198 | kotlinBinder.bind>().to>() 199 | val provider = kotlinBinder.getProvider>() 200 | provider.toString() shouldEqual 201 | "Provider>" 202 | } 203 | }) 204 | } 205 | } 206 | 207 | describe("#getMembersInjector") { 208 | it("injects member fields") { 209 | Guice.createInjector(object : KotlinModule() { 210 | override fun configure() { 211 | val membersInjector = kotlinBinder.getMembersInjector() 212 | membersInjector.toString() shouldEqual 213 | "MembersInjector" 214 | } 215 | }) 216 | } 217 | } 218 | } 219 | }) 220 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/multibindings/KotlinMultibinder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package dev.misfitlabs.kotlinguice4.multibindings 18 | 19 | import com.google.inject.Binder 20 | import com.google.inject.Key 21 | import com.google.inject.Provider 22 | import com.google.inject.TypeLiteral 23 | import com.google.inject.multibindings.Multibinder 24 | import com.google.inject.util.Types 25 | import dev.misfitlabs.kotlinguice4.KotlinModule 26 | import dev.misfitlabs.kotlinguice4.annotatedKey 27 | import dev.misfitlabs.kotlinguice4.binder.KotlinLinkedBindingBuilder 28 | import dev.misfitlabs.kotlinguice4.key 29 | import dev.misfitlabs.kotlinguice4.multibindings.Element.Type.MULTIBINDER 30 | 31 | /** 32 | * A wrapper of [Multibinder] that enhances the binding DSL to allow binding using reified type 33 | * parameters. 34 | * 35 | * By using this class instead of [Multibinder] you can replace the following lines: 36 | * ``` 37 | * val multibinder = Multibinder.newSetBinder(binder(), Snack::class) 38 | * multibinder.addBinding().to(Twix::class.java) 39 | * ``` 40 | * with 41 | * ``` 42 | * val multibinder = KotlinMultibinder.newSetBinder(kotlinBinder) 43 | * multibinder.addBinding().to() 44 | * ``` 45 | * 46 | * @see Multibinder 47 | * @author John Leacox 48 | * @since 1.0 49 | */ 50 | interface KotlinMultibinder { 51 | /** 52 | * Configures the bound set to silently discard duplicate elements. 53 | * 54 | * @see Multibinder.permitDuplicates 55 | */ 56 | fun permitDuplicates(): KotlinMultibinder 57 | 58 | /** 59 | * Returns a binding builder used to add a new element in the set. 60 | * 61 | * @see Multibinder.addBinding 62 | */ 63 | fun addBinding(): KotlinLinkedBindingBuilder 64 | 65 | companion object { 66 | /** 67 | * Returns a new multibinder that collects entries of [T] in a {@link Set}. 68 | */ 69 | inline fun newSetBinder(binder: Binder): KotlinMultibinder { 70 | return newRealSetBinder(binder, key()) 71 | } 72 | 73 | /** 74 | * Returns a new multibinder that collects entries of [T] in a {@link set} that is bound 75 | * with [annotation]. 76 | */ 77 | inline fun newAnnotatedSetBinder(binder: Binder, annotation: Annotation): KotlinMultibinder { 78 | return newRealSetBinder(binder, annotatedKey(annotation)) 79 | } 80 | 81 | /** 82 | * Returns a new multibinder that collects entries of [T] in a {@link Set} that is bound 83 | * with [TAnn]. 84 | */ 85 | inline fun newAnnotatedSetBinder(binder: Binder): 86 | KotlinMultibinder { 87 | return newRealSetBinder(binder, annotatedKey()) 88 | } 89 | 90 | @PublishedApi internal fun newRealSetBinder(binder: Binder, key: Key): 91 | RealKotlinMultibinder { 92 | val skippingBinder = binder.skipSources(RealKotlinMultibinder::class.java, 93 | KotlinMultibinder::class.java, 94 | Companion::class.java) 95 | val multibinder = Multibinder.newSetBinder(skippingBinder, key) 96 | val kotlinMultibinder = RealKotlinMultibinder(multibinder, key) 97 | skippingBinder.install(kotlinMultibinder) 98 | return kotlinMultibinder 99 | } 100 | } 101 | } 102 | 103 | internal class RealKotlinMultibinder(private val delegate: Multibinder, key: Key) : 104 | KotlinMultibinder, KotlinModule() { 105 | private val elementType = key.typeLiteral 106 | private val setKey = setKeyFor(key) 107 | private val collectionOfProvidersKey = setKey.ofType(collectionOfProvidersOf(elementType)) 108 | private val collectionOfJakartaProvidersKey = setKey 109 | .ofType(collectionOfJakartaProvidersOf(elementType)) 110 | private val mutableCollectionOfProvidersKey = setKey 111 | .ofType(mutableCollectionOfProvidersOf(elementType)) 112 | private val mutableCollectionOfJakartaProvidersKey = setKey 113 | .ofType(mutableCollectionOfJakartaProvidersOf(elementType)) 114 | private val setName = RealElement.nameOf(key) 115 | 116 | private fun setKeyFor(key: Key): Key> { 117 | return if (key.annotation == null && key.annotationType == null) { 118 | Key.get(setOf(elementType)) 119 | } else if (key.annotation != null) { 120 | Key.get(setOf(elementType), key.annotation) 121 | } else { 122 | Key.get(setOf(elementType), key.annotationType) 123 | } 124 | } 125 | 126 | override fun configure() { 127 | bind(collectionOfProvidersKey).to(mutableCollectionOfProvidersKey) 128 | bind(collectionOfJakartaProvidersKey).to(mutableCollectionOfJakartaProvidersKey) 129 | } 130 | 131 | override fun addBinding(): KotlinLinkedBindingBuilder { 132 | return KotlinLinkedBindingBuilderImpl(delegate.addBinding()) 133 | } 134 | 135 | override fun permitDuplicates(): KotlinMultibinder { 136 | delegate.permitDuplicates() 137 | return this 138 | } 139 | 140 | fun getKeyForNewItem(): Key { 141 | return Key.get(elementType, RealElement(setName, MULTIBINDER, "")) 142 | } 143 | 144 | // Prevents the module from being installed multiple times. 145 | override fun equals(other: Any?): Boolean { 146 | if (this === other) return true 147 | if (other?.javaClass != javaClass) return false 148 | 149 | other as RealKotlinMultibinder<*> 150 | 151 | if (setKey != other.setKey) return false 152 | 153 | return true 154 | } 155 | 156 | override fun hashCode(): Int { 157 | return setKey.hashCode() 158 | } 159 | 160 | override fun toString(): String { 161 | return (if (setName.isEmpty()) "" else setName + " ") + 162 | "KotlinMultibinder<" + elementType + ">" 163 | } 164 | } 165 | 166 | @Suppress("UNCHECKED_CAST") 167 | internal fun setOf(elementType: TypeLiteral): TypeLiteral> { 168 | return TypeLiteral.get(KotlinTypes.setOf(elementType.type)) as TypeLiteral> 169 | } 170 | 171 | @Suppress("UNCHECKED_CAST") 172 | internal fun collectionOf(elementType: TypeLiteral): TypeLiteral> { 173 | return TypeLiteral.get(KotlinTypes.collectionOf(elementType.type)) 174 | as TypeLiteral> 175 | } 176 | 177 | @Suppress("UNCHECKED_CAST") 178 | internal fun providerOf(elementType: TypeLiteral): TypeLiteral> { 179 | return TypeLiteral.get(Types.providerOf(elementType.type)) 180 | as TypeLiteral> 181 | } 182 | 183 | @Suppress("UNCHECKED_CAST") 184 | internal fun collectionOfProvidersOf(elementType: TypeLiteral): 185 | TypeLiteral>> { 186 | return TypeLiteral.get(KotlinTypes.collectionOf(Types.providerOf(elementType.type))) 187 | as TypeLiteral>> 188 | } 189 | 190 | @Suppress("UNCHECKED_CAST") 191 | internal fun collectionOfJakartaProvidersOf(elementType: TypeLiteral): 192 | TypeLiteral>> { 193 | return TypeLiteral.get(KotlinTypes.collectionOf(Types.jakartaProviderOf(elementType.type))) 194 | as TypeLiteral>> 195 | } 196 | 197 | @Suppress("UNCHECKED_CAST") 198 | internal fun mutableSetOf(elementType: TypeLiteral): TypeLiteral> { 199 | return TypeLiteral.get(KotlinTypes.mutableSetOf(elementType.type)) 200 | as TypeLiteral> 201 | } 202 | 203 | @Suppress("UNCHECKED_CAST") 204 | internal fun mutableCollectionOf(elementType: TypeLiteral): TypeLiteral> { 205 | return TypeLiteral.get(KotlinTypes.mutableCollectionOf(elementType.type)) 206 | as TypeLiteral> 207 | } 208 | 209 | @Suppress("UNCHECKED_CAST") 210 | internal fun jakartaProviderOf(elementType: TypeLiteral): TypeLiteral> { 211 | return TypeLiteral.get(Types.jakartaProviderOf(elementType.type)) 212 | as TypeLiteral> 213 | } 214 | 215 | @Suppress("UNCHECKED_CAST") 216 | internal fun mutableCollectionOfProvidersOf(elementType: TypeLiteral): 217 | TypeLiteral>> { 218 | return TypeLiteral.get(KotlinTypes.mutableCollectionOf(Types.providerOf(elementType.type))) 219 | as TypeLiteral>> 220 | } 221 | 222 | @Suppress("UNCHECKED_CAST") 223 | internal fun mutableCollectionOfJakartaProvidersOf(elementType: TypeLiteral): 224 | TypeLiteral>> { 225 | return TypeLiteral.get(KotlinTypes.mutableCollectionOf(Types.jakartaProviderOf(elementType.type))) 226 | as TypeLiteral>> 227 | } 228 | -------------------------------------------------------------------------------- /kotlin-guice/src/test/kotlin/dev/misfitlabs/kotlinguice4/InjectorExtensionsSpec.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4 19 | 20 | import com.google.inject.ConfigurationException 21 | import com.google.inject.Guice 22 | import com.google.inject.multibindings.MapBinder 23 | import com.google.inject.multibindings.Multibinder 24 | import com.google.inject.multibindings.OptionalBinder 25 | import com.google.inject.spi.InstanceBinding 26 | import jakarta.inject.Provider 27 | import java.util.concurrent.Callable 28 | import org.amshove.kluent.shouldBe 29 | import org.amshove.kluent.shouldBeInstanceOf 30 | import org.amshove.kluent.shouldEqual 31 | import org.amshove.kluent.shouldNotBeNull 32 | import org.amshove.kluent.shouldNotThrow 33 | import org.amshove.kluent.shouldThrow 34 | import org.spekframework.spek2.Spek 35 | import org.spekframework.spek2.style.specification.describe 36 | 37 | /** 38 | * @author John Leacox 39 | */ 40 | object InjectorExtensionsSpec : Spek({ 41 | beforeEachTest { 42 | StaticInjectionObj.reset() 43 | } 44 | 45 | describe("InjectorExtensions") { 46 | describe("#getMembersInjector") { 47 | it("returns a members injector that injects member fields") { 48 | val injector = Guice.createInjector(object : KotlinModule() { 49 | override fun configure() { 50 | bind().toInstance("Hello World") 51 | bind() 52 | .annotatedWith() 53 | .toInstance("Annotated Hello World") 54 | } 55 | }) 56 | 57 | val membersInjection = MembersInjection() 58 | val membersInjector = injector.getMembersInjector() 59 | membersInjector.injectMembers(membersInjection) 60 | 61 | membersInjection.memberInjectionSite shouldEqual "Hello World" 62 | membersInjection.annotatedMemberInjectionSite shouldEqual "Annotated Hello World" 63 | } 64 | } 65 | 66 | describe("#getBinding") { 67 | it("returns a binding for a simple type") { 68 | val injector = Guice.createInjector(object : KotlinModule() { 69 | override fun configure() { 70 | kotlinBinder.requireExplicitBindings() 71 | bind().to() 72 | } 73 | }) 74 | 75 | val getBinding = { injector.getBinding() } 76 | getBinding shouldNotThrow ConfigurationException::class 77 | getBinding().shouldNotBeNull() 78 | } 79 | 80 | it("returns a binding for a complex type") { 81 | val injector = Guice.createInjector(object : KotlinModule() { 82 | override fun configure() { 83 | kotlinBinder.requireExplicitBindings() 84 | bind>().to>() 85 | } 86 | }) 87 | 88 | val getBinding = { injector.getBinding>() } 89 | getBinding shouldNotThrow ConfigurationException::class 90 | 91 | val binding = getBinding() 92 | binding.key shouldEqual key>() 93 | } 94 | 95 | it("returns a binding for an implicit binding") { 96 | val injector = Guice.createInjector(object : KotlinModule() { 97 | override fun configure() {} 98 | }) 99 | 100 | val getBinding = { injector.getBinding() } 101 | getBinding shouldNotThrow ConfigurationException::class 102 | getBinding().shouldNotBeNull() 103 | } 104 | 105 | it("throws a ConfigurationException for a binding that cannot be found") { 106 | val injector = Guice.createInjector(object : KotlinModule() { 107 | override fun configure() { 108 | kotlinBinder.requireExplicitBindings() 109 | bind().toProvider() 110 | } 111 | }) 112 | 113 | val getBinding = { injector.getBinding() } 114 | 115 | getBinding shouldThrow ConfigurationException::class 116 | } 117 | } 118 | 119 | describe("#findBindingsByType") { 120 | it("returns all bindings for the given type across containers types") { 121 | val injector = Guice.createInjector(object : KotlinModule() { 122 | override fun configure() { 123 | Multibinder.newSetBinder(binder(), String::class.java) 124 | .addBinding() 125 | .toInstance("A") 126 | 127 | MapBinder.newMapBinder(binder(), String::class.java, String::class.java) 128 | .addBinding("b") 129 | .toInstance("B") 130 | 131 | OptionalBinder.newOptionalBinder(binder(), String::class.java) 132 | .setDefault() 133 | .toInstance("C") 134 | } 135 | }) 136 | 137 | // OptionalBinder adds Provider bindings, so must filter to instance bindings 138 | val bindings = injector.findBindingsByType() 139 | .filterIsInstance>() 140 | 141 | bindings[0].instance shouldEqual "A" 142 | bindings[1].instance shouldEqual "B" 143 | bindings[2].instance shouldEqual "C" 144 | } 145 | } 146 | 147 | describe("#getProvider") { 148 | it("returns a provider for a simple type") { 149 | val injector = Guice.createInjector(object : KotlinModule() { 150 | override fun configure() { 151 | kotlinBinder.requireExplicitBindings() 152 | bind().to() 153 | } 154 | }) 155 | 156 | val provider = injector.getProvider() 157 | provider shouldBeInstanceOf Provider::class 158 | (provider is Provider) shouldBe true 159 | provider.get().get() shouldEqual "Impl of A" 160 | } 161 | 162 | it("returns a provider for a complex type") { 163 | val injector = Guice.createInjector(object : KotlinModule() { 164 | override fun configure() { 165 | kotlinBinder.requireExplicitBindings() 166 | bind>().to() 167 | } 168 | }) 169 | 170 | val provider = injector.getProvider>() 171 | provider shouldBeInstanceOf Provider::class 172 | (provider is Provider>) shouldBe true 173 | provider.get().call().get() shouldEqual "Impl of A" 174 | } 175 | 176 | it("returns a provider for an implicit binding") { 177 | val injector = Guice.createInjector(object : KotlinModule() { 178 | override fun configure() {} 179 | }) 180 | 181 | val provider = injector.getProvider() 182 | provider shouldBeInstanceOf Provider::class 183 | (provider is Provider) shouldBe true 184 | provider.get().get() shouldEqual "Impl of A" 185 | } 186 | 187 | it("throws a ConfigurationException for a provider that cannot be found") { 188 | val injector = Guice.createInjector(object : KotlinModule() { 189 | override fun configure() { 190 | kotlinBinder.requireExplicitBindings() 191 | bind().toProvider() 192 | } 193 | }) 194 | 195 | val getProvider = { injector.getProvider() } 196 | 197 | getProvider shouldThrow ConfigurationException::class 198 | } 199 | } 200 | 201 | describe("#getInstance") { 202 | it("returns an instance for a simple type") { 203 | val injector = Guice.createInjector(object : KotlinModule() { 204 | override fun configure() { 205 | kotlinBinder.requireExplicitBindings() 206 | bind().to() 207 | } 208 | }) 209 | 210 | val instance = injector.getInstance() 211 | instance.get() shouldEqual "Impl of A" 212 | } 213 | 214 | it("returns an instance for a complex type") { 215 | val injector = Guice.createInjector(object : KotlinModule() { 216 | override fun configure() { 217 | kotlinBinder.requireExplicitBindings() 218 | bind>().to() 219 | } 220 | }) 221 | 222 | val instance = injector.getInstance>() 223 | instance.call().get() shouldEqual "Impl of A" 224 | } 225 | 226 | it("returns an instance for an implicit binding") { 227 | val injector = Guice.createInjector(object : KotlinModule() { 228 | override fun configure() {} 229 | }) 230 | 231 | val instance = injector.getInstance() 232 | instance.get() shouldEqual "Impl of A" 233 | } 234 | 235 | it("throws a ConfigurationException for an instance that cannot be found") { 236 | val injector = Guice.createInjector(object : KotlinModule() { 237 | override fun configure() { 238 | kotlinBinder.requireExplicitBindings() 239 | bind().toProvider() 240 | } 241 | }) 242 | 243 | val getInstance = { injector.getInstance() } 244 | getInstance shouldThrow ConfigurationException::class 245 | } 246 | } 247 | } 248 | }) 249 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /kotlin-guice/src/test/kotlin/dev/misfitlabs/kotlinguice4/multibindings/KotlinMultibinderSpec.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package dev.misfitlabs.kotlinguice4.multibindings 18 | 19 | import com.google.inject.Guice 20 | import com.google.inject.Key 21 | import com.google.inject.ProvisionException 22 | import com.google.inject.TypeLiteral 23 | import com.google.inject.multibindings.ProvidesIntoSet 24 | import com.google.inject.name.Names 25 | import com.google.inject.spi.ElementSource 26 | import com.google.inject.util.Providers 27 | import dev.misfitlabs.kotlinguice4.KotlinModule 28 | import java.util.concurrent.Callable 29 | import org.amshove.kluent.shouldEqual 30 | import org.amshove.kluent.shouldThrow 31 | import org.spekframework.spek2.Spek 32 | import org.spekframework.spek2.style.specification.describe 33 | 34 | /** 35 | * @author John Leacox 36 | */ 37 | object KotlinMultibinderSpec : Spek({ 38 | describe("KotlinMultibinder") { 39 | it("skips the multibinder classes in the source trace") { 40 | val outerModule = object : KotlinModule() { 41 | override fun configure() { 42 | val aBinder = KotlinMultibinder.newSetBinder(kotlinBinder) 43 | aBinder.addBinding().to() 44 | } 45 | } 46 | 47 | val injector = Guice.createInjector(outerModule) 48 | 49 | val source = injector.getBinding(Key.get(object : TypeLiteral>() {})) 50 | .source as ElementSource 51 | val stackTraceElement = source.declaringSource as StackTraceElement 52 | 53 | stackTraceElement.className shouldEqual outerModule::class.java.name 54 | } 55 | 56 | describe("#newSetBinder") { 57 | it("binds simple types into a set") { 58 | val injector = Guice.createInjector(object : KotlinModule() { 59 | override fun configure() { 60 | val aBinder = KotlinMultibinder.newSetBinder(kotlinBinder) 61 | aBinder.addBinding().to() 62 | aBinder.addBinding().to() 63 | 64 | val callableBinder = KotlinMultibinder.newSetBinder>(kotlinBinder) 65 | callableBinder.addBinding().to() 66 | } 67 | }) 68 | 69 | val setContainer = injector.getInstance(Key 70 | .get(object : TypeLiteral>() {})) 71 | 72 | setContainer.set.size shouldEqual 2 73 | } 74 | 75 | it("binds complex types into a set") { 76 | val injector = Guice.createInjector(object : KotlinModule() { 77 | override fun configure() { 78 | val callableBinder = KotlinMultibinder.newSetBinder>(kotlinBinder) 79 | callableBinder.addBinding().to() 80 | callableBinder.addBinding().to>() 81 | 82 | val aBinder = KotlinMultibinder.newSetBinder(kotlinBinder) 83 | aBinder.addBinding().to() 84 | aBinder.addBinding().to() 85 | } 86 | }) 87 | 88 | val setContainer = injector.getInstance(Key 89 | .get(object : TypeLiteral>>() {})) 90 | 91 | setContainer.set.size shouldEqual 2 92 | } 93 | 94 | it("forbids duplicate elements") { 95 | val module1 = object : KotlinModule() { 96 | override fun configure() { 97 | val stringBinder = KotlinMultibinder.newSetBinder(kotlinBinder) 98 | stringBinder.addBinding().toProvider(Providers.of("Hello World")) 99 | } 100 | } 101 | val module2 = object : KotlinModule() { 102 | override fun configure() { 103 | val stringBinder = KotlinMultibinder.newSetBinder(kotlinBinder) 104 | stringBinder.addBinding().toInstance("Hello World") 105 | } 106 | } 107 | 108 | val injector = Guice.createInjector(module1, module2) 109 | 110 | val getInstance = { 111 | injector.getInstance(Key.get(object : TypeLiteral>() {})) 112 | } 113 | getInstance shouldThrow ProvisionException::class 114 | } 115 | 116 | it("silently ignores duplicates when using permitDuplicates") { 117 | val module1 = object : KotlinModule() { 118 | override fun configure() { 119 | val stringBinder = KotlinMultibinder.newSetBinder(kotlinBinder) 120 | stringBinder.addBinding().toProvider(Providers.of("Hello World")) 121 | } 122 | } 123 | val module2 = object : KotlinModule() { 124 | override fun configure() { 125 | val stringBinder = KotlinMultibinder.newSetBinder(kotlinBinder) 126 | stringBinder.permitDuplicates() 127 | stringBinder.addBinding().toInstance("Hello World") 128 | } 129 | } 130 | 131 | val injector = Guice.createInjector(module1, module2) 132 | 133 | val set = injector.getInstance(Key.get(object : TypeLiteral>() {})) 134 | set.size shouldEqual 1 135 | set shouldEqual setOf("Hello World") 136 | } 137 | } 138 | 139 | describe("#newAnnotatedSetBinder") { 140 | it("binds simple types into an annotated set") { 141 | val injector = Guice.createInjector(object : KotlinModule() { 142 | override fun configure() { 143 | val aBinder = KotlinMultibinder 144 | .newAnnotatedSetBinder(kotlinBinder) 145 | aBinder.addBinding().to() 146 | aBinder.addBinding().to() 147 | 148 | val unannotatedABinder = KotlinMultibinder.newSetBinder(kotlinBinder) 149 | unannotatedABinder.addBinding().to() 150 | } 151 | }) 152 | 153 | val set = injector.getInstance(Key 154 | .get(object : TypeLiteral>() {}, Annotated::class.java)) 155 | 156 | set.size shouldEqual 2 157 | } 158 | 159 | it("binds simple types into a named set") { 160 | val named = Names.named("A Name") 161 | 162 | val injector = Guice.createInjector(object : KotlinModule() { 163 | override fun configure() { 164 | val aBinder = KotlinMultibinder 165 | .newAnnotatedSetBinder(kotlinBinder, named) 166 | aBinder.addBinding().to() 167 | aBinder.addBinding().to() 168 | 169 | val unannotatedABinder = KotlinMultibinder.newSetBinder(kotlinBinder) 170 | unannotatedABinder.addBinding().to() 171 | } 172 | }) 173 | 174 | val set = injector.getInstance(Key.get(object : TypeLiteral>() {}, named)) 175 | 176 | set.size shouldEqual 2 177 | } 178 | 179 | it("binds complex types into an annotated set") { 180 | val injector = Guice.createInjector(object : KotlinModule() { 181 | override fun configure() { 182 | val callableBinder = KotlinMultibinder 183 | .newAnnotatedSetBinder, Annotated>(kotlinBinder) 184 | callableBinder.addBinding().to() 185 | callableBinder.addBinding().to>() 186 | 187 | val unannotatedCallableBinder = KotlinMultibinder 188 | .newSetBinder>(kotlinBinder) 189 | unannotatedCallableBinder.addBinding().to() 190 | } 191 | }) 192 | 193 | val set = injector.getInstance(Key 194 | .get(object : TypeLiteral>>() {}, 195 | Annotated::class.java)) 196 | 197 | set.size shouldEqual 2 198 | } 199 | } 200 | 201 | describe("@ProvidesIntoSet") { 202 | it("binds simple types into a set") { 203 | val injector = Guice.createInjector(object : KotlinModule() { 204 | override fun configure() { 205 | install(KotlinMultibindingsScanner.asModule()) 206 | } 207 | 208 | @ProvidesIntoSet 209 | fun provideAImpl(): A { 210 | return AImpl() 211 | } 212 | 213 | @ProvidesIntoSet 214 | fun provideB(): A { 215 | return B() 216 | } 217 | 218 | @ProvidesIntoSet 219 | fun provideACallable(): Callable { 220 | return ACallable() 221 | } 222 | }) 223 | 224 | val setContainer = injector.getInstance(Key 225 | .get(object : TypeLiteral>() {})) 226 | 227 | setContainer.set.size shouldEqual 2 228 | } 229 | 230 | it("binds complex types into a set") { 231 | val injector = Guice.createInjector(object : KotlinModule() { 232 | override fun configure() { 233 | install(KotlinMultibindingsScanner.asModule()) 234 | } 235 | 236 | @ProvidesIntoSet 237 | fun provideACallable(): Callable { 238 | return ACallable() 239 | } 240 | 241 | @ProvidesIntoSet 242 | fun provideTCallable(): Callable { 243 | return TCallable() 244 | } 245 | 246 | @ProvidesIntoSet 247 | fun provideAImpl(): A { 248 | return AImpl() 249 | } 250 | }) 251 | 252 | val setContainer = injector.getInstance(Key 253 | .get(object : TypeLiteral>>() {})) 254 | 255 | setContainer.set.size shouldEqual 2 256 | } 257 | 258 | it("binds simple types into an annotated set") { 259 | val injector = Guice.createInjector(object : KotlinModule() { 260 | override fun configure() { 261 | install(KotlinMultibindingsScanner.asModule()) 262 | } 263 | 264 | @ProvidesIntoSet 265 | @Annotated 266 | fun provideAImpl(): A { 267 | return AImpl() 268 | } 269 | 270 | @ProvidesIntoSet 271 | @Annotated 272 | fun provideB(): A { 273 | return B() 274 | } 275 | 276 | @ProvidesIntoSet 277 | fun provideACallable(): Callable { 278 | return ACallable() 279 | } 280 | }) 281 | 282 | val set = injector.getInstance(Key 283 | .get(object : TypeLiteral>() {}, 284 | Annotated::class.java)) 285 | 286 | set.size shouldEqual 2 287 | } 288 | 289 | it("binds complex types into an annotated set") { 290 | val injector = Guice.createInjector(object : KotlinModule() { 291 | override fun configure() { 292 | install(KotlinMultibindingsScanner.asModule()) 293 | } 294 | 295 | @ProvidesIntoSet 296 | @Annotated 297 | fun provideAnnotatedACallable(): Callable { 298 | return ACallable() 299 | } 300 | 301 | @ProvidesIntoSet 302 | @Annotated 303 | fun provideAnnotatedTCallable(): Callable { 304 | return TCallable() 305 | } 306 | 307 | @ProvidesIntoSet 308 | fun provideUnannotatedACallable(): Callable { 309 | return ACallable() 310 | } 311 | }) 312 | 313 | val set = injector.getInstance(Key 314 | .get(object : TypeLiteral>>() {}, 315 | Annotated::class.java)) 316 | 317 | set.size shouldEqual 2 318 | } 319 | } 320 | } 321 | }) 322 | -------------------------------------------------------------------------------- /kotlin-guice/src/test/kotlin/dev/misfitlabs/kotlinguice4/KotlinPrivateBinderSpec.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4 19 | 20 | import com.google.inject.CreationException 21 | import com.google.inject.Guice 22 | import com.google.inject.spi.ElementSource 23 | import dev.misfitlabs.kotlinguice4.binder.annotatedWith 24 | import dev.misfitlabs.kotlinguice4.binder.to 25 | import jakarta.inject.Inject 26 | import jakarta.inject.Singleton 27 | import java.util.concurrent.Callable 28 | import org.amshove.kluent.shouldBe 29 | import org.amshove.kluent.shouldBeInstanceOf 30 | import org.amshove.kluent.shouldEqual 31 | import org.amshove.kluent.shouldNotBe 32 | import org.amshove.kluent.shouldThrow 33 | import org.spekframework.spek2.Spek 34 | import org.spekframework.spek2.style.specification.describe 35 | 36 | /** 37 | * @author Brian van de Boogaard. 38 | */ 39 | object KotlinPrivateBinderSpec : Spek({ 40 | 41 | describe("KotlinPrivateModule") { 42 | 43 | beforeEachTest { 44 | StaticInjectionObj.reset() 45 | } 46 | 47 | it("throws a CreationException when a non-exposed binding is attempted to be used") { 48 | val injector = { 49 | Guice.createInjector(object : KotlinModule() { 50 | override fun configure() { 51 | install(object : KotlinPrivateModule() { 52 | override fun configure() { 53 | kotlinBinder.bind().to() 54 | 55 | // Do not expose A 56 | } 57 | }) 58 | 59 | kotlinBinder.bind() // Bind a type that requires an A 60 | } 61 | }) 62 | } 63 | 64 | injector shouldThrow CreationException::class 65 | } 66 | 67 | it("allows getting an instance of the exposed binding") { 68 | val injector = Guice.createInjector(object : KotlinModule() { 69 | override fun configure() { 70 | install(object : KotlinPrivateModule() { 71 | override fun configure() { 72 | kotlinBinder.bind().to() 73 | 74 | kotlinBinder.expose() 75 | } 76 | }) 77 | 78 | kotlinBinder.bind() // Bind a type that requires an A 79 | } 80 | }) 81 | 82 | val a = injector.getInstance() 83 | a.get() shouldBe "Impl of A" 84 | } 85 | 86 | it("injects an exposed binding into classes using the binding") { 87 | val injector = Guice.createInjector(object : KotlinModule() { 88 | override fun configure() { 89 | install(object : KotlinPrivateModule() { 90 | override fun configure() { 91 | kotlinBinder.bind().to() 92 | 93 | kotlinBinder.expose() 94 | } 95 | }) 96 | 97 | kotlinBinder.bind() // Bind a type that requires an A 98 | } 99 | }) 100 | 101 | val aContainer = injector.getInstance() 102 | aContainer.a.get() shouldBe "Impl of A" 103 | } 104 | 105 | it("injects a private binding into an exposed class configured in the same module") { 106 | val injector = Guice.createInjector(object : KotlinModule() { 107 | override fun configure() { 108 | install(object : KotlinPrivateModule() { 109 | override fun configure() { 110 | kotlinBinder.bind().to() 111 | kotlinBinder.bind() // Bind a type that requires an A 112 | 113 | kotlinBinder.expose() // Only expose the container 114 | } 115 | }) 116 | } 117 | }) 118 | 119 | val aContainer = injector.getInstance() 120 | aContainer.a.get() shouldBe "Impl of A" 121 | } 122 | 123 | it("skips the KotlinPrivateBinder class in the source trace") { 124 | val outerModule = object : KotlinPrivateModule() { 125 | override fun configure() { 126 | kotlinBinder.bind().to() 127 | kotlinBinder.expose() 128 | } 129 | } 130 | 131 | val injector = Guice.createInjector(outerModule) 132 | 133 | val source = injector.getBinding(A::class.java).source as ElementSource 134 | val stackTraceElement = source.declaringSource as StackTraceElement 135 | 136 | stackTraceElement.className shouldEqual outerModule::class.java.name 137 | } 138 | 139 | describe("#bindScope") { 140 | it("binds a custom scope using a scope annotation type parameter") { 141 | val scope = TestScope() 142 | val injector = Guice.createInjector(object : KotlinPrivateModule() { 143 | override fun configure() { 144 | kotlinBinder.bindScope(scope) 145 | kotlinBinder.bind().to().`in`() 146 | 147 | kotlinBinder.expose() 148 | } 149 | }) 150 | 151 | val a = injector.getInstance(A::class.java) 152 | a shouldBe injector.getInstance(A::class.java) 153 | 154 | scope.reset() 155 | 156 | a shouldNotBe injector.getInstance(A::class.java) 157 | } 158 | } 159 | 160 | describe("#bind") { 161 | it("binds source using a type parameter") { 162 | val injector = Guice.createInjector(object : KotlinPrivateModule() { 163 | override fun configure() { 164 | kotlinBinder.bind().to(AImpl::class.java) 165 | 166 | kotlinBinder.expose() 167 | } 168 | }) 169 | 170 | val a = injector.getInstance(A::class.java) 171 | 172 | a.get() shouldEqual "Impl of A" 173 | } 174 | 175 | it("binds a complex source using a type parameter") { 176 | val injector = Guice.createInjector(object : KotlinPrivateModule() { 177 | override fun configure() { 178 | kotlinBinder.bind>().to(ACallable::class.java) 179 | 180 | kotlinBinder.expose>() 181 | } 182 | }) 183 | 184 | val a = injector.getInstance(key>()) 185 | a.call().get() shouldEqual "Impl of A" 186 | } 187 | 188 | it("binds to a target using a type parameter") { 189 | val injector = Guice.createInjector(object : KotlinPrivateModule() { 190 | override fun configure() { 191 | kotlinBinder.bind().to() 192 | 193 | kotlinBinder.expose() 194 | } 195 | }) 196 | 197 | val a = injector.getInstance(A::class.java) 198 | 199 | a.get() shouldEqual "Impl of A" 200 | } 201 | 202 | it("binds to a complex target using a type parameter") { 203 | val injector = Guice.createInjector(object : KotlinPrivateModule() { 204 | override fun configure() { 205 | kotlinBinder.bind>().to>() 206 | 207 | kotlinBinder.expose>() 208 | } 209 | }) 210 | 211 | val callable = injector.getInstance(key>()) 212 | callable.call() shouldEqual null 213 | } 214 | 215 | it("binds with an annotation using a type parameter") { 216 | val injector = Guice.createInjector(object : KotlinPrivateModule() { 217 | override fun configure() { 218 | kotlinBinder.bind().to() 219 | kotlinBinder.bind().annotatedWith().to() 220 | 221 | kotlinBinder.expose().annotatedWith() 222 | } 223 | }) 224 | 225 | val a = injector.getInstance(annotatedKey()) 226 | 227 | a.get() shouldEqual "Impl of A" 228 | } 229 | 230 | it("binds to a provider using a type parameter") { 231 | val injector = Guice.createInjector(object : KotlinPrivateModule() { 232 | override fun configure() { 233 | kotlinBinder.bind().toProvider() 234 | 235 | kotlinBinder.expose() 236 | } 237 | }) 238 | 239 | val a = injector.getInstance(A::class.java) 240 | 241 | a shouldBeInstanceOf B::class.java 242 | } 243 | 244 | it("binds to a complex provider using a type parameter") { 245 | val injector = Guice.createInjector(object : KotlinPrivateModule() { 246 | override fun configure() { 247 | kotlinBinder.bind>().toProvider>>() 248 | 249 | kotlinBinder.expose>() 250 | } 251 | }) 252 | 253 | injector.getInstance(key>()) 254 | } 255 | 256 | it("binds in a scope") { 257 | val injector = Guice.createInjector(object : KotlinPrivateModule() { 258 | override fun configure() { 259 | kotlinBinder.bind().to().`in`() 260 | 261 | kotlinBinder.expose() 262 | } 263 | }) 264 | 265 | val a = injector.getInstance(A::class.java) 266 | a shouldBe injector.getInstance(A::class.java) 267 | } 268 | 269 | it("binds wildcard types") { 270 | val injector = Guice.createInjector(object : KotlinPrivateModule() { 271 | override fun configure() { 272 | kotlinBinder.bind>().to() 273 | 274 | kotlinBinder.expose>() 275 | } 276 | }) 277 | 278 | val callable: Callable<*> = injector.getInstance(key>()) 279 | callable shouldBeInstanceOf ACallable::class.java 280 | } 281 | } 282 | 283 | describe("#bindConstant") { 284 | it("binds to a target using a type parameter and annotation") { 285 | class ClassWithConstant @Inject constructor(@Annotated val constant: Class) 286 | 287 | val injector = Guice.createInjector(object : KotlinPrivateModule() { 288 | override fun configure() { 289 | kotlinBinder.bindConstant() 290 | .annotatedWith() 291 | .to>() 292 | 293 | kotlinBinder.expose(annotatedKey, Annotated>()) 294 | } 295 | }) 296 | 297 | val classWithConstant = injector.getInstance(ClassWithConstant::class.java) 298 | classWithConstant.constant shouldEqual Iterator::class.java 299 | } 300 | } 301 | 302 | describe("#requestStaticInjection") { 303 | it("injects static fields") { 304 | Guice.createInjector(object : KotlinPrivateModule() { 305 | override fun configure() { 306 | kotlinBinder.bind().toInstance("Statically Injected") 307 | requestStaticInjection() 308 | } 309 | }) 310 | 311 | StaticInjectionObj.staticInjectionSite shouldEqual "Statically Injected" 312 | } 313 | } 314 | 315 | describe("#getProvider") { 316 | it("get a provider for a simple type") { 317 | Guice.createInjector(object : KotlinPrivateModule() { 318 | override fun configure() { 319 | kotlinBinder.bind().to() 320 | val provider = kotlinBinder.getProvider() 321 | provider.toString() shouldEqual "Provider" 322 | } 323 | }) 324 | } 325 | 326 | it("get a provider for an annotated key") { 327 | Guice.createInjector(object : KotlinPrivateModule() { 328 | override fun configure() { 329 | kotlinBinder.bind>().to>() 330 | val provider = kotlinBinder.getProvider>() 331 | provider.toString() shouldEqual 332 | "Provider>" 333 | } 334 | }) 335 | } 336 | } 337 | 338 | describe("#getMembersInjector") { 339 | it("injects member fields") { 340 | Guice.createInjector(object : KotlinPrivateModule() { 341 | override fun configure() { 342 | val membersInjector = getMembersInjector() 343 | membersInjector.toString() shouldEqual 344 | "MembersInjector" 345 | } 346 | }) 347 | } 348 | } 349 | } 350 | }) 351 | -------------------------------------------------------------------------------- /kotlin-guice/src/main/kotlin/dev/misfitlabs/kotlinguice4/multibindings/KotlinMapBinder.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package dev.misfitlabs.kotlinguice4.multibindings 18 | 19 | import com.google.inject.Binder 20 | import com.google.inject.Key 21 | import com.google.inject.Provider 22 | import com.google.inject.TypeLiteral 23 | import com.google.inject.multibindings.MapBinder 24 | import com.google.inject.util.Types 25 | import dev.misfitlabs.kotlinguice4.KotlinModule 26 | import dev.misfitlabs.kotlinguice4.binder.KotlinLinkedBindingBuilder 27 | import dev.misfitlabs.kotlinguice4.multibindings.Element.Type.MAPBINDER 28 | import dev.misfitlabs.kotlinguice4.typeLiteral 29 | import kotlin.collections.Map.Entry 30 | 31 | /** 32 | * A wrapper of [MapBinder] that enhances the binding DSL to allow binding using reified type 33 | * parameters. 34 | * 35 | * By using this class instead of [MapBinder] you can replace the following lines: 36 | * ``` 37 | * val mapbinder = MapBinder.newMapBinder(binder(), String::class.java, Snack::class.java) 38 | * mapbinder.addBinding("twix").to(Twix::class.java) 39 | * ``` 40 | * with 41 | * ``` 42 | * val mapbinder = KotlinMapBinder.newMapBinder(kotlinBinder) 43 | * mapbinder.addBinding("twix").to() 44 | * ``` 45 | * 46 | * @see MapBinder 47 | * @author John Leacox 48 | * @since 1.0 49 | */ 50 | interface KotlinMapBinder { 51 | /** 52 | * Configures the bound set to silently discard duplicate elements. 53 | * 54 | * @see MapBinder.permitDuplicates 55 | */ 56 | fun permitDuplicates(): KotlinMapBinder 57 | 58 | /** 59 | * Returns a binding builder used to add a new element in the set. 60 | * 61 | * @see MapBinder.addBinding 62 | */ 63 | fun addBinding(key: K): KotlinLinkedBindingBuilder 64 | 65 | companion object { 66 | /** 67 | * Returns a new mapbinder that collects entries of [K]/[V] in a {@link Map}. 68 | */ 69 | inline fun newMapBinder(binder: Binder): KotlinMapBinder { 70 | val keyType = typeLiteral() 71 | val valueType = typeLiteral() 72 | val mapBinder = MapBinder.newMapBinder(binder, keyType, valueType) 73 | return newRealMapBinder(binder, 74 | mapBinder, 75 | keyType, 76 | valueType, 77 | Key.get(mapOf(keyType, valueType))) 78 | } 79 | /** 80 | * Returns a new mapbinder that collects entries of [K]/[V] in a {@link Map} that is bound 81 | * with [annotation]. 82 | */ 83 | inline fun newAnnotatedMapBinder(binder: Binder, annotation: Annotation): KotlinMapBinder { 84 | val keyType = typeLiteral() 85 | val valueType = typeLiteral() 86 | val mapBinder = MapBinder.newMapBinder(binder, keyType, valueType, annotation) 87 | return newRealMapBinder(binder, 88 | mapBinder, 89 | keyType, 90 | valueType, 91 | Key.get(mapOf(keyType, valueType), annotation)) 92 | } 93 | 94 | /** 95 | * Returns a new mapbinder that collects entries of [K]/[V] in a {@link Map} that is bound 96 | * with [TAnn]. 97 | */ 98 | inline fun 99 | newAnnotatedMapBinder(binder: Binder): KotlinMapBinder { 100 | val keyType = typeLiteral() 101 | val valueType = typeLiteral() 102 | val mapBinder = MapBinder.newMapBinder(binder, keyType, valueType, TAnn::class.java) 103 | return newRealMapBinder(binder, 104 | mapBinder, 105 | keyType, 106 | valueType, 107 | Key.get(mapOf(keyType, valueType), TAnn::class.java)) 108 | } 109 | 110 | internal fun newRealMapBinder( 111 | binder: Binder, 112 | keyType: TypeLiteral, 113 | valueTypeAndAnnotation: Key 114 | ): RealKotlinMapBinder { 115 | val valueType = valueTypeAndAnnotation.typeLiteral 116 | val mapKey = valueTypeAndAnnotation.ofType(mapOf(keyType, valueType)) 117 | val mapBinder = when { 118 | valueTypeAndAnnotation.annotation != null -> MapBinder.newMapBinder(binder, 119 | keyType, 120 | valueType, 121 | valueTypeAndAnnotation.annotation) 122 | valueTypeAndAnnotation.annotationType != null -> MapBinder.newMapBinder(binder, 123 | keyType, 124 | valueType, 125 | valueTypeAndAnnotation.annotationType) 126 | else -> MapBinder.newMapBinder(binder, keyType, valueType) 127 | } 128 | return newRealMapBinder(binder, mapBinder, keyType, valueType, mapKey) 129 | } 130 | 131 | @PublishedApi internal fun newRealMapBinder( 132 | binder: Binder, 133 | mapBinder: MapBinder, 134 | keyType: TypeLiteral, 135 | valueType: TypeLiteral, 136 | mapKey: Key> 137 | ): RealKotlinMapBinder { 138 | val skippingBinder = binder.skipSources(RealKotlinMapBinder::class.java, 139 | KotlinMapBinder::class.java, 140 | Companion::class.java) 141 | val kotlinMapBinder = RealKotlinMapBinder(mapBinder, 142 | skippingBinder, 143 | keyType, 144 | valueType, 145 | mapKey) 146 | skippingBinder.install(kotlinMapBinder) 147 | return kotlinMapBinder 148 | } 149 | } 150 | } 151 | 152 | internal class RealKotlinMapBinder( 153 | private val delegate: MapBinder, 154 | private val binder: Binder, 155 | private val keyType: TypeLiteral, 156 | private val valueType: TypeLiteral, 157 | mapKey: Key> 158 | ) : KotlinMapBinder, KotlinModule() { 159 | private val bindingSelection = MapBindingSelection(keyType, valueType, mapKey) 160 | private val setName = RealElement.nameOf(mapKey) 161 | 162 | override fun configure() { 163 | bind(bindingSelection.providerMapKey).to(bindingSelection.mutableProviderMapKey) 164 | bind(bindingSelection.jakartaProviderMapKey).to(bindingSelection.mutableJakartaProviderMapKey) 165 | 166 | bind(bindingSelection.setOfEntryOfJakartaProviderKey) 167 | .to(bindingSelection.mutableSetOfEntryOfJakartaProviderKey) 168 | 169 | bind(bindingSelection.collectionOfProviderOfEntryOfProviderKey) 170 | .to(bindingSelection.mutableCollectionOfProviderOfEntryOfProviderKey) 171 | bind(bindingSelection.collectionOfJakartaProviderOfEntryOfProviderKey) 172 | .to(bindingSelection.mutableCollectionOfJakartaProviderOfEntryOfProviderKey) 173 | } 174 | 175 | override fun permitDuplicates(): KotlinMapBinder { 176 | delegate.permitDuplicates() 177 | binder.install(KotlinMultimapModule(bindingSelection)) 178 | return this 179 | } 180 | 181 | override fun addBinding(key: K): KotlinLinkedBindingBuilder { 182 | return KotlinLinkedBindingBuilderImpl(delegate.addBinding(key)) 183 | } 184 | 185 | @Suppress("UNUSED_PARAMETER") 186 | fun getKeyForNewValue(key: K): Key { 187 | return Key.get(valueType, RealElement(setName, MAPBINDER, keyType.toString())) 188 | } 189 | 190 | // Prevents the module from being installed multiple times. 191 | override fun equals(other: Any?): Boolean { 192 | if (this === other) return true 193 | if (other?.javaClass != javaClass) return false 194 | 195 | other as RealKotlinMapBinder<*, *> 196 | 197 | if (bindingSelection != other.bindingSelection) return false 198 | 199 | return true 200 | } 201 | 202 | override fun hashCode(): Int { 203 | return bindingSelection.hashCode() 204 | } 205 | 206 | override fun toString(): String { 207 | return (if (setName.isEmpty()) "" else setName + " ") + 208 | "KotlinMapBinder<" + keyType + ", " + valueType + ">" 209 | } 210 | } 211 | 212 | @Suppress("UNCHECKED_CAST") 213 | @PublishedApi 214 | internal fun mapOf(keyType: TypeLiteral, valueType: TypeLiteral): 215 | TypeLiteral> { 216 | return TypeLiteral 217 | .get(KotlinTypes.mapOf(keyType.type, valueType.type)) as TypeLiteral> 218 | } 219 | 220 | @Suppress("UNCHECKED_CAST") 221 | internal fun mapOfProviderOf( 222 | keyType: TypeLiteral, 223 | valueType: TypeLiteral 224 | ): TypeLiteral>> { 225 | return TypeLiteral.get(KotlinTypes.mapOf(keyType.type, Types.providerOf(valueType.type))) 226 | as TypeLiteral>> 227 | } 228 | 229 | @Suppress("UNCHECKED_CAST") 230 | internal fun mapOfJakartaProviderOf(keyType: TypeLiteral, valueType: TypeLiteral): 231 | TypeLiteral>> { 232 | val providerType = Types.jakartaProviderOf(valueType.type) 233 | val type = KotlinTypes.mapOf(keyType.type, providerType) 234 | return TypeLiteral.get(type) as TypeLiteral>> 235 | } 236 | 237 | @Suppress("UNCHECKED_CAST") 238 | internal fun mapOfSetOfProviderOf(keyType: TypeLiteral, valueType: TypeLiteral): 239 | TypeLiteral>>> { 240 | return TypeLiteral.get(KotlinTypes.mapOf(keyType.type, 241 | KotlinTypes.setOf(Types.providerOf(valueType.type)))) 242 | as TypeLiteral>>> 243 | } 244 | 245 | @Suppress("UNCHECKED_CAST") 246 | internal fun mapOfSetOfJakartaProviderOf(keyType: TypeLiteral, valueType: TypeLiteral): 247 | TypeLiteral>>> { 248 | return TypeLiteral.get(KotlinTypes.mapOf(keyType.type, 249 | KotlinTypes.setOf(Types.jakartaProviderOf(valueType.type)))) 250 | as TypeLiteral>>> 251 | } 252 | 253 | @Suppress("UNCHECKED_CAST") 254 | internal fun mapOfCollectionOfProviderOf(keyType: TypeLiteral, valueType: TypeLiteral): 255 | TypeLiteral>>> { 256 | return TypeLiteral.get(KotlinTypes.mapOf(keyType.type, 257 | KotlinTypes.collectionOf(Types.providerOf(valueType.type)))) 258 | as TypeLiteral>>> 259 | } 260 | 261 | @Suppress("UNCHECKED_CAST") 262 | internal fun mapOfCollectionOfJakartaProviderOf( 263 | keyType: TypeLiteral, 264 | valueType: TypeLiteral 265 | ): TypeLiteral>>> { 266 | return TypeLiteral.get(KotlinTypes.mapOf(keyType.type, 267 | KotlinTypes.collectionOf(Types.jakartaProviderOf(valueType.type)))) 268 | as TypeLiteral>>> 269 | } 270 | 271 | @Suppress("UNCHECKED_CAST") 272 | internal fun entryOfProviderOf( 273 | keyType: TypeLiteral, 274 | valueType: TypeLiteral 275 | ): TypeLiteral>> { 276 | return TypeLiteral.get( 277 | Types.newParameterizedTypeWithOwner( 278 | Map::class.java, 279 | Entry::class.java, 280 | keyType.type, 281 | Types.providerOf(valueType.type))) as TypeLiteral>> 282 | } 283 | 284 | @Suppress("UNCHECKED_CAST") 285 | internal fun entryOfJakartaProviderOf(keyType: TypeLiteral, valueType: TypeLiteral): 286 | TypeLiteral>> { 287 | return TypeLiteral.get( 288 | Types.newParameterizedTypeWithOwner( 289 | Map::class.java, 290 | Entry::class.java, 291 | keyType.type, 292 | Types.jakartaProviderOf(valueType.type))) 293 | as TypeLiteral>> 294 | } 295 | 296 | @Suppress("UNCHECKED_CAST") 297 | internal fun mutableMapOf(keyType: TypeLiteral, valueType: TypeLiteral): 298 | TypeLiteral> { 299 | return TypeLiteral.get(KotlinTypes.mutableMapOf(keyType.type, valueType.type)) 300 | as TypeLiteral> 301 | } 302 | 303 | @Suppress("UNCHECKED_CAST") 304 | internal fun mutableMapOfProviderOf(keyType: TypeLiteral, valueType: TypeLiteral): 305 | TypeLiteral>> { 306 | return TypeLiteral.get(KotlinTypes.mutableMapOf(keyType.type, 307 | Types.providerOf(valueType.type))) as TypeLiteral>> 308 | } 309 | 310 | @Suppress("UNCHECKED_CAST") 311 | internal fun mutableMapOfJakartaProviderOf(keyType: TypeLiteral, valueType: TypeLiteral): 312 | TypeLiteral>> { 313 | return TypeLiteral.get(KotlinTypes.mutableMapOf(keyType.type, 314 | Types.jakartaProviderOf(valueType.type))) 315 | as TypeLiteral>> 316 | } 317 | 318 | @Suppress("UNCHECKED_CAST") 319 | internal fun mutableMapOfMutableSetOfProviderOf( 320 | keyType: TypeLiteral, 321 | valueType: TypeLiteral 322 | ): TypeLiteral>>> { 323 | return TypeLiteral.get(KotlinTypes.mutableMapOf(keyType.type, 324 | KotlinTypes.mutableSetOf(Types.providerOf(valueType.type)))) 325 | as TypeLiteral>>> 326 | } 327 | 328 | @Suppress("UNCHECKED_CAST") 329 | internal fun mutableMapOfMutableSetOfJakartaProviderOf( 330 | keyType: TypeLiteral, 331 | valueType: TypeLiteral 332 | ): TypeLiteral>>> { 333 | return TypeLiteral.get(KotlinTypes.mutableMapOf(keyType.type, 334 | KotlinTypes.mutableSetOf(Types.jakartaProviderOf(valueType.type)))) 335 | as TypeLiteral>>> 336 | } 337 | 338 | @Suppress("UNCHECKED_CAST") 339 | internal fun mutableMapOfMutableCollectionOfProviderOf( 340 | keyType: TypeLiteral, 341 | valueType: TypeLiteral 342 | ): TypeLiteral>>> { 343 | return TypeLiteral.get(KotlinTypes.mutableMapOf(keyType.type, 344 | KotlinTypes.mutableCollectionOf(Types.providerOf(valueType.type)))) 345 | as TypeLiteral>>> 346 | } 347 | 348 | @Suppress("UNCHECKED_CAST") 349 | internal fun mutableMapOfMutableCollectionOfJakartaProviderOf( 350 | keyType: TypeLiteral, 351 | valueType: TypeLiteral 352 | ): TypeLiteral>>> { 353 | return TypeLiteral.get(KotlinTypes.mutableMapOf(keyType.type, 354 | KotlinTypes.mutableCollectionOf(Types.jakartaProviderOf(valueType.type)))) 355 | as TypeLiteral>>> 356 | } 357 | -------------------------------------------------------------------------------- /kotlin-guice/src/test/kotlin/dev/misfitlabs/kotlinguice4/KotlinModuleSpec.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 John Leacox 3 | * Copyright (C) 2017 Brian van de Boogaard 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package dev.misfitlabs.kotlinguice4 19 | 20 | import com.google.inject.Binder 21 | import com.google.inject.CreationException 22 | import com.google.inject.Guice 23 | import com.google.inject.Key 24 | import com.google.inject.Provides 25 | import com.google.inject.name.Names 26 | import com.google.inject.spi.ElementSource 27 | import com.nhaarman.mockitokotlin2.any 28 | import com.nhaarman.mockitokotlin2.doReturn 29 | import com.nhaarman.mockitokotlin2.mock 30 | import dev.misfitlabs.kotlinguice4.binder.annotatedWith 31 | import dev.misfitlabs.kotlinguice4.binder.to 32 | import jakarta.inject.Inject 33 | import jakarta.inject.Singleton 34 | import java.util.concurrent.Callable 35 | import org.amshove.kluent.shouldBe 36 | import org.amshove.kluent.shouldBeInstanceOf 37 | import org.amshove.kluent.shouldContain 38 | import org.amshove.kluent.shouldEqual 39 | import org.amshove.kluent.shouldNotBe 40 | import org.amshove.kluent.shouldNotBeNull 41 | import org.amshove.kluent.shouldThrow 42 | import org.spekframework.spek2.Spek 43 | import org.spekframework.spek2.style.specification.describe 44 | 45 | /** 46 | * @author John Leacox 47 | */ 48 | object KotlinModuleSpec : Spek({ 49 | beforeEachTest { 50 | StaticInjectionObj.reset() 51 | } 52 | 53 | describe("KotlinModule") { 54 | it("skips the KotlinBinder class in the source trace") { 55 | val outerModule = object : KotlinModule() { 56 | override fun configure() { 57 | bind().to() 58 | } 59 | } 60 | 61 | val injector = Guice.createInjector(outerModule) 62 | 63 | val source = injector.getBinding(A::class.java).source as ElementSource 64 | val stackTraceElement = source.declaringSource as StackTraceElement 65 | 66 | stackTraceElement.className shouldEqual outerModule::class.java.name 67 | } 68 | 69 | describe("#bindScope") { 70 | it("binds a custom scope using a scope annotation type parameter") { 71 | val scope = TestScope() 72 | val injector = Guice.createInjector(object : KotlinModule() { 73 | override fun configure() { 74 | bindScope(scope) 75 | bind().to().`in`() 76 | } 77 | }) 78 | 79 | val a = injector.getInstance(A::class.java) 80 | a shouldBe injector.getInstance(A::class.java) 81 | 82 | scope.reset() 83 | 84 | a shouldNotBe injector.getInstance(A::class.java) 85 | } 86 | } 87 | 88 | describe("#bind") { 89 | it("binds source using a type parameter") { 90 | val injector = Guice.createInjector(object : KotlinModule() { 91 | override fun configure() { 92 | bind().to(AImpl::class.java) 93 | } 94 | }) 95 | 96 | val a = injector.getInstance(A::class.java) 97 | 98 | a.get() shouldEqual "Impl of A" 99 | } 100 | 101 | it("binds a complex source using a type parameter") { 102 | val injector = Guice.createInjector(object : KotlinModule() { 103 | override fun configure() { 104 | bind>().to(ACallable::class.java) 105 | } 106 | }) 107 | 108 | val a = injector.getInstance(key>()) 109 | a.call().get() shouldEqual "Impl of A" 110 | } 111 | 112 | it("binds to a target using a type parameter") { 113 | val injector = Guice.createInjector(object : KotlinModule() { 114 | override fun configure() { 115 | bind().to() 116 | } 117 | }) 118 | 119 | val a = injector.getInstance(A::class.java) 120 | 121 | a.get() shouldEqual "Impl of A" 122 | } 123 | 124 | it("binds to a complex target using a type parameter") { 125 | val injector = Guice.createInjector(object : KotlinModule() { 126 | override fun configure() { 127 | bind>().to>() 128 | } 129 | }) 130 | 131 | val callable = injector.getInstance(key>()) 132 | callable.call() shouldEqual null 133 | } 134 | 135 | it("binds with an annotation using a type parameter") { 136 | val injector = Guice.createInjector(object : KotlinModule() { 137 | override fun configure() { 138 | bind().to() 139 | bind().annotatedWith().to() 140 | } 141 | }) 142 | 143 | val a = injector.getInstance(Key.get(A::class.java, Annotated::class.java)) 144 | 145 | a.get() shouldEqual "Impl of A" 146 | } 147 | 148 | it("binds with an annotation using an annotation instance") { 149 | val named = Names.named("Some Name") 150 | 151 | val injector = Guice.createInjector(object : KotlinModule() { 152 | override fun configure() { 153 | bind().to() 154 | bind().annotatedWith(named).to() 155 | } 156 | }) 157 | 158 | val a = injector.getInstance(Key.get(A::class.java, named)) 159 | 160 | a.get() shouldEqual "Impl of A" 161 | } 162 | 163 | it("binds to a provider using a type parameter") { 164 | val injector = Guice.createInjector(object : KotlinModule() { 165 | override fun configure() { 166 | bind().toProvider() 167 | } 168 | }) 169 | 170 | val a = injector.getInstance(A::class.java) 171 | 172 | a shouldBeInstanceOf B::class.java 173 | } 174 | 175 | it("binds to a complex provider using a type parameter") { 176 | val injector = Guice.createInjector(object : KotlinModule() { 177 | override fun configure() { 178 | bind>().toProvider>>() 179 | } 180 | }) 181 | 182 | injector.getInstance(key>()) 183 | } 184 | 185 | it("binds in a scope") { 186 | val injector = Guice.createInjector(object : KotlinModule() { 187 | override fun configure() { 188 | bind().to().`in`() 189 | } 190 | }) 191 | 192 | val a = injector.getInstance(A::class.java) 193 | a shouldBe injector.getInstance(A::class.java) 194 | } 195 | 196 | it("binds wildcard types") { 197 | val injector = Guice.createInjector(object : KotlinModule() { 198 | override fun configure() { 199 | bind>().to() 200 | } 201 | }) 202 | 203 | val callable: Callable<*> = injector.getInstance(key>()) 204 | callable shouldBeInstanceOf ACallable::class.java 205 | } 206 | 207 | describe("when binding to a null instance") { 208 | it("throws a CreationException with message that skips internal sources") { 209 | val outerModule = object : KotlinModule() { 210 | override fun configure() { 211 | bind().toInstance(null) 212 | } 213 | } 214 | 215 | val createInjector = { 216 | Guice.createInjector(outerModule) 217 | } 218 | 219 | val exception = (createInjector shouldThrow CreationException::class).exception 220 | exception.message!! shouldContain outerModule::class.java.name 221 | } 222 | } 223 | } 224 | 225 | describe("#bindConstant") { 226 | it("binds to a target using a type parameter and annotation") { 227 | class ClassWithConstant @Inject constructor(@Annotated val constant: Class) 228 | 229 | val injector = Guice.createInjector(object : KotlinModule() { 230 | override fun configure() { 231 | bindConstant().annotatedWith().to>() 232 | } 233 | }) 234 | 235 | val classWithConstant = injector.getInstance(ClassWithConstant::class.java) 236 | classWithConstant.constant shouldEqual Iterator::class.java 237 | } 238 | } 239 | 240 | describe("#requestStaticInjection") { 241 | it("injects static fields") { 242 | Guice.createInjector(object : KotlinModule() { 243 | override fun configure() { 244 | bind().toInstance("Statically Injected") 245 | requestStaticInjection() 246 | } 247 | }) 248 | 249 | StaticInjectionObj.staticInjectionSite shouldEqual "Statically Injected" 250 | } 251 | } 252 | 253 | describe("#requireBinding") { 254 | it("throws a CreationException for a simple type that is unbound") { 255 | val createInjector = { 256 | Guice.createInjector(object : KotlinModule() { 257 | override fun configure() { 258 | requireBinding() 259 | } 260 | }) 261 | } 262 | 263 | createInjector shouldThrow CreationException::class 264 | } 265 | 266 | it("throws a CreationException for a complex type that is unbound") { 267 | val createInjector = { 268 | Guice.createInjector(object : KotlinModule() { 269 | override fun configure() { 270 | requireBinding>() 271 | // Bind something that matches Callable::class but not the reified type 272 | bind(Callable::class.java).to(ACallable::class.java) 273 | } 274 | }) 275 | } 276 | 277 | createInjector shouldThrow CreationException::class 278 | } 279 | } 280 | 281 | describe("#getProvider") { 282 | it("get a provider for a simple type") { 283 | Guice.createInjector(object : KotlinModule() { 284 | override fun configure() { 285 | bind().to() 286 | val provider = getProvider() 287 | provider.toString() shouldEqual "Provider" 288 | } 289 | }) 290 | } 291 | 292 | it("get a provider for an annotated key") { 293 | Guice.createInjector(object : KotlinModule() { 294 | override fun configure() { 295 | bind>().to>() 296 | val provider = kotlinBinder.getProvider>() 297 | provider.toString() shouldEqual 298 | "Provider>" 299 | } 300 | }) 301 | } 302 | } 303 | 304 | describe("#getMembersInjector") { 305 | it("injects member fields") { 306 | Guice.createInjector(object : KotlinModule() { 307 | override fun configure() { 308 | val membersInjector = getMembersInjector() 309 | membersInjector.toString() shouldEqual 310 | "MembersInjector" 311 | } 312 | }) 313 | } 314 | } 315 | 316 | describe("methods with @Provides annotation") { 317 | it("binds from return type") { 318 | val injector = Guice.createInjector(object : KotlinModule() { 319 | override fun configure() {} 320 | 321 | @Provides 322 | fun provideA(): A { 323 | return AImpl() 324 | } 325 | }) 326 | 327 | val a = injector.getInstance(A::class.java) 328 | 329 | a.get() shouldEqual "Impl of A" 330 | } 331 | } 332 | 333 | it("caches kotlin binder") { 334 | var firstKotlinBinder: KotlinBinder? = null 335 | var secondKotlinBinder: KotlinBinder? = null 336 | val testModule = object : KotlinModule() { 337 | override fun configure() { 338 | firstKotlinBinder = kotlinBinder 339 | secondKotlinBinder = kotlinBinder 340 | } 341 | } 342 | 343 | testModule.configure(newMockBinder()) 344 | 345 | firstKotlinBinder shouldBe secondKotlinBinder 346 | } 347 | 348 | it("invalidates cached kotlin binder when #configure is called with a new binder") { 349 | var cachedBinder: Binder? = null 350 | val testModule = object : KotlinModule() { 351 | override fun configure() { 352 | cachedBinder = kotlinBinder 353 | } 354 | } 355 | 356 | testModule.configure(newMockBinder()) 357 | val firstKotlinBinder = cachedBinder 358 | 359 | testModule.configure(newMockBinder()) 360 | val secondKotlinBinder = cachedBinder 361 | 362 | firstKotlinBinder.shouldNotBeNull() shouldNotBe (secondKotlinBinder) 363 | } 364 | 365 | it("does not invalidate cached kotlin binder when #configure is called with the same binder") { 366 | var cachedBinder: Binder? = null 367 | val testModule = object : KotlinModule() { 368 | override fun configure() { 369 | cachedBinder = kotlinBinder 370 | } 371 | } 372 | 373 | val mockBinder = newMockBinder() 374 | 375 | testModule.configure(mockBinder) 376 | val firstKotlinBinder = cachedBinder 377 | 378 | testModule.configure(mockBinder) 379 | val secondKotlinBinder = cachedBinder 380 | 381 | firstKotlinBinder.shouldNotBeNull() shouldBe secondKotlinBinder 382 | } 383 | } 384 | }) 385 | 386 | private fun newMockBinder(): Binder { 387 | return mock { 388 | on { skipSources(any()) } doReturn it 389 | } 390 | } 391 | --------------------------------------------------------------------------------