Repository: beworker/magnet Branch: master Commit: f60e84a1f7b4 Files: 378 Total size: 18.0 MB Directory structure: gitextract_enrgbkx0/ ├── .editorconfig ├── .github/ │ ├── stale.yml │ └── workflows/ │ └── android.yml ├── .gitignore ├── .project ├── .settings/ │ └── org.eclipse.buildship.core.prefs ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.gradle ├── documentation/ │ └── diagrams/ │ └── design.graphml ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── magnet/ │ ├── build.gradle │ ├── gradle.properties │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── magnet/ │ │ ├── Classifier.java │ │ ├── Factory.java │ │ ├── Instance.java │ │ ├── Magnet.java │ │ ├── Registry.java │ │ ├── Scope.java │ │ ├── Scoping.java │ │ ├── SelectorFilter.java │ │ ├── Visitor.java │ │ └── internal/ │ │ ├── FactoryFilter.java │ │ ├── Generated.java │ │ ├── ImmutableArrayList.java │ │ ├── Index.java │ │ ├── InstanceBucket.java │ │ ├── InstanceFactory.java │ │ ├── InstanceManager.java │ │ ├── InternalFactory.java │ │ ├── MagnetInstanceManager.java │ │ ├── MagnetScope.java │ │ └── Range.java │ └── test/ │ ├── java/ │ │ └── magnet/ │ │ └── internal/ │ │ ├── FactoryFilter_MagnetInstanceManagerTest.java │ │ ├── FactoryFilter_MagnetScopeTest.java │ │ ├── InstanceBucketTest.java │ │ ├── InstrumentedScope.java │ │ ├── MagnetInstanceManagerTest.java │ │ ├── MagnetScope_CircularDependencyTest.java │ │ ├── MagnetScope_DisposeTest.java │ │ ├── MagnetScope_FindDeepForSiblingTypesTest.java │ │ ├── MagnetScope_GetManyTest.java │ │ ├── MagnetScope_ManyInstancesInMultipleScopesTest.java │ │ ├── MagnetScope_RegisterAndGetTest.java │ │ ├── MagnetScope_SiblingTypesTest.java │ │ ├── MagnetScopingDirectTest.java │ │ ├── MagnetScopingNoneTest.java │ │ ├── MagnetScopingTopmostDependsOnDirectTest.java │ │ ├── MagnetScopingTopmostDependsOnTopmostTest.java │ │ ├── MagnetScopingTopmostDependsOnUnscopedTest.java │ │ ├── Scope_GetManyAnySiblingTypesTest_Issue95.java │ │ ├── Scope_LimitDirectScoping_DependencyInNonReachableChildScopeTest.java │ │ ├── Scope_LimitDirectScoping_InstanceWithDependencyTest.java │ │ ├── Scope_LimitDirectScoping_SingleInstanceTest.java │ │ ├── Scope_LimitTest.java │ │ ├── Scope_LimitWithDependencyTest.java │ │ ├── VisitInstancesTest.java │ │ ├── VisitScopesTest.java │ │ ├── events/ │ │ │ ├── ObservableScopeVisitor.java │ │ │ ├── OnEnterScope.java │ │ │ ├── OnExitScope.java │ │ │ └── OnInstance.java │ │ └── observer/ │ │ ├── ScopeObserver.java │ │ └── ScopeValidator.java │ └── resources/ │ └── mockito-extensions/ │ └── org.mockito.plugins.MockMaker ├── magnet-kotlin/ │ ├── build.gradle │ ├── gradle.properties │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── magnet/ │ │ ├── MagnetExt.kt │ │ ├── ScopeExt.kt │ │ └── internal/ │ │ ├── ManyLazy.kt │ │ ├── OptionalLazy.kt │ │ └── SingleLazy.kt │ └── test/ │ └── java/ │ └── magnet/ │ └── kotlin/ │ ├── ScopeExtTest.kt │ └── internal/ │ ├── ManyLazyTest.kt │ ├── OptionalLazyTest.kt │ └── SingleLazyTest.kt ├── magnet-processor/ │ ├── build.gradle │ ├── gradle.properties │ ├── libs/ │ │ └── tools.jar │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── magnet/ │ │ │ └── processor/ │ │ │ ├── MagnetProcessor.kt │ │ │ ├── common/ │ │ │ │ ├── AptUtils.kt │ │ │ │ └── KotlinMethodMetadata.kt │ │ │ ├── instances/ │ │ │ │ ├── FactoryTypeModel.kt │ │ │ │ ├── InstanceProcessor.kt │ │ │ │ ├── aspects/ │ │ │ │ │ ├── classifier/ │ │ │ │ │ │ └── ClassifierAttributeParser.kt │ │ │ │ │ ├── disabled/ │ │ │ │ │ │ └── DisabledAttributeParser.kt │ │ │ │ │ ├── disposer/ │ │ │ │ │ │ ├── DisposeMethodGenerator.kt │ │ │ │ │ │ ├── DisposerAttributeParser.kt │ │ │ │ │ │ ├── DisposerValidator.kt │ │ │ │ │ │ └── IsDisposableMethodGenerator.kt │ │ │ │ │ ├── factory/ │ │ │ │ │ │ ├── CreateMethodGenerator.kt │ │ │ │ │ │ ├── CustomFactoryCreateMethodGenerator.kt │ │ │ │ │ │ ├── FactoryAttributeParser.kt │ │ │ │ │ │ └── StandardFactoryCreateMethodGenerator.kt │ │ │ │ │ ├── index/ │ │ │ │ │ │ └── FactoryIndexCodeGenerator.kt │ │ │ │ │ ├── limitedto/ │ │ │ │ │ │ ├── GetLimitMethodGenerator.kt │ │ │ │ │ │ ├── LimitedToAttributeParser.kt │ │ │ │ │ │ └── LimitedToValidator.kt │ │ │ │ │ ├── scoping/ │ │ │ │ │ │ ├── GetScopingMethodGenerator.kt │ │ │ │ │ │ └── ScopingAttributeParser.kt │ │ │ │ │ ├── selector/ │ │ │ │ │ │ ├── GetSelectorMethodGenerator.kt │ │ │ │ │ │ └── SelectorAttributeParser.kt │ │ │ │ │ ├── siblings/ │ │ │ │ │ │ └── GetSiblingTypesMethodGenerator.kt │ │ │ │ │ └── type/ │ │ │ │ │ ├── TypeAndTypesValidator.kt │ │ │ │ │ ├── TypeAttributeParser.kt │ │ │ │ │ └── TypesAttributeParser.kt │ │ │ │ ├── generator/ │ │ │ │ │ ├── CodeGenerator.kt │ │ │ │ │ ├── CodeWriter.kt │ │ │ │ │ └── FactoryTypeCodeGenerator.kt │ │ │ │ └── parser/ │ │ │ │ ├── AspectValidator.kt │ │ │ │ ├── AttributeParser.kt │ │ │ │ ├── InstanceParser.kt │ │ │ │ ├── InstanceParserForClass.kt │ │ │ │ ├── InstanceParserForMethod.kt │ │ │ │ └── ParserInstance.kt │ │ │ └── registry/ │ │ │ ├── Model.kt │ │ │ ├── RegistryGenerator.kt │ │ │ ├── RegistryParser.kt │ │ │ ├── RegistryProcessor.kt │ │ │ └── instances/ │ │ │ ├── IndexGeneratorVisitor.kt │ │ │ ├── Indexer.kt │ │ │ ├── InstanceIndexGenerator.kt │ │ │ ├── Model.kt │ │ │ └── SectionsCreatorVisitor.kt │ │ └── resources/ │ │ └── META-INF/ │ │ ├── gradle/ │ │ │ └── incremental.annotation.processors │ │ └── services/ │ │ └── javax.annotation.processing.Processor │ └── test/ │ ├── java/ │ │ └── magnet/ │ │ └── processor/ │ │ ├── GenerateRegistryForInstanceFactoriesTest.kt │ │ ├── IndexerTest.kt │ │ ├── InstanceCustomFactoryProcessorTest.kt │ │ ├── InstanceDisposerTest.kt │ │ ├── InstanceIndexProcessorTest.kt │ │ ├── MagnetProcessorFactoryNamesTest.kt │ │ ├── MagnetProcessorTest.kt │ │ ├── SelectorInFactoryClassTest.kt │ │ └── SiblingTypesTest.kt │ └── resources/ │ ├── GenerateRegistryForInstanceFactoriesTest/ │ │ ├── App.java │ │ ├── Implementation1.java │ │ ├── Implementation3_1.java │ │ ├── Implementation3_2.java │ │ ├── Implementation4_1.java │ │ ├── Implementation4_2.java │ │ ├── Implementation5_1.java │ │ ├── Implementation5_2.java │ │ ├── Implementation6_1.java │ │ ├── Implementation6_2.java │ │ ├── Interface1.java │ │ ├── Interface3.java │ │ ├── Interface4.java │ │ ├── Interface5.java │ │ ├── Interface6_1.java │ │ ├── Interface6_2.java │ │ ├── Interface7.java │ │ └── expected/ │ │ ├── MagnetIndexer1.java │ │ ├── MagnetIndexer2.java │ │ ├── MagnetIndexer3.java │ │ ├── MagnetIndexer4.java │ │ ├── MagnetIndexer5.java │ │ ├── MagnetIndexer6.java │ │ └── MagnetIndexer7.java │ ├── InstanceCustomFactoryProcessorTest/ │ │ ├── CustomFactory1.java │ │ ├── CustomFactory2.java │ │ ├── CustomFactory3.java │ │ ├── Implementation1.java │ │ ├── Implementation2.java │ │ ├── Implementation3.java │ │ ├── Interface1.java │ │ ├── Interface2.java │ │ ├── Interface3.java │ │ └── expected/ │ │ ├── Implementation1MagnetFactory.java │ │ ├── Implementation2MagnetFactory.java │ │ └── Implementation3MagnetFactory.java │ ├── InstanceDisposerTest/ │ │ ├── Implementation1.java │ │ ├── Implementation2.java │ │ ├── Implementation3.java │ │ ├── Implementation4.java │ │ ├── Implementation5.java │ │ ├── Interface.java │ │ └── expected/ │ │ └── Implementation1MagnetFactory.java │ ├── InstanceIndexProcessorTest/ │ │ ├── Implementation1.java │ │ ├── Implementation2.java │ │ ├── Interface1.java │ │ ├── Interface2.java │ │ └── generated/ │ │ ├── app_test_Implementation1MagnetFactory.java │ │ └── app_test_Implementation2MagnetFactory.java │ ├── MagnetProcessorFactoryNamesTest/ │ │ ├── Delegate1.java │ │ ├── Interface1.java │ │ └── generated/ │ │ ├── Delegate1MagnetFactory.java │ │ └── Interface1DelegateMagnetFactory.java │ ├── MagnetProcessorTest/ │ │ ├── AppExtensionRegistry.java │ │ ├── Constructor_PackagePrivate_PackagePrivate/ │ │ │ └── UnderTest.java │ │ ├── Constructor_PackagePrivate_Private/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── Constructor_Private/ │ │ │ └── UnderTest.java │ │ ├── Constructor_Protected/ │ │ │ └── UnderTest.java │ │ ├── Constructor_Public_PackagePrivate/ │ │ │ └── UnderTest.java │ │ ├── Constructor_Public_Private/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── Constructor_Public_Protected/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── Constructor_Public_Public/ │ │ │ └── UnderTest.java │ │ ├── Constructor_Public_Public_Kotlin/ │ │ │ └── UnderTest.java │ │ ├── Covariance_Constructor_ManyParameter/ │ │ │ ├── Foo.java │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── Covariance_Constructor_SingleParameter/ │ │ │ ├── Foo.java │ │ │ └── UnderTest.java │ │ ├── DefaultArguments_JvmOverloads_AtTheEnd/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── DefaultArguments_JvmOverloads_InTheMiddle/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── DefaultArguments_JvmOverloads_Mixed/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── DisabledTab.java │ │ ├── Executor.java │ │ ├── ExecutorImpl.java │ │ ├── ExecutorMaster.java │ │ ├── Generics_GetOptional_Unchecked/ │ │ │ ├── Dependency.java │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── Generics_GetSingle_Unchecked/ │ │ │ ├── Dependency.java │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── Generics_ProvideTypeWithParameter/ │ │ │ ├── Parameter.java │ │ │ ├── Type.java │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestProvideTypeMagnetFactory.java │ │ ├── Generics_ProvideTypeWithParameter_NoClassifier/ │ │ │ ├── Parameter.java │ │ │ ├── Type.java │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestProvideTypeMagnetFactory.java │ │ ├── HomePage.java │ │ ├── HomePageMenuItem.java │ │ ├── HomePageNoParams.java │ │ ├── HomePageWithClassifierParams.java │ │ ├── HomePageWithGenericParam.java │ │ ├── HomePageWithManyParameterizedParams.java │ │ ├── HomePageWithManyParameterizedWildcardInParams.java │ │ ├── HomePageWithManyParameterizedWildcardKnownParams.java │ │ ├── HomePageWithManyParameterizedWildcardOutParams.java │ │ ├── HomePageWithManyParams.java │ │ ├── HomePageWithManyWildcardParams.java │ │ ├── HomePageWithParams.java │ │ ├── HomePageWithScope.java │ │ ├── HomePageWithStaticConstructor.java │ │ ├── HomePageWithStaticConstructorDisabled.java │ │ ├── HomePageWithStaticConstructorSingle.java │ │ ├── HomeRepository.java │ │ ├── Lazy_Constructor_ManyParameter/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── Lazy_Constructor_ManyParameter_NullableGenericType/ │ │ │ └── UnderTest.java │ │ ├── Lazy_Constructor_ManyParameter_NullableListType/ │ │ │ └── UnderTest.java │ │ ├── Lazy_Constructor_ManyParameter_Wildcard/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── Lazy_Constructor_NoKotlinMetadata/ │ │ │ └── UnderTest.java │ │ ├── Lazy_Constructor_OptionalParameter/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── Lazy_Constructor_OptionalParameter_Wildcard/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── Lazy_Constructor_SingleParameter/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── Lazy_Constructor_SingleParameter_ParameterizedType/ │ │ │ ├── Foo.java │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── Lazy_Constructor_SingleParameter_Wildcard/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── Lazy_Method_NoKotlinMetadata/ │ │ │ └── UnderTest.java │ │ ├── Lazy_Method_OptionalParameter/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestProvideUnderTestDepMagnetFactory.java │ │ ├── Lazy_Method_OptionalParameter_Wildcard/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestProvideUnderTestDepMagnetFactory.java │ │ ├── Lazy_Method_SingleParameter/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestProvideUnderTestDepMagnetFactory.java │ │ ├── Lazy_Method_SingleParameter_Wildcard/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestProvideUnderTestDepMagnetFactory.java │ │ ├── Limit_Empty_NoGetter/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── Limit_NotEmpty_HasGetter/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── Limit_ReservedAsterisks_Fails/ │ │ │ └── UnderTest.java │ │ ├── Limit_ScopingDirect_GeneratesGetter/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── Limit_ScopingUnscoped_Fails/ │ │ │ └── UnderTest.java │ │ ├── MenuItem.java │ │ ├── Page.java │ │ ├── ScopeParameter_CustomName/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── ScopeParameter_CustomName_KotlinClass/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── ScopeParameter_DefaultName/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── StaticMethodNeedsDependencyWithClassifier/ │ │ │ ├── Constants.java │ │ │ ├── Input.java │ │ │ ├── Output.java │ │ │ ├── StaticFunction.java │ │ │ └── generated/ │ │ │ └── StaticFunctionProvideInputMagnetFactory.java │ │ ├── StaticMethodProvidesInnerClass/ │ │ │ ├── PowerManager.java │ │ │ ├── PowerManagerProvider.java │ │ │ └── expected/ │ │ │ └── PowerManagerProviderProvideWakeLockMagnetFactory.java │ │ ├── Tab.java │ │ ├── TypeAutoDetect_ExtendsObjectNoInterfaces/ │ │ │ ├── UnderTest.java │ │ │ └── expected/ │ │ │ └── UnderTestMagnetFactory.java │ │ ├── UnimplementedTab.java │ │ ├── UserData.java │ │ ├── UserPage.java │ │ ├── UserPageMenuItem.java │ │ ├── WorkProcessor.java │ │ └── generated/ │ │ ├── ForInterfaceWithGenericType_ExecutorMagnetFactory.java │ │ ├── HomePageMagnetFactory.java │ │ ├── HomePageNoParamsMagnetFactory.java │ │ ├── HomePageWithClassifierParamsMagnetFactory.java │ │ ├── HomePageWithManyParameterizedParamsMagnetFactory.java │ │ ├── HomePageWithManyParameterizedWildcardInParamsMagnetFactory.java │ │ ├── HomePageWithManyParameterizedWildcardKnownParamsMagnetFactory.java │ │ ├── HomePageWithManyParameterizedWildcardOutParamsMagnetFactory.java │ │ ├── HomePageWithManyParamsMagnetFactory.java │ │ ├── HomePageWithManyWildcardParamsMagnetFactory.java │ │ ├── HomePageWithParamsMagnetFactory.java │ │ ├── HomePageWithScopeMagnetFactory.java │ │ └── HomePageWithStaticConstructorSingleCreateRepositoriesMagnetFactory.java │ ├── SelectorInFactoryClassTest/ │ │ ├── Implementation1.java │ │ ├── Implementation10.java │ │ ├── Implementation2.java │ │ ├── Implementation3.java │ │ ├── Implementation4.java │ │ ├── Implementation5.java │ │ ├── Implementation6.java │ │ ├── Implementation7.java │ │ ├── Implementation8.java │ │ ├── Implementation9.java │ │ ├── Interface.java │ │ └── generated/ │ │ ├── Implementation10MagnetFactory.java │ │ ├── Implementation1MagnetFactory.java │ │ ├── Implementation7MagnetFactory.java │ │ ├── Implementation8MagnetFactory.java │ │ └── Implementation9MagnetFactory.java │ └── SiblingTypesTest/ │ ├── Implementation1.java │ ├── Implementation2.java │ ├── Implementation3.java │ ├── Implementation4.java │ ├── Implementation5.java │ ├── Interface1.java │ ├── Interface2.java │ └── generated/ │ ├── Implementation4Interface1MagnetFactory.java │ └── Implementation4Interface2MagnetFactory.java ├── magnetx-app/ │ ├── build.gradle │ ├── gradle.properties │ └── src/ │ └── main/ │ └── kotlin/ │ └── magnetx/ │ └── AppExtension.kt ├── magnetx-app-rx3android/ │ ├── build.gradle │ ├── gradle.properties │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── java/ │ └── magnetx/ │ └── RxAndroidAppExtension.kt ├── magnetx-app-rxandroid/ │ ├── build.gradle │ ├── gradle.properties │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── java/ │ └── magnetx/ │ └── RxAndroidAppExtension.kt ├── magnetx-app-stetho/ │ ├── build.gradle │ ├── gradle.properties │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── java/ │ └── magnetx/ │ └── app/ │ └── stetho/ │ └── StethoAppExtension.kt ├── magnetx-app-stetho-scope/ │ ├── build.gradle │ ├── gradle.properties │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── java/ │ └── magnetx/ │ └── app/ │ └── stetho/ │ └── scope/ │ ├── ScopeDumper.kt │ ├── StethoScopeDumpPlugin.kt │ └── StethoScopeInitializer.kt ├── magnetx-selector-android/ │ ├── build.gradle │ ├── gradle.properties │ └── src/ │ └── main/ │ └── kotlin/ │ └── magnetx/ │ └── AndroidSelectorFilter.kt ├── magnetx-selector-features/ │ ├── build.gradle │ ├── gradle.properties │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── java/ │ └── magnetx/ │ └── FeaturesSelectorFilter.kt └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ [*.{kt,kts}] disabled_rules=import-ordering ================================================ FILE: .github/stale.yml ================================================ # Number of days of inactivity before an issue becomes stale daysUntilStale: 30 # Number of days of inactivity before a stale issue is closed daysUntilClose: 7 # Issues with these labels will never be considered stale exemptLabels: - bug - enhancement - help wanted # Label to use when marking an issue as stale staleLabel: wontfix # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. # Comment to post when closing a stale issue. Set to `false` to disable closeComment: false ================================================ FILE: .github/workflows/android.yml ================================================ name: Build Magnet on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: set up JDK 11 uses: actions/setup-java@v3 with: java-version: '11' distribution: 'temurin' cache: gradle - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle run: ./gradlew build ================================================ FILE: .gitignore ================================================ out/ build/ captures/ classes/ reports/ library/.idea/ .idea/ .gradle/ .navigation/ *.iml .DS_Store local.properties ================================================ FILE: .project ================================================ magnet Project magnet created by Buildship. org.eclipse.buildship.core.gradleprojectbuilder org.eclipse.buildship.core.gradleprojectnature 1626289350744 30 org.eclipse.core.resources.regexFilterMatcher node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ ================================================ FILE: .settings/org.eclipse.buildship.core.prefs ================================================ arguments= auto.sync=false build.scans.enabled=false connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) connection.project.dir= eclipse.preferences.version=1 gradle.user.home= java.home=/Library/Java/JavaVirtualMachines/jdk-11.jdk/Contents/Home jvm.arguments= offline.mode=false override.workspace.settings=true show.console.view=true show.executions.view=true ================================================ FILE: CHANGELOG.md ================================================ # Changelog ## 3.8 (release) - 2023.03.27 - Same as 3.8 (snapshot) ## 3.8 (snapshot) - 2023.01.20 - Bump versions: - Kotlin 1.8.0 - Kotlin Metadata JVM 0.6.0 ## 3.7 (release) - 2023.01.20 - Same as 3.7 (snapshot) ## 3.7 (snapshot) - 2022.08.13 - Bump versions: - Kotlin 1.7.10 - Android Gradle Plugin (AGP) 7.2.0 - Stetho 1.6.0 - Kotlin Metadata JVM 0.5.0 ## 3.6 (release) - 2022.08.13 - Migrate to Kotlin 1.6.21, Java 11 and AGP 7.0.4. - Add `Scope.isDisposed()` method. - Remove `magnetx-app-leakcannary`. The extension is not needed anymore because the library has a built-in ability to autostart. - Fixed stetho extension ## 3.6-rc3 - 2022.05.13 - Fixed stetho extension Final release version. Same as 3.6-rc3 ## 3.6-rc1 (Renierit) - 2022.04.30 - Migrate to Kotlin 1.6.21, Java 11 and AGP 7.0.4. - Add `Scope.isDisposed()` method. - Remove `magnetx-app-leakcannary`. The extension is not needed anymore because the library has a built-in ability to autostart. ## 3.5 (Pyrrhotite) - 2021.07.14 - Migrate to Kotlin 1.5.0 ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2018 Sergej Shafarenka, www.halfbit.de Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ [![Kotlin version badge](https://img.shields.io/badge/kotlin-1.8.0-blue.svg)](http://kotlinlang.org/) [![Maven Central](http://img.shields.io/maven-central/v/de.halfbit/magnet.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22de.halfbit%22%20a%3A%22magnet%22) ![maintenance-status](https://img.shields.io/badge/maintenance-as--is-yellow.svg) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0) ‼️ 27.01.2023: This project is not actively developed anymore ‼️ # 🧲 Magnet Magnet is a concise, scope tree based Dependency Injection (DI) library designed for highly modular Android applications. It consists of two parts: an annotation processor (Kotlin) and a reflection free runtime library (Java + Kotlin). # Design Magnet defines and opetates on two core concepts: `Scopes` and `Instances`. `Scope` is a container for instances. Scopes can be combined into a hierarchical tree by referencing parent scopes. The most top scope of the tree hierarchy, which has no parent scope, is called the root scope. `Instance` is a concrete occurrence of an injected type. Instances can be allocated in scopes (scoped instances) or outside of scopes (unscoped instances). # The Dependency Rule Scopes depend on each other using the strong dependency rule - *scope dependency can only point towards its parent scope*. The dependency direction between two scopes enforces the direction of dependencies between instances allocated in those scopes. Instances allocated in a parent scope can know nothing about instances allocated in its child scopes. This simple design rule helps preventing memory leaks and allows safe disposal of child scopes and garbage collecting instances allocated there. # Getting Started In the example below we will compose a very naive `MediaPlayer` which loads media using a `MediaLoader` and then plays the media. ```kotlin fun main() { val rootScope = MagnetScope.createRootScope() val playerScope = rootScope.createSubscope { bind(Uri.parse("https://my-media-file")) } // mark 1 val mediaPlayer = playerScope.getSingle() mediaPlayer.playWhenReady() // mark 2 Thread.sleep(5000) playerScope.dispose() // mark 3 } // MediaPlayer.kt interface MediaPlayer { fun playWhenReady() } @Instance(type = MediaPlayer::class, disposer = "dispose") internal class DefaultMediaPlayer( private val assetId: Uri, private val mediaLoader: MediaLoader ) : MediaPlayer { override fun playWhenReady() { ... } fun dispose() { ... } } // MediaLoader.kt interface MediaLoader { fun load(mediaUri: Uri): Single } @Instance(type = MediaLoader::class) internal class DefaultMediaLoader() : MediaLoader { override fun load(mediaUri: Uri): Single { ... } } ``` The diagram below shows how Magnet manages the scope hierarchy when different marks of the main function are reached. At `Mark 1`, two scopes are created and the `Uri` instance gets bound into the `playerScope`. At `Mark 2`, `mediaPlayer` and `mediaLoader` instances get allocated in respective scopes. `mediaPlayer` is allocated in the `playerScope` because one of its dependencies, the `Uri`, is located in `playerScope`. Magnet cannot move `mediaPlayer` up to the `rootScope` because this would break the dependency rule described above. `mediaLoader` has no dependencies, that's why it is allocated in the `rootScope`. This instance allocation logic is specific to Magnet DI and is called auto-scoping. See developer documentation for more detail. At `Mark 3`, the `playerScope` gets disposed and all its instances are garbage collected. For more information refer to Magnet documentation. # Documentation 1. [Developer Guide](https://www.halfbit.de/magnet/developer-guide/) 2. [Dependency auto-scoping](https://github.com/beworker/magnet/wiki/Dependency-auto-scoping) 3. [Scope Inspection](https://github.com/beworker/magnet/wiki/Scope-Inspection) 4. [How to Inject Android ViewModels](https://github.com/beworker/magnet/issues/69#issuecomment-468033997) 5. [Blog: Magnet - an alternative to Dagger](https://www.thomaskeller.biz/blog/2019/10/09/magnet-an-alternative-to-dagger/) 6. [Co2Monitor sample app](https://github.com/beworker/co2monitor/tree/master/android-client) 7. [Another sample app](https://github.com/beworker/g1) # Features - Minimalistic API - Auto-scoping of instances - Hierarchical, disposable scopes - Kotlin friendly annotation - Injection into Kotlin constructors with default arguments - Injection from binary libraries - Dependency inversion - No direct references to Magnet generated code - No reflection for injection, apt generated factory classes - Extensible - some `magnetx` extensions are available - Customizable - custom factories and instance selectors # Why Magnet? Magnet was crafted with simplicity and development speed in mind. It lets developers spend less time on DI configuration and do more other stuff, also more mistakes when used inattentively. Magnet motivates you writing highly modular apps because it makes DI so simple. It can even inject instances from the libraries added in build scripts without necessity to adapt source code. Magnet could be interesting for those, who needs an easy to configure and simple DI with more runtime control. # Why not Magnet? If compile time consistency validation is your highest priority, I recommend using awesome [Dagger2](https://github.com/google/dagger) instead. You will spend slightly more time on DI configuration but Dagger2 lets you keep it highly consistent and error prone (in most cases) very early in the development cycle - at compile time. Peace ✌️ and have fun. # Gradle Kotlin ```gradle repositories { mavenCentral() } dependencies { api 'de.halfbit:magnet-kotlin:' kapt 'de.halfbit:magnet-processor:' } ``` Java ```gradle repositories { mavenCentral() } dependencies { api 'de.halfbit:magnet:' annotationProcessor 'de.halfbit:magnet-processor:' } ``` # Compatibility | Kotlin Version | Magnet Version | |----------------|----------------| | 1.8.x | 3.8 | | 1.7.x | 3.7 | | 1.6.x | 3.6 | | 1.5.x | 3.5 | | 1.4.x | 3.4 | | 1.3.x | 3.4 | # Proguard & R8 ``` -keep class magnet.internal.MagnetIndexer { *; } ``` # Build from Sources 1. Set JAVA_HOME variable to JDK 11. 2. Import project into Android Studio. 3. Set Gradle → Gradle Settings... → Gradle JDK to JDK 11. 4. To build the project run `./gradlew build` 5. To release the project run `./gradlew publish` # Maven repository configurations | Repository | Configuration | |------------|----------------------------------------------------------------------------| | Central | mavenCentral() | | Snapshot | maven { url = "https://oss.sonatype.org/content/repositories/snapshots/" } | | Local | mavenLocal() | # License ``` Copyright 2018-2023 Sergej Shafarenka, www.halfbit.de Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ``` ================================================ FILE: build.gradle ================================================ buildscript { ext.kotlin_version = '1.8.0' repositories { mavenCentral() maven { url 'https://maven.google.com' } } dependencies { classpath "com.android.tools.build:gradle:7.2.0" classpath "com.vanniktech:gradle-maven-publish-plugin:0.19.0" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } allprojects { repositories { mavenCentral() } configurations { all { resolutionStrategy { force deps.kotlinjdk } } } } ext { javaVersion = JavaVersion.VERSION_11 } ext.deps = [:] // open source ext.deps.kotlinjdk = "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" ext.deps.javapoet = 'com.squareup:javapoet:1.13.0' ext.deps.android = 'com.google.android:android:4.1.1.4' ext.deps.rxandroid = 'io.reactivex.rxjava2:rxandroid:2.1.1' ext.deps.rx3android = 'io.reactivex.rxjava3:rxandroid:3.0.0' ext.deps.stetho = 'com.facebook.stetho:stetho:1.6.0' ext.deps.kotlinMetadata = 'org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.6.0' ext.deps.annotations = 'org.jetbrains:annotations:16.0.1' // testing ext.deps.junit = 'junit:junit:4.12' ext.deps.jsr305 = 'com.google.code.findbugs:jsr305:3.0.2' ext.deps.mockito = 'org.mockito:mockito-core:4.5.1' ext.deps.mockitoKotlin = 'org.mockito.kotlin:mockito-kotlin:4.0.0' ext.deps.truth = 'com.google.truth:truth:1.1.3' ext.deps.compileTesting = 'com.google.testing.compile:compile-testing:0.21.0' ================================================ FILE: documentation/diagrams/design.graphml ================================================ Scope Instance <parent> <dependencies> ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ #Wed Dec 18 22:04:35 CET 2019 distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME ================================================ FILE: gradle.properties ================================================ org.gradle.jvmargs=-Xmx1536m android.useAndroidX=true GROUP=de.halfbit VERSION_NAME=3.8 VERSION_CODE=0 POM_DESCRIPTION=Dependency injection library for Android POM_URL=https://github.com/sergejsha/magnet/ POM_INCEPTION_YEAR=2018 POM_SCM_URL=https://github.com/sergejsha/magnet/ POM_SCM_CONNECTION=scm:git:git://github.com/sergejsha/magnet.git POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/sergejsha/magnet.git POM_LICENSE_NAME=The Apache Software License, Version 2.0 POM_LICENSE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt POM_LICENSE_DIST=repo POM_DEVELOPER_ID=sergejsha POM_DEVELOPER_NAME=Sergej Shafarenka POM_DEVELOPER_URL=https://github.com/sergejsha/ ================================================ FILE: gradlew ================================================ #!/usr/bin/env sh ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn () { echo "$*" } die () { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Escape application args save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } APP_ARGS=$(save "$@") # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then cd "$(dirname "$0")" fi exec "$JAVACMD" "$@" ================================================ FILE: gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: magnet/build.gradle ================================================ plugins { id 'java-library' id 'com.vanniktech.maven.publish' } sourceCompatibility = javaVersion targetCompatibility = javaVersion dependencies { compileOnly deps.annotations testImplementation deps.junit testImplementation deps.mockito testImplementation deps.truth testCompileOnly deps.annotations } ================================================ FILE: magnet/gradle.properties ================================================ POM_NAME=Magnet Runtime Library POM_ARTIFACT_ID=magnet POM_PACKAGING=jar ================================================ FILE: magnet/src/main/java/magnet/Classifier.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.RetentionPolicy.CLASS; /** * Magnet can bind multiple instances of the same type into a scope. Use classifier * with a unique value to differentiate between those instances. * *
 *      // bind instances
 *      scope.bind(Context.class, application, "app-context");
 *      scope.bind(Context.class, activity, "activity-context");
 *
 *      // get instances directly
 *      Context app = scope.getSingle(Context.class, "app-context");
 *      Context activity = scope.getSingle(Context.class, "activity-context");
 *
 *      // get instances via constructor injection
 *      @Instance(type = MyImplementation.class)
 *      public MyImplementation(
 *          @Classifier("app-context") Context app,
 *          @Classifier("activity-context") Context activity,
 *      ) {
 *          ...
 *      }
 * 
*/ @Retention(CLASS) @Target({ElementType.PARAMETER, ElementType.METHOD}) public @interface Classifier { String NONE = ""; String value(); } ================================================ FILE: magnet/src/main/java/magnet/Factory.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet; import org.jetbrains.annotations.NotNull; /** Custom factory to be used with {@link Instance#factory()}. */ public interface Factory { /** Implementation must return instance of give type without delegating the call to the given scope. */ @NotNull T create( @NotNull Scope scope, @NotNull Class type, @NotNull String classifier, @NotNull Scoping scoping, @NotNull Instantiator instantiator ); /** Instantiator creates new instance of given type using {@code new} operator. */ interface Instantiator { @NotNull T instantiate(Scope scope); } } ================================================ FILE: magnet/src/main/java/magnet/Instance.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet; import static java.lang.annotation.RetentionPolicy.CLASS; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** * Magnet instantiates classes marked with this annotation automatically. Annotated * classes must have a single, none private constructor. Constructor is allowed to have * parameters which are dependencies for this instance. Magnet checks constructor * parameters and tries to resolve corresponding dependencies by looking into accessible * scopes. If no suitable instances were found in scopes, Magnet searches for * {@code Instance}-annotated classes and tried to instantiate them. If constructor's * dependencies cannot be fulfilled, Magnet will fail at runtime with corresponding * error message. * *

* In the example below we declare two dependent types and instantiate them in scope. * *

 *     @Instance(type=TypeA.class)
 *     class TypeA {
 *         TypeA() {}
 *     }
 *
 *     @Instance(type=TypeB.class)
 *     class TypeB {
 *         final TypeA typeA;
 *         TypeB(@NonNull TypeA dependency) {
 *             typeA = dependency;
 *         }
 *     }
 *
 *     ...
 *
 *     // get instance of typeB
 *     TypeB typeB = scope.getSingle(TypeB.class);
 *
 *     // typeA has been provided by Magnet
 *     typeB.typeA != null
 *
 * 
* *

* Nullability. Magnet is capable of detecting whether dependency can or cannot be null. * If a constructor's parameter is annotated as nullable and Magnet cannot provide instance of * parameter's type, then null is provided instead. * *

* Classification. Same interface can be implemented by many classes. Classifier is used * to differentiate between those implementations. See {@link Classifier} for more detail. * *

* Scoping. Magnet can bind created instances into scope for reuse. Instance can * specify whether and how its instances should be bound into the scope. See {@link Scoping} * for more detail. */ @Retention(CLASS) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface Instance { /** * Type to use when annotated instance gets registered in scope. Annotated class must * actually implement this type. */ Class type() default void.class; /** * Multiple types to use when annotated instance gets registered in scope. Annotated * class must actually implement all these types. Properties {@link #type()} and * {@code #types()} are exclusively mutual and cannot be used together. */ Class[] types() default void.class; /** * Classifier to use when annotated instance gets registered in scope. */ String classifier() default Classifier.NONE; /** * Scoping rule to be applied when instance of annotated class gets created. */ Scoping scoping() default Scoping.TOPMOST; /** * Limit tag to be used with {@link Scoping#TOPMOST} algorithm. If a limit tag is * preset then the instance will be placed at or below the scope having same limit * tag applied to it. If multiple scopes have the same limit tag, then the closest scope * to the scope where the instance gets requested is used. */ String limitedTo() default ""; /** * Experimental. Magnet will only create instance of the annotated class if * this selector expression is true after evaluation. Magnet currently support single * type of expression: *

* android.api (comparison operator) (api version) *

* For instance, the expression android.api >= 28 will only create * annotated instance if Build.VERSION.SDK_INT >= 28. For the other versions * null is returned. Make sure to use optional injection to handle * this case. */ String selector() default SelectorFilter.DEFAULT_SELECTOR; /** * Custom factory to be used for creating instance instead of the generated one. */ Class factory() default Factory.class; /** * Name of optional disposer method to be called, when whole scope gets disposed. */ String disposer() default ""; /** * Magnet ignores this annotation when this flag is set to true. */ boolean disabled() default false; } ================================================ FILE: magnet/src/main/java/magnet/Magnet.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet; import magnet.internal.InternalFactory; import org.jetbrains.annotations.NotNull; public final class Magnet { private Magnet() { } public static @NotNull Scope createRootScope() { return InternalFactory.createRootScope(); } } ================================================ FILE: magnet/src/main/java/magnet/Registry.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.RetentionPolicy.CLASS; /** * This marker annotation instructs Magnet to generate a registry indexing all * instances available in classpath. Index applies to sources as well as libraries. * Apply this annotation to any interface or class in your main application module. */ @Retention(CLASS) @Target({ElementType.TYPE}) public @interface Registry {} ================================================ FILE: magnet/src/main/java/magnet/Scope.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; /** * Scope is a container for objects which are stored there at runtime. * *

* Binding. Binding is the way of putting objects into the scope. * *

 * Scope root = Magnet.createScope()
 *                 .bind(app, Application.class)
 *                 .bind(String.class, "#FF0000", "red-color");
 * 
* *

* Chaining. Scopes can be chained using * parent-child relation, or rather a child-parent relation because * a child scope (aka subscope) holds a reference to its parent and not * the other way around. * *

 * Scope subscope = root.createSubscope()
 *                   .bind(activity, Context.class)
 *                   .bind(String.class, "#F30303", "red-color")
 *                   .bind(String.class, "#00FF00", "green-color");
 * 
* * Provisioning. Scope has multiple get-methods for providing * objects it stores. Magnet will look through the whole scope's chain up * to the root parent scope to provide an object. First found match gets * returned. * *
 * // object overwriting
 * String red = root.getSingle(String.class, "red-color"); // "#FF0000" (from root)
 * String red = subscope.getSingle(String.class, "red-color"); // "#F30303" (from subscope)
 *
 * // scope chaining
 * Application app = subscope.getSingle(Application.class); // app (from root)
 * Context context = subscope.getSingle(Context.class); // activity (from subscope)
 * String green = subscope.getSingle(String.class, "green-color"); // "#00FF00" (from subscope)
 *
 * // optional provisioning
 * String red = root.getOptional(String.class, "red-color"); "#FF0000" (from root)
 * String yellow = subscope.getSingle(String.class, "yellow-color"); // throws IllegalStateException
 * String yellow = subscope.getOptional(String.class, "yellow-color"); // null, optional was not found
 * 
* *

* Automatic binding (injection). * Magnet can instantiate {@link Instance}-annotated classes and bind their instances * into respective scopes automatically. If instantiated classes have dependencies, Magnet * will resolve those dependencies too. In this respect Magnet works as dependency injection * library. * *

In the example below Magnet will create instance of {@code toaster} by taking required * dependencies from the scopes. * *

 * @Instance(type = Toaster.class)
 * class Toaster {
 *     Toaster(
 *         Application app,
 *         @Classifier("red-color") String red,
 *         @Classifier("green-color") String green
 *     ) { ... }
 *
 * Toaster toaster = subscope.getSingle(Toaster.class);
 * toaster != null
 * 
*/ public interface Scope { /** Returns an object from the scope or {@code null}, if object was not found. */ @Nullable T getOptional(@NotNull Class type); /** Returns an object from the scope or {@code null}, if object was not found. */ @Nullable T getOptional(@NotNull Class type, @NotNull String classifier); /** Returns an object from the scope or throws exception, if object was not found. */ @NotNull T getSingle(@NotNull Class type); /** Returns an object from the scope or throws exception, if object was not found. */ @NotNull T getSingle(@NotNull Class type, @NotNull String classifier); /** Returns a list of objects or empty list, if no objects were found. */ @NotNull List getMany(@NotNull Class type); /** Returns a list of objects or empty list, if no objects were found. */ @NotNull List getMany(@NotNull Class type, @NotNull String classifier); /** Binds given instance into this scope. */ @NotNull Scope bind(@NotNull Class type, @NotNull T instance); /** Binds given instance into this scope. */ @NotNull Scope bind(@NotNull Class type, @NotNull T instance, @NotNull String classifier); /** Sets given limits to this scope. */ @NotNull Scope limit(String... limits); /** Creates a new child scope of this scope. */ @NotNull Scope createSubscope(); /** Disposes this and all children scopes. Notifies instances with {@link magnet.Instance#disposer()}. */ void dispose(); /** Returns `true` is the scope is disposed, or `false` otherwise. */ boolean isDisposed(); /** Visits all instances and child scopes of given depth (use {@code Integer.MAX_VALUE} for visiting all scopes). */ void accept(Visitor visitor, int depth); } ================================================ FILE: magnet/src/main/java/magnet/Scoping.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet; /** * Declares the way Magnet binds instances of @Instance-annotated * classes into scopes. See each separate setting for more detail. */ public enum Scoping { /** * Magnet will not bind created instance into any scope. This is equivalent * to having a factory creating new instances each time when instance is * requested. * *

* In the example below, both instances {@code typeA} and {@code typeB} are * not bound into the {@code scope}. Each new {@code scope.getSingle(TypeB.class)} * call will return new instances of {@code typeA} and {@code typeB}. * *

     *
     * @Instance(type = TypeA.class, scoping = Scoping.UNSCOPED)
     * class TypeA {
     *     TypeA() {}
     * }
     *
     * @Instance(type = TypeB.class, scoping = Scoping.UNSCOPED)
     * class TypeB {
     *     final TypeA typeA;
     *     TypeB(@NonNull TypeA dependency) {
     *         typeA = dependency;
     *     }
     * }
     *
     * ...
     *
     * Scope scope = Magnet.createScope();
     *
     * TypeB typeB = scope.getSingle(TypeB.class);
     * TypeA typeA = typeB.typeA;
     * TypeA typeA2 = scope.getSingle(TypeA.class);
     * typeA !== typeA2 // different instances
     *
     * 
*/ UNSCOPED, /** * Magnet will bind created instance into the most top scope in the chain of scopes, * where all dependencies for the created instance are still fulfilled. * *

* Scopes in Magnet can build a chain of parent-child relations (see {@link Scope} for * more detail). This option allows binding instances of annotated class into a one * of parent scopes. Magnet goes up the scope chain and checks, whether all dependencies * for the instance can still be satisfied in that scope. The most top reached scope * with satisfied dependencies is the scope, into which the instance gets bound. * *

* In the example below both instances {@code typeA} and {@code typeB} are bound into * {@code root}. * *

     *
     * @Instance(type = TypeA.class, scoping = Scoping.TOPMOST)
     * class TypeA {
     *     TypeA() {}
     * }
     *
     * @Instance(type = TypeB.class)
     * class TypeB {
     *     final TypeA typeA;
     *     TypeB(@NonNull TypeA dependency) {
     *         typeA = dependency;
     *     }
     * }
     *
     * ...
     *
     * Scope root = Magnet.createScope();
     * Scope scope = root.createSubscope();
     *
     * TypeB typeB = scope.getSingle(TypeB.class);
     * TypeA typeA = typeB.typeA;
     * TypeA typeA2 = scope.getSingle(TypeA.class);
     * typeA === typeA2 // same instance
     *
     * 
* * Explanation: * *
    *
  • {@code TypeA} has no dependencies and thus it gets bound to {@code root}; *
  • {@code TypeB} depends on {@code TypeA} and because the instance of {@code TypeA} * is available in {@code root}, and instance of {@code TypeB} can also be created * in {@code root}. *
* *

* In the example below we bind instance of {@code typeA} into {@code scope} and * {@code typeB} instance gets bound into the {@code scope} too. * *

     *
     * @Instance(type = TypeA.class, scoping = Scoping.TOPMOST)
     * class TypeA {
     *     TypeA() {}
     * }
     *
     * @Instance(type = TypeB.class, scoping = Scoping.TOPMOST)
     * class TypeB {
     *     final TypeA typeA;
     *     TypeB(@NonNull TypeA dependency) {
     *         typeA = dependency;
     *     }
     * }
     *
     * ...
     *
     * Scope root = Magnet.createScope();
     * Scope scope = root.createSubscope().bind(TypeA.class, new TypeA());
     *
     * TypeB typeB = scope.getSingle(TypeB.class);
     * TypeA typeA = typeB.typeA;
     * TypeA typeA2 = scope.getSingle(TypeA.class);
     * typeA === typeA2 // same instance
     *
     * 
* * Explanation: * *
    *
  • {@code typeB} cannot be bound in a scope above its dependencies; *
  • {@code typeA} is available in {@code scope}, thus the {@code scope} is the * top most scope for dependent {@code typeB} too. *
*/ TOPMOST, /** * Magnet will bind created instance into the same scope, in which this instance * has been created. * *

* In the example below, both instances {@code typeA} and {@code typeB} are bound * into {@code scope}. Each new {@code scope.getSingle(TypeB.class)} call on the * same instance of {@code scope} will return same instances of {@code typeA} and * {@code typeB}. * *

     *
     * @Instance(type = TypeA.class, scoping = Scoping.DIRECT)
     * class TypeA {
     *     TypeA() {}
     * }
     *
     * @Instance(type = TypeB.class, scoping = Scoping.DIRECT)
     * class TypeB {
     *     final TypeA typeA;
     *     TypeB(@NonNull TypeA dependency) {
     *         typeA = dependency;
     *     }
     * }
     *
     * ...
     *
     * Scope root = Magnet.createScope();
     * Scope scope = root.createSubscope();
     *
     * TypeB typeB = scope.getSingle(TypeB.class);
     * TypeA typeA = typeB.typeA;
     * TypeA typeA2 = scope.getSingle(TypeA.class);
     * typeA === typeA2 // same instance
     *
     * 
*/ DIRECT } ================================================ FILE: magnet/src/main/java/magnet/SelectorFilter.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet; import org.jetbrains.annotations.NotNull; /** Selector handler for processing {@link Instance#selector()} value at runtime. */ public abstract class SelectorFilter { public static final String DEFAULT_SELECTOR = ""; public abstract boolean filter(@NotNull String[] selector); } ================================================ FILE: magnet/src/main/java/magnet/Visitor.java ================================================ package magnet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** * Implementation of this interface should be used with {@link magnet.Scope#accept(Visitor, int)} * for iterating though instances and subscopes of a scope. Scope visiting begins with iterating * through all instances and then through all subscopes. */ public interface Visitor { /** Provision type. */ enum Provision {BOUND, INJECTED} /** Visited instance. */ interface Instance { @NotNull Scoping getScoping(); @NotNull String getClassifier(); @NotNull String getLimit(); @NotNull Class getType(); @NotNull Object getValue(); @NotNull Provision getProvision(); } /** Visited scope. */ interface Scope { @Nullable String[] getLimits(); } /** * Called when new scope is entered. * * @param scope entered scope. * @param parent parent scope of the entered scope. * @return true to visit instances of this scope, false to skip instances. */ boolean onEnterScope(@NotNull Scope scope, @Nullable Scope parent); /** * Called when visiting new instance between {@link #onEnterScope(Scope, Scope)} * and {@link #onExitScope(Scope)} calls. * * @param instance visited instance. * @return true to visit the next instance in the scope or false * to skip all other instances in this scope. */ boolean onInstance(@NotNull Instance instance); /** * Called when previously entered scope is exited. * * @param scope exited scope. */ void onExitScope(@NotNull Scope scope); } ================================================ FILE: magnet/src/main/java/magnet/internal/FactoryFilter.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; /* Subject to change. For internal use only. */ interface FactoryFilter { boolean filter(InstanceFactory factory); } ================================================ FILE: magnet/src/main/java/magnet/internal/Generated.java ================================================ package magnet.internal; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.RetentionPolicy.CLASS; /** Subject to change. For internal use only. */ @Retention(CLASS) @Target({ElementType.TYPE}) public @interface Generated {} ================================================ FILE: magnet/src/main/java/magnet/internal/ImmutableArrayList.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; import java.util.AbstractList; /* Subject to change. For internal use only. */ class ImmutableArrayList extends AbstractList { private final E[] elements; ImmutableArrayList(E[] elements) { this.elements = elements; } @Override public E get(int i) { if (i < 0 || i >= elements.length) { throw new IndexOutOfBoundsException( String.format( "Cannot find element with index %s, array length: %s", i, elements.length)); } return elements[i]; } @Override public int size() { return elements.length; } } ================================================ FILE: magnet/src/main/java/magnet/internal/Index.java ================================================ package magnet.internal; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.RetentionPolicy.CLASS; /** Subject to change. For internal use only. */ @Retention(CLASS) @Target({ElementType.TYPE}) public @interface Index { Class factoryType(); Class factoryClass(); String instanceType(); String classifier(); } ================================================ FILE: magnet/src/main/java/magnet/internal/InstanceBucket.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import magnet.Scoping; import magnet.Visitor; /* Subject to change. For internal use only. */ @SuppressWarnings("unchecked") final class InstanceBucket { @NotNull private final OnInstanceListener listener; @NotNull private InstanceBucket.Instance instance; @NotNull private MagnetScope scope; InstanceBucket( @NotNull MagnetScope scope, @Nullable InstanceFactory factory, @NotNull Class objectType, @NotNull T object, @NotNull String classifier, @NotNull OnInstanceListener listener ) { this.scope = scope; this.listener = listener; this.instance = createSingleInstance(factory, objectType, object, classifier); } @NotNull MagnetScope getScope() { return scope; } @NotNull T getSingleInstance() { if (instance instanceof InjectedInstance) { return ((InjectedInstance) instance).object; } else if (instance instanceof BoundInstance) { return ((BoundInstance) instance).object; } MultiObjectInstance multiObjectInstance = (MultiObjectInstance) instance; throw new IllegalStateException( String.format( "Single instance requested, while many instances are stored: %s", multiObjectInstance.instances ) ); } @Nullable T getOptional(@Nullable Class> factoryType) { if (instance instanceof InjectedInstance) { InjectedInstance single = (InjectedInstance) instance; if (single.factory.getClass() == factoryType) { return single.object; } return null; } else if (instance instanceof BoundInstance) { BoundInstance single = (BoundInstance) instance; if (factoryType == null) { return single.object; } return null; } return (T) ((MultiObjectInstance) instance).getOptional(factoryType); } @NotNull List getMany() { if (instance instanceof InjectedInstance) { return Collections.singletonList(((InjectedInstance) instance).object); } else if (instance instanceof BoundInstance) { return Collections.singletonList(((BoundInstance) instance).object); } return ((MultiObjectInstance) instance).getMany(); } void registerObject( @Nullable InstanceFactory factory, @NotNull Class objectType, @NotNull T object, @NotNull String classifier ) { if (this.instance instanceof InstanceBucket.MultiObjectInstance) { MultiObjectInstance many = (MultiObjectInstance) this.instance; many.putSingle(createSingleInstance(factory, objectType, object, classifier)); } else { MultiObjectInstance many = new MultiObjectInstance<>((SingleObjectInstance) this.instance); many.putSingle(createSingleInstance(factory, objectType, object, classifier)); this.instance = many; } } boolean hasInstanceWithFactory(@Nullable InstanceFactory factory) { return instance.hasObjectWithFactory(factory); } private @NotNull InstanceBucket.SingleObjectInstance createSingleInstance( @Nullable InstanceFactory factory, @NotNull Class objectType, @NotNull T object, @NotNull String classifier ) { SingleObjectInstance single; if (factory == null) { single = new BoundInstance<>(objectType, object, classifier); } else { single = new InjectedInstance<>(factory, objectType, object, classifier); } listener.onInstanceCreated(single); return single; } public boolean accept(Visitor visitor) { if (instance instanceof SingleObjectInstance) { return ((SingleObjectInstance) instance).accept(visitor); } else { return ((MultiObjectInstance) instance).accept(visitor); } } interface Instance { boolean hasObjectWithFactory(@Nullable InstanceFactory factory); } static abstract class SingleObjectInstance implements Instance { final @NotNull Class objectType; final @NotNull T object; final @NotNull String classifier; SingleObjectInstance( @NotNull Class objectType, @NotNull T object, @NotNull String classifier ) { this.objectType = objectType; this.object = object; this.classifier = classifier; } public boolean accept(Visitor visitor) { if (this instanceof InjectedInstance) { return visitor.onInstance((InjectedInstance) this); } else { return visitor.onInstance((BoundInstance) this); } } } static class BoundInstance extends SingleObjectInstance implements Visitor.Instance { BoundInstance(@NotNull Class objectType, @NotNull T object, @NotNull String classifier) { super(objectType, object, classifier); } @Override public boolean hasObjectWithFactory(@Nullable InstanceFactory factory) { return true; } @Override public @NotNull Scoping getScoping() { return Scoping.DIRECT; } @Override public @NotNull String getClassifier() { return classifier; } @Override public @NotNull String getLimit() { return ""; } @Override public @NotNull Class getType() { return objectType; } @Override public @NotNull Object getValue() { return object; } @Override public @NotNull Visitor.Provision getProvision() { return Visitor.Provision.BOUND; } } static class InjectedInstance extends SingleObjectInstance implements Visitor.Instance { @NotNull final InstanceFactory factory; InjectedInstance( @NotNull InstanceFactory factory, @NotNull Class objectType, @NotNull T object, @NotNull String classifier ) { super(objectType, object, classifier); this.factory = factory; } @Override public boolean hasObjectWithFactory(@Nullable InstanceFactory factory) { return factory == this.factory; } @Override public @NotNull Scoping getScoping() { return factory.getScoping(); } @Override public @NotNull String getClassifier() { return classifier; } @Override public @NotNull String getLimit() { return factory.getLimit(); } @Override public @NotNull Class getType() { return objectType; } @Override public @NotNull Object getValue() { return object; } @Override public @NotNull Visitor.Provision getProvision() { return Visitor.Provision.INJECTED; } } private static class MultiObjectInstance implements Instance { private final @NotNull HashMap>, SingleObjectInstance> instances; MultiObjectInstance(@NotNull InstanceBucket.SingleObjectInstance single) { instances = new HashMap<>(8); putSingle(single); } @NotNull List getMany() { List result = new ArrayList<>(this.instances.size()); for (SingleObjectInstance single : this.instances.values()) { result.add(single.object); } return result; } @Nullable T getOptional(@Nullable Class> factoryType) { SingleObjectInstance single = instances.get(factoryType); if (single == null) return null; return single.object; } void putSingle(@NotNull InstanceBucket.SingleObjectInstance single) { @Nullable final Class> factoryType; if (single instanceof InjectedInstance) { factoryType = (Class>) ((InjectedInstance) single).factory.getClass(); } else if (single instanceof BoundInstance) { factoryType = null; } else { throw new IllegalStateException("Unsupported SingleObjectInstance type."); } instances.put(factoryType, single); } @Override public boolean hasObjectWithFactory(@Nullable InstanceFactory factory) { return instances.containsKey(factory == null ? null : factory.getClass()); } public boolean accept(Visitor visitor) { Collection> singleObjectInstances = instances.values(); boolean takeNext = true; for (SingleObjectInstance instance : singleObjectInstances) { takeNext = instance.accept(visitor); if (!takeNext) break; } return takeNext; } } interface OnInstanceListener { void onInstanceCreated(SingleObjectInstance instance); } } ================================================ FILE: magnet/src/main/java/magnet/internal/InstanceFactory.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; import magnet.Scope; import magnet.Scoping; /* Subject to change. For internal use only. */ public abstract class InstanceFactory { public abstract T create(Scope scope); public Scoping getScoping() { return Scoping.TOPMOST; } public String getLimit() { return ""; } public Class[] getSiblingTypes() { return null; } public String[] getSelector() { return null; } public boolean isDisposable() { return false; } public void dispose(T instance) { throw new IllegalStateException( String.format("Instance %s is not disposable", instance) ); } } ================================================ FILE: magnet/src/main/java/magnet/internal/InstanceManager.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; /* Subject to change. For internal use only. */ interface InstanceManager { @Nullable InstanceFactory getInstanceFactory( Class instanceType, String classifier, Class> factoryType); @Nullable InstanceFactory getFilteredInstanceFactory( Class type, String classifier, FactoryFilter factoryFilter); @NotNull List> getManyInstanceFactories( Class type, String classifier, FactoryFilter factoryFilter); } ================================================ FILE: magnet/src/main/java/magnet/internal/InternalFactory.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; import magnet.Scope; import org.jetbrains.annotations.NotNull; /* Subject to change. For internal use only. */ public final class InternalFactory { private static final InstanceManager INSTANCE_MANAGER = new MagnetInstanceManager(); private InternalFactory() {} static @NotNull Scope createRootScope(@NotNull InstanceManager instanceManager) { return new MagnetScope(null, instanceManager); } public static @NotNull Scope createRootScope() { return new MagnetScope(null, INSTANCE_MANAGER); } } ================================================ FILE: magnet/src/main/java/magnet/internal/MagnetInstanceManager.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; import magnet.Registry; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; /* Subject to change. For internal use only. */ @SuppressWarnings("unchecked") final class MagnetInstanceManager implements InstanceManager { private InstanceFactory[] factories; private Map index; MagnetInstanceManager() { registerInstanceFactories(); } private void registerInstanceFactories() { try { Class magnetClass = Class.forName("magnet.internal.MagnetIndexer"); Method registerFactories = magnetClass.getMethod("register", MagnetInstanceManager.class); registerFactories.invoke(magnetClass, this); } catch (Exception e) { System.err.println( String.format( "MagnetIndexer cannot be found. Add a @%s-annotated class to the application module.", Registry.class ) ); } } // called by generated index class void register(InstanceFactory[] factories, Map index) { this.factories = factories; this.index = index; } @Override public @Nullable InstanceFactory getInstanceFactory( Class instanceType, String classifier, Class> factoryType ) { Range range = getOptionalRange(instanceType, classifier); if (range == null) return null; if (range.getCount() == 1) return factories[range.getFrom()]; for (int index = range.getFrom(), afterLast = range.getFrom() + range.getCount(); index < afterLast; index++) { InstanceFactory candidate = factories[index]; if (candidate.getClass() == factoryType) { return candidate; } } return null; } @Override public @Nullable InstanceFactory getFilteredInstanceFactory( Class type, String classifier, FactoryFilter factoryFilter ) { Range range = getOptionalRange(type, classifier); if (range == null) { return null; } if (range.getCount() == 1) { InstanceFactory factory = factories[range.getFrom()]; if (factoryFilter.filter(factory)) { return factory; } return null; } InstanceFactory factory = null; for (int index = range.getFrom(), afterLast = range.getFrom() + range.getCount(); index < afterLast; index++) { InstanceFactory candidate = factories[index]; if (factoryFilter.filter(candidate)) { if (factory != null) { throw new IllegalStateException( String.format( "Multiple implementations of type %s (classifier: %s) can be injected," + " while single implementation is expected. Overloaded factories: %s, %s", type, classifier, factory, candidate ) ); } factory = candidate; } } return factory; } @Override public @NotNull List> getManyInstanceFactories( Class type, String classifier, FactoryFilter factoryFilter ) { Object indexed = index.get(type); if (indexed instanceof Range) { Range range = (Range) indexed; if (range.getClassifier().equals(classifier)) { return factoriesFromRange(range, factoryFilter); } return Collections.emptyList(); } if (indexed instanceof Map) { Map ranges = (Map) indexed; Range range = ranges.get(classifier); if (range != null) { return factoriesFromRange(range, factoryFilter); } return Collections.emptyList(); } return Collections.emptyList(); } private Range getOptionalRange(Class type, String classifier) { Object indexed = index.get(type); if (indexed == null) { return null; } if (indexed instanceof Range) { Range range = (Range) indexed; if (classifier.equals(range.getClassifier())) { return (Range) indexed; } return null; } if (indexed instanceof Map) { Map ranges = (Map) indexed; return ranges.get(classifier); } throw new IllegalStateException( String.format("Unsupported index type: %s", indexed.getClass()) ); } private List> factoriesFromRange(Range range, FactoryFilter factoryFilter) { List> filteredFactories = null; for (int index = range.getFrom(), afterLast = range.getFrom() + range.getCount(); index < afterLast; index++) { InstanceFactory factory = factories[index]; if (factory.getSelector() != null) { if (filteredFactories == null) { filteredFactories = new ArrayList<>(range.getCount()); } if (factoryFilter.filter(factory)) { filteredFactories.add(factory); } } } if (filteredFactories != null) { return filteredFactories; } InstanceFactory[] factories = new InstanceFactory[range.getCount()]; System.arraycopy(this.factories, range.getFrom(), factories, 0, range.getCount()); return new ImmutableArrayList<>(factories); } } ================================================ FILE: magnet/src/main/java/magnet/internal/MagnetScope.java ================================================ /* * Copyright (C) 2018-2019 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.lang.ref.WeakReference; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import magnet.Classifier; import magnet.Scope; import magnet.Scoping; import magnet.SelectorFilter; import magnet.Visitor; /* Subject to change. For internal use only. */ final class MagnetScope implements Scope, Visitor.Scope, FactoryFilter, InstanceBucket.OnInstanceListener { private static final byte CARDINALITY_OPTIONAL = 0; private static final byte CARDINALITY_SINGLE = 1; private static final byte CARDINALITY_MANY = 2; private final @Nullable MagnetScope parent; private final @NotNull InstanceManager instanceManager; private final int depth; private @Nullable WeakScopeReference childrenScopes; private @Nullable List disposables; private @Nullable String[] limits; private boolean disposed = false; final @NotNull Map instanceBuckets; @SuppressWarnings("AnonymousHasLambdaAlternative") private final @NotNull ThreadLocal instantiationContext = new ThreadLocal() { @Override protected InstantiationContext initialValue() { return new InstantiationContext(); } }; MagnetScope(@Nullable MagnetScope parent, @NotNull InstanceManager instanceManager) { this.depth = parent == null ? 0 : parent.depth + 1; this.parent = parent; this.instanceManager = instanceManager; this.instanceBuckets = new HashMap<>(32, 0.75f); } @Override public @Nullable T getOptional(@NotNull Class type) { checkNotDisposed(); InstanceFactory factory = instanceManager .getFilteredInstanceFactory(type, Classifier.NONE, this); return findOrInjectOptional(type, Classifier.NONE, factory, CARDINALITY_OPTIONAL); } @Override public @Nullable T getOptional(@NotNull Class type, @NotNull String classifier) { checkNotDisposed(); InstanceFactory factory = instanceManager .getFilteredInstanceFactory(type, classifier, this); return findOrInjectOptional(type, classifier, factory, CARDINALITY_OPTIONAL); } @Override public @NotNull T getSingle(@NotNull Class type) { return getSingle(type, Classifier.NONE); } @Override public @NotNull T getSingle(@NotNull Class type, @NotNull String classifier) { checkNotDisposed(); @Nullable InstanceFactory factory = instanceManager .getFilteredInstanceFactory(type, classifier, this); T object = findOrInjectOptional(type, classifier, factory, CARDINALITY_SINGLE); if (object == null) { throw new IllegalStateException( String.format( "Instance of type '%s' (classifier: '%s') was not found in scopes.", type.getName(), classifier ) ); } return object; } @Override public @NotNull List getMany(@NotNull Class type) { checkNotDisposed(); return getManyObjects(type, Classifier.NONE); } @Override public @NotNull List getMany(@NotNull Class type, @NotNull String classifier) { checkNotDisposed(); return getManyObjects(type, classifier); } @Override public @NotNull Scope bind(@NotNull Class type, @NotNull T object) { bind(type, object, Classifier.NONE); return this; } @Override public @NotNull Scope bind( @NotNull Class type, @NotNull T object, @NotNull String classifier ) { checkNotDisposed(); final String key = key(type, classifier); Object existing = instanceBuckets.put( key, new InstanceBucket<>( /* scope = */ this, /* factory = */ null, /* instanceType = */ type, /* instance = */ object, /* classifier = */ classifier, /* listener = */ this ) ); if (existing != null) { throw new IllegalStateException( String.format( "Instance of type %s already registered. Existing instance %s, new instance %s", key, existing, object ) ); } return this; } @Override public @NotNull Scope createSubscope() { checkNotDisposed(); MagnetScope child = new MagnetScope(this, instanceManager); childrenScopes = new WeakScopeReference(child, childrenScopes); return child; } @Override public @NotNull Scope limit(String... limits) { if (this.limits != null) { throw new IllegalStateException( String.format( "Cannot set limits to '%s' because they must only be applied once." + " Current limits '%s'", Arrays.toString(limits), Arrays.toString(this.limits)) ); } for (String limit : limits) { if (limit.length() == 0 || limit.equals("*")) { throw new IllegalStateException("Limit must not be empty or be a '*'"); } } Arrays.sort(limits); this.limits = limits; return this; } @Override @SuppressWarnings("unchecked") public void dispose() { if (disposed) return; WeakScopeReference weakScope = childrenScopes; if (weakScope != null) { do { MagnetScope scope = weakScope.get(); if (scope != null) { scope.dispose(); } weakScope = weakScope.next; } while (weakScope != null); } if (disposables != null) { for (int i = disposables.size(); i-- > 0; ) { InstanceBucket.InjectedInstance single = disposables.get(i); single.factory.dispose(single.object); } } disposed = true; if (parent != null) { parent.onChildScopeDisposed(this); } } @Override public boolean isDisposed() { return disposed; } private void onChildScopeDisposed(MagnetScope childScope) { if (childrenScopes == null) return; WeakScopeReference prevWeakScope = null; WeakScopeReference weakScope = childrenScopes; do { MagnetScope scope = weakScope.get(); if (scope == childScope) { if (prevWeakScope == null) { childrenScopes = weakScope.next; } else { prevWeakScope.next = weakScope.next; } break; } prevWeakScope = weakScope; weakScope = weakScope.next; } while (weakScope != null); } @Override public void onInstanceCreated(InstanceBucket.SingleObjectInstance instance) { if (instance instanceof InstanceBucket.InjectedInstance) { InstanceBucket.InjectedInstance injected = (InstanceBucket.InjectedInstance) instance; if (injected.factory.isDisposable()) { if (disposables == null) { disposables = new ArrayList<>(8); } disposables.add(injected); } } } private void checkNotDisposed() { if (disposed) throw new IllegalStateException("Scope is already disposed."); } @Override public boolean filter(@NotNull InstanceFactory factory) { String[] selector = factory.getSelector(); if (selector == null) { return true; } SelectorFilter selectorFilter = getOptional(SelectorFilter.class, selector[0]); if (selectorFilter == null) { throw new IllegalStateException( String.format( "Factory %s requires selector '%s', which implementation is not available in the scope." + " Make sure to add corresponding %s implementation to the classpath.", factory, Arrays.toString(selector), SelectorFilter.class) ); } return selectorFilter.filter(selector); } private @NotNull List getManyObjects(Class type, String classifier) { List> factories = instanceManager.getManyInstanceFactories(type, classifier, this); if (factories.size() == 0) return Collections.emptyList(); List objects = new ArrayList<>(factories.size()); for (InstanceFactory factory : factories) { T object = findOrInjectOptional(type, classifier, factory, CARDINALITY_MANY); if (object != null) objects.add(object); } return objects; } @SuppressWarnings("unchecked") private @Nullable T findOrInjectOptional( @NotNull Class objectType, @NotNull String classifier, @Nullable InstanceFactory factory, byte cardinality ) { @NotNull InstantiationContext instantiationContext = this.instantiationContext.get(); @NotNull String key = key(objectType, classifier); InstanceBucket deepInstanceBucket = findDeepInstanceBucket(key, factory); if (factory == null) { if (deepInstanceBucket == null) { if (cardinality == CARDINALITY_SINGLE) { throw new IllegalStateException( String.format( "Instance of type '%s' (classifier: '%s') was not found in scopes.", objectType.getName(), classifier)); } return null; } instantiationContext.onDependencyFound(deepInstanceBucket.getScope().depth, key); return deepInstanceBucket.getSingleInstance(); } boolean keepInScope = factory.getScoping() != Scoping.UNSCOPED; if (keepInScope) { if (deepInstanceBucket != null) { boolean isSingleOrOptional = cardinality != CARDINALITY_MANY; if (isSingleOrOptional) { instantiationContext.onDependencyFound(deepInstanceBucket.getScope().depth, key); return deepInstanceBucket.getSingleInstance(); } T object = deepInstanceBucket.getOptional((Class>) factory.getClass()); if (object != null) { instantiationContext.onDependencyFound(deepInstanceBucket.getScope().depth, key); return object; } } } instantiationContext.onBeginInstantiation(key); T object = factory.create(this); Instantiation instantiation = instantiationContext.onEndInstantiation(); int objectDepth = instantiation.dependencyDepth; Scoping objectScoping = factory.getScoping(); @NotNull String objectLimit = factory.getLimit(); if (objectLimit.length() > 0) { if (objectScoping == Scoping.TOPMOST) { objectDepth = findTopMostLimitedObjectDepth(objectLimit, objectDepth); } else if (objectScoping == Scoping.DIRECT) { objectDepth = findDirectLimitedObjectDepth( objectLimit, objectDepth, object, objectType, classifier, instantiation ); } if (objectDepth < 0) throwLimitNotFound(object, objectType, classifier, objectLimit); } else { if (objectScoping == Scoping.DIRECT) objectDepth = this.depth; } instantiationContext.onDependencyFound(objectDepth, key); if (keepInScope) { boolean canRegisterAtDeepInstanceBucket = deepInstanceBucket != null && deepInstanceBucket.getScope().depth == objectDepth; if (canRegisterAtDeepInstanceBucket) { deepInstanceBucket.registerObject(factory, objectType, object, classifier); } else { registerInstanceInScope( key, objectDepth, factory, objectType, object, classifier ); } Class[] siblingFactoryTypes = factory.getSiblingTypes(); if (siblingFactoryTypes != null) { for (int i = 0, size = siblingFactoryTypes.length; i < size; i += 2) { Class siblingObjectType = siblingFactoryTypes[i]; String siblingKey = key(siblingObjectType, classifier); InstanceFactory siblingFactory = instanceManager.getInstanceFactory( siblingObjectType, classifier, siblingFactoryTypes[i + 1] ); registerInstanceInScope( siblingKey, objectDepth, siblingFactory, siblingObjectType, object, classifier ); } } } return object; } private int findTopMostLimitedObjectDepth(String objectLimit, int objectDepth) { @Nullable MagnetScope scope = this; while (scope != null) { if (objectDepth > scope.depth) { return objectDepth; } else if (scope.hasLimit(objectLimit)) { return scope.depth; } scope = scope.parent; } return -1; } private int findDirectLimitedObjectDepth( String objectLimit, int objectDepth, T object, Class objectType, String classifier, Instantiation instantiation ) { @Nullable MagnetScope scope = this; int limitingScopeDepth = -1; while (scope != null) { if (scope.hasLimit(objectLimit)) { limitingScopeDepth = scope.depth; break; } scope = scope.parent; } if (limitingScopeDepth > -1 && limitingScopeDepth < objectDepth) { StringBuilder logDetails = new StringBuilder(); buildInstanceDetails(logDetails, object, objectType, classifier, objectLimit); throw new IllegalStateException( String.format( "Cannot register instance in limiting scope [depth: %s] because its" + " dependency '%s' is located in non-reachable child scope [depth: %s].\n%s", limitingScopeDepth, instantiation.dependencyKey, instantiation.dependencyDepth, logDetails ) ); } return limitingScopeDepth; } private boolean hasLimit(@NotNull String limit) { if (limit.length() == 0 || limits == null) { return false; } return Arrays.binarySearch(limits, limit) > -1; } private void registerInstanceInScope( @NotNull String key, int depth, @Nullable InstanceFactory factory, @NotNull Class objectType, @NotNull T object, @NotNull String classifier ) { if (this.depth == depth) { @SuppressWarnings("unchecked") final InstanceBucket bucket = instanceBuckets.get(key); if (bucket == null) { instanceBuckets.put( key, new InstanceBucket<>(this, factory, objectType, object, classifier, this) ); } else { bucket.registerObject(factory, objectType, object, classifier); } return; } if (parent == null) { throw new IllegalStateException( String.format( "Cannot register instance %s, factory: %s, depth: %s", object, factory, depth ) ); } parent.registerInstanceInScope(key, depth, factory, objectType, object, classifier); } @Nullable @SuppressWarnings("unchecked") private InstanceBucket findDeepInstanceBucket( @NotNull String key, @Nullable InstanceFactory factory ) { @Nullable InstanceBucket bucket = (InstanceBucket) instanceBuckets.get(key); if (bucket != null && bucket.hasInstanceWithFactory(factory)) return bucket; if (parent == null) return null; return parent.findDeepInstanceBucket(key, factory); } @NotNull static String key(Class type, String classifier) { if (classifier == null || classifier.length() == 0) { return type.getName(); } return classifier + "@" + type.getName(); } @Override public void accept(Visitor visitor, int depth) { acceptAtLevel(0, visitor, depth); } private void acceptAtLevel(int level, Visitor visitor, int depth) { if (disposed) return; boolean visitScopes = true; if (visitor.onEnterScope(this, parent)) { Collection buckets = this.instanceBuckets.values(); for (InstanceBucket bucket : buckets) { if (!bucket.accept(visitor)) { visitScopes = false; break; } } } if (visitScopes && level < depth) { WeakScopeReference scopeRef = this.childrenScopes; while (scopeRef != null) { MagnetScope scope = scopeRef.get(); if (scope != null) scope.acceptAtLevel(level + 1, visitor, depth); scopeRef = scopeRef.next; } } visitor.onExitScope(this); } @Override public @Nullable String[] getLimits() { return limits; } private static void buildInstanceDetails( StringBuilder builder, T object, Class objectType, String classifier, String objectLimit ) { builder .append("Instance:") .append("\n\tobject : ").append(object) .append("\n\ttype : ").append(objectType.getName()); if (classifier.length() > 0) { builder .append("\n\tclassifier: '").append(classifier).append("'"); } builder .append("\n\tlimit : '").append(objectLimit).append("'"); } private void throwLimitNotFound(T object, Class objectType, String classifier, String objectLimit) { StringBuilder logDetail = new StringBuilder(); buildInstanceDetails(logDetail, object, objectType, classifier, objectLimit); logDetail.append(" <- required limit"); logDetail.append("\n\nSearched scopes:\n\t-> "); @Nullable MagnetScope scope = this; while (scope != null) { String scopeName = scope.toString(); if (scope.limits != null) { logDetail.append('('); for (String limit : scope.limits) { logDetail.append("'").append(limit).append("', "); } logDetail.setLength(logDetail.length() - 2); logDetail.append(") ").append(scope.toString()); } else logDetail.append(scopeName); logDetail.append("\n\t-> "); scope = scope.parent; } logDetail.setLength(logDetail.length() - 5); logDetail.append(" "); throw new IllegalStateException( String.format( "Cannot register instance because no scope with limit '%s' has been found.\n%s", objectLimit, logDetail ) ); } private final static class InstantiationContext { private final ArrayDeque instantiations = new ArrayDeque<>(); private Instantiation currentInstantiation; void onBeginInstantiation(String key) { if (currentInstantiation != null) { instantiations.addFirst(currentInstantiation); } currentInstantiation = new Instantiation(key); if (instantiations.contains(currentInstantiation)) { throw createCircularDependencyException(); } } @NotNull Instantiation onEndInstantiation() { Instantiation instantiation = currentInstantiation; currentInstantiation = instantiations.isEmpty() ? null : instantiations.pollFirst(); return instantiation; } void onDependencyFound(int dependencyDepth, @NotNull String dependencyKey) { if (currentInstantiation == null) return; if (dependencyDepth > currentInstantiation.dependencyDepth) { currentInstantiation.dependencyDepth = dependencyDepth; currentInstantiation.dependencyKey = dependencyKey; } } private IllegalStateException createCircularDependencyException() { Instantiation[] objects = instantiations.toArray(new Instantiation[0]); StringBuilder builder = new StringBuilder() .append("Dependency injection failed because of unresolved circular dependency: "); for (int i = objects.length; i-- > 0; ) { builder.append(objects[i].key).append(" -> "); } builder.append(currentInstantiation.key); return new IllegalStateException(builder.toString()); } } private final static class Instantiation { final String key; int dependencyDepth; @Nullable String dependencyKey; Instantiation(String key) { this.key = key; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Instantiation that = (Instantiation) o; return key.equals(that.key); } @Override public int hashCode() { return key.hashCode(); } } private final static class WeakScopeReference extends WeakReference { private @Nullable WeakScopeReference next; WeakScopeReference(MagnetScope referent, @Nullable WeakScopeReference next) { super(referent); this.next = next; } } } ================================================ FILE: magnet/src/main/java/magnet/internal/Range.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; /* Subject to change. For internal use only. */ final class Range { private final int from; private final int count; private final String classifier; public Range(int from, int count, String classifier) { this.from = from; this.count = count; this.classifier = classifier; } public int getFrom() { return from; } public int getCount() { return count; } public String getClassifier() { return classifier; } } ================================================ FILE: magnet/src/test/java/magnet/internal/FactoryFilter_MagnetInstanceManagerTest.java ================================================ package magnet.internal; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import java.util.HashMap; import java.util.List; import java.util.Map; import magnet.Classifier; @RunWith(MockitoJUnitRunner.Silent.class) public class FactoryFilter_MagnetInstanceManagerTest { private MagnetInstanceManager instanceManager; @Mock FactoryFilter factoryFilter; @Mock InstanceFactory instanceFactory1; @Mock InstanceFactory instanceFactory21; @Mock InstanceFactory instanceFactory22; @Before public void before() { instanceManager = new MagnetInstanceManager(); InstanceFactory[] factories = new InstanceFactory[]{ instanceFactory1, instanceFactory21, instanceFactory22, }; Map index = new HashMap<>(); index.put(Interface1.class, new Range(0, 1, Classifier.NONE)); index.put(Interface2.class, new Range(1, 2, Classifier.NONE)); instanceManager.register(factories, index); } @Test public void getOptionalFactoryReturnsFactoryWhenFilterReturnsTrue() { when(factoryFilter.filter(instanceFactory1)).thenReturn(true); InstanceFactory factory = instanceManager .getFilteredInstanceFactory(Interface1.class, Classifier.NONE, factoryFilter); assertThat(factory).isSameInstanceAs(instanceFactory1); } @Test public void getOptionalFactoryReturnsNullWhenFilterReturnsTrue() { when(factoryFilter.filter(instanceFactory1)).thenReturn(false); InstanceFactory factory = instanceManager .getFilteredInstanceFactory(Interface1.class, Classifier.NONE, factoryFilter); assertThat(factory).isNull(); } @Test public void getOptionalFactoryReturnsFactoryWhenOneOfFiltersReturnsTrue() { when(factoryFilter.filter(instanceFactory21)).thenReturn(false); when(factoryFilter.filter(instanceFactory22)).thenReturn(true); InstanceFactory factory = instanceManager .getFilteredInstanceFactory(Interface2.class, Classifier.NONE, factoryFilter); assertThat(factory).isSameInstanceAs(instanceFactory22); } @Test public void getOptionalFactoryReturnsNullWhenAllFiltersReturnFalse() { when(factoryFilter.filter(instanceFactory21)).thenReturn(false); when(factoryFilter.filter(instanceFactory22)).thenReturn(false); InstanceFactory factory = instanceManager .getFilteredInstanceFactory(Interface2.class, Classifier.NONE, factoryFilter); assertThat(factory).isNull(); } @Test(expected = IllegalStateException.class) public void getOptionalFactoryFailsWhenMultipleFactoriesDetected() { when(factoryFilter.filter(instanceFactory21)).thenReturn(true); when(factoryFilter.filter(instanceFactory22)).thenReturn(true); instanceManager.getFilteredInstanceFactory(Interface2.class, Classifier.NONE, factoryFilter); } @Test public void getManyFactoriesReturnsEmptyListWhenAllFiltersReturnFalse() { when(instanceFactory21.getSelector()).thenReturn(new String[]{"test"}); when(instanceFactory22.getSelector()).thenReturn(new String[]{"test"}); when(factoryFilter.filter(instanceFactory21)).thenReturn(false); when(factoryFilter.filter(instanceFactory22)).thenReturn(false); List> factories = instanceManager .getManyInstanceFactories(Interface2.class, Classifier.NONE, factoryFilter); assertThat(factories).isNotNull(); assertThat(factories).isEmpty(); } @Test public void getManyFactoriesReturnsSingleFactoryWhenSingleFilterReturnsTrue() { when(instanceFactory21.getSelector()).thenReturn(new String[]{"test"}); when(instanceFactory22.getSelector()).thenReturn(new String[]{"test"}); when(factoryFilter.filter(instanceFactory21)).thenReturn(false); when(factoryFilter.filter(instanceFactory22)).thenReturn(true); List> factories = instanceManager .getManyInstanceFactories(Interface2.class, Classifier.NONE, factoryFilter); assertThat(factories).isNotNull(); assertThat(factories).hasSize(1); assertThat(factories.get(0)).isSameInstanceAs(instanceFactory22); } @Test public void getManyFactoriesReturnsAllFactoriesWhenAllFiltersReturnTrue() { when(instanceFactory21.getSelector()).thenReturn(new String[]{"test"}); when(instanceFactory22.getSelector()).thenReturn(new String[]{"test"}); when(factoryFilter.filter(instanceFactory21)).thenReturn(true); when(factoryFilter.filter(instanceFactory22)).thenReturn(true); List> factories = instanceManager .getManyInstanceFactories(Interface2.class, Classifier.NONE, factoryFilter); assertThat(factories).isNotNull(); assertThat(factories).hasSize(2); assertThat(factories.get(0)).isSameInstanceAs(instanceFactory21); assertThat(factories.get(1)).isSameInstanceAs(instanceFactory22); } @Test public void getManyFactoriesReturnsAllFactoriesWhenNoSelectorsDefined() { when(instanceFactory21.getSelector()).thenReturn(null); when(instanceFactory22.getSelector()).thenReturn(null); List> factories = instanceManager .getManyInstanceFactories(Interface2.class, Classifier.NONE, factoryFilter); verify(factoryFilter, never()).filter(any()); assertThat(factories).isNotNull(); assertThat(factories).hasSize(2); assertThat(factories.get(0)).isSameInstanceAs(instanceFactory21); assertThat(factories.get(1)).isSameInstanceAs(instanceFactory22); } @Test public void getManyFactoriesReturnsFactoriesWhenSomeFactoriesHaveSelectors() { when(instanceFactory21.getSelector()).thenReturn(null); when(instanceFactory22.getSelector()).thenReturn(new String[]{"test"}); when(factoryFilter.filter(instanceFactory22)).thenReturn(true); List> factories = instanceManager .getManyInstanceFactories(Interface2.class, Classifier.NONE, factoryFilter); verify(factoryFilter, never()).filter(instanceFactory21); assertThat(factories).isNotNull(); assertThat(factories).hasSize(1); assertThat(factories.get(0)).isSameInstanceAs(instanceFactory22); } interface Interface1 {} interface Interface2 {} } ================================================ FILE: magnet/src/test/java/magnet/internal/FactoryFilter_MagnetScopeTest.java ================================================ package magnet.internal; import magnet.SelectorFilter; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.Silent.class) public class FactoryFilter_MagnetScopeTest { private InstrumentedScope scope; @Mock InstanceManager instanceManager; @Mock InstanceFactory instanceFactory; @Mock SelectorFilter selectorFilter; @Before public void before() { scope = new InstrumentedScope(new MagnetScope(null, instanceManager)); } @Test public void noSelectorReturnsTrue() { when(instanceFactory.getSelector()).thenReturn(null); boolean result = scope.filter(instanceFactory); assertThat(result).isTrue(); } @Test(expected = IllegalStateException.class) public void missingSelectorFactoryThrows() { when(instanceFactory.getSelector()).thenReturn(new String[]{"test"}); scope.filter(instanceFactory); } @Test public void existingSelectorReturnsFilterTrue() { scope.bind(SelectorFilter.class, selectorFilter, "test"); when(instanceFactory.getSelector()).thenReturn(new String[]{"test"}); when(selectorFilter.filter(any())).thenReturn(true); boolean result = scope.filter(instanceFactory); assertThat(result).isTrue(); } @Test public void existingSelectorReturnsFilterFalse() { scope.bind(SelectorFilter.class, selectorFilter, "test"); when(instanceFactory.getSelector()).thenReturn(new String[]{"test"}); when(selectorFilter.filter(any())).thenReturn(false); boolean result = scope.filter(instanceFactory); assertThat(result).isFalse(); } } ================================================ FILE: magnet/src/test/java/magnet/internal/InstanceBucketTest.java ================================================ package magnet.internal; import magnet.Classifier; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import java.util.List; import static com.google.common.truth.Truth.assertThat; @RunWith(MockitoJUnitRunner.StrictStubs.class) public class InstanceBucketTest { @Mock private Factory1 factory1; @Mock private Factory2 factory2; @Mock private Interface1 instance1; @Mock private Interface1 instance2; @Mock private InstanceBucket.OnInstanceListener listener; @Mock private MagnetScope scope; @Test @SuppressWarnings("unchecked") public void test_getScope() { InstanceBucket instances = new InstanceBucket( scope, factory1, Interface1.class, instance1, Classifier.NONE, listener ); assertThat(instances.getScope()).isSameInstanceAs(scope); } @Test @SuppressWarnings("unchecked") public void test_getInstances_SingleItem() { InstanceBucket instances = new InstanceBucket( scope, factory1, Interface1.class, instance1, Classifier.NONE, listener ); List result = instances.getMany(); assertThat(result).containsExactly(instance1); } @Test @SuppressWarnings("unchecked") public void test_getInstances_ManyItems() { InstanceBucket instances = new InstanceBucket( scope, factory1, Interface1.class, instance1, Classifier.NONE, listener ); instances.registerObject(factory2, Interface1.class, instance2, Classifier.NONE); List result = instances.getMany(); assertThat(result).containsExactly(instance1, instance2); } @Test @SuppressWarnings("unchecked") public void test_getSingleInstance() { InstanceBucket instances = new InstanceBucket( scope, factory1, Interface1.class, instance1, Classifier.NONE, listener ); Interface1 result = instances.getSingleInstance(); assertThat(result).isSameInstanceAs(instance1); } interface Interface1 {} abstract static class Factory1 extends InstanceFactory {} abstract static class Factory2 extends InstanceFactory {} } ================================================ FILE: magnet/src/test/java/magnet/internal/InstrumentedScope.java ================================================ package magnet.internal; import magnet.Scope; import magnet.Visitor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.List; /** Used for testing MagnetScopeContainer. */ public class InstrumentedScope implements Scope, FactoryFilter { public final MagnetScope scope; public InstrumentedScope(MagnetScope scope) { this.scope = scope; } public InstrumentedScope(Scope scope) { this.scope = (MagnetScope) scope; } @Override public @Nullable T getOptional(@NotNull Class type) { return scope.getOptional(type); } @Override public @Nullable T getOptional(@NotNull Class type, @NotNull String classifier) { return scope.getOptional(type, classifier); } @Override public @NotNull T getSingle(@NotNull Class type) { return scope.getSingle(type); } @Override public @NotNull T getSingle(@NotNull Class type, @NotNull String classifier) { return scope.getSingle(type, classifier); } @Override public @NotNull List getMany(@NotNull Class type) { return scope.getMany(type); } @Override public @NotNull List getMany(@NotNull Class type, @NotNull String classifier) { return scope.getMany(type, classifier); } @Override public @NotNull Scope bind(@NotNull Class type, @NotNull T instance) { scope.bind(type, instance); return this; } @Override public @NotNull Scope bind(@NotNull Class type, @NotNull T instance, @NotNull String classifier) { scope.bind(type, instance, classifier); return this; } @Override public @NotNull Scope createSubscope() { return new InstrumentedScope((MagnetScope) scope.createSubscope()); } @Override public @NotNull Scope limit(String... limits) { return scope.limit(limits); } @Override public void dispose() { scope.dispose(); } @Override public boolean isDisposed() { return scope.isDisposed(); } @Override public void accept(Visitor visitor, int depth) { scope.accept(visitor, depth); } @Override public boolean filter(InstanceFactory factory) { return scope.filter(factory); } /** Returns and object registered right in this scope or null if no object was registered. */ @SuppressWarnings("unchecked") T getOptionalInScope(Class type, String classifier) { InstanceBucket instance = scope.instanceBuckets.get(MagnetScope.key(type, classifier)); return instance == null ? null : instance.getSingleInstance(); } /** Returns list of objects registered right in this scope. */ @SuppressWarnings("unchecked") List getManyInScope(Class type, String classifier) { InstanceBucket instance = scope.instanceBuckets.get(MagnetScope.key(type, classifier)); return instance == null ? Collections.emptyList() : instance.getMany(); } /** Injects given object right into the scope, as I would be injected using given factory. */ @SuppressWarnings("unchecked") public InstrumentedScope instrumentObjectIntoScope( InstanceFactory factory, Class objectType, T object, String classifier ) { String key = MagnetScope.key(objectType, classifier); InstanceBucket existing = scope.instanceBuckets.get(key); if (existing == null) { scope.instanceBuckets.put( key, new InstanceBucket(scope, factory, objectType, object, classifier, scope) ); } else { existing.registerObject(factory, objectType, object, classifier); } return this; } InstrumentedScope createInstrumentedSubscope() { return new InstrumentedScope((MagnetScope) scope.createSubscope()); } } ================================================ FILE: magnet/src/test/java/magnet/internal/MagnetInstanceManagerTest.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; import magnet.Classifier; import magnet.Scope; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.Silent.class) public class MagnetInstanceManagerTest { private static final String CLASSIFIER_LOCAL = "local"; @Mock InstanceFactory instanceFactoryType1Impl1; @Mock InstanceFactory instanceFactoryType1Impl2; @Mock InstanceFactory instanceFactoryType2Impl1; @Mock InstanceFactory instanceFactoryType2Impl2; @Mock InstanceFactory instanceFactoryType3Impl1; @Mock FactoryFilter factoryFilter; @Mock Scope scope; private MagnetInstanceManager instManager; @Before public void before() { instManager = new MagnetInstanceManager(); when(factoryFilter.filter(any())).thenReturn(true); when(instanceFactoryType1Impl1.create(scope)).thenReturn(new Type1Impl()); when(instanceFactoryType1Impl2.create(scope)).thenReturn(new Type1Impl()); when(instanceFactoryType2Impl1.create(scope)).thenReturn(new Type2Impl()); when(instanceFactoryType2Impl2.create(scope)).thenReturn(new Type2Impl()); when(instanceFactoryType3Impl1.create(scope)).thenReturn(new Type3Impl()); InstanceFactory[] factories = new InstanceFactory[]{ instanceFactoryType1Impl1, instanceFactoryType1Impl2, instanceFactoryType2Impl1, instanceFactoryType2Impl2, instanceFactoryType3Impl1 }; Map index = new HashMap<>(); Map ranges1 = new HashMap<>(); ranges1.put(Classifier.NONE, new Range(0, 1, Classifier.NONE)); ranges1.put(CLASSIFIER_LOCAL, new Range(1, 1, CLASSIFIER_LOCAL)); index.put(Type1.class, ranges1); index.put(Type2.class, new Range(2, 2, Classifier.NONE)); index.put(Type3.class, new Range(4, 1, CLASSIFIER_LOCAL)); instManager.register(factories, index); } @Test public void getOptionalFactory_Classified_Existing_SingleTypeInstance() { // when InstanceFactory factory = instManager.getFilteredInstanceFactory( Type3.class, CLASSIFIER_LOCAL, factoryFilter); // then assertThat(factory).isNotNull(); } @Test public void getOptionalFactory_Classified_Existing_ManyTypeInstances() { // when InstanceFactory factory = instManager.getFilteredInstanceFactory( Type1.class, CLASSIFIER_LOCAL, factoryFilter); // then assertThat(factory).isNotNull(); } @Test public void getOptionalFactory_NotClassified_Existing() { // when InstanceFactory factory = instManager.getFilteredInstanceFactory( Type1.class, Classifier.NONE, factoryFilter); // then assertThat(factory).isNotNull(); } @Test public void getOptionalFactory_Classified_NotExisting() { // when InstanceFactory factory = instManager.getFilteredInstanceFactory( String.class, CLASSIFIER_LOCAL, factoryFilter); // then assertThat(factory).isNull(); } @Test public void getOptionalFactory_NotClassified_NotExisting() { // when InstanceFactory factory = instManager.getFilteredInstanceFactory( String.class, Classifier.NONE, factoryFilter); // then assertThat(factory).isNull(); } @Test public void getManyFactories_NotClassified_ManyTypeInstances() { // when List> factories = instManager.getManyInstanceFactories( Type1.class, Classifier.NONE, factoryFilter); // then assertThat(factories).hasSize(1); assertThat(factories.get(0)).isEqualTo(instanceFactoryType1Impl1); } @Test public void getManyFactories_Classified_ManyTypeInstances() { // when List> factories = instManager.getManyInstanceFactories( Type1.class, CLASSIFIER_LOCAL, factoryFilter); // then assertThat(factories).hasSize(1); assertThat(factories.get(0)).isEqualTo(instanceFactoryType1Impl2); } @Test public void getManyFactories_NotClassified_SingleTypeInstances() { // when List> factories = instManager.getManyInstanceFactories( Type2.class, Classifier.NONE, factoryFilter); // then assertThat(factories).hasSize(2); assertThat(factories).containsExactly(instanceFactoryType2Impl1, instanceFactoryType2Impl2); } @Test public void getManyFactories_Classified_SingleTypeInstances() { // when List> factories = instManager.getManyInstanceFactories( Type3.class, CLASSIFIER_LOCAL, factoryFilter); // then assertThat(factories).hasSize(1); assertThat(factories.get(0)).isEqualTo(instanceFactoryType3Impl1); } interface Type1 {} interface Type2 {} interface Type3 {} class Type1Impl implements Type1 {} class Type2Impl implements Type2 {} class Type3Impl implements Type3 {} } ================================================ FILE: magnet/src/test/java/magnet/internal/MagnetScope_CircularDependencyTest.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; import magnet.Scope; import magnet.Scoping; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.HashMap; import java.util.List; import java.util.Map; @RunWith(JUnit4.class) public class MagnetScope_CircularDependencyTest { private MagnetScope scope; @Before public void before() { scope = new MagnetScope(null, new StubInstanceManager()); } @Test(expected = IllegalStateException.class) public void dependency_One_Two_Three_One() { scope.getSingle(MenuItem.class, "one"); } @Test(expected = IllegalStateException.class) public void dependency_Four_Four() { scope.getSingle(MenuItem.class, "four"); } @Test(expected = IllegalStateException.class) public void dependency_Five_Constructor_Five() { scope.getSingle(MenuItem.class, "five"); } private static class MenuItemOneFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { scope.getSingle(MenuItem.class, "two"); return new MenuItemOne(); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } private static class MenuItemTwoFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { scope.getSingle(MenuItem.class, "three"); return new MenuItemTwo(); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } private static class MenuItemThreeFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { scope.getSingle(MenuItem.class, "one"); return new MenuItemThree(); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } private static class MenuItemFourFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { scope.getSingle(MenuItem.class, "four"); return new MenuItemFour(); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } private static class MenuItemFiveFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { return new MenuItemFive(scope); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } private static class StubInstanceManager implements InstanceManager { private final Map> factories; StubInstanceManager() { factories = new HashMap<>(); factories.put("one", new MenuItemOneFactory()); factories.put("two", new MenuItemTwoFactory()); factories.put("three", new MenuItemThreeFactory()); factories.put("four", new MenuItemFourFactory()); factories.put("five", new MenuItemFiveFactory()); } @Override public InstanceFactory getInstanceFactory( Class instanceType, String classifier, Class> factoryType ) { throw new UnsupportedOperationException(); } @Override public InstanceFactory getFilteredInstanceFactory( Class type, String classifier, FactoryFilter factoryFilter ) { //noinspection unchecked return (InstanceFactory) factories.get(classifier); } @Override public List> getManyInstanceFactories( Class type, String classifier, FactoryFilter factoryFilter ) { throw new UnsupportedOperationException(); } } private interface MenuItem {} private static class MenuItemOne implements MenuItem {} private static class MenuItemTwo implements MenuItem {} private static class MenuItemThree implements MenuItem {} private static class MenuItemFour implements MenuItem {} private static class MenuItemFive implements MenuItem { public MenuItemFive(Scope scope) { scope.getSingle(MenuItem.class, "five"); } } } ================================================ FILE: magnet/src/test/java/magnet/internal/MagnetScope_DisposeTest.java ================================================ package magnet.internal; import magnet.Classifier; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.StrictStubs.class) public class MagnetScope_DisposeTest { @Mock private InstanceManager instanceManager; @Mock private Factory1 factory1; @Mock private Factory2 factory2; @Mock private Factory3 factory3; @Mock private Factory4 factory4; @Mock private Interface instance1; @Mock private Interface instance2; @Mock private Interface instance3; @Mock private Interface instance4; @Test public void disposeSingleScope_NoClassifier() { InstrumentedScope scope = new InstrumentedScope( new MagnetScope(null, instanceManager) ); when(factory1.isDisposable()).thenReturn(true); scope.instrumentObjectIntoScope(factory1, Interface.class, instance1, Classifier.NONE); scope.instrumentObjectIntoScope(factory2, Interface.class, instance2, Classifier.NONE); when(factory3.isDisposable()).thenReturn(true); scope.instrumentObjectIntoScope(factory3, Interface.class, instance3, Classifier.NONE); scope.dispose(); verify(factory1, times(1)).dispose(instance1); verify(factory2, never()).dispose(any()); verify(factory3, times(1)).dispose(instance3); } @Test public void disposeSingleScope_WithClassifiers() { InstrumentedScope scope = new InstrumentedScope( new MagnetScope(null, instanceManager) ); when(factory1.isDisposable()).thenReturn(true); scope.instrumentObjectIntoScope(factory1, Interface.class, instance1, "classifier1"); scope.instrumentObjectIntoScope(factory2, Interface.class, instance2, "classifier2"); when(factory3.isDisposable()).thenReturn(true); scope.instrumentObjectIntoScope(factory3, Interface.class, instance3, Classifier.NONE); scope.dispose(); verify(factory1, times(1)).dispose(instance1); verify(factory2, never()).dispose(any()); verify(factory3, times(1)).dispose(instance3); } @Test public void disposingParentScope_DisposesChildrenScopes() { MagnetScope parentScope; InstrumentedScope parent = new InstrumentedScope(parentScope = new MagnetScope(null, instanceManager)); when(factory1.isDisposable()).thenReturn(true); parent.instrumentObjectIntoScope(factory1, Interface.class, instance1, Classifier.NONE); InstrumentedScope child = new InstrumentedScope((MagnetScope) parentScope.createSubscope()); when(factory2.isDisposable()).thenReturn(true); child.instrumentObjectIntoScope(factory2, Interface.class, instance2, Classifier.NONE); child.instrumentObjectIntoScope(factory3, Interface.class, instance3, Classifier.NONE); parent.dispose(); verify(factory1, times(1)).dispose(instance1); verify(factory2, times(1)).dispose(instance2); verify(factory3, never()).dispose(instance3); } @Test public void disposingChildScope_KeepsParentScopes() { MagnetScope parentScope; InstrumentedScope parent = new InstrumentedScope(parentScope = new MagnetScope(null, instanceManager)); parent.instrumentObjectIntoScope(factory1, Interface.class, instance1, Classifier.NONE); InstrumentedScope child = new InstrumentedScope((MagnetScope) parentScope.createSubscope()); when(factory2.isDisposable()).thenReturn(true); child.instrumentObjectIntoScope(factory2, Interface.class, instance2, Classifier.NONE); child.dispose(); verify(factory2, times(1)).dispose(instance2); verify(factory1, never()).dispose(any()); } @Test(expected = IllegalStateException.class) public void disposedScopeThrowsException() { InstrumentedScope scope = new InstrumentedScope( new MagnetScope(null, instanceManager) ); when(factory1.isDisposable()).thenReturn(true); scope.instrumentObjectIntoScope(factory1, Interface.class, instance1, "classifier1"); scope.dispose(); scope.getOptional(String.class); } @Test public void disposeChildScope_InReversOrdered() { InstrumentedScope scope = new InstrumentedScope( new MagnetScope(null, instanceManager) ); when(factory1.isDisposable()).thenReturn(true); when(factory2.isDisposable()).thenReturn(true); when(factory3.isDisposable()).thenReturn(true); scope.instrumentObjectIntoScope(factory1, Interface.class, instance1, Classifier.NONE); scope.instrumentObjectIntoScope(factory2, Interface.class, instance2, Classifier.NONE); scope.instrumentObjectIntoScope(factory3, Interface.class, instance3, Classifier.NONE); scope.dispose(); InOrder order = inOrder(factory3, factory2, factory1); order.verify(factory3, times(1)).dispose(instance3); order.verify(factory2, times(1)).dispose(instance2); order.verify(factory1, times(1)).dispose(instance1); } @Test public void disposeParentScope_InReversOrdered() { when(factory1.isDisposable()).thenReturn(true); when(factory2.isDisposable()).thenReturn(true); when(factory3.isDisposable()).thenReturn(true); MagnetScope parentScope; InstrumentedScope parent = new InstrumentedScope(parentScope = new MagnetScope(null, instanceManager)); parent.instrumentObjectIntoScope(factory1, Interface.class, instance1, Classifier.NONE); InstrumentedScope child = new InstrumentedScope((MagnetScope) parentScope.createSubscope()); when(factory2.isDisposable()).thenReturn(true); child.instrumentObjectIntoScope(factory2, Interface.class, instance2, Classifier.NONE); parent.instrumentObjectIntoScope(factory3, Interface.class, instance3, Classifier.NONE); parent.dispose(); InOrder order = inOrder(factory2, factory3, factory1); order.verify(factory2, times(1)).dispose(instance2); order.verify(factory3, times(1)).dispose(instance3); order.verify(factory1, times(1)).dispose(instance1); } @Test public void disposeFirstChildThenParentScopes() { when(factory1.isDisposable()).thenReturn(true); InstrumentedScope parent = new InstrumentedScope(new MagnetScope(null, instanceManager)) .instrumentObjectIntoScope(factory1, Interface.class, instance1, Classifier.NONE); when(factory2.isDisposable()).thenReturn(true); InstrumentedScope child = parent.createInstrumentedSubscope() .instrumentObjectIntoScope(factory2, Interface.class, instance2, Classifier.NONE); child.dispose(); parent.dispose(); InOrder order = inOrder(factory2, factory1); order.verify(factory2, times(1)).dispose(instance2); order.verify(factory1, times(1)).dispose(instance1); } @Test public void disposeMultipleChildScopes_123() { InstrumentedScope[] child = prepareMultipleChildrenScopes(); child[1].dispose(); child[2].dispose(); child[3].dispose(); child[0].dispose(); InOrder order = inOrder(factory2, factory3, factory4, factory1); order.verify(factory2, times(1)).dispose(instance2); order.verify(factory3, times(1)).dispose(instance3); order.verify(factory4, times(1)).dispose(instance4); order.verify(factory1, times(1)).dispose(instance1); } @Test public void disposeMultipleChildScopes_132() { InstrumentedScope[] child = prepareMultipleChildrenScopes(); child[1].dispose(); child[3].dispose(); child[2].dispose(); child[0].dispose(); InOrder order = inOrder(factory2, factory4, factory3, factory1); order.verify(factory2, times(1)).dispose(instance2); order.verify(factory4, times(1)).dispose(instance4); order.verify(factory3, times(1)).dispose(instance3); order.verify(factory1, times(1)).dispose(instance1); } @Test public void disposeMultipleChildScopes_213() { InstrumentedScope[] child = prepareMultipleChildrenScopes(); child[2].dispose(); child[1].dispose(); child[3].dispose(); child[0].dispose(); InOrder order = inOrder(factory3, factory2, factory4, factory1); order.verify(factory3, times(1)).dispose(instance3); order.verify(factory2, times(1)).dispose(instance2); order.verify(factory4, times(1)).dispose(instance4); order.verify(factory1, times(1)).dispose(instance1); } @Test public void disposeMultipleChildScopes_231() { InstrumentedScope[] child = prepareMultipleChildrenScopes(); child[2].dispose(); child[3].dispose(); child[1].dispose(); child[0].dispose(); InOrder order = inOrder(factory3, factory4, factory2, factory1); order.verify(factory3, times(1)).dispose(instance3); order.verify(factory4, times(1)).dispose(instance4); order.verify(factory2, times(1)).dispose(instance2); order.verify(factory1, times(1)).dispose(instance1); } @Test public void disposeMultipleChildScopes_312() { InstrumentedScope[] child = prepareMultipleChildrenScopes(); child[3].dispose(); child[1].dispose(); child[2].dispose(); child[0].dispose(); InOrder order = inOrder(factory4, factory2, factory3, factory1); order.verify(factory4, times(1)).dispose(instance4); order.verify(factory2, times(1)).dispose(instance2); order.verify(factory3, times(1)).dispose(instance3); order.verify(factory1, times(1)).dispose(instance1); } @Test public void disposeMultipleChildScopes_321() { InstrumentedScope[] child = prepareMultipleChildrenScopes(); child[3].dispose(); child[2].dispose(); child[1].dispose(); child[0].dispose(); InOrder order = inOrder(factory4, factory3, factory2, factory1); order.verify(factory4, times(1)).dispose(instance4); order.verify(factory3, times(1)).dispose(instance3); order.verify(factory2, times(1)).dispose(instance2); order.verify(factory1, times(1)).dispose(instance1); } private InstrumentedScope[] prepareMultipleChildrenScopes() { when(factory1.isDisposable()).thenReturn(true); InstrumentedScope parent = new InstrumentedScope(new MagnetScope(null, instanceManager)) .instrumentObjectIntoScope(factory1, Interface.class, instance1, Classifier.NONE); when(factory2.isDisposable()).thenReturn(true); InstrumentedScope child1 = parent.createInstrumentedSubscope() .instrumentObjectIntoScope(factory2, Interface.class, instance2, Classifier.NONE); when(factory3.isDisposable()).thenReturn(true); InstrumentedScope child2 = parent.createInstrumentedSubscope() .instrumentObjectIntoScope(factory3, Interface.class, instance3, Classifier.NONE); when(factory4.isDisposable()).thenReturn(true); InstrumentedScope child3 = parent.createInstrumentedSubscope() .instrumentObjectIntoScope(factory4, Interface.class, instance4, Classifier.NONE); return new InstrumentedScope[]{parent, child1, child2, child3}; } interface Interface {} abstract static class Factory1 extends InstanceFactory {} abstract static class Factory2 extends InstanceFactory {} abstract static class Factory3 extends InstanceFactory {} abstract static class Factory4 extends InstanceFactory {} } ================================================ FILE: magnet/src/test/java/magnet/internal/MagnetScope_FindDeepForSiblingTypesTest.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; import magnet.Classifier; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.Silent.class) public class MagnetScope_FindDeepForSiblingTypesTest { @Mock private Implementation1Factory factory1; @Mock private SiblingImplementation1Factory factory21; @Mock private SiblingImplementation2Factory factory22; @Mock private Implementation instance1; @Mock private SiblingImplementation instance2; private MagnetInstanceManager instanceManager; @Before public void before() { instanceManager = new MagnetInstanceManager(); InstanceFactory[] factories = new InstanceFactory[]{factory1, factory21, factory22}; Map index = new HashMap<>(); index.put(Interface.class, new Range(0, 2, Classifier.NONE)); index.put(SiblingInterface.class, new Range(2, 1, Classifier.NONE)); instanceManager.register(factories, index); when(factory1.create(any())).thenReturn(Mockito.mock(Interface.class)); when(factory21.create(any())).thenReturn(Mockito.mock(Interface.class)); when(factory22.create(any())).thenReturn(Mockito.mock(SiblingInterface.class)); when(factory21.getSiblingTypes()).thenReturn(new Class[]{SiblingInterface.class, SiblingImplementation2Factory.class}); when(factory22.getSiblingTypes()).thenReturn(new Class[]{Interface.class, SiblingImplementation1Factory.class}); } @Test public void getMany_collectsAlsoSiblingInstancesFromScope() { // given InstrumentedScope scope0 = new InstrumentedScope( new MagnetScope(null, instanceManager) ); scope0.instrumentObjectIntoScope(factory21, Interface.class, instance2, Classifier.NONE); scope0.instrumentObjectIntoScope(factory22, SiblingInterface.class, instance2, Classifier.NONE); InstrumentedScope scope1 = (InstrumentedScope) scope0.createSubscope(); scope1.instrumentObjectIntoScope(factory1, Interface.class, instance1, Classifier.NONE); // when List many = scope1.getMany(Interface.class); // then assertThat(many).containsExactly(instance1, instance2); } // -- configuration private interface Interface {} private interface SiblingInterface {} private static class Implementation implements Interface {} private static class SiblingImplementation implements Interface, SiblingInterface {} private abstract static class Implementation1Factory extends InstanceFactory {} private abstract static class SiblingImplementation1Factory extends InstanceFactory {} private abstract static class SiblingImplementation2Factory extends InstanceFactory {} } ================================================ FILE: magnet/src/test/java/magnet/internal/MagnetScope_GetManyTest.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; import magnet.Scope; import magnet.Scoping; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.google.common.truth.Truth.assertThat; @RunWith(JUnit4.class) public class MagnetScope_GetManyTest { private InstrumentedScope scope1; private InstrumentedScope scope2; @Before public void before() { scope1 = new InstrumentedScope( new MagnetScope(null, new StubInstanceManager())); scope2 = (InstrumentedScope) scope1 .createSubscope() .bind(Dependency2.class, new Dependency2()); } @Test public void getSingleScopedMany() { // when List oneList = scope1.getMany(MenuItem.class, "one"); // then assertThat(oneList).hasSize(3); assertThat(oneList).containsNoDuplicates(); List onesInScope = scope1.getManyInScope(MenuItem.class, "one"); assertThat(onesInScope).hasSize(3); assertThat(onesInScope).containsNoDuplicates(); assertThat(onesInScope).containsAtLeastElementsIn(oneList); } @Test(expected = IllegalStateException.class) public void getMultiScopedMany_requestScope1() { scope1.getMany(MenuItem.class, "two"); } @Test public void getMultiScopedMany_requestScope2() { // when List twoList = scope2.getMany(MenuItem.class, "two"); // then assertThat(twoList).hasSize(2); assertThat(twoList).containsNoDuplicates(); List oneScope1 = scope1.getManyInScope(MenuItem.class, "one"); List twoScope1 = scope1.getManyInScope(MenuItem.class, "two"); List oneScope2 = scope2.getManyInScope(MenuItem.class, "one"); List twoScope2 = scope2.getManyInScope(MenuItem.class, "two"); assertThat(oneScope1).hasSize(3); assertThat(twoScope1).hasSize(1); assertThat(oneScope2).hasSize(0); assertThat(twoScope2).hasSize(1); assertThat(twoList).contains(twoScope1.get(0)); assertThat(twoList).contains(twoScope2.get(0)); } private static class MenuItemOne1Factory extends InstanceFactory { @Override public MenuItem create(Scope scope) { return new MenuItemOne1(); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } private static class MenuItemOne2Factory extends InstanceFactory { @Override public MenuItem create(Scope scope) { return new MenuItemOne2(); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } private static class MenuItemOne3Factory extends InstanceFactory { @Override public MenuItem create(Scope scope) { return new MenuItemOne3(); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } private static class MenuItemTwo1Factory extends InstanceFactory { @Override public MenuItem create(Scope scope) { return new MenuItemTwo1(); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } private static class MenuItemTwo2Factory extends InstanceFactory { @Override public MenuItem create(Scope scope) { scope.getSingle(Dependency2.class); scope.getMany(MenuItem.class, "one"); return new MenuItemTwo2(); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } @SuppressWarnings("unchecked") private static class StubInstanceManager implements InstanceManager { private final Map factories; StubInstanceManager() { factories = new HashMap<>(); List oneList = new ArrayList<>(); oneList.add(new MenuItemOne1Factory()); oneList.add(new MenuItemOne2Factory()); oneList.add(new MenuItemOne3Factory()); factories.put("one", oneList); List twoList = new ArrayList<>(); twoList.add(new MenuItemTwo1Factory()); twoList.add(new MenuItemTwo2Factory()); factories.put("two", twoList); } @Override public InstanceFactory getInstanceFactory( Class instanceType, String classifier, Class> factoryType ) { throw new UnsupportedOperationException(); } @Override public InstanceFactory getFilteredInstanceFactory( Class type, String classifier, FactoryFilter factoryFilter ) { return (InstanceFactory) factories.get(classifier); } @Override public List> getManyInstanceFactories( Class type, String classifier, FactoryFilter factoryFilter ) { return (List>) factories.get(classifier); } } private interface MenuItem {} private static class MenuItemOne1 implements MenuItem {} private static class MenuItemOne2 implements MenuItem {} private static class MenuItemOne3 implements MenuItem {} private static class MenuItemTwo1 implements MenuItem {} private static class MenuItemTwo2 implements MenuItem {} private static class Dependency2 {} } ================================================ FILE: magnet/src/test/java/magnet/internal/MagnetScope_ManyInstancesInMultipleScopesTest.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; import magnet.Classifier; import magnet.Scope; import magnet.Scoping; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.ArrayList; import java.util.List; import static com.google.common.truth.Truth.assertThat; @RunWith(JUnit4.class) public class MagnetScope_ManyInstancesInMultipleScopesTest { private InstrumentedScope scope1; private InstrumentedScope scope2; private InstrumentedScope scope3; private InstanceImpl1Factory factory1 = new InstanceImpl1Factory(); private InstanceImpl2Factory factory2 = new InstanceImpl2Factory(); private InstanceImpl3Factory factory3 = new InstanceImpl3Factory(); @Before public void before() { scope1 = new InstrumentedScope(new MagnetScope(null, new StubInstanceManager())); scope1.instrumentObjectIntoScope(factory1, InstanceType.class, new InstanceImpl1(), Classifier.NONE); scope2 = (InstrumentedScope) scope1.createSubscope(); scope2.instrumentObjectIntoScope(factory2, InstanceType.class, new InstanceImpl2(), Classifier.NONE); scope3 = (InstrumentedScope) scope2.createSubscope(); } @Test public void getSingleScopedMany() { // given // when List instances = scope3.getMany(InstanceType.class, ""); // then assertThat(instances).hasSize(3); assertThat(instances).containsNoDuplicates(); assertThat(scope3.getOptionalInScope(InstanceType.class, "")).isNull(); assertThat(scope2.getOptionalInScope(InstanceType.class, "")).isNotNull(); List instancesInScope1 = scope1.getManyInScope(InstanceType.class, ""); assertThat(instancesInScope1).isNotNull(); assertThat(instancesInScope1).hasSize(2); } private static class InstanceImpl1Factory extends InstanceFactory { @Override public InstanceType create(Scope scope) { return new InstanceImpl1(); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } private static class InstanceImpl2Factory extends InstanceFactory { @Override public InstanceType create(Scope scope) { return new InstanceImpl2(); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } private static class InstanceImpl3Factory extends InstanceFactory { @Override public InstanceType create(Scope scope) { return new InstanceImpl3(); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } @SuppressWarnings("unchecked") private class StubInstanceManager implements InstanceManager { private final List instanceTypeFactories = new ArrayList<>(); StubInstanceManager() { instanceTypeFactories.add(factory1); instanceTypeFactories.add(factory2); instanceTypeFactories.add(factory3); } @Override public InstanceFactory getInstanceFactory( Class instanceType, String classifier, Class> factoryType ) { throw new UnsupportedOperationException(); } @Override public InstanceFactory getFilteredInstanceFactory( Class type, String classifier, FactoryFilter factoryFilter ) { throw new UnsupportedOperationException(); } @Override public List> getManyInstanceFactories( Class type, String classifier, FactoryFilter factoryFilter ) { if (type == InstanceType.class) { return (List>) instanceTypeFactories; } throw new UnsupportedOperationException(); } } private interface InstanceType {} private static class InstanceImpl1 implements InstanceType {} private static class InstanceImpl2 implements InstanceType {} private static class InstanceImpl3 implements InstanceType {} } ================================================ FILE: magnet/src/test/java/magnet/internal/MagnetScope_RegisterAndGetTest.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; import static com.google.common.truth.Truth.assertThat; import magnet.Scope; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.StrictStubs.class) public class MagnetScope_RegisterAndGetTest { @Mock InstanceManager instanceManager; private Scope scope; @Before public void before() { scope = new MagnetScope(null, instanceManager); } @Test public void noClassifier_GetOptionalNotRegistered() { // when Integer dependency = scope.getOptional(Integer.class); // then assertThat(dependency).isNull(); } @Test public void noClassifier_GetOptionalRegistered() { // given scope.bind(Integer.class, 100); // when Integer dependency = scope.getOptional(Integer.class); // then assertThat(dependency).isEqualTo(100); } @Test(expected = IllegalStateException.class) public void noClassifier_GetSingleNotRegistered() { scope.getSingle(Integer.class); } @Test public void noClassifier_GetSingleRegistered() { // given scope.bind(Integer.class, 100); // when Integer dependency = scope.getSingle(Integer.class); // then assertThat(dependency).isEqualTo(100); } @Test(expected = IllegalStateException.class) public void noClassifier_RegisterOverwrite() { scope.bind(Integer.class, 100); scope.bind(Integer.class, 200); } @Test public void classifier_GetOptionalNotRegistered() { // when Integer dependency = scope.getOptional(Integer.class, "common"); // then assertThat(dependency).isNull(); } @Test public void classifier_GetOptionalRegisteredNoClassifier() { // given scope.bind(Integer.class, 100, "common"); // when Integer dependency = scope.getOptional(Integer.class); // then assertThat(dependency).isNull(); } @Test public void classifier_GetOptionalRegisteredWrongClassifier() { // given scope.bind(Integer.class, 100, "common"); // when Integer dependency = scope.getOptional(Integer.class, "wrong"); // then assertThat(dependency).isNull(); } @Test(expected = IllegalStateException.class) public void classifier_GetSingleNotRegistered() { scope.getSingle(Integer.class, "common"); } @Test public void classifier_GetSingleRegistered() { // given scope.bind(Integer.class, 100, "common"); // when Integer dependency = scope.getSingle(Integer.class, "common"); // then assertThat(dependency).isEqualTo(100); } @Test(expected = IllegalStateException.class) public void classifier_RegisterOverwrite() { scope.bind(Integer.class, 100, "common"); scope.bind(Integer.class, 200, "common"); } } ================================================ FILE: magnet/src/test/java/magnet/internal/MagnetScope_SiblingTypesTest.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; import magnet.Classifier; import magnet.Scope; import magnet.Scoping; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; import java.util.HashMap; import java.util.Map; import static com.google.common.truth.Truth.assertThat; @RunWith(MockitoJUnitRunner.Silent.class) public class MagnetScope_SiblingTypesTest { @Test public void sameInstanceIsRegisteredScope() { MagnetInstanceManager instanceManager = createMagnetInstanceManager(Scoping.TOPMOST); Scope topMost = new MagnetScope(null, instanceManager); Scope directScope = topMost.createSubscope(); Interface1 instance1 = directScope.getSingle(Interface1.class); Interface2 instance2 = directScope.getSingle(Interface2.class); assertThat(instance1).isSameInstanceAs(instance2); } @Test public void sameInstanceIsRegisteredScopeInReverseOrder() { MagnetInstanceManager instanceManager = createMagnetInstanceManager(Scoping.TOPMOST); Scope topMost = new MagnetScope(null, instanceManager); Scope directScope = topMost.createSubscope(); Interface2 instance2 = directScope.getSingle(Interface2.class); Interface1 instance1 = directScope.getSingle(Interface1.class); assertThat(instance1).isSameInstanceAs(instance2); } @Test public void sameInstanceIsRegisteredInTopMostScope() { MagnetInstanceManager instanceManager = createMagnetInstanceManager(Scoping.TOPMOST); Scope topMost = new MagnetScope(null, instanceManager); Scope directScope = topMost.createSubscope(); directScope.getSingle(Interface1.class); Interface1 instance1 = topMost.getSingle(Interface1.class); Interface2 instance2 = topMost.getSingle(Interface2.class); assertThat(instance1).isSameInstanceAs(instance2); } @Test public void sameInstanceIsRegisteredInDirectScope() { MagnetInstanceManager instanceManager = createMagnetInstanceManager(Scoping.DIRECT); Scope topMost = new MagnetScope(null, instanceManager); Scope directScope = topMost.createSubscope(); Interface1 instance1 = directScope.getSingle(Interface1.class); Interface2 instance2 = directScope.getSingle(Interface2.class); assertThat(instance1).isSameInstanceAs(instance2); } @Test public void sameInstanceIsNotRegisteredInTopMostScope() { MagnetInstanceManager instanceManager = createMagnetInstanceManager(Scoping.DIRECT); MagnetScope topMost = new MagnetScope(null, instanceManager); Scope directScope = topMost.createSubscope(); directScope.getSingle(Interface1.class); assertThat(topMost.instanceBuckets).isEmpty(); } private static MagnetInstanceManager createMagnetInstanceManager(Scoping scoping) { MagnetInstanceManager instanceManager = new MagnetInstanceManager(); InstanceFactory[] factories = new InstanceFactory[]{ new ImplementationInterface1Factory(scoping), new ImplementationInterface2Factory(scoping) }; Map index = new HashMap<>(); index.put(Interface1.class, new Range(0, 1, Classifier.NONE)); index.put(Interface2.class, new Range(1, 1, Classifier.NONE)); instanceManager.register(factories, index); return instanceManager; } interface Interface1 {} interface Interface2 {} static class Implementation implements Interface1, Interface2 {} static class ImplementationInterface1Factory extends InstanceFactory { private Scoping scoping; ImplementationInterface1Factory(Scoping scoping) { this.scoping = scoping; } @Override public Interface1 create(Scope scope) { return new Implementation(); } @Override public Scoping getScoping() { return this.scoping; } @Override public Class[] getSiblingTypes() { return new Class[]{Interface2.class, ImplementationInterface2Factory.class}; } } static class ImplementationInterface2Factory extends InstanceFactory { private Scoping scoping; ImplementationInterface2Factory(Scoping scoping) { this.scoping = scoping; } @Override public Interface2 create(Scope scope) { return new Implementation(); } @Override public Scoping getScoping() { return this.scoping; } @Override public Class[] getSiblingTypes() { return new Class[]{Interface1.class, ImplementationInterface1Factory.class}; } } } ================================================ FILE: magnet/src/test/java/magnet/internal/MagnetScopingDirectTest.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; import magnet.Scope; import magnet.Scoping; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.google.common.truth.Truth.assertThat; @RunWith(JUnit4.class) public class MagnetScopingDirectTest { private InstrumentedScope scope1; private InstrumentedScope scope2; private InstrumentedScope scope3; @Before public void before() { InstanceManager instanceManager = new StubInstanceManager(); scope1 = (InstrumentedScope) new InstrumentedScope( new MagnetScope(null, instanceManager)) .bind(Dependency1.class, new Dependency1()); scope2 = (InstrumentedScope) scope1 .createSubscope() .bind(Dependency2.class, new Dependency2()); scope3 = (InstrumentedScope) scope2 .createSubscope() .bind(Dependency3.class, new Dependency3()); } @Test public void itemOne_Scope1() { // when MenuItem menuItem = scope1.getSingle(MenuItem.class, "one"); // then assertThat(menuItem).isNotNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "one")).isNotNull(); } @Test public void itemOne_Scope2() { // when MenuItem menuItem = scope2.getSingle(MenuItem.class, "one"); // then assertThat(menuItem).isNotNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "one")).isNotNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "one")).isNull(); } @Test public void itemOne_Scope3() { // when MenuItem menuItem = scope3.getSingle(MenuItem.class, "one"); // then assertThat(menuItem).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "one")).isNotNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "one")).isNull(); } @Test(expected = IllegalStateException.class) public void itemTwo_Scope1() { // when MenuItem menuItem = scope1.getSingle(MenuItem.class, "two"); } @Test public void itemTwo_Scope2() { // when MenuItem menuItem = scope2.getSingle(MenuItem.class, "two"); // then assertThat(menuItem).isNotNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "two")).isNotNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "two")).isNull(); } @Test public void itemTwo_Scope3() { // when MenuItem menuItem = scope3.getSingle(MenuItem.class, "two"); // then assertThat(menuItem).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "two")).isNotNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "two")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "two")).isNull(); } @Test(expected = IllegalStateException.class) public void itemThree_Scope1() { // when MenuItem menuItem = scope1.getSingle(MenuItem.class, "three"); } @Test(expected = IllegalStateException.class) public void itemThree_Scope2() { // when MenuItem menuItem = scope2.getSingle(MenuItem.class, "three"); } @Test public void itemThree_Scope3() { // when MenuItem menuItem = scope3.getSingle(MenuItem.class, "three"); // then assertThat(menuItem).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "three")).isNotNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "three")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "three")).isNull(); } private static class MenuItemOneFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { scope.getSingle(Dependency1.class); return new MenuItemOne(); } @Override public Scoping getScoping() { return Scoping.DIRECT; } } private static class MenuItemTwoFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { scope.getSingle(MenuItem.class, "one"); scope.getSingle(Dependency2.class); return new MenuItemTwo(); } @Override public Scoping getScoping() { return Scoping.DIRECT; } } private static class MenuItemThreeFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { scope.getSingle(MenuItem.class, "two"); scope.getSingle(Dependency3.class); return new MenuItemThree(); } @Override public Scoping getScoping() { return Scoping.DIRECT; } } @SuppressWarnings("unchecked") private static class StubInstanceManager implements InstanceManager { private final Map> factories; StubInstanceManager() { factories = new HashMap<>(); factories.put("one", new MenuItemOneFactory()); factories.put("two", new MenuItemTwoFactory()); factories.put("three", new MenuItemThreeFactory()); } @Override public InstanceFactory getInstanceFactory( Class instanceType, String classifier, Class> factoryType ) { throw new UnsupportedOperationException(); } @Override public InstanceFactory getFilteredInstanceFactory( Class type, String classifier, FactoryFilter factoryFilter ) { return (InstanceFactory) factories.get(classifier); } @Override public List> getManyInstanceFactories( Class type, String classifier, FactoryFilter factoryFilter ) { throw new UnsupportedOperationException(); } } private interface MenuItem {} private static class MenuItemOne implements MenuItem {} private static class MenuItemTwo implements MenuItem {} private static class MenuItemThree implements MenuItem {} private static class Dependency1 {} private static class Dependency2 {} private static class Dependency3 {} } ================================================ FILE: magnet/src/test/java/magnet/internal/MagnetScopingNoneTest.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; import magnet.Scope; import magnet.Scoping; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.google.common.truth.Truth.assertThat; @RunWith(JUnit4.class) public class MagnetScopingNoneTest { private InstrumentedScope scope1; private InstrumentedScope scope2; private InstrumentedScope scope3; @Before public void before() { InstanceManager instanceManager = new StubInstanceManager(); scope1 = (InstrumentedScope) new InstrumentedScope( new MagnetScope(null, instanceManager)) .bind(Dependency1.class, new Dependency1()); scope2 = (InstrumentedScope) scope1 .createSubscope() .bind(Dependency2.class, new Dependency2()); scope3 = (InstrumentedScope) scope2 .createSubscope() .bind(Dependency3.class, new Dependency3()); } @Test public void itemOne_requestedWithinScope1() { // when MenuItem menuItem = scope1.getSingle(MenuItem.class, "one"); // then assertThat(menuItem).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "one")).isNull(); } @Test public void itemOne_requestedWithinScope2() { // when MenuItem menuItem = scope2.getSingle(MenuItem.class, "one"); // then assertThat(menuItem).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "one")).isNull(); } @Test public void itemOne_requestedWithinScope3() { // when MenuItem menuItem = scope3.getSingle(MenuItem.class, "one"); // then assertThat(menuItem).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "one")).isNull(); } @Test(expected = IllegalStateException.class) public void itemTwo_requestedWithinScope1() { // when scope1.getSingle(MenuItem.class, "two"); } @Test public void itemTwo_requestedWithinScope2() { // when MenuItem menuItem = scope2.getSingle(MenuItem.class, "two"); // then assertThat(menuItem).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "two")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "two")).isEqualTo(menuItem); assertThat(scope1.getOptionalInScope(MenuItem.class, "two")).isNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "one")).isNull(); } @Test public void itemTwo_requestedWithinScope3() { // when MenuItem menuItem = scope3.getSingle(MenuItem.class, "two"); // then assertThat(menuItem).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "two")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "two")).isEqualTo(menuItem); assertThat(scope1.getOptionalInScope(MenuItem.class, "two")).isNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "one")).isNull(); } @Test(expected = IllegalStateException.class) public void itemThree_requestedWithinScope1() { // when scope1.getSingle(MenuItem.class, "three"); } @Test(expected = IllegalStateException.class) public void itemThree_requestedWithinScope2() { // when scope2.getSingle(MenuItem.class, "three"); } @Test public void itemThree_requestedWithinScope3() { // when MenuItem menuItem = scope3.getSingle(MenuItem.class, "three"); // then assertThat(menuItem).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "two")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "two")).isNotNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "two")).isNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "one")).isNull(); } private static class MenuItemOneFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { scope.getSingle(Dependency1.class); return new MenuItemOne(); } @Override public Scoping getScoping() { return Scoping.UNSCOPED; } } private static class MenuItemTwoFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { scope.getSingle(Dependency1.class); scope.getSingle(Dependency2.class); scope.getSingle(MenuItem.class, "one"); return new MenuItemTwo(); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } private static class MenuItemThreeFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { scope.getSingle(Dependency1.class); scope.getSingle(Dependency3.class); scope.getSingle(MenuItem.class, "one"); scope.getSingle(MenuItem.class, "two"); return new MenuItemThree(); } @Override public Scoping getScoping() { return Scoping.UNSCOPED; } } private static class StubInstanceManager implements InstanceManager { private final Map> factories; StubInstanceManager() { factories = new HashMap<>(); factories.put("one", new MenuItemOneFactory()); factories.put("two", new MenuItemTwoFactory()); factories.put("three", new MenuItemThreeFactory()); } @Override public InstanceFactory getInstanceFactory( Class instanceType, String classifier, Class> factoryType ) { throw new UnsupportedOperationException(); } @Override public InstanceFactory getFilteredInstanceFactory( Class type, String classifier, FactoryFilter factoryFilter ) { //noinspection unchecked return (InstanceFactory) factories.get(classifier); } @Override public List> getManyInstanceFactories( Class type, String classifier, FactoryFilter factoryFilter ) { throw new UnsupportedOperationException(); } } private interface MenuItem {} private static class MenuItemOne implements MenuItem {} private static class MenuItemTwo implements MenuItem {} private static class MenuItemThree implements MenuItem {} private static class Dependency1 {} private static class Dependency2 {} private static class Dependency3 {} } ================================================ FILE: magnet/src/test/java/magnet/internal/MagnetScopingTopmostDependsOnDirectTest.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; import magnet.Scope; import magnet.Scoping; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.google.common.truth.Truth.assertThat; @RunWith(JUnit4.class) public class MagnetScopingTopmostDependsOnDirectTest { private InstrumentedScope scope1; private InstrumentedScope scope2; private InstrumentedScope scope3; @Before public void before() { InstanceManager instanceManager = new StubInstanceManager(); scope1 = new InstrumentedScope(new MagnetScope(null, instanceManager)); scope2 = (InstrumentedScope) scope1.createSubscope(); scope3 = (InstrumentedScope) scope2.createSubscope(); } private interface MenuItem {} private static class MenuItemZero implements MenuItem {} private static class MenuItemOne implements MenuItem {} private static class MenuItemTwo implements MenuItem {} private static class MenuItemThree implements MenuItem {} private static class MenuItemZeroFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { return new MenuItemZero(); } @Override public Scoping getScoping() { return Scoping.DIRECT; } } private static class MenuItemOneFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { return new MenuItemOne(); } @Override public Scoping getScoping() { return Scoping.DIRECT; } } private static class MenuItemTwoFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { scope.getSingle(MenuItem.class, "one"); return new MenuItemTwo(); } @Override public Scoping getScoping() { return Scoping.DIRECT; } } private static class MenuItemThreeFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { scope.getSingle(MenuItem.class, "two"); return new MenuItemThree(); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } @Test public void itemThree_getSingleInScope3() { // when MenuItem item = scope3.getSingle(MenuItem.class, "three"); // then assertThat(item).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "three")).isEqualTo(item); assertThat(scope2.getOptionalInScope(MenuItem.class, "three")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "three")).isNull(); } @Test public void itemThree_getSingleInScope2() { // when MenuItem item = scope2.getSingle(MenuItem.class, "three"); // then assertThat(item).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "three")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "three")).isEqualTo(item); assertThat(scope1.getOptionalInScope(MenuItem.class, "three")).isNull(); } @Test public void itemThree_getSingleInScope1() { // when MenuItem item = scope1.getSingle(MenuItem.class, "three"); // then assertThat(item).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "three")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "three")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "three")).isEqualTo(item); } private static class StubInstanceManager implements InstanceManager { private final Map> factories; StubInstanceManager() { factories = new HashMap<>(); factories.put("zero", new MenuItemZeroFactory()); factories.put("one", new MenuItemOneFactory()); factories.put("two", new MenuItemTwoFactory()); factories.put("three", new MenuItemThreeFactory()); } @Override public InstanceFactory getInstanceFactory( Class instanceType, String classifier, Class> factoryType ) { throw new UnsupportedOperationException(); } @Override public InstanceFactory getFilteredInstanceFactory( Class type, String classifier, FactoryFilter factoryFilter ) { //noinspection unchecked return (InstanceFactory) factories.get(classifier); } @Override public List> getManyInstanceFactories( Class type, String classifier, FactoryFilter factoryFilter ) { throw new UnsupportedOperationException(); } } } ================================================ FILE: magnet/src/test/java/magnet/internal/MagnetScopingTopmostDependsOnTopmostTest.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; import magnet.Scope; import magnet.Scoping; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.google.common.truth.Truth.assertThat; @RunWith(JUnit4.class) public class MagnetScopingTopmostDependsOnTopmostTest { private InstrumentedScope scope1; private InstrumentedScope scope2; private InstrumentedScope scope3; @Before public void before() { InstanceManager instanceManager = new StubInstanceManager(); scope1 = (InstrumentedScope) new InstrumentedScope( new MagnetScope(null, instanceManager)) .bind(Dependency1.class, new Dependency1()); scope2 = (InstrumentedScope) scope1 .createSubscope() .bind(Dependency2.class, new Dependency2()); scope3 = (InstrumentedScope) scope2 .createSubscope() .bind(Dependency3.class, new Dependency3()); } private interface MenuItem {} private static class MenuItemZero implements MenuItem {} private static class MenuItemOne implements MenuItem {} private static class MenuItemTwo implements MenuItem {} private static class MenuItemThree implements MenuItem {} private static class Dependency1 {} private static class Dependency2 {} private static class Dependency3 {} private static class MenuItemZeroFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { return new MenuItemZero(); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } private static class MenuItemOneFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { scope.getSingle(Dependency1.class); return new MenuItemOne(); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } private static class MenuItemTwoFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { scope.getSingle(Dependency2.class); scope.getSingle(MenuItem.class, "one"); return new MenuItemTwo(); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } private static class MenuItemThreeFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { scope.getSingle(Dependency3.class); scope.getSingle(MenuItem.class, "two"); return new MenuItemThree(); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } @Test public void itemZero_getSingleInScope3() { // when MenuItem item = scope3.getSingle(MenuItem.class, "zero"); // then assertThat(item).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "zero")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "zero")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "zero")).isEqualTo(item); } @Test public void itemZero_getSingleInScope2() { // when MenuItem item = scope2.getSingle(MenuItem.class, "zero"); // then assertThat(item).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "zero")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "zero")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "zero")).isEqualTo(item); } @Test public void itemZero_getSingleInScope1() { // when MenuItem item = scope1.getSingle(MenuItem.class, "zero"); // then assertThat(item).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "zero")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "zero")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "zero")).isEqualTo(item); } @Test public void itemOne_getSingleInScope3() { // when MenuItem item = scope3.getSingle(MenuItem.class, "one"); // then assertThat(item).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "one")).isEqualTo(item); } @Test public void itemOne_getSingleInScope2() { // when MenuItem item = scope2.getSingle(MenuItem.class, "one"); // then assertThat(item).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "one")).isEqualTo(item); } @Test public void itemOne_getSingleInScope1() { // when MenuItem item = scope1.getSingle(MenuItem.class, "one"); // then assertThat(item).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "one")).isEqualTo(item); } @Test public void itemTwo_getSingleInScope3() { // when MenuItem item = scope3.getSingle(MenuItem.class, "two"); // then assertThat(item).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "two")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "two")).isEqualTo(item); assertThat(scope1.getOptionalInScope(MenuItem.class, "two")).isNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "one")).isNotNull(); } @Test public void itemTwo_getSingleInScope2() { // when MenuItem item = scope2.getSingle(MenuItem.class, "two"); // then assertThat(item).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "two")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "two")).isEqualTo(item); assertThat(scope1.getOptionalInScope(MenuItem.class, "two")).isNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "one")).isNotNull(); } @Test(expected = IllegalStateException.class) public void itemTwo_getSingleInScope1() { // when scope1.getSingle(MenuItem.class, "two"); } @Test public void itemThree_getSingleInScope3() { // when MenuItem item = scope3.getSingle(MenuItem.class, "three"); // then assertThat(item).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "three")).isEqualTo(item); assertThat(scope2.getOptionalInScope(MenuItem.class, "three")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "three")).isNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "two")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "two")).isNotNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "two")).isNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "one")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "one")).isNotNull(); } @Test(expected = IllegalStateException.class) public void itemThree_getSingleInScope2() { // when scope2.getSingle(MenuItem.class, "three"); } @Test(expected = IllegalStateException.class) public void itemThree_getSingleInScope1() { // when scope1.getSingle(MenuItem.class, "three"); } private static class StubInstanceManager implements InstanceManager { private final Map> factories; StubInstanceManager() { factories = new HashMap<>(); factories.put("zero", new MenuItemZeroFactory()); factories.put("one", new MenuItemOneFactory()); factories.put("two", new MenuItemTwoFactory()); factories.put("three", new MenuItemThreeFactory()); } @Override public InstanceFactory getInstanceFactory( Class instanceType, String classifier, Class> factoryType ) { throw new UnsupportedOperationException(); } @Override public InstanceFactory getFilteredInstanceFactory( Class type, String classifier, FactoryFilter factoryFilter ) { //noinspection unchecked return (InstanceFactory) factories.get(classifier); } @Override public List> getManyInstanceFactories( Class type, String classifier, FactoryFilter factoryFilter ) { throw new UnsupportedOperationException(); } } } ================================================ FILE: magnet/src/test/java/magnet/internal/MagnetScopingTopmostDependsOnUnscopedTest.java ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal; import magnet.Scope; import magnet.Scoping; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.google.common.truth.Truth.assertThat; @RunWith(JUnit4.class) public class MagnetScopingTopmostDependsOnUnscopedTest { private InstrumentedScope scope1; private InstrumentedScope scope2; private InstrumentedScope scope3; @Before public void before() { InstanceManager instanceManager = new StubInstanceManager(); scope1 = new InstrumentedScope(new MagnetScope(null, instanceManager)); scope2 = (InstrumentedScope) scope1.createSubscope(); scope3 = (InstrumentedScope) scope2.createSubscope(); } private interface MenuItem {} private static class MenuItemZero implements MenuItem {} private static class MenuItemOne implements MenuItem {} private static class MenuItemTwo implements MenuItem {} private static class MenuItemThree implements MenuItem {} private static class MenuItemZeroFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { return new MenuItemZero(); } @Override public Scoping getScoping() { return Scoping.UNSCOPED; } } private static class MenuItemOneFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { return new MenuItemOne(); } @Override public Scoping getScoping() { return Scoping.UNSCOPED; } } private static class MenuItemTwoFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { scope.getSingle(MenuItem.class, "one"); return new MenuItemTwo(); } @Override public Scoping getScoping() { return Scoping.UNSCOPED; } } private static class MenuItemThreeFactory extends InstanceFactory { @Override public MenuItem create(Scope scope) { scope.getSingle(MenuItem.class, "two"); return new MenuItemThree(); } @Override public Scoping getScoping() { return Scoping.TOPMOST; } } @Test public void itemThree_getSingleInScope3() { // when MenuItem item = scope3.getSingle(MenuItem.class, "three"); // then assertThat(item).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "three")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "three")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "three")).isEqualTo(item); } @Test public void itemThree_getSingleInScope2() { // when MenuItem item = scope2.getSingle(MenuItem.class, "three"); // then assertThat(item).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "three")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "three")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "three")).isEqualTo(item); } @Test public void itemThree_getSingleInScope1() { // when MenuItem item = scope1.getSingle(MenuItem.class, "three"); // then assertThat(item).isNotNull(); assertThat(scope3.getOptionalInScope(MenuItem.class, "three")).isNull(); assertThat(scope2.getOptionalInScope(MenuItem.class, "three")).isNull(); assertThat(scope1.getOptionalInScope(MenuItem.class, "three")).isEqualTo(item); } private static class StubInstanceManager implements InstanceManager { private final Map> factories; StubInstanceManager() { factories = new HashMap<>(); factories.put("zero", new MenuItemZeroFactory()); factories.put("one", new MenuItemOneFactory()); factories.put("two", new MenuItemTwoFactory()); factories.put("three", new MenuItemThreeFactory()); } @Override public InstanceFactory getInstanceFactory( Class instanceType, String classifier, Class> factoryType ) { throw new UnsupportedOperationException(); } @Override public InstanceFactory getFilteredInstanceFactory( Class type, String classifier, FactoryFilter factoryFilter ) { //noinspection unchecked return (InstanceFactory) factories.get(classifier); } @Override public List> getManyInstanceFactories( Class type, String classifier, FactoryFilter factoryFilter ) { throw new UnsupportedOperationException(); } } } ================================================ FILE: magnet/src/test/java/magnet/internal/Scope_GetManyAnySiblingTypesTest_Issue95.java ================================================ package magnet.internal; import magnet.Scope; import magnet.internal.observer.ScopeObserver; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mock; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @RunWith(JUnit4.class) public class Scope_GetManyAnySiblingTypesTest_Issue95 { @Mock private Scope scopeA; @Mock private Scope scopeB; @Test public void test() { // given scopeA = InternalFactory .createRootScope(new MapInstanceManager()) .bind(Bound1.class, new Bound1()); scopeB = scopeA .createSubscope() .bind(Bound3.class, new Bound3()); // when scopeB.getSingle(Dep2Sibling.class); scopeB.getSingle(Dep1.class); // then ScopeObserver observer = new ScopeObserver(); scopeA.accept(observer, Integer.MAX_VALUE); observer.assetThat(scopeA).hasInstanceTypes(Bound1.class); observer.assetThat(scopeB).hasInstanceTypes( Bound3.class, Dep1.class, Dep2.class, Dep2Sibling.class, Dep3.class ); } @SuppressWarnings("unchecked") private static class MapInstanceManager implements InstanceManager { private HashMap factories = new HashMap<>(); MapInstanceManager() { factories.put( Scope_GetManyAnySiblingTypesTest_Issue95.Dep1.class, new Scope_GetManyAnySiblingTypesTest_Issue95.Dep1Factory() ); factories.put( Scope_GetManyAnySiblingTypesTest_Issue95.Dep2.class, new Scope_GetManyAnySiblingTypesTest_Issue95.Dep2Factory() ); factories.put( Scope_GetManyAnySiblingTypesTest_Issue95.Dep2Sibling.class, new Scope_GetManyAnySiblingTypesTest_Issue95.Dep2SiblingFactory() ); factories.put( Scope_GetManyAnySiblingTypesTest_Issue95.Dep3.class, new Scope_GetManyAnySiblingTypesTest_Issue95.Dep3Factory() ); } @Override @Nullable public InstanceFactory getInstanceFactory( Class instanceType, String classifier, Class> factoryType) { return factories.get(instanceType); } @Override @Nullable public InstanceFactory getFilteredInstanceFactory( Class type, String classifier, FactoryFilter factoryFilter) { return factories.get(type); } @Override public @NotNull List> getManyInstanceFactories( Class type, String classifier, FactoryFilter factoryFilter) { List> list = new ArrayList<>(); list.add(factories.get(type)); return list; } } private static class Dep1 {} private static class Dep1Factory extends InstanceFactory { @Override public Dep1 create(Scope scope) { scope.getSingle(Bound1.class); scope.getMany(Dep2.class); return new Dep1(); } } private interface Dep2 {} private static class Dep2Factory extends InstanceFactory { @Override public Dep2 create(Scope scope) { scope.getSingle(Dep3.class); return new Scope_GetManyAnySiblingTypesTest_Issue95.Dep2Impl(); } @Override public Class[] getSiblingTypes() { return new Class[] { Dep2Sibling.class, Scope_GetManyAnySiblingTypesTest_Issue95.Dep2SiblingFactory.class }; } } private interface Dep2Sibling {} private static class Dep2SiblingFactory extends InstanceFactory { @Override public Dep2Sibling create(Scope scope) { scope.getSingle(Dep3.class); return new Scope_GetManyAnySiblingTypesTest_Issue95.Dep2Impl(); } @Override public Class[] getSiblingTypes() { return new Class[] { Dep2.class, Scope_GetManyAnySiblingTypesTest_Issue95.Dep2Factory.class }; } } private static class Dep2Impl implements Dep2, Dep2Sibling { } private static class Dep3 {} private static class Dep3Factory extends InstanceFactory { @Override public Dep3 create(Scope scope) { scope.getSingle(Bound3.class); return new Dep3(); } } private static class Bound1 {} private static class Bound3 {} } ================================================ FILE: magnet/src/test/java/magnet/internal/Scope_LimitDirectScoping_DependencyInNonReachableChildScopeTest.java ================================================ package magnet.internal; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mock; import java.util.HashMap; import java.util.List; import magnet.Scope; import magnet.Scoping; @RunWith(JUnit4.class) public class Scope_LimitDirectScoping_DependencyInNonReachableChildScopeTest { // Scopes: A <- B <- C { Bound2 } // Instances: DIRECT Dep1 -> Dep2 -> Bound2 public @Rule ExpectedException expected = ExpectedException.none(); private final static String LIMIT = "limit"; private @Mock Scope scopeA; private @Mock Scope scopeB; private @Mock Scope scopeC; @Test public void test() { expected.expect(IllegalStateException.class); expected.expectMessage("non-reachable child scope"); // given scopeA = InternalFactory.createRootScope(new HashMapInstanceManager()); scopeB = scopeA.createSubscope().limit(LIMIT); scopeC = scopeB.createSubscope().bind(Bound2.class, new Bound2()); // when scopeC.getSingle(Dep1.class); } @SuppressWarnings("unchecked") private static class HashMapInstanceManager implements InstanceManager { private HashMap factories = new HashMap<>(); HashMapInstanceManager() { factories.put(Dep1.class, new Dep1Factory()); factories.put(Dep2.class, new Dep2Factory()); } @Override public @Nullable InstanceFactory getInstanceFactory( Class instanceType, String classifier, Class> factoryType) { return factories.get(instanceType); } @Override public @Nullable InstanceFactory getFilteredInstanceFactory( Class type, String classifier, FactoryFilter factoryFilter) { return factories.get(type); } @Override public @NotNull List> getManyInstanceFactories( Class type, String classifier, FactoryFilter factoryFilter) { throw new UnsupportedOperationException(); } } private static class Bound2 {} private static class Dep1 {} private static class Dep1Factory extends InstanceFactory { @Override public Dep1 create(Scope scope) { scope.getSingle(Dep2.class); return new Dep1(); } @Override public Scoping getScoping() { return Scoping.DIRECT; } @Override public String getLimit() { return LIMIT; } } private static class Dep2 {} private static class Dep2Factory extends InstanceFactory { @Override public Dep2 create(Scope scope) { scope.getSingle(Bound2.class); return new Dep2(); } } } ================================================ FILE: magnet/src/test/java/magnet/internal/Scope_LimitDirectScoping_InstanceWithDependencyTest.java ================================================ package magnet.internal; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mock; import java.util.HashMap; import java.util.List; import magnet.Scope; import magnet.Scoping; import magnet.internal.observer.ScopeObserver; @RunWith(JUnit4.class) public class Scope_LimitDirectScoping_InstanceWithDependencyTest { // Scopes: A <- B <- C { Bound2 } // Instances: Dep2 -> { DIRECT Dep1, Bound2 } public @Rule ExpectedException expected = ExpectedException.none(); private final static String LIMIT = "limit"; private @Mock Scope scopeA; private @Mock Scope scopeB; private @Mock Scope scopeC; @Test public void test_GetFromUnderLimitedScope() { // given scopeA = InternalFactory.createRootScope(new HashMapInstanceManager()); scopeB = scopeA.createSubscope().limit(LIMIT); scopeC = scopeB.createSubscope().bind(Bound2.class, new Bound2()); // when scopeC.getSingle(Dep2.class); // then ScopeObserver observer = new ScopeObserver(); scopeA.accept(observer, Integer.MAX_VALUE); observer.assetThat(scopeA).hasNoInstances(); observer.assetThat(scopeB).hasInstanceTypes(Dep1.class); observer.assetThat(scopeC).hasInstanceTypes(Bound2.class, Dep2.class); } @Test public void test_GetFromLimitedScope() { expected.expect(IllegalStateException.class); expected.expectMessage("not found in scopes"); // given scopeA = InternalFactory.createRootScope(new HashMapInstanceManager()); scopeB = scopeA.createSubscope().limit(LIMIT); scopeC = scopeB.createSubscope().bind(Bound2.class, new Bound2()); // when scopeB.getSingle(Dep2.class); } @SuppressWarnings("unchecked") private static class HashMapInstanceManager implements InstanceManager { private HashMap factories = new HashMap<>(); HashMapInstanceManager() { factories.put(Dep1.class, new Dep1Factory()); factories.put(Dep2.class, new Dep2Factory()); } @Override public @Nullable InstanceFactory getInstanceFactory( Class instanceType, String classifier, Class> factoryType) { return factories.get(instanceType); } @Override public @Nullable InstanceFactory getFilteredInstanceFactory( Class type, String classifier, FactoryFilter factoryFilter) { return factories.get(type); } @Override public @NotNull List> getManyInstanceFactories( Class type, String classifier, FactoryFilter factoryFilter) { throw new UnsupportedOperationException(); } } private static class Bound2 {} private static class Dep1 {} private static class Dep1Factory extends InstanceFactory { @Override public Dep1 create(Scope scope) { return new Dep1(); } @Override public Scoping getScoping() { return Scoping.DIRECT; } @Override public String getLimit() { return LIMIT; } } private static class Dep2 {} private static class Dep2Factory extends InstanceFactory { @Override public Dep2 create(Scope scope) { scope.getSingle(Dep1.class); scope.getSingle(Bound2.class); return new Dep2(); } } } ================================================ FILE: magnet/src/test/java/magnet/internal/Scope_LimitDirectScoping_SingleInstanceTest.java ================================================ package magnet.internal; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mock; import java.util.HashMap; import java.util.List; import magnet.Scope; import magnet.Scoping; import magnet.internal.observer.ScopeObserver; @RunWith(JUnit4.class) public class Scope_LimitDirectScoping_SingleInstanceTest { // Scopes: A <- B <- C // Instances: DIRECT Dep1 public @Rule ExpectedException expected = ExpectedException.none(); private final static String LIMIT = "limit"; private @Mock Scope scopeA; private @Mock Scope scopeB; private @Mock Scope scopeC; @Test public void test_GetFromUnderLimitedScope() { // given scopeA = InternalFactory.createRootScope(new HashMapInstanceManager()); scopeB = scopeA.createSubscope().limit(LIMIT); scopeC = scopeB.createSubscope(); // when scopeC.getSingle(Dep1.class); // then ScopeObserver observer = new ScopeObserver(); scopeA.accept(observer, Integer.MAX_VALUE); observer.assetThat(scopeA).hasNoInstances(); observer.assetThat(scopeB).hasInstanceTypes(Dep1.class); observer.assetThat(scopeC).hasNoInstances(); } @Test public void test_GetFromLimitedScope() { // given scopeA = InternalFactory.createRootScope(new HashMapInstanceManager()); scopeB = scopeA.createSubscope().limit(LIMIT); scopeC = scopeB.createSubscope(); // when scopeB.getSingle(Dep1.class); // then ScopeObserver observer = new ScopeObserver(); scopeA.accept(observer, Integer.MAX_VALUE); observer.assetThat(scopeA).hasNoInstances(); observer.assetThat(scopeB).hasInstanceTypes(Dep1.class); observer.assetThat(scopeC).hasNoInstances(); } @Test public void test_GetFromAboveLimitedScope() { expected.expect(IllegalStateException.class); expected.expectMessage("no scope with limit"); // given scopeA = InternalFactory.createRootScope(new HashMapInstanceManager()); scopeB = scopeA.createSubscope().limit(LIMIT); scopeC = scopeB.createSubscope(); // when scopeA.getSingle(Dep1.class); } @SuppressWarnings("unchecked") private static class HashMapInstanceManager implements InstanceManager { private HashMap factories = new HashMap<>(); HashMapInstanceManager() { factories.put(Dep1.class, new Dep1Factory()); } @Override public @Nullable InstanceFactory getInstanceFactory( Class instanceType, String classifier, Class> factoryType) { return factories.get(instanceType); } @Override public @Nullable InstanceFactory getFilteredInstanceFactory( Class type, String classifier, FactoryFilter factoryFilter) { return factories.get(type); } @Override public @NotNull List> getManyInstanceFactories( Class type, String classifier, FactoryFilter factoryFilter) { throw new UnsupportedOperationException(); } } private static class Dep1 {} private static class Dep1Factory extends InstanceFactory { @Override public Dep1 create(Scope scope) { return new Dep1(); } @Override public Scoping getScoping() { return Scoping.DIRECT; } @Override public String getLimit() { return LIMIT; } } } ================================================ FILE: magnet/src/test/java/magnet/internal/Scope_LimitTest.java ================================================ package magnet.internal; import magnet.Scope; import magnet.internal.observer.ScopeObserver; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mock; import java.util.HashMap; import java.util.List; @RunWith(JUnit4.class) public class Scope_LimitTest { private final static String LIMIT = "limit"; @Mock private Scope scopeA; @Mock private Scope scopeB; @Mock private Scope scopeC; @Test public void test_limitedInstance_settlesInLimitedSameScope() { // given scopeA = InternalFactory.createRootScope(new HashMapInstanceManager()); scopeB = scopeA.createSubscope(); scopeC = scopeB.createSubscope().limit(LIMIT); // when scopeC.getSingle(Limited.class); // then ScopeObserver observer = new ScopeObserver(); scopeA.accept(observer, Integer.MAX_VALUE); observer.assetThat(scopeA).hasNoInstances(); observer.assetThat(scopeB).hasNoInstances(); observer.assetThat(scopeC).hasInstanceTypes(Limited.class); } @Test public void test_limitedInstance_settlesInLimitedUpperScope() { // given scopeA = InternalFactory.createRootScope(new HashMapInstanceManager()); scopeB = scopeA.createSubscope().limit(LIMIT); scopeC = scopeB.createSubscope(); // when scopeC.getSingle(Limited.class); // then ScopeObserver observer = new ScopeObserver(); scopeA.accept(observer, Integer.MAX_VALUE); observer.assetThat(scopeA).hasNoInstances(); observer.assetThat(scopeB).hasInstanceTypes(Limited.class); observer.assetThat(scopeC).hasNoInstances(); } @Test public void test_limitedInstance_settlesInLimitedTopScope() { // given scopeA = InternalFactory.createRootScope(new HashMapInstanceManager()).limit(LIMIT); scopeB = scopeA.createSubscope(); scopeC = scopeB.createSubscope(); // when scopeC.getSingle(Limited.class); // then ScopeObserver observer = new ScopeObserver(); scopeA.accept(observer, Integer.MAX_VALUE); observer.assetThat(scopeA).hasInstanceTypes(Limited.class); observer.assetThat(scopeB).hasNoInstances(); observer.assetThat(scopeC).hasNoInstances(); } @Test(expected = IllegalStateException.class) public void test_limitedInstance_fails_ifNoLimitFound() { // given scopeA = InternalFactory.createRootScope(new HashMapInstanceManager()); scopeB = scopeA.createSubscope(); scopeC = scopeB.createSubscope(); // when scopeC.getSingle(Limited.class); } @Test(expected = IllegalStateException.class) public void test_limitedInstance_fails_ifQueriedAtParentUnlimitedScope() { // given scopeA = InternalFactory.createRootScope(new HashMapInstanceManager()); scopeB = scopeA.createSubscope().limit(LIMIT); // when scopeA.getSingle(Limited.class); } @Test(expected = IllegalStateException.class) public void test_limitedInstance_fails_ifNoMatchingLimitFound() { // given scopeA = InternalFactory.createRootScope(new HashMapInstanceManager()); scopeB = scopeA.createSubscope().limit("limit-two"); scopeC = scopeB.createSubscope().limit("limit-one"); // when scopeC.getSingle(Limited.class); } @Test public void test_unlimitedInstance_ignoresLimits_settlesInUnlimitedTopScope() { // given scopeA = InternalFactory.createRootScope(new HashMapInstanceManager()); scopeB = scopeA.createSubscope().limit("limit-two"); scopeC = scopeB.createSubscope().limit("limit-one"); // when scopeC.getSingle(Unlimited.class); // then ScopeObserver observer = new ScopeObserver(); scopeA.accept(observer, Integer.MAX_VALUE); observer.assetThat(scopeA).hasInstanceTypes(Unlimited.class); observer.assetThat(scopeB).hasNoInstances(); observer.assetThat(scopeC).hasNoInstances(); } @SuppressWarnings("unchecked") private static class HashMapInstanceManager implements InstanceManager { private HashMap factories = new HashMap<>(); HashMapInstanceManager() { factories.put(Limited.class, new LimitedFactory()); factories.put(Unlimited.class, new UnlimitedFactory()); } @Override @Nullable public InstanceFactory getInstanceFactory( Class instanceType, String classifier, Class> factoryType) { return factories.get(instanceType); } @Override @Nullable public InstanceFactory getFilteredInstanceFactory( Class type, String classifier, FactoryFilter factoryFilter) { return factories.get(type); } @Override public @NotNull List> getManyInstanceFactories( Class type, String classifier, FactoryFilter factoryFilter) { throw new UnsupportedOperationException(); } } private static class Limited {} private static class LimitedFactory extends InstanceFactory { @Override public Limited create(Scope scope) { return new Limited(); } @Override public String getLimit() { return LIMIT; } } private static class Unlimited {} private static class UnlimitedFactory extends InstanceFactory { @Override public Unlimited create(Scope scope) { return new Unlimited(); } } } ================================================ FILE: magnet/src/test/java/magnet/internal/Scope_LimitWithDependencyTest.java ================================================ package magnet.internal; import magnet.Scope; import magnet.internal.observer.ScopeObserver; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mock; import java.util.HashMap; import java.util.List; @RunWith(JUnit4.class) public class Scope_LimitWithDependencyTest { private final static String LIMIT_ONE = "limit-one"; private final static String LIMIT_TWO = "limit-two"; @Mock private Scope scopeA; @Mock private Scope scopeB; @Mock private Scope scopeC; @Mock private Scope scopeD; @Test public void test_dependingInstance_respectsDependencyLimit() { // given scopeA = InternalFactory.createRootScope(new MapInstanceManager()); scopeB = scopeA.createSubscope().limit(LIMIT_ONE); scopeC = scopeB.createSubscope(); scopeD = scopeC.createSubscope().limit(LIMIT_TWO); // when scopeD.getSingle(LimitedOne.class); // then ScopeObserver observer = new ScopeObserver(); scopeA.accept(observer, Integer.MAX_VALUE); observer.assetThat(scopeA).hasNoInstances(); observer.assetThat(scopeB).hasNoInstances(); observer.assetThat(scopeC).hasNoInstances(); observer.assetThat(scopeD).hasInstanceTypes(LimitedOne.class, LimitedTwo.class); } @Test public void test_dependingInstance_respectsOwnLimit() { // given scopeA = InternalFactory.createRootScope(new MapInstanceManager()); scopeB = scopeA.createSubscope().limit(LIMIT_TWO); scopeC = scopeB.createSubscope(); scopeD = scopeC.createSubscope().limit(LIMIT_ONE); // when scopeD.getSingle(LimitedOne.class); // then ScopeObserver observer = new ScopeObserver(); scopeA.accept(observer, Integer.MAX_VALUE); observer.assetThat(scopeA).hasNoInstances(); observer.assetThat(scopeB).hasInstanceTypes(LimitedTwo.class); observer.assetThat(scopeC).hasNoInstances(); observer.assetThat(scopeD).hasInstanceTypes(LimitedOne.class); } @Test public void test_dependingInstance_isCollocatedWithDependency() { // given scopeA = InternalFactory.createRootScope(new MapInstanceManager()); scopeB = scopeA.createSubscope().limit(LIMIT_ONE, LIMIT_TWO); scopeC = scopeB.createSubscope(); // when scopeC.getSingle(LimitedOne.class); // then ScopeObserver observer = new ScopeObserver(); scopeA.accept(observer, Integer.MAX_VALUE); observer.assetThat(scopeA).hasNoInstances(); observer.assetThat(scopeB).hasInstanceTypes(LimitedOne.class, LimitedTwo.class); observer.assetThat(scopeC).hasNoInstances(); } @SuppressWarnings("unchecked") private static class MapInstanceManager implements InstanceManager { private HashMap factories = new HashMap<>(); MapInstanceManager() { factories.put(LimitedOne.class, new LimitedOneFactory()); factories.put(LimitedTwo.class, new LimitedTwoFactory()); } @Override @Nullable public InstanceFactory getInstanceFactory( Class instanceType, String classifier, Class> factoryType) { return factories.get(instanceType); } @Override @Nullable public InstanceFactory getFilteredInstanceFactory( Class type, String classifier, FactoryFilter factoryFilter) { return factories.get(type); } @Override public @NotNull List> getManyInstanceFactories( Class type, String classifier, FactoryFilter factoryFilter) { throw new UnsupportedOperationException(); } } private static class LimitedOne {} private static class LimitedOneFactory extends InstanceFactory { @Override public LimitedOne create(Scope scope) { scope.getSingle(LimitedTwo.class); return new LimitedOne(); } @Override public String getLimit() { return LIMIT_ONE; } } private static class LimitedTwo {} private static class LimitedTwoFactory extends InstanceFactory { @Override public LimitedTwo create(Scope scope) { return new LimitedTwo(); } @Override public String getLimit() { return LIMIT_TWO; } } } ================================================ FILE: magnet/src/test/java/magnet/internal/VisitInstancesTest.java ================================================ package magnet.internal; import magnet.Classifier; import magnet.Magnet; import magnet.Scope; import magnet.Scoping; import magnet.Visitor; import magnet.internal.events.ObservableScopeVisitor; import magnet.internal.events.OnEnterScope; import magnet.internal.events.OnExitScope; import magnet.internal.events.OnInstance; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.junit.Before; import org.junit.Test; import static com.google.common.truth.Truth.assertThat; import static magnet.Visitor.Provision.BOUND; import static magnet.Visitor.Provision.INJECTED; public class VisitInstancesTest { private InstrumentedScope scopeA; private InstrumentedScope scopeB; private Bound bound = new Bound(); private Bound boundA = new Bound(); private Bound boundB = new Bound(); private String classifierA = "classifierA"; private String classifierB = "classifierB"; private InjectedTopMost injectedTopMost = new InjectedTopMost(); private InjectedTopMostFactory injectedTopMostFactory = new InjectedTopMostFactory(); private InjectedDirect injectedDirect = new InjectedDirect(); private InjectedDirectFactory injectedDirectFactory = new InjectedDirectFactory(); private ObservableScopeVisitor visitor; @Before public void before() { scopeA = (InstrumentedScope) new InstrumentedScope(Magnet.createRootScope()) .instrumentObjectIntoScope(injectedTopMostFactory, InjectedTopMost.class, injectedTopMost, Classifier.NONE) .instrumentObjectIntoScope(injectedTopMostFactory, InjectedTopMost.class, injectedTopMost, classifierA) .instrumentObjectIntoScope(injectedTopMostFactory, InjectedTopMost.class, injectedTopMost, classifierB) .bind(Bound.class, bound) .bind(Bound.class, boundA, classifierA); scopeB = (InstrumentedScope) ((InstrumentedScope) scopeA.createSubscope()) .instrumentObjectIntoScope(injectedDirectFactory, InjectedDirect.class, injectedDirect, Classifier.NONE) .bind(Bound.class, boundB, classifierB); } @Test public void visitAllScopes() { visitor = new ObservableScopeVisitor(); scopeA.accept(visitor, Integer.MAX_VALUE); assertThat(visitor.visited).containsExactly( new OnEnterScope(scopeA.scope, null), new OnInstance(BOUND, Bound.class, bound, Classifier.NONE, Scoping.DIRECT), new OnInstance(BOUND, Bound.class, boundA, classifierA, Scoping.DIRECT), new OnInstance(INJECTED, InjectedTopMost.class, injectedTopMost, Classifier.NONE, Scoping.TOPMOST), new OnInstance(INJECTED, InjectedTopMost.class, injectedTopMost, classifierA, Scoping.TOPMOST), new OnInstance(INJECTED, InjectedTopMost.class, injectedTopMost, classifierB, Scoping.TOPMOST), new OnEnterScope(scopeB.scope, scopeA.scope), new OnInstance(BOUND, Bound.class, boundB, classifierB, Scoping.DIRECT), new OnInstance(INJECTED, InjectedDirect.class, injectedDirect, Classifier.NONE, Scoping.DIRECT), new OnExitScope(scopeB.scope), new OnExitScope(scopeA.scope) ).inOrder(); } @Test public void visitRootScope() { visitor = new ObservableScopeVisitor(); scopeA.accept(visitor, 0); assertThat(visitor.visited).containsExactly( new OnEnterScope(scopeA.scope, null), new OnInstance(BOUND, Bound.class, bound, Classifier.NONE, Scoping.DIRECT), new OnInstance(BOUND, Bound.class, boundA, classifierA, Scoping.DIRECT), new OnInstance(INJECTED, InjectedTopMost.class, injectedTopMost, Classifier.NONE, Scoping.TOPMOST), new OnInstance(INJECTED, InjectedTopMost.class, injectedTopMost, classifierA, Scoping.TOPMOST), new OnInstance(INJECTED, InjectedTopMost.class, injectedTopMost, classifierB, Scoping.TOPMOST), new OnExitScope(scopeA.scope) ); } @Test public void visitScope_onEnterScope_skipAllInstances() { visitor = new ObservableScopeVisitor() { @Override public boolean onEnterScope(@NotNull Visitor.Scope scope, @Nullable Visitor.Scope parent) { super.onEnterScope(scope, parent); return false; } }; scopeA.accept(visitor, 0); assertThat(visitor.visited).containsExactly( new OnEnterScope(scopeA.scope, null), new OnExitScope(scopeA.scope) ).inOrder(); } @Test public void visitScope_onInstance_skipAfterSecondInstance() { visitor = new ObservableScopeVisitor() { private int count = 0; @Override public boolean onInstance(@NotNull Instance instance) { super.onInstance(instance); return ++count < 2; } }; scopeA.accept(visitor, Integer.MAX_VALUE); assertThat(visitor.visited).containsExactly( new OnEnterScope(scopeA.scope, null), new OnInstance(INJECTED, InjectedTopMost.class, injectedTopMost, classifierA, Scoping.TOPMOST), new OnInstance(INJECTED, InjectedTopMost.class, injectedTopMost, classifierB, Scoping.TOPMOST), new OnExitScope(scopeA.scope) ).inOrder(); } private static class Bound {} private static class InjectedTopMost {} private static class InjectedDirect {} private class InjectedTopMostFactory extends InstanceFactory { @Override public InjectedTopMost create(Scope scope) { return injectedTopMost; } } private class InjectedDirectFactory extends InstanceFactory { @Override public InjectedDirect create(Scope scope) { return injectedDirect; } @Override public Scoping getScoping() { return Scoping.DIRECT; } } } ================================================ FILE: magnet/src/test/java/magnet/internal/VisitScopesTest.java ================================================ package magnet.internal; import magnet.Magnet; import magnet.Scope; import magnet.internal.events.ObservableScopeVisitor; import magnet.internal.events.OnEnterScope; import magnet.internal.events.OnExitScope; import org.junit.Before; import org.junit.Test; import static com.google.common.truth.Truth.assertThat; public class VisitScopesTest { private Scope scopeA; private Scope scopeB; private Scope scopeB1; private Scope scopeB2; private Scope scopeC; private ObservableScopeVisitor visitor = new ObservableScopeVisitor(); @Before public void before() { scopeA = Magnet.createRootScope(); scopeB = scopeA.createSubscope(); scopeB1 = scopeB.createSubscope(); scopeB2 = scopeB.createSubscope(); scopeC = scopeA.createSubscope(); } @Test public void visitRootScope_DepthUnlimited() { scopeA.accept(visitor, Integer.MAX_VALUE); assertThat(visitor.visited).containsExactly( new OnEnterScope(scopeA, null), new OnEnterScope(scopeC, scopeA), new OnExitScope(scopeC), new OnEnterScope(scopeB, scopeA), new OnEnterScope(scopeB2, scopeB), new OnExitScope(scopeB2), new OnEnterScope(scopeB1, scopeB), new OnExitScope(scopeB1), new OnExitScope(scopeB), new OnExitScope(scopeA) ).inOrder(); } @Test public void visitRootScope_Depth0() { scopeA.accept(visitor, 0); assertThat(visitor.visited).containsExactly( new OnEnterScope(scopeA, null), new OnExitScope(scopeA) ).inOrder(); } @Test public void visitRootScope_Depth1() { scopeA.accept(visitor, 1); assertThat(visitor.visited).containsExactly( new OnEnterScope(scopeA, null), new OnEnterScope(scopeC, scopeA), new OnExitScope(scopeC), new OnEnterScope(scopeB, scopeA), new OnExitScope(scopeB), new OnExitScope(scopeA) ).inOrder(); } @Test public void visitChildScope_Depth0() { scopeB.accept(visitor, 0); assertThat(visitor.visited).containsExactly( new OnEnterScope(scopeB, scopeA), new OnExitScope(scopeB) ).inOrder(); } @Test public void visitChildScope_Depth1() { scopeB.accept(visitor, 1); assertThat(visitor.visited).containsExactly( new OnEnterScope(scopeB, scopeA), new OnEnterScope(scopeB2, scopeB), new OnExitScope(scopeB2), new OnEnterScope(scopeB1, scopeB), new OnExitScope(scopeB1), new OnExitScope(scopeB) ).inOrder(); } } ================================================ FILE: magnet/src/test/java/magnet/internal/events/ObservableScopeVisitor.java ================================================ package magnet.internal.events; import magnet.Visitor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class ObservableScopeVisitor implements Visitor { public List visited = new ArrayList<>(); private List instances = new ArrayList<>(); @Override public boolean onEnterScope(@NotNull Visitor.Scope scope, @Nullable Visitor.Scope parent) { flushInstances(); visited.add(new OnEnterScope(scope, parent)); return true; } @Override public boolean onInstance(@NotNull Instance instance) { instances.add(new OnInstance(instance)); return true; } @Override public void onExitScope(@NotNull Visitor.Scope scope) { flushInstances(); visited.add(new OnExitScope(scope)); } private void flushInstances() { if (instances.size() > 0) { Collections.sort(instances); visited.addAll(instances); instances.clear(); } } } ================================================ FILE: magnet/src/test/java/magnet/internal/events/OnEnterScope.java ================================================ package magnet.internal.events; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Objects; public class OnEnterScope { private final @NotNull Object scope; private final @Nullable Object parent; public OnEnterScope(@NotNull Object scope, @Nullable Object parent) { this.scope = scope; this.parent = parent; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; OnEnterScope that = (OnEnterScope) o; return scope.equals(that.scope) && Objects.equals(parent, that.parent); } @Override public int hashCode() { return Objects.hash(scope, parent); } @Override public String toString() { return "OnEnterScope: " + scope; } } ================================================ FILE: magnet/src/test/java/magnet/internal/events/OnExitScope.java ================================================ package magnet.internal.events; import org.jetbrains.annotations.NotNull; import java.util.Objects; public class OnExitScope { private final @NotNull Object scope; public OnExitScope(@NotNull Object scope) { this.scope = scope; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; OnExitScope that = (OnExitScope) o; return scope.equals(that.scope); } @Override public int hashCode() { return Objects.hash(scope); } @Override public String toString() { return "OnExitScope: " + scope; } } ================================================ FILE: magnet/src/test/java/magnet/internal/events/OnInstance.java ================================================ package magnet.internal.events; import magnet.Scoping; import magnet.Visitor.Instance; import magnet.Visitor.Provision; import org.jetbrains.annotations.NotNull; import java.util.Objects; public class OnInstance implements Comparable { private final Provision provision; private final Class type; private final Object object; private final String classifier; private final Scoping scoping; OnInstance(Instance object) { this.provision = object.getProvision(); this.type = object.getType(); this.object = object.getValue(); this.classifier = object.getClassifier(); this.scoping = object.getScoping(); } public OnInstance( Provision provision, Class type, Object object, String classifier, Scoping scoping ) { this.provision = provision; this.type = type; this.object = object; this.classifier = classifier; this.scoping = scoping; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; OnInstance that = (OnInstance) o; return provision == that.provision && type.equals(that.type) && object.equals(that.object) && classifier.equals(that.classifier) && scoping == that.scoping; } @Override public int hashCode() { return Objects.hash(provision, type, object, classifier, scoping); } @Override public String toString() { return "OnInstance{ " + provision + " " + scoping + " " + type.getSimpleName() + "@" + (classifier.length() == 0 ? "NONE" : classifier) + " }"; } @Override public int compareTo(@NotNull OnInstance o) { return provision.compareTo(o.provision) * 100 + type.getName().compareTo(o.type.getName()) * 10 + classifier.compareTo(o.classifier); } } ================================================ FILE: magnet/src/test/java/magnet/internal/observer/ScopeObserver.java ================================================ package magnet.internal.observer; import magnet.Visitor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.google.common.truth.Truth.assertThat; public class ScopeObserver implements Visitor { @NotNull private final Map validators = new HashMap<>(); @Nullable private DefaultScopeValidator currentScopeValidator; @Override public boolean onEnterScope(@NotNull Visitor.Scope scope, @Nullable Visitor.Scope parent) { flushScope(); currentScopeValidator = new DefaultScopeValidator(scope); return true; } @Override public boolean onInstance(@NotNull Instance instance) { if (currentScopeValidator != null) { currentScopeValidator.addInstance(instance); } return true; } @Override public void onExitScope(@NotNull Visitor.Scope scope) { flushScope(); } private void flushScope() { if (currentScopeValidator != null) { //noinspection Java8ListSort,Convert2Lambda Collections.sort(currentScopeValidator.instances, new Comparator() { @Override public int compare(Instance o1, Instance o2) { return o1.getType().getName().compareTo(o2.getType().getName()); } }); validators.put(currentScopeValidator.scope, currentScopeValidator); } } public ScopeValidator assetThat(magnet.Scope scopeA) { ScopeValidator validator = validators.get(scopeA); if (validator == null) { throw new IllegalStateException( String.format("Scope %s was not observed", scopeA) ); } return validator; } private static class DefaultScopeValidator implements ScopeValidator { private final List instances = new ArrayList<>(); private Scope scope; DefaultScopeValidator(Scope scope) { this.scope = scope; } void addInstance(Instance instance) { instances.add(instance); } @Override public void hasNoInstances() { assertThat(instances.size()).isEqualTo(0); } @Override public void hasInstanceTypes(Class... instanceTypes) { if (instances.size() != instanceTypes.length) { throw new IllegalStateException( String.format( "Expect %s number of instances in %s, while found %s. Actual instances: %s", instanceTypes.length, scope, instances.size(), instances.toString() ) ); } for (int i = 0; i < instances.size(); i++) { if (!instances.get(i).getType().equals(instanceTypes[i])) { throw new IllegalStateException( String.format( "Expect %s at position %s, while found %s. Actual instances: %s", instanceTypes[i], i, instances.get(i), instances.toString() ) ); } } } } } ================================================ FILE: magnet/src/test/java/magnet/internal/observer/ScopeValidator.java ================================================ package magnet.internal.observer; public interface ScopeValidator { void hasNoInstances(); void hasInstanceTypes(Class... instanceTypes); } ================================================ FILE: magnet/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker ================================================ mock-maker-inline ================================================ FILE: magnet-kotlin/build.gradle ================================================ plugins { id 'kotlin' id 'com.vanniktech.maven.publish' } compileKotlin { kotlinOptions { jvmTarget = javaVersion } } compileTestKotlin { kotlinOptions { jvmTarget = javaVersion } } dependencies { api project(':magnet') implementation deps.kotlinjdk testImplementation deps.junit testImplementation deps.truth testImplementation deps.mockito testImplementation deps.mockitoKotlin } ================================================ FILE: magnet-kotlin/gradle.properties ================================================ POM_NAME=Magnet Runtime Kotlin Extensions POM_ARTIFACT_ID=magnet-kotlin POM_PACKAGING=jar ================================================ FILE: magnet-kotlin/src/main/java/magnet/MagnetExt.kt ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet /** Creates new root scope and initializes it using given init-function. */ inline fun createRootScope(init: Scope.() -> Unit): Scope = Magnet.createRootScope().apply(init) /** Creates new root scope. */ fun createRootScope(): Scope = Magnet.createRootScope() ================================================ FILE: magnet-kotlin/src/main/java/magnet/ScopeExt.kt ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet /** Returns an object from the scope or `null`, if object was not found. */ inline fun Scope.getOptional(classifier: String = Classifier.NONE): T? = this.getOptional(T::class.java, classifier) /** Returns an object from the scope or throws exception, if object was not found. */ inline fun Scope.getSingle(classifier: String = Classifier.NONE): T = this.getSingle(T::class.java, classifier) /** Returns a list of objects or empty list, if no objects were found. */ inline fun Scope.getMany(classifier: String = Classifier.NONE): List = this.getMany(T::class.java, classifier) /** Bind given instance into this scope. */ inline fun Scope.bind(instance: T, classifier: String = Classifier.NONE) = this.bind(T::class.java, instance, classifier) /** Creates a subscope of the current scope. */ inline fun Scope.createSubscope(init: Scope.() -> Unit): Scope = this.createSubscope().apply(init) ================================================ FILE: magnet-kotlin/src/main/java/magnet/internal/ManyLazy.kt ================================================ /* * Copyright (C) 2019 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal import magnet.Scope import java.util.concurrent.atomic.AtomicBoolean class ManyLazy( private val scope: Scope, private val type: Class, private val classifier: String ) : Lazy> { private val initialized = AtomicBoolean(false) private lateinit var _value: List override val value: List get() { if (!initialized.getAndSet(true)) { _value = scope.getMany(type, classifier) } return _value } override fun isInitialized(): Boolean = initialized.get() } ================================================ FILE: magnet-kotlin/src/main/java/magnet/internal/OptionalLazy.kt ================================================ /* * Copyright (C) 2019 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal import magnet.Scope import java.util.concurrent.atomic.AtomicBoolean class OptionalLazy( private val scope: Scope, private val type: Class, private val classifier: String ) : Lazy { private val initialized = AtomicBoolean(false) private var _value: T? = null override val value: T? get() { if (!initialized.getAndSet(true)) { _value = scope.getOptional(type, classifier) } return _value } override fun isInitialized(): Boolean = initialized.get() } ================================================ FILE: magnet-kotlin/src/main/java/magnet/internal/SingleLazy.kt ================================================ /* * Copyright (C) 2019 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.internal import magnet.Scope import java.util.concurrent.atomic.AtomicBoolean class SingleLazy( private val scope: Scope, private val type: Class, private val classifier: String ) : Lazy { private val initialized = AtomicBoolean(false) private lateinit var _value: T override val value: T get() { if (!initialized.getAndSet(true)) { _value = scope.getSingle(type, classifier) } return _value } override fun isInitialized(): Boolean = initialized.get() } ================================================ FILE: magnet-kotlin/src/test/java/magnet/kotlin/ScopeExtTest.kt ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.kotlin import magnet.Classifier import magnet.Scope import magnet.bind import magnet.getOptional import magnet.getSingle import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.any import org.mockito.Mockito.anyString import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnitRunner import org.mockito.Mockito.`when` as on @Suppress("UNUSED_VARIABLE") @RunWith(MockitoJUnitRunner.StrictStubs::class) class ScopeExtTest { @Mock lateinit var scope: Scope @Test fun testGet() { // when val value: String? = scope.getOptional() // then verify(scope).getOptional(String::class.java, Classifier.NONE) } @Test fun testRequire() { // when val value = scope.getSingle() // then verify(scope).getSingle(String::class.java, Classifier.NONE) } @Test fun testRegister() { // given on(scope.bind(any(Class::class.java), any(), anyString())).thenReturn(scope) // when scope.bind("component") // then verify(scope).bind(String::class.java, "component", Classifier.NONE) } } ================================================ FILE: magnet-kotlin/src/test/java/magnet/kotlin/internal/ManyLazyTest.kt ================================================ package magnet.kotlin.internal import com.google.common.truth.Truth.assertThat import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.same import org.mockito.kotlin.verify import magnet.Scope import magnet.internal.ManyLazy import org.junit.Test class ManyLazyTest { private val value = listOf("value") private val type = String::class.java private val classifier = "classifier" private val scope: Scope = mock { on { getMany(same(type), same(classifier)) }.thenReturn(value) } private val underTest: Lazy> = ManyLazy(scope, type, classifier) @Test fun `Constructor doesn't call scope_getMany()`() { verify(scope, never()).getMany(type, classifier) } @Test fun `value calls scope_getMany()`() { underTest.value verify(scope).getMany(type, classifier) } @Test fun `value calls scope_getMany() once`() { underTest.value underTest.value verify(scope).getMany(type, classifier) } @Test fun `value returns value`() { assertThat(underTest.value).isSameInstanceAs(value) } @Test fun `isInitialized() returns false when not initialized`() { assertThat(underTest.isInitialized()).isFalse() } @Test fun `isInitialized() returns true when initialized`() { underTest.value assertThat(underTest.isInitialized()).isTrue() } } ================================================ FILE: magnet-kotlin/src/test/java/magnet/kotlin/internal/OptionalLazyTest.kt ================================================ package magnet.kotlin.internal import com.google.common.truth.Truth.assertThat import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.same import org.mockito.kotlin.verify import magnet.Scope import magnet.internal.OptionalLazy import org.junit.Test class OptionalLazyTest { private val value = "value" private val type = String::class.java private val classifier = "classifier" private val scope: Scope = mock { on { getOptional(same(type), same(classifier)) }.thenReturn(value) } private val underTest: Lazy = OptionalLazy(scope, type, classifier) @Test fun `Constructor doesn't call scope_getOptional()`() { verify(scope, never()).getOptional(type, classifier) } @Test fun `value calls scope_getOptional()`() { underTest.value verify(scope).getOptional(type, classifier) } @Test fun `value calls scope_getOptional() once`() { underTest.value underTest.value verify(scope).getOptional(type, classifier) } @Test fun `value returns value`() { assertThat(underTest.value).isSameInstanceAs(value) } @Test fun `isInitialized() returns false when not initialized`() { assertThat(underTest.isInitialized()).isFalse() } @Test fun `isInitialized() returns true when initialized`() { underTest.value assertThat(underTest.isInitialized()).isTrue() } } ================================================ FILE: magnet-kotlin/src/test/java/magnet/kotlin/internal/SingleLazyTest.kt ================================================ package magnet.kotlin.internal import com.google.common.truth.Truth.assertThat import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.same import org.mockito.kotlin.verify import magnet.Scope import magnet.internal.SingleLazy import org.junit.Test class SingleLazyTest { private val value = "value" private val type = String::class.java private val classifier = "classifier" private val scope: Scope = mock { on { getSingle(same(type), same(classifier)) }.thenReturn(value) } private val underTest: Lazy = SingleLazy(scope, type, classifier) @Test fun `Constructor doesn't call scope_getSingle()`() { verify(scope, never()).getSingle(type, classifier) } @Test fun `value calls scope_getSingle()`() { underTest.value verify(scope).getSingle(type, classifier) } @Test fun `value calls scope_getSingle() once`() { underTest.value underTest.value verify(scope).getSingle(type, classifier) } @Test fun `value returns value`() { assertThat(underTest.value).isSameInstanceAs(value) } @Test fun `isInitialized() returns false when not initialized`() { assertThat(underTest.isInitialized()).isFalse() } @Test fun `isInitialized() returns true when initialized`() { underTest.value assertThat(underTest.isInitialized()).isTrue() } } ================================================ FILE: magnet-processor/build.gradle ================================================ plugins { id 'kotlin' id 'com.vanniktech.maven.publish' } compileKotlin { kotlinOptions { jvmTarget = javaVersion } } compileTestKotlin { kotlinOptions { jvmTarget = javaVersion } } if (JavaVersion.current() >= JavaVersion.VERSION_16) { test { jvmArgs( // used "--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", "--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", // the others "--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", "--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", "--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED", "--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", "--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", "--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", ) } } dependencies { implementation project(':magnet') implementation project(':magnet-kotlin') implementation deps.kotlinjdk implementation deps.kotlinMetadata implementation deps.javapoet testImplementation deps.compileTesting testImplementation deps.jsr305 testImplementation files(file("libs/tools.jar")) } ================================================ FILE: magnet-processor/gradle.properties ================================================ POM_NAME=Magnet Annotation Processor POM_ARTIFACT_ID=magnet-processor POM_PACKAGING=jar ================================================ FILE: magnet-processor/libs/tools.jar ================================================ [File too large to display: 17.5 MB] ================================================ FILE: magnet-processor/src/main/java/magnet/processor/MagnetProcessor.kt ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.processor import magnet.Instance import magnet.Registry import magnet.Scope import magnet.processor.common.AnnotationValueExtractor import magnet.processor.common.CompilationException import magnet.processor.common.ValidationException import magnet.processor.instances.InstanceProcessor import magnet.processor.registry.RegistryProcessor import javax.annotation.processing.* import javax.lang.model.SourceVersion import javax.lang.model.element.TypeElement import javax.lang.model.util.Elements import javax.lang.model.util.Types import javax.tools.Diagnostic @SupportedSourceVersion(SourceVersion.RELEASE_11) class MagnetProcessor : AbstractProcessor() { private lateinit var env: MagnetProcessorEnv private lateinit var instanceProcessor: InstanceProcessor private lateinit var registryProcessor: RegistryProcessor override fun init(processingEnvironment: ProcessingEnvironment) { super.init(processingEnvironment) env = MagnetProcessorEnv(processingEnvironment) instanceProcessor = InstanceProcessor(env) registryProcessor = RegistryProcessor(env) } override fun process( annotations: MutableSet, roundEnv: RoundEnvironment ): Boolean { return try { val instancesProcessed = instanceProcessor.process(roundEnv) val registryProcessed = registryProcessor.process(roundEnv) instancesProcessed || registryProcessed } catch (e: ValidationException) { env.reportError(e) false } catch (e: CompilationException) { env.reportError(e) false } catch (e: Throwable) { env.reportError(e) e.printStackTrace() false } } override fun getSupportedAnnotationTypes(): MutableSet { return mutableSetOf( Instance::class.java.name, Scope::class.java.name, Registry::class.java.name ) } } class MagnetProcessorEnv( private val processEnvironment: ProcessingEnvironment ) { val filer: Filer get() = processEnvironment.filer val elements: Elements get() = processEnvironment.elementUtils val types: Types get() = processEnvironment.typeUtils val annotation = AnnotationValueExtractor(elements) fun reportError(e: ValidationException) { processEnvironment.messager.printMessage(Diagnostic.Kind.ERROR, e.message, e.element) } fun reportError(e: CompilationException) { processEnvironment.messager.printMessage( Diagnostic.Kind.ERROR, "Unexpected compilation error," + " please file the bug at https://github.com/beworker/magnet/issues." + " Message: ${e.message ?: "none."}", e.element ) } fun reportError(e: Throwable) { processEnvironment.messager.printMessage( Diagnostic.Kind.ERROR, "Unexpected compilation error," + " please file the bug at https://github.com/beworker/magnet/issues." + " Message: ${e.message ?: "none."}" ) } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/common/AptUtils.kt ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.processor.common import javax.lang.model.element.AnnotationMirror import javax.lang.model.element.AnnotationValue import javax.lang.model.element.Element import javax.lang.model.element.TypeElement import javax.lang.model.type.TypeMirror import javax.lang.model.util.Elements import javax.lang.model.util.SimpleAnnotationValueVisitor6 import javax.lang.model.util.Types inline fun AnnotationMirror.isOfAnnotationType(): Boolean = this.annotationType.toString() == T::class.java.name inline fun Element.eachAttributeOf( block: (name: String, value: AnnotationValue) -> Unit ) { for (annotationMirror in annotationMirrors) { if (annotationMirror.isOfAnnotationType()) { for (entry in annotationMirror.elementValues.entries) { block(entry.key.simpleName.toString(), entry.value) } } } } fun TypeElement.verifyInheritance(element: Element, types: Types) { val isTypeImplemented = types.isAssignable( element.asType(), types.getDeclaredType(this) ) if (!isTypeImplemented) { element.throwValidationError("$element must implement $this") } } class ValidationException(val element: Element, message: String) : Throwable(message) class CompilationException(val element: Element, message: String) : Throwable(message) fun Element.throwValidationError(message: String): Nothing { throw ValidationException(this, message) } fun Element.throwCompilationError(message: String): Nothing { throw CompilationException(this, message) } @Suppress("DEPRECATION") class AnnotationValueExtractor( private val elements: Elements ) : SimpleAnnotationValueVisitor6() { private var value: Any? = null override fun visitString(s: String, p: Void?): Void? { value = s return p } override fun visitType(t: TypeMirror, p: Void?): Void? { value = elements.getTypeElement(t.toString()) return p } fun getStringValue(value: AnnotationValue): String { this.value = null value.accept(this, null) return this.value as String } fun getTypeElement(value: AnnotationValue): TypeElement { this.value = null value.accept(this, null) return this.value as TypeElement } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/common/KotlinMethodMetadata.kt ================================================ /* * Copyright (C) 2019-2021 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.processor.common import kotlinx.metadata.ClassName import kotlinx.metadata.Flag import kotlinx.metadata.Flags import kotlinx.metadata.KmClassVisitor import kotlinx.metadata.KmConstructorVisitor import kotlinx.metadata.KmFunctionVisitor import kotlinx.metadata.KmPackageVisitor import kotlinx.metadata.KmTypeVisitor import kotlinx.metadata.KmValueParameterVisitor import kotlinx.metadata.KmVariance import kotlinx.metadata.jvm.KotlinClassHeader import kotlinx.metadata.jvm.KotlinClassMetadata import javax.lang.model.element.ExecutableElement import javax.lang.model.element.TypeElement interface KotlinMethodMetadata { val method: ExecutableElement fun getTypeMeta(parameterName: String, typeDepth: Int): TypeMeta } data class ParameterMeta( val name: String, val types: List ) data class TypeMeta( val type: String, val nullable: Boolean, val default: Boolean ) interface FunctionSelector { val function: ExecutableElement fun visitFunction(flags: Flags, name: String): Boolean fun acceptFunctionParameters(parameters: Map): Map } internal class MethodFunctionSelector( override val function: ExecutableElement ) : FunctionSelector { override fun visitFunction(flags: Flags, name: String) = function.simpleName.toString() == name override fun acceptFunctionParameters(parameters: Map): Map { if (parameters.size != function.parameters.size) { return emptyMap() } return if (function.hasParameters(parameters)) parameters else emptyMap() } } fun ExecutableElement.hasParameters(parameters: Map): Boolean { if (parameters.values.size != this.parameters.size) return false parameters.values.forEachIndexed { index, parameterMeta -> if (this.parameters[index].simpleName.toString() != parameterMeta.name) { return false } } return true } internal class DefaultKotlinMethodMetadata( metadata: Metadata, private val element: TypeElement, private val functionSelector: FunctionSelector ) : KotlinMethodMetadata { override lateinit var method: ExecutableElement private val parameterMetas: Map = with(metadata) { KotlinClassMetadata.read( KotlinClassHeader( kind, metadataVersion, data1, data2, extraString, packageName, extraInt ) ) }.let { kotlinMetadata -> when (kotlinMetadata) { is KotlinClassMetadata.Class -> AnnotatedClassVisitor(functionSelector).let { kotlinMetadata.accept(it) method = functionSelector.function it.parameters } is KotlinClassMetadata.FileFacade -> AnnotatedPackageVisitor(functionSelector).let { kotlinMetadata.accept(it) method = functionSelector.function it.parameters } else -> throw CompilationException( element = element, message = "Unsupported KotlinClassMetadata of type $kotlinMetadata" ) } } override fun getTypeMeta(parameterName: String, typeDepth: Int): TypeMeta { val parameterMeta = parameterMetas[parameterName] ?: element.throwCompilationError( "Cannot find parameter '$parameterName' in metadata of $element." + " Available parameters: $parameterMetas" ) if (typeDepth >= parameterMeta.types.size) { element.throwCompilationError( "Cannot find TypeMeta depth of $typeDepth in ${parameterMeta.types}." ) } return parameterMeta.types[typeDepth] } } private class AnnotatedPackageVisitor( private val functionSelector: FunctionSelector ) : KmPackageVisitor() { var parameters = emptyMap() override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor? { return if (functionSelector.visitFunction(flags, name)) { val visitedParameters = mutableMapOf() object : KmFunctionVisitor() { override fun visitValueParameter(flags: Flags, name: String): KmValueParameterVisitor? { val valueFlags = flags return object : KmValueParameterVisitor() { override fun visitType(flags: Flags): KmTypeVisitor? { return TypeExtractorVisitor(valueFlags, flags) { typeMeta -> visitedParameters[name] = ParameterMeta(name, typeMeta) } } } } override fun visitEnd() { parameters = functionSelector.acceptFunctionParameters(visitedParameters) } } } else null } } private class AnnotatedClassVisitor( private val functionSelector: FunctionSelector ) : KmClassVisitor() { var parameters = emptyMap() override fun visitConstructor(flags: Flags): KmConstructorVisitor? { return if (functionSelector.visitFunction(flags, CONSTRUCTOR_NAME)) { val visitedParameters = mutableMapOf() object : KmConstructorVisitor() { override fun visitValueParameter(flags: Flags, name: String): KmValueParameterVisitor? { val valueFlags = flags return object : KmValueParameterVisitor() { override fun visitType(flags: Flags): KmTypeVisitor? { return TypeExtractorVisitor(valueFlags, flags) { typeMeta -> visitedParameters[name] = ParameterMeta(name, typeMeta) } } } } override fun visitEnd() { parameters = functionSelector.acceptFunctionParameters(visitedParameters) } } } else null } override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor? { return if (functionSelector.visitFunction(flags, name)) { val visitedParameters = mutableMapOf() object : KmFunctionVisitor() { override fun visitValueParameter(flags: Flags, name: String): KmValueParameterVisitor? { val valueFlags = flags return object : KmValueParameterVisitor() { override fun visitType(flags: Flags): KmTypeVisitor? { return TypeExtractorVisitor(valueFlags, flags) { typeMeta -> visitedParameters[name] = ParameterMeta(name, typeMeta) } } } } override fun visitEnd() { parameters = functionSelector.acceptFunctionParameters(visitedParameters) } } } else null } } private class TypeExtractorVisitor( private val valueFlags: Flags, private val typeFlags: Flags, private val typeMeta: MutableList = mutableListOf(), private val onVisitEnd: OnVisitEnd? = null ) : KmTypeVisitor() { override fun visitClass(name: ClassName) { Flag.ValueParameter.DECLARES_DEFAULT_VALUE(valueFlags).let { default -> typeMeta.add( TypeMeta( type = name, nullable = Flag.Type.IS_NULLABLE(typeFlags), default = default ) ) } } override fun visitArgument(flags: Flags, variance: KmVariance): KmTypeVisitor? = TypeExtractorVisitor(valueFlags, flags, typeMeta) override fun visitEnd() { onVisitEnd?.invoke(typeMeta) } } const val CONSTRUCTOR_NAME = "" private typealias OnVisitEnd = (List) -> Unit ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/FactoryTypeModel.kt ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.processor.instances import com.squareup.javapoet.ClassName import com.squareup.javapoet.TypeName import javax.lang.model.element.Element const val PARAM_SCOPE_NAME = "scope" interface FactoryTypeVisitor { fun enterFactoryClass(factoryType: FactoryType) {} fun enterCreateMethod(createMethod: CreateMethod) {} fun visitCreateMethodParameter(parameter: MethodParameter) {} fun exitCreateMethod(createMethod: CreateMethod) {} fun visit(method: GetScopingMethod) {} fun visit(method: GetLimitMethod) {} fun enterSiblingTypesMethod(method: GetSiblingTypesMethod) {} fun visitSiblingType(type: ClassName) {} fun exitSiblingTypesMethod(method: GetSiblingTypesMethod) {} fun enterGetSelectorMethod(method: GetSelectorMethod) {} fun visitSelectorArgument(argument: String) {} fun exitGetSelectorMethod(method: GetSelectorMethod) {} fun exitFactoryClass(factory: FactoryType) {} } class FactoryType( val element: Element, val interfaceType: ClassName, val classifier: String, val scoping: String, val disabled: Boolean, val factoryType: ClassName, val implementationType: ClassName?, val customFactoryType: TypeName?, val disposerMethodName: String?, val createStatement: CreateStatement, val createMethod: CreateMethod, val getScopingMethod: GetScopingMethod, val getLimitMethod: GetLimitMethod?, val getSelectorMethod: GetSelectorMethod?, val getSiblingTypesMethod: GetSiblingTypesMethod? ) { fun accept(visitor: FactoryTypeVisitor) { visitor.enterFactoryClass(this) createMethod.accept(visitor) getScopingMethod.accept(visitor) getLimitMethod?.accept(visitor) getSiblingTypesMethod?.accept(visitor) getSelectorMethod?.accept(visitor) visitor.exitFactoryClass(this) } } abstract class CreateStatement class TypeCreateStatement( val instanceType: ClassName ) : CreateStatement() class StaticMethodCreateStatement( val staticMethodClassName: ClassName, val staticMethodName: String ) : CreateStatement() class CreateMethod( val methodParameter: List ) { fun accept(visitor: FactoryTypeVisitor) { visitor.enterCreateMethod(this) methodParameter.forEach { parameterNode -> parameterNode.accept(visitor) } visitor.exitCreateMethod(this) } } enum class Cardinality { Single, Optional, Many } sealed class Expression { object Scope : Expression() data class Getter(val cardinality: Cardinality) : Expression() data class LazyGetter(val cardinality: Cardinality) : Expression() } data class MethodParameter( val name: String, val expression: Expression, val returnType: TypeName, val parameterType: TypeName, val classifier: String, val typeErased: Boolean ) { fun accept(visitor: FactoryTypeVisitor) { visitor.visitCreateMethodParameter(this) } } class GetScopingMethod(val scoping: String) { fun accept(visitor: FactoryTypeVisitor) { visitor.visit(this) } } class GetLimitMethod(val limit: String) { fun accept(visitor: FactoryTypeVisitor) { visitor.visit(this) } } class GetSiblingTypesMethod(val siblingTypes: List) { fun accept(visitor: FactoryTypeVisitor) { visitor.enterSiblingTypesMethod(this) for (siblingType in siblingTypes) { visitor.visitSiblingType(siblingType) } visitor.exitSiblingTypesMethod(this) } } class GetSelectorMethod(val selectorArguments: List) { fun accept(visitor: FactoryTypeVisitor) { visitor.enterGetSelectorMethod(this) for (argument in selectorArguments) { visitor.visitSelectorArgument(argument) } visitor.exitGetSelectorMethod(this) } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/InstanceProcessor.kt ================================================ package magnet.processor.instances import magnet.Instance import magnet.processor.MagnetProcessorEnv import magnet.processor.instances.aspects.index.FactoryIndexCodeGenerator import magnet.processor.instances.generator.CodeWriter import magnet.processor.instances.generator.FactoryTypeCodeGenerator import magnet.processor.instances.parser.InstanceParserForClass import magnet.processor.instances.parser.InstanceParserForMethod import javax.annotation.processing.RoundEnvironment import javax.lang.model.util.ElementFilter class InstanceProcessor( private val env: MagnetProcessorEnv ) { private val factoryFromClassAnnotationParser = InstanceParserForClass(env) private val factoryFromMethodAnnotationParser = InstanceParserForMethod(env) private val factoryTypeCodeGenerator = FactoryTypeCodeGenerator() private val factoryIndexCodeGenerator = FactoryIndexCodeGenerator() fun process(roundEnv: RoundEnvironment): Boolean { val annotatedElements = roundEnv.getElementsAnnotatedWith(Instance::class.java) if (annotatedElements.isEmpty()) { return false } val factoryTypes = mutableListOf() ElementFilter.typesIn(annotatedElements).forEach { element -> val parsedFactoryTypes = with(factoryFromClassAnnotationParser) { element.parse() } for (factoryType in parsedFactoryTypes) { if (!factoryType.disabled) { factoryTypes.add(factoryType) } } } ElementFilter.methodsIn(annotatedElements).forEach { element -> val parsedFactoryTypes = with(factoryFromMethodAnnotationParser) { element.parse() } for (factoryType in parsedFactoryTypes) { if (!factoryType.disabled) { factoryTypes.add(factoryType) } } } factoryTypes.sortBy { factoryName(it) } val codeWriters = mutableListOf() factoryTypes.forEach { factoryType -> codeWriters.add(factoryTypeCodeGenerator.generateFrom(factoryType)) codeWriters.add(factoryIndexCodeGenerator.generateFrom(factoryType)) } codeWriters.forEach { codeWriter -> codeWriter.writeInto(env.filer) } return true } } private fun factoryName(factoryType: FactoryType): String = factoryType.factoryType.simpleName() ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/classifier/ClassifierAttributeParser.kt ================================================ package magnet.processor.instances.aspects.classifier import magnet.processor.instances.parser.AttributeParser import magnet.processor.instances.parser.ParserInstance import javax.lang.model.element.AnnotationValue import javax.lang.model.element.Element object ClassifierAttributeParser : AttributeParser("classifier") { override fun Scope.parse(value: AnnotationValue): ParserInstance = instance.copy(classifier = value.value.toString()) } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/disabled/DisabledAttributeParser.kt ================================================ package magnet.processor.instances.aspects.disabled import magnet.processor.instances.parser.AttributeParser import magnet.processor.instances.parser.ParserInstance import javax.lang.model.element.AnnotationValue import javax.lang.model.element.Element object DisabledAttributeParser : AttributeParser("disabled") { override fun Scope.parse(value: AnnotationValue): ParserInstance = instance.copy(disabled = value.value.toString().toBoolean()) } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/disposer/DisposeMethodGenerator.kt ================================================ package magnet.processor.instances.aspects.disposer import com.squareup.javapoet.MethodSpec import com.squareup.javapoet.TypeName import com.squareup.javapoet.TypeSpec import magnet.processor.instances.FactoryType import javax.lang.model.element.Modifier internal class DisposeMethodGenerator { private var methodBuilder: MethodSpec.Builder? = null fun visitFactoryClass(factoryType: FactoryType) { methodBuilder = if (factoryType.disposerMethodName == null) null else MethodSpec .methodBuilder("dispose") .addAnnotation(Override::class.java) .addModifiers(Modifier.PUBLIC) .returns(TypeName.VOID) .addParameter(factoryType.interfaceType, "instance") .addStatement( "((\$T) instance).\$L()", factoryType.implementationType, factoryType.disposerMethodName ) } fun generate(typeBuilder: TypeSpec.Builder) { methodBuilder?.let { typeBuilder.addMethod(it.build()) } } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/disposer/DisposerAttributeParser.kt ================================================ package magnet.processor.instances.aspects.disposer import magnet.processor.common.throwValidationError import magnet.processor.instances.parser.AttributeParser import magnet.processor.instances.parser.ParserInstance import javax.lang.model.element.AnnotationValue import javax.lang.model.element.Element import javax.lang.model.element.ElementKind import javax.lang.model.element.ExecutableElement import javax.lang.model.element.Modifier import javax.lang.model.type.TypeKind object DisposerAttributeParser : AttributeParser("disposer") { override fun Scope.parse(value: AnnotationValue): ParserInstance = instance.copy(disposer = parseMethodName(value)) private fun Scope.parseMethodName(value: AnnotationValue): String { if (element.kind != ElementKind.CLASS) element.throwValidationError("Disposer can be defined for annotated class only.") val methodName = env.annotation .getStringValue(value) .removeSurrounding("\"") val methodElement = element.enclosedElements .find { it.kind == ElementKind.METHOD && it.simpleName.toString() == methodName } ?: element.throwValidationError("Instance must declare disposer method $methodName().") val returnType = (methodElement as ExecutableElement).returnType if (returnType.kind != TypeKind.VOID) element.throwValidationError("Disposer method $methodName() must return void.") if (methodElement.parameters.size != 0) element.throwValidationError("Disposer method $methodName() must have no parameters.") if (methodElement.modifiers.contains(Modifier.PRIVATE)) element.throwValidationError("Disposer method $methodName() must not be 'private'.") return methodName } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/disposer/DisposerValidator.kt ================================================ package magnet.processor.instances.aspects.disposer import magnet.Scoping import magnet.processor.MagnetProcessorEnv import magnet.processor.common.throwValidationError import magnet.processor.instances.parser.AspectValidator import magnet.processor.instances.parser.ParserInstance import javax.lang.model.element.Element object DisposerValidator : AspectValidator { override fun ParserInstance.validate( env: MagnetProcessorEnv ): ParserInstance { if (disposer != null && scoping == Scoping.UNSCOPED.name) element.throwValidationError("Disposer cannot be used with UNSCOPED instances.") return this } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/disposer/IsDisposableMethodGenerator.kt ================================================ package magnet.processor.instances.aspects.disposer import com.squareup.javapoet.MethodSpec import com.squareup.javapoet.TypeName import com.squareup.javapoet.TypeSpec import magnet.processor.instances.FactoryType import javax.lang.model.element.Modifier internal class IsDisposableMethodGenerator { private var methodBuilder: MethodSpec.Builder? = null fun visitFactoryClass(factoryType: FactoryType) { methodBuilder = if (factoryType.disposerMethodName == null) null else MethodSpec .methodBuilder("isDisposable") .addAnnotation(Override::class.java) .addModifiers(Modifier.PUBLIC) .returns(TypeName.BOOLEAN) .addStatement("return true") } fun generate(typeBuilder: TypeSpec.Builder) { methodBuilder?.let { typeBuilder.addMethod(it.build()) } } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/factory/CreateMethodGenerator.kt ================================================ package magnet.processor.instances.aspects.factory import com.squareup.javapoet.* import magnet.internal.ManyLazy import magnet.internal.OptionalLazy import magnet.internal.SingleLazy import magnet.processor.instances.* interface CreateMethodGenerator { fun visitFactoryClass(factoryType: FactoryType) {} fun enterCreateMethod(createMethod: CreateMethod) {} fun visitCreateMethodParameter(parameter: MethodParameter) {} fun exitCreateMethod() {} fun generate(typeBuilder: TypeSpec.Builder) fun CodeBlock.Builder.addCreateParameterStatement(parameter: MethodParameter) { when (val expression = parameter.expression) { is Expression.Getter -> { addStatement( "\$T \$L = scope.${expression.getterName}(\$T.class, \$S)", parameter.returnType, parameter.name, parameter.parameterType, parameter.classifier ) } is Expression.LazyGetter -> { addStatement( "\$T \$L = new \$T($PARAM_SCOPE_NAME, \$T.class, \$S)", parameter.returnType, parameter.name, expression.lazyGetterType, parameter.parameterType, parameter.classifier ) } Expression.Scope -> {} } } fun MethodSpec.Builder.addNewInstanceStatement( constructorParameters: String, createStatement: CreateStatement ): MethodSpec.Builder { when (createStatement) { is TypeCreateStatement -> { addStatement( "return new \$T($constructorParameters)", createStatement.instanceType ) } is StaticMethodCreateStatement -> { addStatement( "return \$T.\$L($constructorParameters)", createStatement.staticMethodClassName, createStatement.staticMethodName ) } } return this } } private val Expression.Getter.getterName: String get() = when (cardinality) { Cardinality.Single -> "getSingle" Cardinality.Optional -> "getOptional" Cardinality.Many -> "getMany" } private val Expression.LazyGetter.lazyGetterType: TypeName get() = when (cardinality) { Cardinality.Single -> ClassName.get(SingleLazy::class.java) Cardinality.Optional -> ClassName.get(OptionalLazy::class.java) Cardinality.Many -> ClassName.get(ManyLazy::class.java) } class DefaultCreateMethodGenerator : CreateMethodGenerator { private val customFactoryGenerator = CustomFactoryCreateMethodGenerator() private val standardFactoryGenerator = StandardFactoryCreateMethodGenerator() private lateinit var impl: CreateMethodGenerator override fun visitFactoryClass(factoryType: FactoryType) { impl = if (factoryType.customFactoryType != null) customFactoryGenerator else standardFactoryGenerator impl.visitFactoryClass(factoryType) } override fun enterCreateMethod(createMethod: CreateMethod) { impl.enterCreateMethod(createMethod) } override fun visitCreateMethodParameter(parameter: MethodParameter) { impl.visitCreateMethodParameter(parameter) } override fun exitCreateMethod() { impl.exitCreateMethod() } override fun generate(typeBuilder: TypeSpec.Builder) { impl.generate(typeBuilder) } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/factory/CustomFactoryCreateMethodGenerator.kt ================================================ package magnet.processor.instances.aspects.factory import com.squareup.javapoet.ClassName import com.squareup.javapoet.CodeBlock import com.squareup.javapoet.FieldSpec import com.squareup.javapoet.MethodSpec import com.squareup.javapoet.ParameterizedTypeName import com.squareup.javapoet.TypeName import com.squareup.javapoet.TypeSpec import magnet.Factory import magnet.Scope import magnet.Scoping import magnet.processor.instances.CreateMethod import magnet.processor.instances.FactoryType import magnet.processor.instances.MethodParameter import magnet.processor.instances.PARAM_SCOPE_NAME import javax.lang.model.element.Modifier class CustomFactoryCreateMethodGenerator : CreateMethodGenerator { private lateinit var factoryType: FactoryType private lateinit var factoryFieldType: TypeName private lateinit var instantiateMethodBuilder: MethodSpec.Builder private lateinit var instantiateMethodCodeBuilder: CodeBlock.Builder private var constructorParametersBuilder = StringBuilder() override fun visitFactoryClass(factoryType: FactoryType) { this.factoryType = factoryType constructorParametersBuilder.setLength(0) val customFactoryType = checkNotNull(factoryType.customFactoryType) factoryFieldType = when (customFactoryType) { is ParameterizedTypeName -> ParameterizedTypeName.get(customFactoryType.rawType, factoryType.interfaceType) else -> customFactoryType } } override fun enterCreateMethod(createMethod: CreateMethod) { instantiateMethodBuilder = MethodSpec .methodBuilder("instantiate") .addAnnotation(Override::class.java) .addModifiers(Modifier.PUBLIC) .addParameter(Scope::class.java, "scope") .returns(factoryType.interfaceType) instantiateMethodCodeBuilder = CodeBlock .builder() } override fun visitCreateMethodParameter(parameter: MethodParameter) { instantiateMethodCodeBuilder.addCreateParameterStatement(parameter) constructorParametersBuilder.append(parameter.name).append(", ") } override fun exitCreateMethod() { if (constructorParametersBuilder.isNotEmpty()) { constructorParametersBuilder.setLength(constructorParametersBuilder.length - 2) } } override fun generate(typeBuilder: TypeSpec.Builder) { typeBuilder .addSuperinterface( ParameterizedTypeName.get( ClassName.get(Factory.Instantiator::class.java), factoryType.interfaceType ) ) .addField( FieldSpec .builder(factoryFieldType, "factory") .addModifiers(Modifier.PRIVATE) .initializer("null") .build() ) .addMethod( MethodSpec .methodBuilder("create") .addAnnotation(Override::class.java) .addModifiers(Modifier.PUBLIC) .addParameter(Scope::class.java, PARAM_SCOPE_NAME) .returns(factoryType.interfaceType) .addCode( CodeBlock.builder() .beginControlFlow("if (factory == null)") .addStatement("factory = new \$T()", factoryFieldType) .endControlFlow() .addStatement( "return factory.create(scope, \$T.class, \$S, \$T.\$L, this)", factoryType.interfaceType, factoryType.classifier, Scoping::class.java, factoryType.scoping ) .build() ) .build() ) .addMethod( instantiateMethodBuilder .addCode(instantiateMethodCodeBuilder.build()) .addNewInstanceStatement( constructorParametersBuilder.toString(), factoryType.createStatement ) .build() ) } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/factory/FactoryAttributeParser.kt ================================================ package magnet.processor.instances.aspects.factory import com.squareup.javapoet.TypeName import magnet.processor.instances.parser.AttributeParser import magnet.processor.instances.parser.ParserInstance import javax.lang.model.element.AnnotationValue import javax.lang.model.element.Element object FactoryAttributeParser : AttributeParser("factory") { override fun Scope.parse(value: AnnotationValue): ParserInstance = instance.copy(factory = parseFactoryType(value)) private fun Scope.parseFactoryType(value: AnnotationValue): TypeName? = TypeName.get(env.annotation.getTypeElement(value).asType()) } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/factory/StandardFactoryCreateMethodGenerator.kt ================================================ package magnet.processor.instances.aspects.factory import com.squareup.javapoet.AnnotationSpec import com.squareup.javapoet.CodeBlock import com.squareup.javapoet.MethodSpec import com.squareup.javapoet.TypeSpec import magnet.Scope import magnet.processor.instances.CreateMethod import magnet.processor.instances.Expression import magnet.processor.instances.FactoryType import magnet.processor.instances.MethodParameter import magnet.processor.instances.PARAM_SCOPE_NAME import javax.lang.model.element.Modifier class StandardFactoryCreateMethodGenerator : CreateMethodGenerator { private lateinit var factoryType: FactoryType private var createMethodBuilder: MethodSpec.Builder? = null private var createMethodCodeBuilder: CodeBlock.Builder? = null private var constructorParametersBuilder = StringBuilder() private var isSuppressUncheckedAdded = false override fun visitFactoryClass(factoryType: FactoryType) { this.factoryType = factoryType createMethodBuilder = null createMethodCodeBuilder = null constructorParametersBuilder.setLength(0) isSuppressUncheckedAdded = false } override fun enterCreateMethod(createMethod: CreateMethod) { createMethodBuilder = MethodSpec .methodBuilder("create") .addAnnotation(Override::class.java) .addModifiers(Modifier.PUBLIC) .addParameter(Scope::class.java, PARAM_SCOPE_NAME) .returns(factoryType.interfaceType) createMethodCodeBuilder = CodeBlock.builder() } override fun visitCreateMethodParameter(parameter: MethodParameter) { createMethodCodeBuilder?.let { builder -> builder.addCreateParameterStatement(parameter) val paramName = if (parameter.expression == Expression.Scope) PARAM_SCOPE_NAME else parameter.name constructorParametersBuilder.append(paramName).append(", ") } createMethodBuilder?.let { builder -> if (parameter.typeErased && !isSuppressUncheckedAdded) { isSuppressUncheckedAdded = true builder.addAnnotation( AnnotationSpec .builder(SuppressWarnings::class.java) .addMember("value", "\"unchecked\"") .build() ) } } } override fun exitCreateMethod() { if (constructorParametersBuilder.isNotEmpty()) { constructorParametersBuilder.setLength(constructorParametersBuilder.length - 2) } } override fun generate(typeBuilder: TypeSpec.Builder) { createMethodBuilder?.let { builder -> createMethodCodeBuilder?.let { codeBuilder -> builder.addCode(codeBuilder.build()) } builder.addNewInstanceStatement( constructorParametersBuilder.toString(), factoryType.createStatement ) typeBuilder.addMethod(builder.build()) } } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/index/FactoryIndexCodeGenerator.kt ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.processor.instances.aspects.index import com.squareup.javapoet.AnnotationSpec import com.squareup.javapoet.ClassName import com.squareup.javapoet.TypeSpec import magnet.internal.Generated import magnet.internal.Index import magnet.internal.InstanceFactory import magnet.processor.instances.generator.CodeGenerator import magnet.processor.instances.generator.CodeWriter import magnet.processor.instances.FactoryType import magnet.processor.instances.FactoryTypeVisitor import javax.lang.model.element.Modifier class FactoryIndexCodeGenerator : FactoryTypeVisitor, CodeGenerator { private lateinit var factoryIndexTypeSpec: TypeSpec private lateinit var factoryIndexClassName: ClassName override fun exitFactoryClass(factory: FactoryType) { val factoryPackage = factory.factoryType.packageName() val factoryName = factory.factoryType.simpleName() val factoryIndexName = "${factoryPackage.replace('.', '_')}_$factoryName" factoryIndexClassName = ClassName.get("magnet.index", factoryIndexName) factoryIndexTypeSpec = TypeSpec .classBuilder(factoryIndexClassName) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addAnnotation(Generated::class.java) .addAnnotation( generateFactoryIndexAnnotation( factory.factoryType, factory.interfaceType.reflectionName(), factory.classifier ) ) .build() } private fun generateFactoryIndexAnnotation( factoryClassName: ClassName, instanceType: String, classifier: String ): AnnotationSpec { return AnnotationSpec.builder(Index::class.java) .addMember("factoryType", "\$T.class", InstanceFactory::class.java) .addMember("factoryClass", "\$T.class", factoryClassName) .addMember("instanceType", "\$S", instanceType) .addMember("classifier", "\$S", classifier) .build() } override fun generateFrom(factoryType: FactoryType): CodeWriter { factoryType.accept(this) return CodeWriter(factoryIndexClassName.packageName(), factoryIndexTypeSpec) } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/limitedto/GetLimitMethodGenerator.kt ================================================ package magnet.processor.instances.aspects.limitedto import com.squareup.javapoet.MethodSpec import com.squareup.javapoet.TypeSpec import magnet.processor.instances.generator.AspectGenerator import magnet.processor.instances.GetLimitMethod import javax.lang.model.element.Modifier internal class GetLimitMethodGenerator : AspectGenerator { private var getLimit: MethodSpec? = null override fun reset() { getLimit = null } override fun generate(classBuilder: TypeSpec.Builder) { getLimit?.let { classBuilder.addMethod(it) } } fun visit(method: GetLimitMethod) { getLimit = MethodSpec .methodBuilder("getLimit") .addModifiers(Modifier.PUBLIC) .addAnnotation(Override::class.java) .returns(String::class.java) .addStatement("return \$S", method.limit) .build() } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/limitedto/LimitedToAttributeParser.kt ================================================ package magnet.processor.instances.aspects.limitedto import magnet.processor.instances.parser.AttributeParser import magnet.processor.instances.parser.ParserInstance import javax.lang.model.element.AnnotationValue import javax.lang.model.element.Element object LimitedToAttributeParser : AttributeParser("limitedTo") { override fun Scope.parse(value: AnnotationValue): ParserInstance = instance.copy(limitedTo = value.value.toString()) } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/limitedto/LimitedToValidator.kt ================================================ package magnet.processor.instances.aspects.limitedto import magnet.Scoping import magnet.processor.MagnetProcessorEnv import magnet.processor.common.throwValidationError import magnet.processor.instances.parser.AspectValidator import magnet.processor.instances.parser.ParserInstance import javax.lang.model.element.Element object LimitedToValidator : AspectValidator { override fun ParserInstance.validate( env: MagnetProcessorEnv ): ParserInstance { if (limitedTo == "*") { element.throwValidationError( "Limit must not use reserved '*' value. Use another value." ) } else if (limitedTo.isNotEmpty() && scoping == Scoping.UNSCOPED.name) { element.throwValidationError( "Limit can only be used with Scoping.TOPMOST and Scoping.DIRECT." + " Current scoping: Scoping.$scoping" ) } return this } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/scoping/GetScopingMethodGenerator.kt ================================================ package magnet.processor.instances.aspects.scoping import com.squareup.javapoet.MethodSpec import com.squareup.javapoet.TypeSpec import magnet.Scoping import magnet.processor.instances.generator.AspectGenerator import magnet.processor.instances.GetScopingMethod import javax.lang.model.element.Modifier internal class GetScopingMethodGenerator : AspectGenerator { private var getScoping: MethodSpec? = null override fun generate(classBuilder: TypeSpec.Builder) { getScoping?.let { classBuilder.addMethod(it) } } override fun reset() { getScoping = null } fun visit(method: GetScopingMethod) { val defaultImplementationAvailable = method.scoping == Scoping.TOPMOST.name if (!defaultImplementationAvailable) { getScoping = MethodSpec .methodBuilder("getScoping") .addModifiers(Modifier.PUBLIC) .addAnnotation(Override::class.java) .returns(Scoping::class.java) .addStatement("return \$T.\$L", Scoping::class.java, method.scoping) .build() } } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/scoping/ScopingAttributeParser.kt ================================================ package magnet.processor.instances.aspects.scoping import magnet.processor.instances.parser.AttributeParser import magnet.processor.instances.parser.ParserInstance import javax.lang.model.element.AnnotationValue import javax.lang.model.element.Element object ScopingAttributeParser : AttributeParser("scoping") { override fun Scope.parse(value: AnnotationValue): ParserInstance = instance.copy(scoping = value.value.toString()) } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/selector/GetSelectorMethodGenerator.kt ================================================ package magnet.processor.instances.aspects.selector import com.squareup.javapoet.ArrayTypeName import com.squareup.javapoet.CodeBlock import com.squareup.javapoet.FieldSpec import com.squareup.javapoet.MethodSpec import com.squareup.javapoet.TypeSpec import magnet.processor.instances.generator.AspectGenerator import magnet.processor.instances.GetSelectorMethod import javax.lang.model.element.Modifier internal class GetSelectorMethodGenerator : AspectGenerator { private var methodSpec: MethodSpec? = null private var constantFieldSpec: FieldSpec? = null private var constantInitializer: CodeBlock.Builder? = null private var argumentsLeft: Int = 0 override fun generate(classBuilder: TypeSpec.Builder) { constantFieldSpec?.let { classBuilder.addField(it) } methodSpec?.let { classBuilder.addMethod(it) } } override fun reset() { methodSpec = null constantFieldSpec = null constantInitializer = null argumentsLeft = 0 } fun enterGetSelectorMethod(method: GetSelectorMethod) { methodSpec = MethodSpec .methodBuilder("getSelector") .addModifiers(Modifier.PUBLIC) .addAnnotation(Override::class.java) .returns(ArrayTypeName.of(String::class.java)) .addStatement("return SELECTOR") .build() constantInitializer = CodeBlock.builder().add("{ ") argumentsLeft = method.selectorArguments.size } fun visitSelectorArgument(argument: String) { if (--argumentsLeft > 0) { checkNotNull(constantInitializer).add("\$S, ", argument) } else { checkNotNull(constantInitializer).add("\$S }", argument) } } fun exitGetSelectorMethod() { constantFieldSpec = FieldSpec .builder(ArrayTypeName.of(String::class.java), "SELECTOR") .addModifiers(Modifier.PRIVATE, Modifier.STATIC) .initializer(checkNotNull(constantInitializer).build()) .build() } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/selector/SelectorAttributeParser.kt ================================================ package magnet.processor.instances.aspects.selector import magnet.processor.common.ValidationException import magnet.processor.instances.parser.AttributeParser import magnet.processor.instances.parser.ParserInstance import javax.lang.model.element.AnnotationValue import javax.lang.model.element.Element object SelectorAttributeParser : AttributeParser("selector") { override fun Scope.parse(value: AnnotationValue): ParserInstance = instance.copy(selector = parse(value.value.toString())) private fun Scope.parse(selector: String): List? { if (selector.isEmpty()) return null val parsedSelector = selector.split(DELIMITER) var isSelectorInvalid: Boolean = parsedSelector.size < 4 || parsedSelector[0].isEmpty() || parsedSelector[1].isEmpty() || parsedSelector[2] !in OPERATORS || parsedSelector[3].isEmpty() if (!isSelectorInvalid) { isSelectorInvalid = when (parsedSelector[2]) { "in", "!in" -> parsedSelector.size != 5 else -> parsedSelector.size != 4 } } if (isSelectorInvalid) { throw ValidationException( element = element, message = "Invalid selector. Expected format:" + " '[selector id].[selector field] [comparison operator] [value]'." + " Supported comparison operators: $OPERATORS." + " Example selectors: 'android.api >= 28', 'android.api in 0..24'" ) } return parsedSelector } } private val DELIMITER = Regex("[\\s|.]+") private val OPERATORS = arrayListOf(">", "<", ">=", "<=", "==", "!=", "in", "!in") ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/siblings/GetSiblingTypesMethodGenerator.kt ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.processor.instances.aspects.siblings import com.squareup.javapoet.ArrayTypeName import com.squareup.javapoet.ClassName import com.squareup.javapoet.CodeBlock import com.squareup.javapoet.FieldSpec import com.squareup.javapoet.MethodSpec import com.squareup.javapoet.TypeSpec import magnet.processor.instances.generator.AspectGenerator import magnet.processor.instances.GetSiblingTypesMethod import javax.lang.model.element.Modifier internal class GetSiblingTypesMethodGenerator : AspectGenerator { private var getSiblingTypes: MethodSpec? = null private var constBuilder: FieldSpec? = null private var constInitializer: CodeBlock.Builder? = null private var typesLeft: Int = 0 fun enterSiblingTypesMethod(method: GetSiblingTypesMethod) { getSiblingTypes = MethodSpec .methodBuilder("getSiblingTypes") .addModifiers(Modifier.PUBLIC) .addAnnotation(Override::class.java) .returns(ArrayTypeName.of(Class::class.java)) .addStatement("return SIBLING_TYPES") .build() constInitializer = CodeBlock.builder().add("{ ") typesLeft = method.siblingTypes.size } fun visitSiblingType(type: ClassName) { if (--typesLeft > 0) { checkNotNull(constInitializer).add("\$T.class, ", type) } else { checkNotNull(constInitializer).add("\$T.class }", type) } } fun exitSiblingTypesMethod() { constBuilder = FieldSpec .builder(ArrayTypeName.of(Class::class.java), "SIBLING_TYPES") .addModifiers(Modifier.PRIVATE, Modifier.STATIC) .initializer(checkNotNull(constInitializer).build()) .build() } override fun reset() { getSiblingTypes = null constBuilder = null constInitializer = null typesLeft = 0 } override fun generate(classBuilder: TypeSpec.Builder) { constBuilder?.let { classBuilder.addField(it) } getSiblingTypes?.let { classBuilder.addMethod(it) } } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/type/TypeAndTypesValidator.kt ================================================ /* * Copyright (C) 2018-2021 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.processor.instances.aspects.type import com.squareup.javapoet.ClassName import magnet.Instance import magnet.Scoping import magnet.processor.MagnetProcessorEnv import magnet.processor.common.throwCompilationError import magnet.processor.common.throwValidationError import magnet.processor.instances.parser.AspectValidator import magnet.processor.instances.parser.ParserInstance import javax.lang.model.element.Element import javax.lang.model.element.TypeElement object TypeAndTypesValidator : AspectValidator { override fun ParserInstance.validate( env: MagnetProcessorEnv ): ParserInstance { val elementWithType = if (element is TypeElement && declaredType == null && declaredTypes.isNullOrEmpty()) copy(declaredType = element.autodetectType()) else this return elementWithType.validateTypes() } private fun ParserInstance.validateTypes(): ParserInstance { val isTypeDeclared = declaredType != null val areTypesDeclared = declaredTypes?.isNotEmpty() ?: false if (isTypeDeclared && areTypesDeclared) element.throwValidationError( "${Instance::class.java} must declare either 'type' or 'types' property, not both." ) if (declaredType != null) { val types = arrayListOf(declaredType) return copy( declaredTypes = arrayListOf(declaredType), types = types.map { ClassName.get(it) } ) } if (declaredTypes != null) { if (scoping == Scoping.UNSCOPED.name) element.throwValidationError( "types() property must be used with scoped instances only. Set " + "scoping to Scoping.DIRECT or Scoping.TOPMOST." ) return copy( types = declaredTypes.map { ClassName.get(it) } ) } element.throwCompilationError("Cannot verify type declaration.") } private fun TypeElement.autodetectType(): TypeElement { if (interfaces.isEmpty()) { val superType = ClassName.get(superclass) if (superType is ClassName && superType.reflectionName() == "java.lang.Object") { return this } } throwValidationError( "${Instance::class.java} must declare either 'type' or 'types' property." ) } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/type/TypeAttributeParser.kt ================================================ package magnet.processor.instances.aspects.type import magnet.processor.common.verifyInheritance import magnet.processor.instances.parser.AttributeParser import magnet.processor.instances.parser.ParserInstance import javax.lang.model.element.AnnotationValue import javax.lang.model.element.Element object TypeAttributeParser : AttributeParser("type") { override fun Scope.parse(value: AnnotationValue): ParserInstance = env.elements.getTypeElement(value.value.toString())?.let { if (isTypeInheritanceEnforced) it.verifyInheritance(element, env.types) instance.copy(declaredType = it) } ?: instance } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/aspects/type/TypesAttributeParser.kt ================================================ package magnet.processor.instances.aspects.type import magnet.processor.common.verifyInheritance import magnet.processor.instances.parser.AttributeParser import magnet.processor.instances.parser.ParserInstance import javax.lang.model.element.AnnotationValue import javax.lang.model.element.Element import javax.lang.model.element.TypeElement import javax.lang.model.type.TypeMirror import javax.lang.model.util.Elements import javax.lang.model.util.SimpleAnnotationValueVisitor6 object TypesAttributeParser : AttributeParser("types") { override fun Scope.parse(value: AnnotationValue): ParserInstance { val typesExtractor = TypesExtractor(env.elements) .apply { value.accept(this, null) } val interfaceTypeElements = typesExtractor.getExtractedValue() ?: return instance if (isTypeInheritanceEnforced) { for (typeElement in interfaceTypeElements) { typeElement.verifyInheritance(element, env.types) } } return instance.copy(declaredTypes = interfaceTypeElements) } } @Suppress("DEPRECATION") private class TypesExtractor(private val elements: Elements) : SimpleAnnotationValueVisitor6() { private val extractedTypes = mutableListOf() fun getExtractedValue(): List? = if (extractedTypes.isEmpty()) null else extractedTypes.map { elements.getTypeElement(it) } override fun visitArray(values: MutableList?, p: Void?): Void? { values?.let { for (value in values) value.accept(this, p) } return p } override fun visitType(typeMirror: TypeMirror?, p: Void?): Void? { typeMirror?.let { extractedTypes.add(it.toString()) } return p } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/generator/CodeGenerator.kt ================================================ package magnet.processor.instances.generator import magnet.processor.instances.FactoryType interface CodeGenerator { fun generateFrom(factoryType: FactoryType): CodeWriter } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/generator/CodeWriter.kt ================================================ package magnet.processor.instances.generator import com.squareup.javapoet.JavaFile import com.squareup.javapoet.TypeSpec import javax.annotation.processing.Filer class CodeWriter( private var filePackage: String, private var fileTypeSpec: TypeSpec ) { fun writeInto(filer: Filer) { JavaFile .builder(filePackage, fileTypeSpec) .skipJavaLangImports(true) .build() .writeTo(filer) } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/generator/FactoryTypeCodeGenerator.kt ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.processor.instances.generator import com.squareup.javapoet.ClassName import com.squareup.javapoet.MethodSpec import com.squareup.javapoet.ParameterizedTypeName import com.squareup.javapoet.TypeName import com.squareup.javapoet.TypeSpec import magnet.internal.Generated import magnet.internal.InstanceFactory import magnet.processor.instances.CreateMethod import magnet.processor.instances.FactoryType import magnet.processor.instances.FactoryTypeVisitor import magnet.processor.instances.GetLimitMethod import magnet.processor.instances.GetScopingMethod import magnet.processor.instances.GetSelectorMethod import magnet.processor.instances.GetSiblingTypesMethod import magnet.processor.instances.MethodParameter import magnet.processor.instances.aspects.limitedto.GetLimitMethodGenerator import magnet.processor.instances.aspects.selector.GetSelectorMethodGenerator import magnet.processor.instances.aspects.disposer.DisposeMethodGenerator import magnet.processor.instances.aspects.disposer.IsDisposableMethodGenerator import magnet.processor.instances.aspects.factory.CreateMethodGenerator import magnet.processor.instances.aspects.factory.DefaultCreateMethodGenerator import magnet.processor.instances.aspects.scoping.GetScopingMethodGenerator import magnet.processor.instances.aspects.siblings.GetSiblingTypesMethodGenerator import javax.lang.model.element.Modifier interface AspectGenerator { fun generate(classBuilder: TypeSpec.Builder) fun reset() } internal class Aspect( private val generator: G ) { private var visited: Boolean = false inline fun visit(block: G.() -> Unit) { block(generator) visited = true } fun generate(classBuilder: TypeSpec.Builder) { if (visited) { generator.generate(classBuilder) } generator.reset() visited = false } } class FactoryTypeCodeGenerator : FactoryTypeVisitor, CodeGenerator { private var factoryTypeSpec: TypeSpec? = null private var factoryClassName: ClassName? = null private var generateGettersInCreateMethod = false private val aspectGetSiblingTypes = Aspect(GetSiblingTypesMethodGenerator()) private val aspectGetScoping = Aspect(GetScopingMethodGenerator()) private val aspectGetLimit = Aspect(GetLimitMethodGenerator()) private val aspectGetSelector = Aspect(GetSelectorMethodGenerator()) private val createMethodGenerator: CreateMethodGenerator = DefaultCreateMethodGenerator() private val isDisposableMethodGenerator = IsDisposableMethodGenerator() private val disposeMethodGenerator = DisposeMethodGenerator() override fun enterFactoryClass(factoryType: FactoryType) { generateGettersInCreateMethod = factoryType.customFactoryType == null createMethodGenerator.visitFactoryClass(factoryType) isDisposableMethodGenerator.visitFactoryClass(factoryType) disposeMethodGenerator.visitFactoryClass(factoryType) } override fun enterCreateMethod(createMethod: CreateMethod) { factoryTypeSpec = null createMethodGenerator.enterCreateMethod(createMethod) } override fun visitCreateMethodParameter(parameter: MethodParameter) { createMethodGenerator.visitCreateMethodParameter(parameter) } override fun exitCreateMethod(createMethod: CreateMethod) { createMethodGenerator.exitCreateMethod() } override fun visit(method: GetScopingMethod) { aspectGetScoping.visit { visit(method) } } override fun visit(method: GetLimitMethod) { aspectGetLimit.visit { visit(method) } } override fun enterSiblingTypesMethod(method: GetSiblingTypesMethod) { aspectGetSiblingTypes.visit { enterSiblingTypesMethod(method) } } override fun visitSiblingType(type: ClassName) { aspectGetSiblingTypes.visit { visitSiblingType(type) } } override fun exitSiblingTypesMethod(method: GetSiblingTypesMethod) { aspectGetSiblingTypes.visit { exitSiblingTypesMethod() } } override fun enterGetSelectorMethod(method: GetSelectorMethod) { aspectGetSelector.visit { enterGetSelectorMethod(method) } } override fun visitSelectorArgument(argument: String) { aspectGetSelector.visit { visitSelectorArgument(argument) } } override fun exitGetSelectorMethod(method: GetSelectorMethod) { aspectGetSelector.visit { exitGetSelectorMethod() } } override fun exitFactoryClass(factory: FactoryType) { factoryClassName = factory.factoryType val classBuilder: TypeSpec.Builder = TypeSpec .classBuilder(factoryClassName) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addAnnotation(Generated::class.java) .superclass(generateFactorySuperInterface(factory)) createMethodGenerator.generate(classBuilder) aspectGetScoping.generate(classBuilder) aspectGetLimit.generate(classBuilder) aspectGetSiblingTypes.generate(classBuilder) aspectGetSelector.generate(classBuilder) isDisposableMethodGenerator.generate(classBuilder) disposeMethodGenerator.generate(classBuilder) classBuilder .addMethod(generateGetTypeMethod(factory)) factoryTypeSpec = classBuilder.build() } private fun generateGetTypeMethod(factoryType: FactoryType): MethodSpec { return MethodSpec .methodBuilder("getType") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(Class::class.java) .addStatement("return \$T.class", factoryType.interfaceType) .build() } private fun generateFactorySuperInterface(factoryType: FactoryType): TypeName { return ParameterizedTypeName.get( ClassName.get(InstanceFactory::class.java), factoryType.interfaceType ) } override fun generateFrom(factoryType: FactoryType): CodeWriter { factoryType.accept(this) return CodeWriter(this.factoryClassName!!.packageName(), factoryTypeSpec!!) } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/parser/AspectValidator.kt ================================================ package magnet.processor.instances.parser import magnet.processor.MagnetProcessorEnv import magnet.processor.instances.aspects.disposer.DisposerValidator import magnet.processor.instances.aspects.limitedto.LimitedToValidator import magnet.processor.instances.aspects.type.TypeAndTypesValidator import javax.lang.model.element.Element interface AspectValidator { fun ParserInstance.validate(env: MagnetProcessorEnv): ParserInstance object Registry { val VALIDATORS: List = listOf( TypeAndTypesValidator, DisposerValidator, LimitedToValidator ) } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/parser/AttributeParser.kt ================================================ package magnet.processor.instances.parser import magnet.processor.MagnetProcessorEnv import magnet.processor.instances.aspects.classifier.ClassifierAttributeParser import magnet.processor.instances.aspects.disabled.DisabledAttributeParser import magnet.processor.instances.aspects.disposer.DisposerAttributeParser import magnet.processor.instances.aspects.factory.FactoryAttributeParser import magnet.processor.instances.aspects.limitedto.LimitedToAttributeParser import magnet.processor.instances.aspects.scoping.ScopingAttributeParser import magnet.processor.instances.aspects.selector.SelectorAttributeParser import magnet.processor.instances.aspects.type.TypeAttributeParser import magnet.processor.instances.aspects.type.TypesAttributeParser import javax.lang.model.element.AnnotationValue import javax.lang.model.element.Element abstract class AttributeParser(val name: String) { data class Scope( val isTypeInheritanceEnforced: Boolean, val instance: ParserInstance, val element: Element, val env: MagnetProcessorEnv ) abstract fun Scope.parse(value: AnnotationValue): ParserInstance object Registry { val PARSERS = mapOf( ClassifierAttributeParser.name to ClassifierAttributeParser, DisabledAttributeParser.name to DisabledAttributeParser, DisposerAttributeParser.name to DisposerAttributeParser, FactoryAttributeParser.name to FactoryAttributeParser, LimitedToAttributeParser.name to LimitedToAttributeParser, ScopingAttributeParser.name to ScopingAttributeParser, SelectorAttributeParser.name to SelectorAttributeParser, TypeAttributeParser.name to TypeAttributeParser, TypesAttributeParser.name to TypesAttributeParser ) } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/parser/InstanceParser.kt ================================================ /* * Copyright (C) 2018-2019 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.processor.instances.parser import com.squareup.javapoet.ClassName import com.squareup.javapoet.ParameterSpec import com.squareup.javapoet.ParameterizedTypeName import com.squareup.javapoet.TypeName import com.squareup.javapoet.WildcardTypeName import magnet.Classifier import magnet.Instance import magnet.Scope import magnet.processor.MagnetProcessorEnv import magnet.processor.common.KotlinMethodMetadata import magnet.processor.common.eachAttributeOf import magnet.processor.common.isOfAnnotationType import magnet.processor.common.throwCompilationError import magnet.processor.common.throwValidationError import magnet.processor.instances.Cardinality import magnet.processor.instances.Expression import magnet.processor.instances.FactoryType import magnet.processor.instances.MethodParameter import magnet.processor.instances.parser.AspectValidator.Registry.VALIDATORS import magnet.processor.instances.parser.AttributeParser.Registry.PARSERS import javax.lang.model.element.Element import javax.lang.model.element.VariableElement import javax.lang.model.type.TypeKind const val FACTORY_SUFFIX = "MagnetFactory" private const val CLASS_NULLABLE = ".Nullable" internal abstract class InstanceParser( private val env: MagnetProcessorEnv, private val isTypeInheritanceEnforced: Boolean ) { private val scopeTypeName = ClassName.get(Scope::class.java) private val listTypeName = ClassName.get(List::class.java) private val lazyTypeName = ClassName.get(Lazy::class.java) fun E.parse(): List { onBeforeParsing() var scope = AttributeParser.Scope( isTypeInheritanceEnforced = isTypeInheritanceEnforced, instance = ParserInstance(this), element = this, env = env ) eachAttributeOf { name, value -> PARSERS[name]?.apply { scope = scope.copy( instance = scope.parse(value) ) } ?: throwCompilationError( "Unsupported attribute '$name'." + " Do you use the same versions of magnet processor and runtime libraries?" ) } var instance = scope.instance for (validator in VALIDATORS) { with(validator) { instance = instance.validate(env) } } return generateFactories(instance) } protected open fun E.onBeforeParsing() {} protected abstract fun generateFactories(instance: ParserInstance): List protected fun parseMethodParameter( element: Element, variable: VariableElement, methodMeta: KotlinMethodMetadata? ): MethodParameter { val variableType = variable.asType() if (variableType.kind == TypeKind.TYPEVAR) { element.throwValidationError( "Constructor parameter '${variable.simpleName}' is specified using a generic" + " type which is not supported by Magnet. Use a non-parameterized class or" + " interface type instead. To inject current scope into an instance," + " add 'scope: Scope' to the constructor parameters." ) } val paramSpec = ParameterSpec.get(variable) val paramType = paramSpec.type val paramName = paramSpec.name var paramReturnType: TypeName = paramType var paramExpression: Expression = Expression.Scope var paramParameterType: TypeName = paramType var paramClassifier: String = Classifier.NONE var paramTypeErased = false paramParameterType.parseParamType( paramName, methodMeta, variable ) { returnType, expression, parameterType, classifier, erased -> paramReturnType = returnType paramExpression = expression paramParameterType = parameterType paramClassifier = classifier paramTypeErased = erased } return MethodParameter( name = paramName, expression = paramExpression, returnType = paramReturnType, parameterType = paramParameterType, classifier = paramClassifier, typeErased = paramTypeErased ) } private fun TypeName.parseParamType( paramName: String, methodMeta: KotlinMethodMetadata?, variable: VariableElement, block: ( returnType: TypeName, expression: Expression, parameterType: TypeName, classifier: String, typeErased: Boolean ) -> Unit ) { var paramReturnType: TypeName = this var paramExpression: Expression = Expression.Scope var paramParameterType: TypeName = this var paramClassifier: String = Classifier.NONE var paramTypeErased = false when (this) { scopeTypeName -> { paramExpression = Expression.Scope } is ParameterizedTypeName -> { when (rawType) { listTypeName -> { val (type, erased) = firstArgumentRawType(variable) paramParameterType = type paramTypeErased = erased paramReturnType = if (erased) listTypeName else ParameterizedTypeName.get(listTypeName, paramParameterType) paramExpression = Expression.Getter(Cardinality.Many) variable.annotations { _, classifier -> paramClassifier = classifier } } lazyTypeName -> { if (methodMeta == null) variable.throwValidationError( "Lazy can only be used with Kotlin classes." ) parseLazyArgumentType( paramName, methodMeta, variable ) { returnType, cardinality, parameterType -> paramReturnType = ParameterizedTypeName.get(lazyTypeName, returnType) paramParameterType = parameterType paramExpression = Expression.LazyGetter(cardinality) variable.annotations { _, classifier -> paramClassifier = classifier } } } else -> { paramParameterType = rawType paramTypeErased = true variable.annotations { cardinality, classifier -> paramExpression = Expression.Getter(cardinality) paramClassifier = classifier } } } } else -> { variable.annotations { cardinality, classifier -> paramExpression = Expression.Getter(cardinality) paramClassifier = classifier } } } block( paramReturnType, paramExpression, paramParameterType, paramClassifier, paramTypeErased ) } private fun ParameterizedTypeName.parseLazyArgumentType( paramName: String, methodMeta: KotlinMethodMetadata, variable: VariableElement, block: ( returnType: TypeName, cardinality: Cardinality, parameterType: TypeName ) -> Unit ) { when (val argumentType = typeArguments.first().withoutWildcards(variable)) { scopeTypeName -> variable.throwValidationError("Lazy cannot be parametrized with Scope type.") is ParameterizedTypeName -> { when (argumentType.rawType) { lazyTypeName -> variable.throwValidationError("Lazy cannot be parametrized with another Lazy type.") listTypeName -> { if (methodMeta.getTypeMeta(paramName, 1).nullable) { variable.throwValidationError( "Lazy must be parametrized with none nullable List type." ) } when (val listArgumentType = argumentType.typeArguments.first().withoutWildcards(variable)) { is ParameterizedTypeName -> { block( ParameterizedTypeName.get(listTypeName, listArgumentType), Cardinality.Many, listArgumentType.rawType ) } else -> { if (methodMeta.getTypeMeta(paramName, 2).nullable) { variable.throwValidationError( "Lazy> must be parametrized with none nullable type." ) } block( ParameterizedTypeName.get(listTypeName, listArgumentType), Cardinality.Many, listArgumentType ) } } } else -> { block( argumentType, methodMeta.getNullableCardinality(paramName, 1), argumentType.rawType ) } } } else -> { block( argumentType, methodMeta.getNullableCardinality(paramName, 1), argumentType ) } } } } private fun KotlinMethodMetadata.getNullableCardinality(paramName: String, paramDepth: Int): Cardinality = if (getTypeMeta(paramName, paramDepth).nullable) Cardinality.Optional else Cardinality.Single private fun TypeName.withoutWildcards(element: Element): TypeName = if (this is WildcardTypeName) { checkBounds(element) upperBounds.first() } else this private fun WildcardTypeName.firstUpperBoundsRawType(element: Element): Pair { checkBounds(element) return when (val type = upperBounds.first()) { is ParameterizedTypeName -> type.rawType to true is WildcardTypeName -> type.firstUpperBoundsRawType(element) else -> type to false } } private fun WildcardTypeName.checkBounds(element: Element) { if (lowerBounds.size > 0) { element.throwValidationError( "Magnet supports single upper bounds class parameter only," + " while lower bounds class parameter was found." ) } if (upperBounds.size > 1) { element.throwValidationError( "Magnet supports single upper bounds class parameter only," + " for example List<${upperBounds.first()}>" ) } } private fun ParameterizedTypeName.firstArgumentRawType(element: Element): Pair { if (typeArguments.size > 1) { element.throwValidationError("Magnet supports type parametrized with a single argument only.") } return when (val argumentType = typeArguments.first()) { is ParameterizedTypeName -> argumentType.rawType to true is WildcardTypeName -> argumentType.firstUpperBoundsRawType(element) else -> argumentType to false } } private inline fun VariableElement.annotations(block: (Cardinality, String) -> Unit) { var cardinality = Cardinality.Single var classifier = Classifier.NONE annotationMirrors.forEach { annotationMirror -> if (annotationMirror.isOfAnnotationType()) { val declaredClassifier: String? = annotationMirror.elementValues.values.firstOrNull()?.value.toString() declaredClassifier?.let { classifier = it.removeSurrounding("\"", "\"") } } else { val annotationType = annotationMirror.annotationType.toString() if (annotationType.endsWith(CLASS_NULLABLE)) { cardinality = Cardinality.Optional } } } block(cardinality, classifier) } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/parser/InstanceParserForClass.kt ================================================ /* * Copyright (C) 2018-2021 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.processor.instances.parser import com.squareup.javapoet.ClassName import kotlinx.metadata.Flag import kotlinx.metadata.Flags import magnet.Instance import magnet.processor.MagnetProcessorEnv import magnet.processor.common.CONSTRUCTOR_NAME import magnet.processor.common.DefaultKotlinMethodMetadata import magnet.processor.common.FunctionSelector import magnet.processor.common.KotlinMethodMetadata import magnet.processor.common.ParameterMeta import magnet.processor.common.hasParameters import magnet.processor.common.throwCompilationError import magnet.processor.common.throwValidationError import magnet.processor.instances.CreateMethod import magnet.processor.instances.FactoryType import magnet.processor.instances.GetLimitMethod import magnet.processor.instances.GetScopingMethod import magnet.processor.instances.GetSelectorMethod import magnet.processor.instances.GetSiblingTypesMethod import magnet.processor.instances.MethodParameter import magnet.processor.instances.TypeCreateStatement import javax.lang.model.element.Element import javax.lang.model.element.ExecutableElement import javax.lang.model.element.Modifier import javax.lang.model.element.TypeElement import javax.lang.model.util.ElementFilter internal class InstanceParserForClass( env: MagnetProcessorEnv ) : InstanceParser(env, true) { override fun generateFactories(instance: ParserInstance): List { val instanceType = ClassName.get(instance.element) val instancePackage = instanceType.packageName() return instance.types.map { val hasSiblingTypes = instance.types.size > 1 val getSiblingTypesMethod = if (hasSiblingTypes) { val types = instance.types - it val siblingTypes = mutableListOf() for (type in types) { siblingTypes.add(type) val factoryName = generateFactoryName(true, instanceType, type) siblingTypes.add(ClassName.bestGuess("$instancePackage.$factoryName")) } GetSiblingTypesMethod(siblingTypes) } else null val selectorAttributes = instance.selector val getSelectorMethod = if (selectorAttributes == null) null else GetSelectorMethod(selectorAttributes) val getLimitMethod = if (instance.limitedTo.isEmpty()) null else GetLimitMethod(instance.limitedTo) val factoryName = generateFactoryName(hasSiblingTypes, instanceType, it) FactoryType( element = instance.element, interfaceType = it, classifier = instance.classifier, scoping = instance.scoping, disposerMethodName = instance.disposer, disabled = instance.disabled, customFactoryType = instance.factory, implementationType = instanceType, factoryType = ClassName.bestGuess("$instancePackage.$factoryName"), createStatement = TypeCreateStatement(instanceType), createMethod = parseCreateMethod(instance.element), getScopingMethod = GetScopingMethod(instance.scoping), getLimitMethod = getLimitMethod, getSelectorMethod = getSelectorMethod, getSiblingTypesMethod = getSiblingTypesMethod ) } } private fun parseCreateMethod(element: TypeElement): CreateMethod { val constructors = ElementFilter .constructorsIn(element.enclosedElements) .filterNot { it.modifiers.contains(Modifier.PRIVATE) || it.modifiers.contains(Modifier.PROTECTED) } val methodMeta: KotlinMethodMetadata? = element .getAnnotation(Metadata::class.java) ?.let { DefaultKotlinMethodMetadata( metadata = it, element = element, functionSelector = ConstructorFunctionSelector(element, constructors) ) } val methodParameters = mutableListOf().apply { val constructor = if (methodMeta == null) selectJavaConstructor(constructors, element) else selectKotlinConstructor(methodMeta) for (parameter in constructor.parameters) { add(parseMethodParameter(element, parameter, methodMeta)) } } return CreateMethod(methodParameters) } } private fun Element.throwExactlyOneConstructorRequired(): Nothing = throwValidationError( "Classes annotated with ${magnet.Instance::class.java} must have exactly one" + " public or package-private constructor." ) private fun generateFactoryName( hasSiblingsTypes: Boolean, instanceType: ClassName, interfaceType: ClassName ): String = if (hasSiblingsTypes) "${instanceType.getFullName()}${interfaceType.getFullName()}$FACTORY_SUFFIX" else "${instanceType.getFullName()}$FACTORY_SUFFIX" private fun ClassName.getFullName(): String { if (enclosingClassName() == null) { return simpleName() } val nameBuilder = StringBuilder(simpleName()) var typeClassName = this while (typeClassName.enclosingClassName() != null) { nameBuilder.insert(0, typeClassName.enclosingClassName().simpleName()) typeClassName = typeClassName.enclosingClassName() } return nameBuilder.toString() } private fun selectJavaConstructor(constructors: List, element: TypeElement): ExecutableElement = if (constructors.size == 1) constructors[0] else element.throwExactlyOneConstructorRequired() private fun selectKotlinConstructor(methodMeta: KotlinMethodMetadata): ExecutableElement = methodMeta.method private class ConstructorFunctionSelector( private val element: TypeElement, private val constructors: List ) : FunctionSelector { override val function: ExecutableElement get() = overloadConstructor ?.let { return it } ?: element.throwExactlyOneConstructorRequired() private var overloadConstructor: ExecutableElement? = null override fun visitFunction(flags: Flags, name: String): Boolean = name == CONSTRUCTOR_NAME && !Flag.Constructor.IS_SECONDARY(flags) override fun acceptFunctionParameters(parameters: Map): Map { val overloadedParameters = parameters.filter { it.value.types.firstOrNull()?.default != true } overloadConstructor = constructors.find { it.hasParameters(overloadedParameters) } if (overloadConstructor == null) { val primaryConstructor = constructors.find { it.hasParameters(parameters) } ?: element.throwCompilationError( "Overloaded secondary constructor expected.\n" + " Primary constructor: $parameters\n" + " Secondary constructor: $overloadedParameters" ) primaryConstructor.throwValidationError( "Constructor with default arguments in a class annotated with ${Instance::class}" + " must have @JmvOverloads annotation." + " Use: class ${element.simpleName} @JvmOverloads constructor(...)" ) } return overloadedParameters } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/parser/InstanceParserForMethod.kt ================================================ /* * Copyright (C) 2018-2021 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.processor.instances.parser import com.squareup.javapoet.ClassName import com.squareup.javapoet.ParameterizedTypeName import com.squareup.javapoet.TypeName import magnet.Classifier import magnet.Instance import magnet.processor.MagnetProcessorEnv import magnet.processor.common.CompilationException import magnet.processor.common.DefaultKotlinMethodMetadata import magnet.processor.common.KotlinMethodMetadata import magnet.processor.common.MethodFunctionSelector import magnet.processor.common.throwValidationError import magnet.processor.instances.CreateMethod import magnet.processor.instances.FactoryType import magnet.processor.instances.GetLimitMethod import magnet.processor.instances.GetScopingMethod import magnet.processor.instances.GetSelectorMethod import magnet.processor.instances.GetSiblingTypesMethod import magnet.processor.instances.MethodParameter import magnet.processor.instances.StaticMethodCreateStatement import javax.lang.model.element.Element import javax.lang.model.element.ExecutableElement import javax.lang.model.element.Modifier import javax.lang.model.element.TypeElement /** Awesome static factory method parser. */ internal class InstanceParserForMethod( env: MagnetProcessorEnv ) : InstanceParser(env, false) { override fun ExecutableElement.onBeforeParsing() { if (!modifiers.contains(Modifier.STATIC)) throwValidationError( "Method annotated by ${Instance::class.java} must be 'static'" ) if (modifiers.contains(Modifier.PRIVATE)) throwValidationError( "Method annotated by ${Instance::class.java} must not be 'private'" ) } override fun generateFactories(instance: ParserInstance): List { val element = instance.element val staticMethodReturnType = TypeName.get(element.returnType) for (type in instance.types) { if (type != staticMethodReturnType) { if (staticMethodReturnType is ParameterizedTypeName) { if (instance.classifier == Classifier.NONE) { element.throwValidationError( "Method providing a parametrised type must have 'classifier' value" + " set in @${Instance::class.java.simpleName} annotation." ) } } else { element.throwValidationError( "Method must return instance of ${type.reflectionName()} as declared" + " by @${Instance::class.java.simpleName} annotation." + " Returned type: $staticMethodReturnType." ) } } } val staticMethodClassName = ClassName.get(element.enclosingElement as TypeElement) val staticMethodName = element.simpleName.toString() val uniqueFactoryNameBuilder = StringBuilder() .append(staticMethodClassName.packageName()) .append('.') .append(staticMethodClassName.simpleName().capitalize()) .append(staticMethodName.capitalize()) val topmostElement = element.getTopmostTypeElement() val methodMeta: KotlinMethodMetadata? = topmostElement .getAnnotation(Metadata::class.java) ?.let { DefaultKotlinMethodMetadata( metadata = it, element = topmostElement, functionSelector = MethodFunctionSelector(element) ) } val methodParameters = mutableListOf() element.parameters.forEach { variable -> val methodParameter = parseMethodParameter(element, variable, methodMeta) methodParameters.add(methodParameter) uniqueFactoryNameBuilder.append(methodParameter.name.capitalize()) } val instanceFullName = uniqueFactoryNameBuilder.toString() return instance.types.map { val isSingleTypeFactory = instance.types.size == 1 val getSiblingTypesMethod = if (isSingleTypeFactory) null else { val types = instance.types - it val siblingTypes = mutableListOf() for (type in types) { siblingTypes.add(type) val factoryFullName = generateFactoryName(false, instanceFullName, type) siblingTypes.add(ClassName.bestGuess(factoryFullName)) } GetSiblingTypesMethod(siblingTypes) } val selectorAttributes = instance.selector val getSelectorMethod = if (selectorAttributes == null) null else GetSelectorMethod(selectorAttributes) val getLimitMethod = if (instance.limitedTo.isEmpty()) null else GetLimitMethod(instance.limitedTo) val factoryFullName = generateFactoryName(isSingleTypeFactory, instanceFullName, it) FactoryType( element = element, interfaceType = it, classifier = instance.classifier, scoping = instance.scoping, disposerMethodName = instance.disposer, disabled = instance.disabled, customFactoryType = instance.factory, implementationType = null, factoryType = ClassName.bestGuess(factoryFullName), createStatement = StaticMethodCreateStatement(staticMethodClassName, staticMethodName), createMethod = CreateMethod(methodParameters), getScopingMethod = GetScopingMethod(instance.scoping), getLimitMethod = getLimitMethod, getSelectorMethod = getSelectorMethod, getSiblingTypesMethod = getSiblingTypesMethod ) } } } private fun String.capitalize() = replaceFirstChar { if (it.isLowerCase()) it.uppercaseChar().toString() else it.toString() } private fun Element.getTopmostTypeElement(): TypeElement { var result: TypeElement? = null var element: Element? = this while (element != null) { if (element is TypeElement) { result = element } element = element.enclosingElement } return result ?: throw CompilationException( element = this, message = "Static method must be declared in a class." ) } private fun generateFactoryName(isSingleTypeFactory: Boolean, instanceName: String, it: ClassName): String = if (isSingleTypeFactory) "${instanceName}MagnetFactory" else "$instanceName${it.simpleName()}MagnetFactory" ================================================ FILE: magnet-processor/src/main/java/magnet/processor/instances/parser/ParserInstance.kt ================================================ package magnet.processor.instances.parser import com.squareup.javapoet.ClassName import com.squareup.javapoet.TypeName import magnet.Classifier import magnet.Scoping import javax.lang.model.element.Element import javax.lang.model.element.TypeElement data class ParserInstance( val element: E, val declaredType: TypeElement? = null, val declaredTypes: List? = null, val types: List = emptyList(), val classifier: String = Classifier.NONE, val scoping: String = Scoping.TOPMOST.name, val limitedTo: String = "", val selector: List? = null, val factory: TypeName? = null, val disposer: String? = null, val disabled: Boolean = false ) ================================================ FILE: magnet-processor/src/main/java/magnet/processor/registry/Model.kt ================================================ package magnet.processor.registry import com.squareup.javapoet.ClassName class Model private constructor() { class Registry( val instanceFactories: List ) class InstanceFactory( val factoryClass: ClassName, val instanceType: ClassName, val classifier: String ) } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/registry/RegistryGenerator.kt ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.processor.registry import com.squareup.javapoet.ClassName import com.squareup.javapoet.MethodSpec import com.squareup.javapoet.ParameterSpec import com.squareup.javapoet.TypeSpec import magnet.internal.Generated import magnet.processor.instances.generator.CodeWriter import magnet.processor.registry.instances.InstanceIndexGenerator import javax.lang.model.element.Modifier private const val INSTANCE_MANAGER = "instanceManager" private const val INSTANCE_MANAGER_NAME = "MagnetInstanceManager" private const val INSTANCE_MANAGER_PACKAGE = "magnet.internal" class RegistryGenerator { private val instanceIndexGenerator = InstanceIndexGenerator() fun generate(registry: Model.Registry): CodeWriter { val instanceFactoriesIndex = instanceIndexGenerator.generate(registry) val registryClassName = ClassName.bestGuess(REGISTRY_CLASS_NAME) val factoryRegistryClassName = ClassName.get(INSTANCE_MANAGER_PACKAGE, INSTANCE_MANAGER_NAME) val typeSpec = TypeSpec .classBuilder(registryClassName) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addAnnotation(Generated::class.java) .addMethod(MethodSpec .methodBuilder("register") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addParameter(ParameterSpec .builder(factoryRegistryClassName, INSTANCE_MANAGER) .build()) .addCode(instanceFactoriesIndex) .addStatement("\$L.register(factories, index)", INSTANCE_MANAGER) .build()) .build() val packageName = registryClassName.packageName() return CodeWriter(packageName, typeSpec) } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/registry/RegistryParser.kt ================================================ package magnet.processor.registry import com.squareup.javapoet.ClassName import magnet.internal.Index import magnet.internal.InstanceFactory import magnet.processor.common.AnnotationValueExtractor import magnet.processor.common.isOfAnnotationType import javax.lang.model.AnnotatedConstruct import javax.lang.model.element.PackageElement import javax.lang.model.element.TypeElement class RegistryParser( private val extractor: AnnotationValueExtractor ) { fun parse(element: PackageElement): Model.Registry { val instanceFactories = mutableListOf() val factoryIndexElements = element.enclosedElements ?: emptyList() for (factoryIndexElement in factoryIndexElements) { factoryIndexElement.annotationValues { factoryType, factoryClass, instanceType, classifier -> when { factoryType.isOfType(InstanceFactory::class.java) -> instanceFactories.add( Model.InstanceFactory( factoryClass = factoryClass, instanceType = instanceType, classifier = classifier ) ) } } } return Model.Registry( instanceFactories = instanceFactories ) } private inline fun AnnotatedConstruct.annotationValues( block: ( factoryType: ClassName, factoryClass: ClassName, instanceType: ClassName, classifier: String ) -> Unit ) { var factoryType: TypeElement? = null var factoryClass: TypeElement? = null var instanceType: String? = null var classifier: String? = null for (annotationMirror in annotationMirrors) { if (annotationMirror.isOfAnnotationType()) { for (entry in annotationMirror.elementValues.entries) { val entryName = entry.key.simpleName.toString() val entryValue = entry.value when (entryName) { "factoryType" -> factoryType = extractor.getTypeElement(entryValue) "factoryClass" -> factoryClass = extractor.getTypeElement(entryValue) "instanceType" -> instanceType = extractor.getStringValue(entryValue) "classifier" -> classifier = extractor.getStringValue(entryValue) } } break } } block( ClassName.get(requireNotNull(factoryType)), ClassName.get(requireNotNull(factoryClass)), ClassName.bestGuess(requireNotNull(instanceType)), requireNotNull(classifier) ) } } private fun ClassName.isOfType(type: Class<*>): Boolean = packageName() == type.`package`.name && simpleName() == type.simpleName ================================================ FILE: magnet-processor/src/main/java/magnet/processor/registry/RegistryProcessor.kt ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.processor.registry import magnet.Registry import magnet.processor.MagnetProcessorEnv import javax.annotation.processing.RoundEnvironment const val REGISTRY_CLASS_NAME = "magnet.internal.MagnetIndexer" const val INDEX_PACKAGE = "magnet.index" class RegistryProcessor( private val env: MagnetProcessorEnv ) { private val registryParser by lazy { RegistryParser(env.annotation) } private val magnetIndexerGenerator by lazy { RegistryGenerator() } private var generateRegistryOnNextRound = false fun process(roundEnv: RoundEnvironment): Boolean { val generatedRegistryElement = env.elements.getTypeElement(REGISTRY_CLASS_NAME) if (generatedRegistryElement != null) { return false } val annotatedRegistryElement = roundEnv.getElementsAnnotatedWith(Registry::class.java) if (!generateRegistryOnNextRound) { generateRegistryOnNextRound = annotatedRegistryElement.isNotEmpty() return false } val packageElement = env.elements.getPackageElement(INDEX_PACKAGE) val registry = if (packageElement != null) registryParser.parse(packageElement) else Model.Registry(instanceFactories = emptyList()) magnetIndexerGenerator .generate(registry) .writeInto(env.filer) return true } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/registry/instances/IndexGeneratorVisitor.kt ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.processor.registry.instances import com.squareup.javapoet.ClassName import com.squareup.javapoet.CodeBlock import magnet.processor.registry.instances.Model.Index import magnet.processor.registry.instances.Model.IndexVisitor import magnet.processor.registry.instances.Model.Inst import magnet.processor.registry.instances.Model.Range import magnet.processor.registry.instances.Model.Section class IndexGeneratorVisitor : IndexVisitor { val indexBuilder: CodeBlock.Builder = CodeBlock.builder() val targetsBuilder: CodeBlock.Builder = CodeBlock.builder() private val rangeClassName: ClassName = ClassName.bestGuess("magnet.internal.Range") private var generateSingleRange = false private var currentSection: Section? = null private var sectionIndex = 0 override fun visit(inst: Inst) { // nop } override fun visit(index: Index) { // nop } override fun visit(section: Section) { generateSingleRange = section.ranges.size == 1 currentSection = section if (generateSingleRange) { return } val targetsName = "ranges${++sectionIndex}" indexBuilder.addStatement( "index.put(\$T.getType(), \$L)", section.firstFactory, targetsName ) val mapSize = Math.max(Math.round(section.ranges.size / .75f), 8) targetsBuilder.addStatement( "\$T<\$T, \$T> \$L = new \$T<>($mapSize)", Map::class.java, String::class.java, rangeClassName, targetsName, HashMap::class.java ) } override fun visit(range: Range) { if (generateSingleRange) { currentSection?.let { indexBuilder.addStatement( "index.put(\$T.getType(), new \$T(\$L, \$L, \$S))", range.firstFactory, rangeClassName, range.from, range.impls.size, range.classifier ) } return } val targetsName = "ranges$sectionIndex" targetsBuilder.addStatement( "\$L.put(\$S, new \$T(\$L, \$L, \$S))", targetsName, range.classifier, rangeClassName, range.from, range.impls.size, range.classifier ) } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/registry/instances/Indexer.kt ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.processor.registry.instances import magnet.processor.registry.instances.Model.Index import magnet.processor.registry.instances.Model.Inst import magnet.processor.registry.instances.Model.InstComparator class Indexer( private val comparator: Comparator = InstComparator() ) { fun index(instances: List): Index { val sorted = instances.sortedWith(comparator) val indexer = SectionsCreatorVisitor() sorted.forEach { it.accept(indexer) } return Index(sorted, indexer.sections) } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/registry/instances/InstanceIndexGenerator.kt ================================================ package magnet.processor.registry.instances import com.squareup.javapoet.ClassName import com.squareup.javapoet.CodeBlock import magnet.internal.InstanceFactory import magnet.processor.registry.Model.Registry internal class InstanceIndexGenerator { fun generate(registry: Registry): CodeBlock { val index = Indexer().index( registry.instanceFactories.map { Model.Inst( type = it.instanceType.toQualifiedName(), classifier = it.classifier, factory = it.factoryClass ) } ) return CodeBlock.builder() .add(generateArrayOfFactoriesCodeBlock(index)) .add(generateIndexCodeBlock(index)) .build() } private fun generateIndexCodeBlock(index: Model.Index): CodeBlock { val indexGenerator = IndexGeneratorVisitor() index.accept(indexGenerator) val mapSize = Math.max(Math.round(index.instances.size / 0.75f), 16) return CodeBlock.builder() .addStatement( "\$T<\$T, \$T> index = new \$T<>($mapSize)", Map::class.java, Class::class.java, Object::class.java, HashMap::class.java ) .add(indexGenerator.targetsBuilder.build()) .add(indexGenerator.indexBuilder.build()) .build() } private fun generateArrayOfFactoriesCodeBlock(index: Model.Index): CodeBlock { if (index.instances.isEmpty()) { return CodeBlock.builder() .addStatement("\$T[] factories = new \$T[0]", InstanceFactory::class.java, InstanceFactory::class.java) .build() } else { val builder = CodeBlock.builder() .add("\$T[] factories = new \$T[] {", InstanceFactory::class.java, InstanceFactory::class.java) .indent() index.instances.forEach { builder.add("\nnew \$T(),", it.factory) } return builder .unindent() .add("\n};\n") .build() } } } private fun ClassName.toQualifiedName(): String = "${this.packageName()}.${this.simpleName()}" ================================================ FILE: magnet-processor/src/main/java/magnet/processor/registry/instances/Model.kt ================================================ package magnet.processor.registry.instances import com.squareup.javapoet.ClassName class Model private constructor() { class Index( val instances: List, val sections: List
) { fun accept(visitor: IndexVisitor) { visitor.visit(this) sections.forEach { it.accept(visitor) } } } class Inst( val type: String, val classifier: String, val factory: ClassName ) { fun accept(visitor: InstVisitor) { visitor.visit(this) } } class InstComparator : Comparator { override fun compare(left: Inst, right: Inst): Int { val c1 = left.type.compareTo(right.type) if (c1 != 0) { return c1 } val c2 = left.classifier.compareTo(right.classifier) if (c2 != 0) { return c2 } return left.factory.compareTo(right.factory) } } interface InstVisitor { fun visit(inst: Inst) } class Range( val type: String, val classifier: String, private val inst: Inst, val from: Int ) { val impls = mutableListOf() val firstFactory get() = impls[0].factory init { impls.add(inst) } fun accept(visitor: IndexVisitor) { visitor.visit(this) impls.forEach { it.accept(visitor) } } } class Section( val type: String ) { val ranges = mutableMapOf() val firstFactory get() = ranges.values.elementAt(0).firstFactory fun accept(visitor: IndexVisitor) { visitor.visit(this) ranges.forEach { it.value.accept(visitor) } } } interface IndexVisitor : InstVisitor { fun visit(index: Index) fun visit(section: Section) fun visit(range: Range) } } ================================================ FILE: magnet-processor/src/main/java/magnet/processor/registry/instances/SectionsCreatorVisitor.kt ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.processor.registry.instances import magnet.processor.registry.instances.Model.Inst import magnet.processor.registry.instances.Model.InstVisitor import magnet.processor.registry.instances.Model.Range import magnet.processor.registry.instances.Model.Section class SectionsCreatorVisitor : InstVisitor { val sections: List
get() { return sectionsByType.map { it.value } } private val sectionsByType = mutableMapOf() private var currentRange: Range? = null override fun visit(inst: Inst) { if (currentRange == null) { addRange(inst) return } currentRange?.let { if (it.type == inst.type && it.classifier == inst.classifier) { it.impls.add(inst) return } addRange(inst) return } } private fun addRange(inst: Inst) { val section = sectionsByType.getOrPut(inst.type) { Section(inst.type) } val rangeIndex = calculateIndex() val range = Range(inst.type, inst.classifier, inst, rangeIndex) section.ranges[range.classifier] = range currentRange = range } private fun calculateIndex(): Int { currentRange?.let { return it.from + it.impls.size } return 0 } } ================================================ FILE: magnet-processor/src/main/resources/META-INF/gradle/incremental.annotation.processors ================================================ magnet.processor.MagnetProcessor,aggregating ================================================ FILE: magnet-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor ================================================ magnet.processor.MagnetProcessor ================================================ FILE: magnet-processor/src/test/java/magnet/processor/GenerateRegistryForInstanceFactoriesTest.kt ================================================ package magnet.processor import com.google.testing.compile.CompilationSubject import com.google.testing.compile.Compiler import com.google.testing.compile.JavaFileObjects import org.junit.Test import javax.tools.JavaFileObject class GenerateRegistryForInstanceFactoriesTest { private fun withResource(name: String): JavaFileObject = JavaFileObjects.forResource(javaClass.simpleName + '/' + name) @Test fun `No factories`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("App.java") ) CompilationSubject.assertThat(compilation).succeeded() CompilationSubject.assertThat(compilation) .generatedSourceFile("magnet/internal/MagnetIndexer") .hasSourceEquivalentTo(withResource("expected/MagnetIndexer2.java")) } @Test fun `Single factory`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("App.java"), withResource("Interface1.java"), withResource("Implementation1.java") ) CompilationSubject.assertThat(compilation).succeeded() CompilationSubject.assertThat(compilation) .generatedSourceFile("magnet/internal/MagnetIndexer") .hasSourceEquivalentTo(withResource("expected/MagnetIndexer1.java")) } @Test fun `Single factory, inner implementation`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("App.java"), withResource("Interface7.java") ) CompilationSubject.assertThat(compilation).succeeded() CompilationSubject.assertThat(compilation) .generatedSourceFile("magnet/internal/MagnetIndexer") .hasSourceEquivalentTo(withResource("expected/MagnetIndexer7.java")) } @Test fun `Many factories, same type`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("App.java"), withResource("Interface3.java"), withResource("Implementation3_1.java"), withResource("Implementation3_2.java") ) CompilationSubject.assertThat(compilation).succeeded() CompilationSubject.assertThat(compilation) .generatedSourceFile("magnet/internal/MagnetIndexer") .hasSourceEquivalentTo(withResource("expected/MagnetIndexer3.java")) } @Test fun `Many factories, same type, same classifier`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("App.java"), withResource("Interface5.java"), withResource("Implementation5_1.java"), withResource("Implementation5_2.java") ) CompilationSubject.assertThat(compilation).succeeded() CompilationSubject.assertThat(compilation) .generatedSourceFile("magnet/internal/MagnetIndexer") .hasSourceEquivalentTo(withResource("expected/MagnetIndexer5.java")) } @Test fun `Many factories, same type, different classifiers`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("App.java"), withResource("Interface4.java"), withResource("Implementation4_1.java"), withResource("Implementation4_2.java") ) CompilationSubject.assertThat(compilation).succeeded() CompilationSubject.assertThat(compilation) .generatedSourceFile("magnet/internal/MagnetIndexer") .hasSourceEquivalentTo(withResource("expected/MagnetIndexer4.java")) } @Test fun `Many factories, different types`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("App.java"), withResource("Interface6_1.java"), withResource("Interface6_2.java"), withResource("Implementation6_1.java"), withResource("Implementation6_2.java") ) CompilationSubject.assertThat(compilation).succeeded() CompilationSubject.assertThat(compilation) .generatedSourceFile("magnet/internal/MagnetIndexer") .hasSourceEquivalentTo(withResource("expected/MagnetIndexer6.java")) } } ================================================ FILE: magnet-processor/src/test/java/magnet/processor/IndexerTest.kt ================================================ package magnet.processor import com.google.common.truth.Truth.assertThat import com.squareup.javapoet.ClassName import magnet.processor.registry.instances.Indexer import magnet.processor.registry.instances.Model.Inst import org.junit.Test class IndexerTest { @Test fun test_IndexNodes() { val index = Indexer().index(unsortedNodes()) assertThat(index.instances.toTypedArray() contentDeepEquals sortedNodes().toTypedArray()) } @Test fun test_IndexSections() { val index = Indexer().index(unsortedNodes()) assertThat(index.sections.size).isEqualTo(3) assertThat(index.sections[0].type).isEqualTo("AType") assertThat(index.sections[0].ranges.size).isEqualTo(3) assertThat(index.sections[1].type).isEqualTo("BType") assertThat(index.sections[1].ranges.size).isEqualTo(1) assertThat(index.sections[2].type).isEqualTo("CType") assertThat(index.sections[2].ranges.size).isEqualTo(4) } private fun unsortedNodes(): List { val factory = ClassName.bestGuess("Factory") return listOf( Inst("CType", "four", factory), Inst("CType", "two", factory), Inst("CType", "one", factory), Inst("CType", "three", factory), Inst("CType", "four", factory), Inst("BType", "", factory), Inst("BType", "", factory), Inst("BType", "", factory), Inst("AType", "", factory), Inst("AType", "one", factory), Inst("AType", "", factory), Inst("AType", "two", factory), Inst("AType", "one", factory) ) } private fun sortedNodes(): List { val factory = ClassName.bestGuess("Factory") return listOf( Inst("AType", "", factory), Inst("AType", "", factory), Inst("AType", "one", factory), Inst("AType", "one", factory), Inst("AType", "two", factory), Inst("BType", "", factory), Inst("BType", "", factory), Inst("BType", "", factory), Inst("CType", "four", factory), Inst("CType", "four", factory), Inst("CType", "one", factory), Inst("CType", "three", factory), Inst("CType", "two", factory) ) } } ================================================ FILE: magnet-processor/src/test/java/magnet/processor/InstanceCustomFactoryProcessorTest.kt ================================================ package magnet.processor import com.google.testing.compile.CompilationSubject import com.google.testing.compile.Compiler import com.google.testing.compile.JavaFileObjects import org.junit.Test import javax.tools.JavaFileObject class InstanceCustomFactoryProcessorTest { private fun withResource(name: String): JavaFileObject = JavaFileObjects.forResource(javaClass.simpleName + '/' + name) @Test fun `Not parametrized custom Factory`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface1.java"), withResource("Implementation1.java"), withResource("CustomFactory1.java") ) CompilationSubject.assertThat(compilation).succeeded() CompilationSubject.assertThat(compilation) .generatedSourceFile("test/Implementation1MagnetFactory") .hasSourceEquivalentTo(withResource("expected/Implementation1MagnetFactory.java")) } @Test fun `Parametrized custom Factory`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface2.java"), withResource("Implementation2.java"), withResource("CustomFactory2.java") ) CompilationSubject.assertThat(compilation).succeeded() CompilationSubject.assertThat(compilation) .generatedSourceFile("test/Implementation2MagnetFactory") .hasSourceEquivalentTo(withResource("expected/Implementation2MagnetFactory.java")) } @Test fun `Custom Factory for implementation with dependencies`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface3.java"), withResource("Implementation3.java"), withResource("CustomFactory3.java") ) CompilationSubject.assertThat(compilation).succeeded() CompilationSubject.assertThat(compilation) .generatedSourceFile("test/Implementation3MagnetFactory") .hasSourceEquivalentTo(withResource("expected/Implementation3MagnetFactory.java")) } } ================================================ FILE: magnet-processor/src/test/java/magnet/processor/InstanceDisposerTest.kt ================================================ package magnet.processor import com.google.testing.compile.CompilationSubject import com.google.testing.compile.Compiler import com.google.testing.compile.JavaFileObjects import org.junit.Test import javax.tools.JavaFileObject class InstanceDisposerTest { private fun withResource(name: String): JavaFileObject = JavaFileObjects.forResource(javaClass.simpleName + '/' + name) @Test fun `Disposer method gets generated`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface.java"), withResource("Implementation1.java") ) CompilationSubject.assertThat(compilation).succeeded() CompilationSubject.assertThat(compilation) .generatedSourceFile("test/Implementation1MagnetFactory") .hasSourceEquivalentTo(withResource("expected/Implementation1MagnetFactory.java")) } @Test fun `Disposer must be present`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface.java"), withResource("Implementation2.java") ) CompilationSubject.assertThat(compilation).failed() CompilationSubject.assertThat(compilation).hadErrorContaining("disposer method") } @Test fun `Disposer must have no parameters`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface.java"), withResource("Implementation3.java") ) CompilationSubject.assertThat(compilation).failed() CompilationSubject.assertThat(compilation).hadErrorContaining("must have no parameters") } @Test fun `Disposer must return void`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface.java"), withResource("Implementation4.java") ) CompilationSubject.assertThat(compilation).failed() CompilationSubject.assertThat(compilation).hadErrorContaining("must return void") } @Test fun `Disposer cannot be used with UNSCOPED instances`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface.java"), withResource("Implementation5.java") ) CompilationSubject.assertThat(compilation).failed() CompilationSubject.assertThat(compilation).hadErrorContaining("UNSCOPED") } } ================================================ FILE: magnet-processor/src/test/java/magnet/processor/InstanceIndexProcessorTest.kt ================================================ package magnet.processor import com.google.testing.compile.CompilationSubject import com.google.testing.compile.CompilationSubject.assertThat import com.google.testing.compile.Compiler import com.google.testing.compile.JavaFileObjects import org.junit.Test import javax.tools.JavaFileObject class InstanceIndexProcessorTest { private fun withResource(name: String): JavaFileObject = JavaFileObjects.forResource(javaClass.simpleName + '/' + name) @Test fun `InstanceFactory index gets generated with classifier`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface1.java"), withResource("Implementation1.java") ) assertThat(compilation).succeeded() CompilationSubject.assertThat(compilation) .generatedSourceFile("magnet.index/app_test_Implementation1MagnetFactory") .hasSourceEquivalentTo(withResource("generated/app_test_Implementation1MagnetFactory.java")) } @Test fun `InstanceFactory index gets generated without classifier`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface2.java"), withResource("Implementation2.java") ) assertThat(compilation).succeeded() CompilationSubject.assertThat(compilation) .generatedSourceFile("magnet.index/app_test_Implementation2MagnetFactory") .hasSourceEquivalentTo(withResource("generated/app_test_Implementation2MagnetFactory.java")) } } ================================================ FILE: magnet-processor/src/test/java/magnet/processor/MagnetProcessorFactoryNamesTest.kt ================================================ package magnet.processor import com.google.testing.compile.CompilationSubject import com.google.testing.compile.Compiler import com.google.testing.compile.JavaFileObjects import org.junit.Test import javax.tools.JavaFileObject class MagnetProcessorFactoryNamesTest { private fun withResource(name: String): JavaFileObject = JavaFileObjects.forResource(javaClass.simpleName + '/' + name) @Test fun `Generate fully named factories for top level classes`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Delegate1.java") ) CompilationSubject.assertThat(compilation).succeededWithoutWarnings() CompilationSubject.assertThat(compilation) .generatedSourceFile("test/Delegate1MagnetFactory") .hasSourceEquivalentTo(withResource("generated/Delegate1MagnetFactory.java")) } @Test fun `Generate fully named factories for inner classes`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface1.java") ) CompilationSubject.assertThat(compilation).succeededWithoutWarnings() CompilationSubject.assertThat(compilation) .generatedSourceFile("test/Interface1DelegateMagnetFactory") .hasSourceEquivalentTo(withResource("generated/Interface1DelegateMagnetFactory.java")) } } ================================================ FILE: magnet-processor/src/test/java/magnet/processor/MagnetProcessorTest.kt ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnet.processor import com.google.testing.compile.CompilationSubject.assertThat import com.google.testing.compile.Compiler import com.google.testing.compile.JavaFileObjects import org.junit.Test import javax.tools.JavaFileObject class MagnetProcessorTest { @Test fun generateFactory_NoParams() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("HomePageNoParams.java"), withResource("Page.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/extension/HomePageNoParamsMagnetFactory") .hasSourceEquivalentTo(withResource("generated/HomePageNoParamsMagnetFactory.java")) } @Test fun generateFactory_WithScope() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("HomePageWithScope.java"), withResource("Page.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/extension/HomePageWithScopeMagnetFactory") .hasSourceEquivalentTo(withResource("generated/HomePageWithScopeMagnetFactory.java")) } @Test fun generateFactory_WithArbitraryParams() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("HomePageWithParams.java"), withResource("Page.java"), withResource("HomeRepository.java"), withResource("UserData.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/extension/HomePageWithParamsMagnetFactory") .hasSourceEquivalentTo(withResource("generated/HomePageWithParamsMagnetFactory.java")) } @Test fun generateFactory_WithArbitraryParamsAndScope() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("HomePage.java"), withResource("Page.java"), withResource("HomeRepository.java"), withResource("UserData.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/extension/HomePageMagnetFactory") .hasSourceEquivalentTo(withResource("generated/HomePageMagnetFactory.java")) } @Test fun generateFactory_FailsOnGenericTypeInConstructorParameter() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("HomePageWithGenericParam.java"), withResource("Page.java") ) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("is specified using a generic type") } @Test fun generateFactory_TypeNotImplemented() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Tab.java"), withResource("UnimplementedTab.java") ) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("must implement") } @Test fun generateFactory_DisabledAnnotation() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Tab.java"), withResource("DisabledTab.java") ) assertThat(compilation).succeeded() com.google.common.truth.Truth.assertThat(compilation.generatedFiles().size).isEqualTo(2) } @Test fun generateFactory_WithClassifierParams() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("HomePageWithClassifierParams.java"), withResource("Page.java"), withResource("HomeRepository.java"), withResource("UserData.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/extension/HomePageWithClassifierParamsMagnetFactory") .hasSourceEquivalentTo(withResource("generated/HomePageWithClassifierParamsMagnetFactory.java")) } @Test fun generateFactory_WithManyParams() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("HomePageWithManyParams.java"), withResource("Page.java"), withResource("HomeRepository.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/extension/HomePageWithManyParamsMagnetFactory") .hasSourceEquivalentTo(withResource("generated/HomePageWithManyParamsMagnetFactory.java")) } @Test fun generateFactory_WithManyParameterizedParams() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("HomePageWithManyParameterizedParams.java"), withResource("Page.java"), withResource("WorkProcessor.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/extension/HomePageWithManyParameterizedParamsMagnetFactory") .hasSourceEquivalentTo(withResource("generated/HomePageWithManyParameterizedParamsMagnetFactory.java")) } @Test fun generateFactory_WithManyParameterizedWildcardOutParams() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("HomePageWithManyParameterizedWildcardOutParams.java"), withResource("Page.java"), withResource("WorkProcessor.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/extension/HomePageWithManyParameterizedWildcardOutParamsMagnetFactory") .hasSourceEquivalentTo(withResource("generated/HomePageWithManyParameterizedWildcardOutParamsMagnetFactory.java")) } @Test fun generateFactory_WithManyParameterizedWildcardInParams() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("HomePageWithManyParameterizedWildcardInParams.java"), withResource("Page.java"), withResource("WorkProcessor.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/extension/HomePageWithManyParameterizedWildcardInParamsMagnetFactory") .hasSourceEquivalentTo(withResource("generated/HomePageWithManyParameterizedWildcardInParamsMagnetFactory.java")) } @Test fun generateFactory_WithManyParameterizedWildcardKnownParams() { // This is what Kotlin provides to annotation processor, when Kotlin generics are used as parameters val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("HomePageWithManyParameterizedWildcardKnownParams.java"), withResource("Page.java"), withResource("WorkProcessor.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/extension/HomePageWithManyParameterizedWildcardKnownParamsMagnetFactory") .hasSourceEquivalentTo(withResource("generated/HomePageWithManyParameterizedWildcardKnownParamsMagnetFactory.java")) } @Test fun generateFactory_WithManyWildcardParams() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("HomePageWithManyWildcardParams.java"), withResource("Page.java"), withResource("HomeRepository.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/extension/HomePageWithManyWildcardParamsMagnetFactory") .hasSourceEquivalentTo(withResource("generated/HomePageWithManyWildcardParamsMagnetFactory.java")) } @Test fun generateFactory_UsingStaticMethod() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("HomePageWithStaticConstructor.java"), withResource("HomePageWithStaticConstructorSingle.java"), withResource("Page.java"), withResource("HomeRepository.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/extension/utils/HomePageWithStaticConstructorSingleCreateRepositoriesMagnetFactory") .hasSourceEquivalentTo(withResource("generated/HomePageWithStaticConstructorSingleCreateRepositoriesMagnetFactory.java")) } @Test fun generateFactory_StaticMethodProvidesInnerClass() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("StaticMethodProvidesInnerClass/PowerManager.java"), withResource("StaticMethodProvidesInnerClass/PowerManagerProvider.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/PowerManagerProviderProvideWakeLockMagnetFactory") .hasSourceEquivalentTo(withResource("StaticMethodProvidesInnerClass/expected/PowerManagerProviderProvideWakeLockMagnetFactory.java")) } @Test fun generateFactory_Covariance_Constructor_ManyParameter() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Covariance_Constructor_ManyParameter/Foo.java"), withResource("Covariance_Constructor_ManyParameter/UnderTest.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("Covariance_Constructor_ManyParameter/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_Covariance_Constructor_SingleParameter() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Covariance_Constructor_SingleParameter/Foo.java"), withResource("Covariance_Constructor_SingleParameter/UnderTest.java") ) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("is specified using a generic type") } @Test fun generateFactory_Lazy_Constructor_NoKotlinMetadata() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile(withResource("Lazy_Constructor_NoKotlinMetadata/UnderTest.java")) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("can only be used with Kotlin classes") } @Test fun generateFactory_Lazy_Constructor_OptionalParameter() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile(withResource("Lazy_Constructor_OptionalParameter/UnderTest.java")) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("Lazy_Constructor_OptionalParameter/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_Lazy_Constructor_OptionalParameter_Wildcard() { val root = "Lazy_Constructor_OptionalParameter_Wildcard" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile(withResource("$root/UnderTest.java")) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("$root/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_Lazy_Constructor_SingleParameter() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile(withResource("Lazy_Constructor_SingleParameter/UnderTest.java")) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("Lazy_Constructor_SingleParameter/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_Lazy_Constructor_SingleParameter_Wildcard() { val root = "Lazy_Constructor_SingleParameter_Wildcard" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile(withResource("$root/UnderTest.java")) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("$root/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_Lazy_Constructor_ManyParameter() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile(withResource("Lazy_Constructor_ManyParameter/UnderTest.java")) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("Lazy_Constructor_ManyParameter/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_Lazy_Constructor_ManyParameter_Wildcard() { val root = "Lazy_Constructor_ManyParameter_Wildcard" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile(withResource("$root/UnderTest.java")) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("$root/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_Lazy_Constructor_ManyParameter_NullableGenericType() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile(withResource("Lazy_Constructor_ManyParameter_NullableGenericType/UnderTest.java")) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("be parametrized with none nullable type") } @Test fun generateFactory_Lazy_Constructor_ManyParameter_NullableListType() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile(withResource("Lazy_Constructor_ManyParameter_NullableListType/UnderTest.java")) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("be parametrized with none nullable List type") } @Test fun generateFactory_Lazy_Method_SingleParameter() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile(withResource("Lazy_Method_SingleParameter/UnderTest.java")) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestProvideUnderTestDepMagnetFactory") .hasSourceEquivalentTo(withResource("Lazy_Method_SingleParameter/expected/UnderTestProvideUnderTestDepMagnetFactory.java")) } @Test fun generateFactory_Lazy_Method_SingleParameter_Wildcard() { val root = "Lazy_Method_SingleParameter_Wildcard" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile(withResource("$root/UnderTest.java")) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestProvideUnderTestDepMagnetFactory") .hasSourceEquivalentTo(withResource("$root/expected/UnderTestProvideUnderTestDepMagnetFactory.java")) } @Test fun generateFactory_Generics_ProvideTypeWithParameter() { val path = "Generics_ProvideTypeWithParameter" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/UnderTest.java"), withResource("$path/Type.java"), withResource("$path/Parameter.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestProvideTypeMagnetFactory") .hasSourceEquivalentTo(withResource("$path/expected/UnderTestProvideTypeMagnetFactory.java")) } @Test fun generateFactory_Generics_ProvideTypeWithParameter_NoClassifier() { val path = "Generics_ProvideTypeWithParameter_NoClassifier" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/UnderTest.java"), withResource("$path/Type.java"), withResource("$path/Parameter.java") ) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("must have 'classifier' value") } @Test fun generateFactory_Lazy_Constructor_SingleParameter_ParameterizedType() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Lazy_Constructor_SingleParameter_ParameterizedType/Foo.java"), withResource("Lazy_Constructor_SingleParameter_ParameterizedType/UnderTest.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("Lazy_Constructor_SingleParameter_ParameterizedType/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_Lazy_Method_OptionalParameter() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile(withResource("Lazy_Method_OptionalParameter/UnderTest.java")) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestProvideUnderTestDepMagnetFactory") .hasSourceEquivalentTo(withResource("Lazy_Method_OptionalParameter/expected/UnderTestProvideUnderTestDepMagnetFactory.java")) } @Test fun generateFactory_Lazy_Method_OptionalParameter_Wildcard() { val root = "Lazy_Method_OptionalParameter_Wildcard" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile(withResource("$root/UnderTest.java")) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestProvideUnderTestDepMagnetFactory") .hasSourceEquivalentTo(withResource("$root/expected/UnderTestProvideUnderTestDepMagnetFactory.java")) } @Test fun generateFactory_Limit_NotEmpty_GenerateGetter() { val root = "Limit_NotEmpty_HasGetter" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile(withResource("$root/UnderTest.java")) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("$root/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_Limit_Empty_NoGetter() { val root = "Limit_Empty_NoGetter" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile(withResource("$root/UnderTest.java")) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("$root/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_Limit_ReservedAsterix_Fails() { val root = "Limit_ReservedAsterisks_Fails" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile(withResource("$root/UnderTest.java")) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("Use another value") } @Test fun generateFactory_Limit_ScopingDirect_GeneratesGetter() { val root = "Limit_ScopingDirect_GeneratesGetter" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile(withResource("$root/UnderTest.java")) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("$root/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_Limit_ScopingUnscoped_Fails() { val root = "Limit_ScopingUnscoped_Fails" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile(withResource("$root/UnderTest.java")) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("Limit can only be used with Scoping.TOPMOST") } @Test fun generateFactory_Lazy_Method_NoKotlinMetadata() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile(withResource("Lazy_Method_NoKotlinMetadata/UnderTest.java")) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("can only be used with Kotlin classes") } @Test fun generateFactory_ScopeParameter_CustomName() { val path = "ScopeParameter_CustomName" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/UnderTest.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("$path/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_ScopeParameter_CustomName_KotlinClass() { val path = "ScopeParameter_CustomName_KotlinClass" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/UnderTest.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("$path/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_ScopeParameter_DefaultName() { val path = "ScopeParameter_DefaultName" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/UnderTest.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("$path/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_DefaultArguments_JvmOverloads_AtTheEnd() { val path = "DefaultArguments_JvmOverloads_AtTheEnd" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/UnderTest.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("$path/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_DefaultArguments_JvmOverloads_InTheMiddle() { val path = "DefaultArguments_JvmOverloads_InTheMiddle" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/UnderTest.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("$path/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_DefaultArguments_JvmOverloads_Mixed() { val path = "DefaultArguments_JvmOverloads_Mixed" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/UnderTest.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("$path/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_StaticMethodNeedsDependencyWithClassifier() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("StaticMethodNeedsDependencyWithClassifier/Constants.java"), withResource("StaticMethodNeedsDependencyWithClassifier/Input.java"), withResource("StaticMethodNeedsDependencyWithClassifier/Output.java"), withResource("StaticMethodNeedsDependencyWithClassifier/StaticFunction.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/StaticFunctionProvideInputMagnetFactory") .hasSourceEquivalentTo(withResource("StaticMethodNeedsDependencyWithClassifier/generated/StaticFunctionProvideInputMagnetFactory.java")) } @Test fun generateFactory_DisabledAnnotation_UsingStaticMethod() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("HomePageWithStaticConstructor.java"), withResource("HomePageWithStaticConstructorDisabled.java"), withResource("Page.java"), withResource("HomeRepository.java") ) assertThat(compilation).succeededWithoutWarnings() com.google.common.truth.Truth.assertThat(compilation.generatedFiles().size).isEqualTo(4) } @Test fun generateFactoryIndex_ForInterfaceWithGenericType() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Executor.java"), withResource("ExecutorImpl.java"), withResource("AppExtensionRegistry.java") ) assertThat(compilation) .generatedSourceFile("app/extension/ExecutorImplMagnetFactory") .hasSourceEquivalentTo(withResource("generated/ForInterfaceWithGenericType_ExecutorMagnetFactory.java")) } @Test fun generateFactory_Constructor_Public_PackagePrivate() { val path = "Constructor_Public_PackagePrivate" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/UnderTest.java") ) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("must have exactly one public or package-private constructor") } @Test fun generateFactory_Constructor_Public_Public() { val path = "Constructor_Public_Public" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/UnderTest.java") ) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("must have exactly one public or package-private constructor") } @Test fun generateFactory_Constructor_Public_Protected() { val path = "Constructor_Public_Protected" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/UnderTest.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("$path/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_Constructor_Public_Private() { val path = "Constructor_Public_Private" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/UnderTest.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("$path/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_Constructor_PackagePrivate_PackagePrivate() { val path = "Constructor_PackagePrivate_PackagePrivate" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/UnderTest.java") ) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("must have exactly one public or package-private constructor") } @Test fun generateFactory_Constructor_Private() { val path = "Constructor_Private" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/UnderTest.java") ) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("must have exactly one public or package-private constructor") } @Test fun generateFactory_Constructor_Protected() { val path = "Constructor_Protected" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/UnderTest.java") ) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("must have exactly one public or package-private constructor") } @Test fun generateFactory_Constructor_PackagePrivate_Private() { val path = "Constructor_PackagePrivate_Private" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/UnderTest.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("$path/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_Constructor_Public_Public_Kotlin() { val path = "Constructor_Public_Public_Kotlin" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/UnderTest.java") ) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("must have exactly one public or package-private constructor") } @Test fun generateFactory_Generics_GetSingle_Unchecked() { val path = "Generics_GetSingle_Unchecked" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/Dependency.java"), withResource("$path/UnderTest.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("$path/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_Generics_GetOptional_Unchecked() { val path = "Generics_GetOptional_Unchecked" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/Dependency.java"), withResource("$path/UnderTest.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("$path/expected/UnderTestMagnetFactory.java")) } @Test fun generateFactory_TypeAutoDetect_ExtendsObjectNoInterfaces() { val path = "TypeAutoDetect_ExtendsObjectNoInterfaces" val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("$path/UnderTest.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("app/UnderTestMagnetFactory") .hasSourceEquivalentTo(withResource("$path/expected/UnderTestMagnetFactory.java")) } private fun withResource(name: String): JavaFileObject { return JavaFileObjects.forResource(javaClass.simpleName + '/' + name) } } ================================================ FILE: magnet-processor/src/test/java/magnet/processor/SelectorInFactoryClassTest.kt ================================================ package magnet.processor import com.google.testing.compile.CompilationSubject import com.google.testing.compile.Compiler import com.google.testing.compile.JavaFileObjects import org.junit.Test import javax.tools.JavaFileObject class SelectorInFactoryClassTest { @Test fun `Empty selector is allowed`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface.java"), withResource("Implementation1.java") ) CompilationSubject.assertThat(compilation).succeededWithoutWarnings() CompilationSubject.assertThat(compilation) .generatedSourceFile("selector/Implementation1MagnetFactory") .hasSourceEquivalentTo(withResource("generated/Implementation1MagnetFactory.java")) } @Test fun `Invalid selector fails compilation`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface.java"), withResource("Implementation2.java") ) CompilationSubject.assertThat(compilation).failed() CompilationSubject.assertThat(compilation).hadErrorContaining("Invalid selector") } @Test fun `Empty selector id fails compilation`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface.java"), withResource("Implementation3.java") ) CompilationSubject.assertThat(compilation).failed() CompilationSubject.assertThat(compilation).hadErrorContaining("Invalid selector") } @Test fun `Empty selector filed fails compilation`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface.java"), withResource("Implementation4.java") ) CompilationSubject.assertThat(compilation).failed() CompilationSubject.assertThat(compilation).hadErrorContaining("Invalid selector") } @Test fun `Invalid selector operator fails compilation`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface.java"), withResource("Implementation5.java") ) CompilationSubject.assertThat(compilation).failed() CompilationSubject.assertThat(compilation).hadErrorContaining("Invalid selector") } @Test fun `Empty selector operand fails compilation`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface.java"), withResource("Implementation6.java") ) CompilationSubject.assertThat(compilation).failed() CompilationSubject.assertThat(compilation).hadErrorContaining("Invalid selector") } @Test fun `Valid selector (1 operand)`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface.java"), withResource("Implementation7.java") ) CompilationSubject.assertThat(compilation).succeededWithoutWarnings() CompilationSubject.assertThat(compilation) .generatedSourceFile("selector/Implementation7MagnetFactory") .hasSourceEquivalentTo(withResource("generated/Implementation7MagnetFactory.java")) } @Test fun `Valid selector (2 operands, in)`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface.java"), withResource("Implementation8.java") ) CompilationSubject.assertThat(compilation).succeededWithoutWarnings() CompilationSubject.assertThat(compilation) .generatedSourceFile("selector/Implementation8MagnetFactory") .hasSourceEquivalentTo(withResource("generated/Implementation8MagnetFactory.java")) } @Test fun `Valid selector (1 operand, !=)`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface.java"), withResource("Implementation9.java") ) CompilationSubject.assertThat(compilation).succeededWithoutWarnings() CompilationSubject.assertThat(compilation) .generatedSourceFile("selector/Implementation9MagnetFactory") .hasSourceEquivalentTo(withResource("generated/Implementation9MagnetFactory.java")) } @Test fun `Valid selector (2 operands, !in)`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface.java"), withResource("Implementation10.java") ) CompilationSubject.assertThat(compilation).succeededWithoutWarnings() CompilationSubject.assertThat(compilation) .generatedSourceFile("selector/Implementation10MagnetFactory") .hasSourceEquivalentTo(withResource("generated/Implementation10MagnetFactory.java")) } private fun withResource(name: String): JavaFileObject = JavaFileObjects.forResource(javaClass.simpleName + '/' + name) } ================================================ FILE: magnet-processor/src/test/java/magnet/processor/SiblingTypesTest.kt ================================================ package magnet.processor import com.google.testing.compile.CompilationSubject.assertThat import com.google.testing.compile.Compiler import com.google.testing.compile.JavaFileObjects import org.junit.Test import javax.tools.JavaFileObject class SiblingTypesTest { @Test fun `Either type() or types() is required`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface1.java"), withResource("Interface2.java"), withResource("Implementation1.java") ) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("must declare") } @Test fun `Both type() or types() are not allowed`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface1.java"), withResource("Interface2.java"), withResource("Implementation2.java") ) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("not both") } @Test fun `Inheritance verification fails for types()`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface1.java"), withResource("Interface2.java"), withResource("Implementation3.java") ) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("must implement siblings.Interface2") } @Test fun `types() generates multiple factories`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface1.java"), withResource("Interface2.java"), withResource("Implementation4.java") ) assertThat(compilation).succeededWithoutWarnings() assertThat(compilation) .generatedSourceFile("siblings/Implementation4Interface1MagnetFactory") .hasSourceEquivalentTo(withResource("generated/Implementation4Interface1MagnetFactory.java")) assertThat(compilation) .generatedSourceFile("siblings/Implementation4Interface2MagnetFactory") .hasSourceEquivalentTo(withResource("generated/Implementation4Interface2MagnetFactory.java")) } @Test fun `types() must be used with scoped instances`() { val compilation = Compiler.javac() .withProcessors(MagnetProcessor()) .compile( withResource("Interface1.java"), withResource("Interface2.java"), withResource("Implementation5.java") ) assertThat(compilation).failed() assertThat(compilation).hadErrorContaining("must be used with scoped instances") } private fun withResource(name: String): JavaFileObject = JavaFileObjects.forResource(javaClass.simpleName + '/' + name) } ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/App.java ================================================ package test; import magnet.Registry; @Registry class App {} ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/Implementation1.java ================================================ package test; import magnet.Instance; @Instance(type = Interface1.class) class Implementation1 implements Interface1 {} ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/Implementation3_1.java ================================================ package test; import magnet.Instance; @Instance(type = Interface3.class) class Implementation3_1 implements Interface3 {} ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/Implementation3_2.java ================================================ package test; import magnet.Instance; @Instance(type = Interface3.class) class Implementation3_2 implements Interface3 {} ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/Implementation4_1.java ================================================ package test; import magnet.Instance; @Instance( type = Interface4.class, classifier = "one" ) class Implementation4_1 implements Interface4 {} ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/Implementation4_2.java ================================================ package test; import magnet.Instance; @Instance( type = Interface4.class, classifier = "two" ) class Implementation4_2 implements Interface4 {} ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/Implementation5_1.java ================================================ package test; import magnet.Instance; @Instance( type = Interface5.class, classifier = "zero" ) class Implementation5_1 implements Interface5 {} ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/Implementation5_2.java ================================================ package test; import magnet.Instance; @Instance( type = Interface5.class, classifier = "zero" ) class Implementation5_2 implements Interface5 {} ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/Implementation6_1.java ================================================ package test; import magnet.Instance; @Instance(type = Interface6_1.class) class Implementation6_1 implements Interface6_1 {} ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/Implementation6_2.java ================================================ package test; import magnet.Instance; @Instance(type = Interface6_2.class) class Implementation6_2 implements Interface6_2 {} ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/Interface1.java ================================================ package test; interface Interface1 {} ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/Interface3.java ================================================ package test; interface Interface3 {} ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/Interface4.java ================================================ package test; interface Interface4 {} ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/Interface5.java ================================================ package test; interface Interface5 {} ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/Interface6_1.java ================================================ package test; interface Interface6_1 {} ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/Interface6_2.java ================================================ package test; interface Interface6_2 {} ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/Interface7.java ================================================ package test; import magnet.Instance; interface Interface7 { @Instance(type = Interface7.class) class Implementation7 implements Interface7 {} } ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/expected/MagnetIndexer1.java ================================================ package magnet.internal; import java.util.HashMap; import java.util.Map; import test.Implementation1MagnetFactory; @Generated public final class MagnetIndexer { public static void register(MagnetInstanceManager instanceManager) { InstanceFactory[] factories = new InstanceFactory[] { new Implementation1MagnetFactory(), }; Map index = new HashMap<>(16); index.put(Implementation1MagnetFactory.getType(), new Range(0, 1, "")); instanceManager.register(factories, index); } } ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/expected/MagnetIndexer2.java ================================================ package magnet.internal; import java.util.HashMap; import java.util.Map; @Generated public final class MagnetIndexer { public static void register(MagnetInstanceManager instanceManager) { InstanceFactory[] factories = new InstanceFactory[0]; Map index = new HashMap<>(16); instanceManager.register(factories, index); } } ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/expected/MagnetIndexer3.java ================================================ package magnet.internal; import java.util.HashMap; import java.util.Map; import test.Implementation3_1MagnetFactory; import test.Implementation3_2MagnetFactory; @Generated public final class MagnetIndexer { public static void register(MagnetInstanceManager instanceManager) { InstanceFactory[] factories = new InstanceFactory[] { new Implementation3_1MagnetFactory(), new Implementation3_2MagnetFactory(), }; Map index = new HashMap<>(16); index.put(Implementation3_1MagnetFactory.getType(), new Range(0, 2, "")); instanceManager.register(factories, index); } } ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/expected/MagnetIndexer4.java ================================================ package magnet.internal; import java.util.HashMap; import java.util.Map; import test.Implementation4_1MagnetFactory; import test.Implementation4_2MagnetFactory; @Generated public final class MagnetIndexer { public static void register(MagnetInstanceManager instanceManager) { InstanceFactory[] factories = new InstanceFactory[] { new Implementation4_1MagnetFactory(), new Implementation4_2MagnetFactory(), }; Map index = new HashMap<>(16); Map ranges1 = new HashMap<>(8); ranges1.put("one", new Range(0, 1, "one")); ranges1.put("two", new Range(1, 1, "two")); index.put(Implementation4_1MagnetFactory.getType(), ranges1); instanceManager.register(factories, index); } ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/expected/MagnetIndexer5.java ================================================ package magnet.internal; import java.util.HashMap; import java.util.Map; import test.Implementation5_1MagnetFactory; import test.Implementation5_2MagnetFactory; @Generated public final class MagnetIndexer { public static void register(MagnetInstanceManager instanceManager) { InstanceFactory[] factories = new InstanceFactory[] { new Implementation5_1MagnetFactory(), new Implementation5_2MagnetFactory(), }; Map index = new HashMap<>(16); index.put(Implementation5_1MagnetFactory.getType(), new Range(0, 2, "zero")); instanceManager.register(factories, index); } } ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/expected/MagnetIndexer6.java ================================================ package magnet.internal; import java.util.HashMap; import java.util.Map; import test.Implementation6_1MagnetFactory; import test.Implementation6_2MagnetFactory; @Generated public final class MagnetIndexer { public static void register(MagnetInstanceManager instanceManager) { InstanceFactory[] factories = new InstanceFactory[] { new Implementation6_1MagnetFactory(), new Implementation6_2MagnetFactory(), }; Map index = new HashMap<>(16); index.put(Implementation6_1MagnetFactory.getType(), new Range(0, 1, "")); index.put(Implementation6_2MagnetFactory.getType(), new Range(1, 1, "")); instanceManager.register(factories, index); } } ================================================ FILE: magnet-processor/src/test/resources/GenerateRegistryForInstanceFactoriesTest/expected/MagnetIndexer7.java ================================================ package magnet.internal; import java.util.HashMap; import java.util.Map; import test.Interface7Implementation7MagnetFactory; @Generated public final class MagnetIndexer { public static void register(MagnetInstanceManager instanceManager) { InstanceFactory[] factories = new InstanceFactory[] { new Interface7Implementation7MagnetFactory(), }; Map index = new HashMap<>(16); index.put(Interface7Implementation7MagnetFactory.getType(), new Range(0, 1, "")); instanceManager.register(factories, index); } } ================================================ FILE: magnet-processor/src/test/resources/InstanceCustomFactoryProcessorTest/CustomFactory1.java ================================================ package test; import magnet.Factory; import magnet.Scope; import magnet.Scoping; import org.jetbrains.annotations.NotNull; class CustomFactory1 implements Factory { @NotNull @Override public Interface1 create( @NotNull Scope scope, @NotNull Class type, @NotNull String classifier, @NotNull Scoping scoping, @NotNull Instantiator instantiator ) { return null; } } ================================================ FILE: magnet-processor/src/test/resources/InstanceCustomFactoryProcessorTest/CustomFactory2.java ================================================ package test; import magnet.Factory; import magnet.Scope; import magnet.Scoping; import org.jetbrains.annotations.NotNull; class CustomFactory2 implements Factory { @NotNull @Override public T create( @NotNull Scope scope, @NotNull Class type, @NotNull String classifier, @NotNull Scoping scoping, @NotNull Instantiator instantiator) { return null; } } ================================================ FILE: magnet-processor/src/test/resources/InstanceCustomFactoryProcessorTest/CustomFactory3.java ================================================ package test; import magnet.Factory; import magnet.Scope; import magnet.Scoping; import org.jetbrains.annotations.NotNull; class CustomFactory3 implements Factory { @NotNull @Override public Interface3 create( @NotNull Scope scope, @NotNull Class type, @NotNull String classifier, @NotNull Scoping scoping, @NotNull Instantiator instantiator ) { return null; } } ================================================ FILE: magnet-processor/src/test/resources/InstanceCustomFactoryProcessorTest/Implementation1.java ================================================ package test; import magnet.Instance; @Instance( type = Interface1.class, factory = CustomFactory1.class ) public class Implementation1 implements Interface1 {} ================================================ FILE: magnet-processor/src/test/resources/InstanceCustomFactoryProcessorTest/Implementation2.java ================================================ package test; import magnet.Instance; @Instance( type = Interface2.class, factory = CustomFactory2.class ) public class Implementation2 implements Interface2 {} ================================================ FILE: magnet-processor/src/test/resources/InstanceCustomFactoryProcessorTest/Implementation3.java ================================================ package test; import magnet.Instance; @Instance( type = Interface3.class, factory = CustomFactory3.class ) public class Implementation3 implements Interface3 { public Implementation3(String value1, Long value2) {} } ================================================ FILE: magnet-processor/src/test/resources/InstanceCustomFactoryProcessorTest/Interface1.java ================================================ package test; public interface Interface1 {} ================================================ FILE: magnet-processor/src/test/resources/InstanceCustomFactoryProcessorTest/Interface2.java ================================================ package test; public interface Interface2 {} ================================================ FILE: magnet-processor/src/test/resources/InstanceCustomFactoryProcessorTest/Interface3.java ================================================ package test; public interface Interface3 {} ================================================ FILE: magnet-processor/src/test/resources/InstanceCustomFactoryProcessorTest/expected/Implementation1MagnetFactory.java ================================================ package test; import magnet.Factory; import magnet.Scope; import magnet.Scoping; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class Implementation1MagnetFactory extends InstanceFactory implements Factory.Instantiator { private CustomFactory1 factory = null; @Override public Interface1 create(Scope scope) { if (factory == null) { factory = new CustomFactory1(); } return factory.create(scope, Interface1.class, "", Scoping.TOPMOST, this); } @Override public Interface1 instantiate(Scope scope) { return new Implementation1(); } public static Class getType() { return Interface1.class; } } ================================================ FILE: magnet-processor/src/test/resources/InstanceCustomFactoryProcessorTest/expected/Implementation2MagnetFactory.java ================================================ package test; import magnet.Factory; import magnet.Scope; import magnet.Scoping; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class Implementation2MagnetFactory extends InstanceFactory implements Factory.Instantiator { private CustomFactory2 factory = null; @Override public Interface2 create(Scope scope) { if (factory == null) { factory = new CustomFactory2(); } return factory.create(scope, Interface2.class, "", Scoping.TOPMOST, this); } @Override public Interface2 instantiate(Scope scope) { return new Implementation2(); } public static Class getType() { return Interface2.class; } } ================================================ FILE: magnet-processor/src/test/resources/InstanceCustomFactoryProcessorTest/expected/Implementation3MagnetFactory.java ================================================ package test; import magnet.Factory; import magnet.Scope; import magnet.Scoping; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class Implementation3MagnetFactory extends InstanceFactory implements Factory.Instantiator { private CustomFactory3 factory = null; @Override public Interface3 create(Scope scope) { if (factory == null) { factory = new CustomFactory3(); } return factory.create(scope, Interface3.class, "", Scoping.TOPMOST, this); } @Override public Interface3 instantiate(Scope scope) { String value1 = scope.getSingle(String.class, ""); Long value2 = scope.getSingle(Long.class, ""); return new Implementation3(value1, value2); } public static Class getType() { return Interface3.class; } } ================================================ FILE: magnet-processor/src/test/resources/InstanceDisposerTest/Implementation1.java ================================================ package test; import magnet.Instance; @Instance( type = Interface.class, disposer = "disposeIt" ) public class Implementation1 implements Interface { void disposeIt() {} } ================================================ FILE: magnet-processor/src/test/resources/InstanceDisposerTest/Implementation2.java ================================================ package test; import magnet.Instance; @Instance( type = Interface.class, disposer = "disposeIt" ) public class Implementation2 implements Interface { void dispose() {} } ================================================ FILE: magnet-processor/src/test/resources/InstanceDisposerTest/Implementation3.java ================================================ package test; import magnet.Instance; @Instance( type = Interface.class, disposer = "disposeIt" ) public class Implementation3 implements Interface { void disposeIt(String value) {} } ================================================ FILE: magnet-processor/src/test/resources/InstanceDisposerTest/Implementation4.java ================================================ package test; import magnet.Instance; @Instance( type = Interface.class, disposer = "disposeIt" ) public class Implementation4 implements Interface { String disposeIt() { return null; } } ================================================ FILE: magnet-processor/src/test/resources/InstanceDisposerTest/Implementation5.java ================================================ package test; import magnet.Instance; import magnet.Scoping; @Instance( type = Interface.class, scoping = Scoping.UNSCOPED, disposer = "disposeIt" ) public class Implementation5 implements Interface { void disposeIt() { return null; } } ================================================ FILE: magnet-processor/src/test/resources/InstanceDisposerTest/Interface.java ================================================ package test; public interface Interface {} ================================================ FILE: magnet-processor/src/test/resources/InstanceDisposerTest/expected/Implementation1MagnetFactory.java ================================================ package test; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class Implementation1MagnetFactory extends InstanceFactory { @Override public Interface create(Scope scope) { return new Implementation1(); } @Override public boolean isDisposable() { return true; } @Override public void dispose(Interface instance) { ((Implementation1) instance).disposeIt(); } public static Class getType() { return Interface.class; } } ================================================ FILE: magnet-processor/src/test/resources/InstanceIndexProcessorTest/Implementation1.java ================================================ package app.test; import app.Interface1; import magnet.Instance; @Instance( type = Interface1.class, classifier = "implementation1" ) class Implementation1 implements Interface1 { Implementation1() {} @Override public int getId() { return 0; } } ================================================ FILE: magnet-processor/src/test/resources/InstanceIndexProcessorTest/Implementation2.java ================================================ package app.test; import app.Interface2; import magnet.Instance; @Instance(type = Interface2.class) class Implementation2 implements Interface2 {} ================================================ FILE: magnet-processor/src/test/resources/InstanceIndexProcessorTest/Interface1.java ================================================ package app; public interface Interface1 { int getId(); } ================================================ FILE: magnet-processor/src/test/resources/InstanceIndexProcessorTest/Interface2.java ================================================ package app; public interface Interface2 {} ================================================ FILE: magnet-processor/src/test/resources/InstanceIndexProcessorTest/generated/app_test_Implementation1MagnetFactory.java ================================================ package magnet.index; import app.test.Implementation1MagnetFactory; import magnet.internal.Generated; import magnet.internal.Index; import magnet.internal.InstanceFactory; @Generated @Index( factoryType = InstanceFactory.class, factoryClass = Implementation1MagnetFactory.class, instanceType = "app.Interface1", classifier = "implementation1" ) public final class app_test_Implementation1MagnetFactory {} ================================================ FILE: magnet-processor/src/test/resources/InstanceIndexProcessorTest/generated/app_test_Implementation2MagnetFactory.java ================================================ package magnet.index; import app.test.Implementation2MagnetFactory; import magnet.internal.Generated; import magnet.internal.Index; import magnet.internal.InstanceFactory; @Generated @Index( factoryType = InstanceFactory.class, factoryClass = Implementation2MagnetFactory.class, instanceType = "app.Interface2", classifier = "" ) public final class app_test_Implementation2MagnetFactory {} ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorFactoryNamesTest/Delegate1.java ================================================ package test; import magnet.Instance; @Instance(type = Delegate1.class) class Delegate1 {} ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorFactoryNamesTest/Interface1.java ================================================ package test; import magnet.Instance; interface Interface1 { @Instance(type = Interface1.Delegate.class) class Delegate {} } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorFactoryNamesTest/generated/Delegate1MagnetFactory.java ================================================ package test; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class Delegate1MagnetFactory extends InstanceFactory { @Override public Delegate1 create(Scope scope) { return new Delegate1(); } public static Class getType() { return Delegate1.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorFactoryNamesTest/generated/Interface1DelegateMagnetFactory.java ================================================ package test; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class Interface1DelegateMagnetFactory extends InstanceFactory { @Override public Interface1.Delegate create(Scope scope) { return new Interface1.Delegate(); } public static Class getType() { return Interface1.Delegate.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/AppExtensionRegistry.java ================================================ package app; import magnet.Registry; @Registry public interface AppExtensionRegistry {} ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Constructor_PackagePrivate_PackagePrivate/UnderTest.java ================================================ package app; import magnet.Instance; import magnet.Scope; @Instance(type = UnderTest.class) class UnderTest { UnderTest(Scope scope) { } UnderTest() { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Constructor_PackagePrivate_Private/UnderTest.java ================================================ package app; import magnet.Instance; import magnet.Scope; @Instance(type = UnderTest.class) class UnderTest { UnderTest(Scope scope) { } private UnderTest() { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Constructor_PackagePrivate_Private/expected/UnderTestMagnetFactory.java ================================================ package app; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { return new UnderTest(scope); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Constructor_Private/UnderTest.java ================================================ package app; import magnet.Instance; @Instance(type = UnderTest.class) class UnderTest { private UnderTest() { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Constructor_Protected/UnderTest.java ================================================ package app; import magnet.Instance; @Instance(type = UnderTest.class) class UnderTest { protected UnderTest() { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Constructor_Public_PackagePrivate/UnderTest.java ================================================ package app; import magnet.Instance; import magnet.Scope; @Instance(type = UnderTest.class) class UnderTest { public UnderTest(Scope scope) { } UnderTest() { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Constructor_Public_Private/UnderTest.java ================================================ package app; import magnet.Instance; import magnet.Scope; @Instance(type = UnderTest.class) class UnderTest { public UnderTest(Scope scope) { } private UnderTest() { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Constructor_Public_Private/expected/UnderTestMagnetFactory.java ================================================ package app; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { return new UnderTest(scope); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Constructor_Public_Protected/UnderTest.java ================================================ package app; import magnet.Instance; import magnet.Scope; @Instance(type = UnderTest.class) class UnderTest { public UnderTest(Scope scope) { } protected UnderTest() { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Constructor_Public_Protected/expected/UnderTestMagnetFactory.java ================================================ package app; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { return new UnderTest(scope); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Constructor_Public_Public/UnderTest.java ================================================ package app; import magnet.Instance; import magnet.Scope; @Instance(type = UnderTest.class) class UnderTest { public UnderTest(Scope scope) { } public UnderTest() { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Constructor_Public_Public_Kotlin/UnderTest.java ================================================ package app; import kotlin.Metadata; import magnet.Instance; import magnet.Scope; import org.jetbrains.annotations.NotNull; @Metadata( mv = {1, 1, 15}, bv = {1, 0, 3}, k = 1, d1 = {"\u0000\u001a\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\b\u0007\u0018\u00002\u00020\u0001B\u000f\b\u0016\u0012\u0006\u0010\u0002\u001a\u00020\u0003¢\u0006\u0002\u0010\u0004B\u000f\b\u0016\u0012\u0006\u0010\u0005\u001a\u00020\u0006¢\u0006\u0002\u0010\u0007¨\u0006\b"}, d2 = {"Lapp/UnderTest;", "", "value", "", "(Ljava/lang/String;)V", "parentScope", "Lmagnet/Scope;", "(Lmagnet/Scope;)V", "magnet-processor"} ) @Instance( type = UnderTest.class ) public final class UnderTest { public UnderTest(@NotNull String value) { super(); } public UnderTest(@NotNull Scope parentScope) { super(); } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Covariance_Constructor_ManyParameter/Foo.java ================================================ package app; public class Foo { } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Covariance_Constructor_ManyParameter/UnderTest.java ================================================ package app; import magnet.Instance; import java.util.List; @Instance(type = UnderTest.class) public class UnderTest { public UnderTest(List dep) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Covariance_Constructor_ManyParameter/expected/UnderTestMagnetFactory.java ================================================ package app; import java.util.List; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { List dep = scope.getMany(Foo.class, ""); return new UnderTest(dep); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Covariance_Constructor_SingleParameter/Foo.java ================================================ package app; public class Foo { } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Covariance_Constructor_SingleParameter/UnderTest.java ================================================ package app; import magnet.Instance; @Instance(type = UnderTest.class) public class UnderTest { public UnderTest(T dep) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/DefaultArguments_JvmOverloads_AtTheEnd/UnderTest.java ================================================ package app; import kotlin.Metadata; import magnet.Instance; import org.jetbrains.annotations.NotNull; @Metadata( mv = {1, 1, 15}, bv = {1, 0, 3}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018\u00002\u00020\u0001B!\b\u0007\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u0012\u0006\u0010\u0004\u001a\u00020\u0003\u0012\b\b\u0002\u0010\u0005\u001a\u00020\u0003¢\u0006\u0002\u0010\u0006R\u000e\u0010\u0002\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000R\u000e\u0010\u0004\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000R\u000e\u0010\u0005\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\u0007"}, d2 = {"Lapp/UnderTest;", "", "value1", "", "value2", "value3", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", "magnet-processor"} ) @Instance( type = UnderTest.class ) public final class UnderTest { public UnderTest(@NotNull String value1, @NotNull String value2, @NotNull String value3) { super(); } public UnderTest(@NotNull String value1, @NotNull String value2) { super(); } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/DefaultArguments_JvmOverloads_AtTheEnd/expected/UnderTestMagnetFactory.java ================================================ package app; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { String value1 = scope.getSingle(String.class, ""); String value2 = scope.getSingle(String.class, ""); return new UnderTest(value1, value2); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/DefaultArguments_JvmOverloads_InTheMiddle/UnderTest.java ================================================ package app; import kotlin.Metadata; import magnet.Instance; import org.jetbrains.annotations.NotNull; @Metadata( mv = {1, 1, 15}, bv = {1, 0, 3}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018\u00002\u00020\u0001B!\b\u0007\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u0012\b\b\u0002\u0010\u0004\u001a\u00020\u0003\u0012\u0006\u0010\u0005\u001a\u00020\u0003¢\u0006\u0002\u0010\u0006R\u000e\u0010\u0002\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000R\u000e\u0010\u0004\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000R\u000e\u0010\u0005\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\u0007"}, d2 = {"Lapp/UnderTest;", "", "value1", "", "value2", "value3", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", "magnet-processor"} ) @Instance( type = UnderTest.class ) public final class UnderTest { public UnderTest(@NotNull String value1, @NotNull String value2, @NotNull String value3) { super(); } public UnderTest(@NotNull String value1, @NotNull String value3) { super(); } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/DefaultArguments_JvmOverloads_InTheMiddle/expected/UnderTestMagnetFactory.java ================================================ package app; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { String value1 = scope.getSingle(String.class, ""); String value3 = scope.getSingle(String.class, ""); return new UnderTest(value1, value3); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/DefaultArguments_JvmOverloads_Mixed/UnderTest.java ================================================ package app; import kotlin.Metadata; import magnet.Instance; import org.jetbrains.annotations.NotNull; @Metadata( mv = {1, 1, 15}, bv = {1, 0, 3}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0005\b\u0007\u0018\u00002\u00020\u0001B+\b\u0007\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u0012\b\b\u0002\u0010\u0004\u001a\u00020\u0003\u0012\u0006\u0010\u0005\u001a\u00020\u0003\u0012\b\b\u0002\u0010\u0006\u001a\u00020\u0003¢\u0006\u0002\u0010\u0007R\u000e\u0010\u0002\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000R\u000e\u0010\u0004\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000R\u000e\u0010\u0005\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000R\u000e\u0010\u0006\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\b"}, d2 = {"Lapp/UnderTest;", "", "value1", "", "value2", "value3", "value4", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", "magnet-processor"} ) @Instance( type = UnderTest.class ) public final class UnderTest { public UnderTest(@NotNull String value1, @NotNull String value2, @NotNull String value3, @NotNull String value4) { super(); } public UnderTest(@NotNull String value1, @NotNull String value2, @NotNull String value3) { super(); } public UnderTest(@NotNull String value1, @NotNull String value3) { super(); } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/DefaultArguments_JvmOverloads_Mixed/expected/UnderTestMagnetFactory.java ================================================ package app; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { String value1 = scope.getSingle(String.class, ""); String value3 = scope.getSingle(String.class, ""); return new UnderTest(value1, value3); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/DisabledTab.java ================================================ package app.extension; import magnet.Instance; @Instance(type = Tab.class, disabled = true) class UnimplementedTypeTab implements Tab {} ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Executor.java ================================================ package app.extension; interface Executor { void execute(R runnable); } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/ExecutorImpl.java ================================================ package app.extension; import magnet.Instance; @Instance( type = Executor.class ) class ExecutorImpl implements Executor { @Override public void execute(Runnable runnable) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/ExecutorMaster.java ================================================ package app.extension; import magnet.Instance; @Instance(type = ExecutorMaster.class) class ExecutorMaster { public ExecutorMaster(Executor executor) {} } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Generics_GetOptional_Unchecked/Dependency.java ================================================ package app; interface Dependency { void run(); } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Generics_GetOptional_Unchecked/UnderTest.java ================================================ package app; import org.jetbrains.annotations.Nullable; import magnet.Instance; @Instance(type = UnderTest.class) class UnderTest { public UnderTest(@Nullable Dependency dependency) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Generics_GetOptional_Unchecked/expected/UnderTestMagnetFactory.java ================================================ package app; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override @SuppressWarnings("unchecked") public UnderTest create(Scope scope) { Dependency dependency = scope.getOptional(Dependency.class, ""); return new UnderTest(dependency); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Generics_GetSingle_Unchecked/Dependency.java ================================================ package app; interface Dependency { void run(); } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Generics_GetSingle_Unchecked/UnderTest.java ================================================ package app; import magnet.Instance; @Instance(type = UnderTest.class) class UnderTest { public UnderTest(Dependency dependency) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Generics_GetSingle_Unchecked/expected/UnderTestMagnetFactory.java ================================================ package app; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override @SuppressWarnings("unchecked") public UnderTest create(Scope scope) { Dependency dependency = scope.getSingle(Dependency.class, ""); return new UnderTest(dependency); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Generics_ProvideTypeWithParameter/Parameter.java ================================================ package app; class Parameter { } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Generics_ProvideTypeWithParameter/Type.java ================================================ package app; interface Type { } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Generics_ProvideTypeWithParameter/UnderTest.java ================================================ package app; import magnet.Instance; class UnderTest { @Instance(type = Type.class, classifier = "parameter-type") public static Type provideType() { return new Type() { }; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Generics_ProvideTypeWithParameter/expected/UnderTestProvideTypeMagnetFactory.java ================================================ package app; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class UnderTestProvideTypeMagnetFactory extends InstanceFactory { @Override public Type create(Scope scope) { return UnderTest.provideType(); } public static Class getType() { return Type.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Generics_ProvideTypeWithParameter_NoClassifier/Parameter.java ================================================ package app; class Parameter { } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Generics_ProvideTypeWithParameter_NoClassifier/Type.java ================================================ package app; interface Type { } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Generics_ProvideTypeWithParameter_NoClassifier/UnderTest.java ================================================ package app; import magnet.Instance; class UnderTest { @Instance(type = Type.class) public static Type provideType() { return new Type() { }; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Generics_ProvideTypeWithParameter_NoClassifier/expected/UnderTestProvideTypeMagnetFactory.java ================================================ package app; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class UnderTestProvideTypeMagnetFactory extends InstanceFactory { @Override public Type create(Scope scope) { return UnderTest.provideType(); } public static Class getType() { return Type.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/HomePage.java ================================================ package app.extension; import app.HomeRepository; import app.Page; import app.UserData; import magnet.Instance; import magnet.Scope; @Instance(type = Page.class) class HomePage implements Page { HomePage( HomeRepository homeRepository, UserData userData, Scope scope ) { } @Override public void show() { // nop } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/HomePageMenuItem.java ================================================ package app.extension; import app.MenuItem; import magnet.Instance; import magnet.Scope; @Instance( type = MenuItem.class, classifier = "main-menu" ) class HomePageMenuItem implements MenuItem { HomePageMenuItem(Scope scope) { } @Override public int getId() { return 1; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/HomePageNoParams.java ================================================ package app.extension; import app.Page; import magnet.Instance; @Instance(type = Page.class) class HomePageNoParams implements Page { HomePageNoParams() { } @Override public void show() { // nop } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/HomePageWithClassifierParams.java ================================================ package app.extension; import javax.annotation.Nullable; import app.HomeRepository; import app.Page; import app.UserData; import magnet.Instance; import magnet.Classifier; @Instance(type = Page.class) class HomePageWithClassifierParams implements Page { private static final String LOCAL = "local"; HomePageWithClassifierParams( @Nullable @Classifier(LOCAL) HomeRepository homeRepository, @Classifier("global") UserData userData ) { } @Override public void show() { // nop } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/HomePageWithGenericParam.java ================================================ package app.extension; import app.Page; import magnet.Instance; @Instance(type = Page.class) class HomePageWithGenericParam implements Page { HomePageWithGenericParam(T genericThing) { } @Override public void show() { // nop } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/HomePageWithManyParameterizedParams.java ================================================ package app.extension; import java.util.List; import javax.annotation.Nullable; import app.WorkProcessor; import app.Page; import magnet.Classifier; import magnet.Instance; @Instance(type = Page.class) class HomePageWithManyParameterizedParams implements Page { HomePageWithManyParameterizedParams( List> variant1, @Classifier("global") List> variant2, @Nullable List> variant3, @Nullable @Classifier("global") List> variant4 ) { } @Override public void show() { // nop } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/HomePageWithManyParameterizedWildcardInParams.java ================================================ package app.extension; import java.util.List; import javax.annotation.Nullable; import app.WorkProcessor; import app.Page; import magnet.Classifier; import magnet.Instance; @Instance(type = Page.class) class HomePageWithManyParameterizedWildcardInParams implements Page { HomePageWithManyParameterizedWildcardInParams( List> variant1, @Classifier("global") List> variant2, @Nullable List> variant3, @Nullable @Classifier("global") List> variant4 ) { } @Override public void show() { // nop } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/HomePageWithManyParameterizedWildcardKnownParams.java ================================================ package app.extension; import java.util.List; import javax.annotation.Nullable; import app.WorkProcessor; import app.Page; import magnet.Classifier; import magnet.Instance; @Instance(type = Page.class) class HomePageWithManyParameterizedWildcardKnownParams implements Page { HomePageWithManyParameterizedWildcardKnownParams( List> variant1, @Classifier("global") List> variant2, @Nullable List> variant3, @Nullable @Classifier("global") List> variant4 ) { } @Override public void show() { // nop } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/HomePageWithManyParameterizedWildcardOutParams.java ================================================ package app.extension; import java.util.List; import javax.annotation.Nullable; import app.WorkProcessor; import app.Page; import magnet.Classifier; import magnet.Instance; @Instance(type = Page.class) class HomePageWithManyParameterizedWildcardOutParams implements Page { HomePageWithManyParameterizedWildcardOutParams( List> variant1, @Classifier("global") List> variant2, @Nullable List> variant3, @Nullable @Classifier("global") List> variant4 ) { } @Override public void show() { // nop } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/HomePageWithManyParams.java ================================================ package app.extension; import javax.annotation.Nullable; import app.HomeRepository; import app.Page; import java.util.List; import magnet.Classifier; import magnet.Instance; @Instance(type = Page.class) class HomePageWithManyParams implements Page { HomePageWithManyParams( List variant1, @Classifier("global") List variant2, @Nullable List variant3, @Nullable @Classifier("global") List variant4 ) { } @Override public void show() { // nop } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/HomePageWithManyWildcardParams.java ================================================ package app.extension; import java.util.List; import app.HomeRepository; import app.Page; import magnet.Instance; @Instance(type = Page.class) class HomePageWithManyWildcardParams implements Page { HomePageWithManyWildcardParams( List repositories ) { } @Override public void show() { // nop } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/HomePageWithParams.java ================================================ package app.extension; import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; import app.HomeRepository; import app.Page; import app.UserData; import magnet.Instance; @Instance(type = Page.class) class HomePageWithParams implements Page { HomePageWithParams( @Nullable HomeRepository homeRepository, @NotNull UserData userData ) { } @Override public void show() { // nop } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/HomePageWithScope.java ================================================ package app.extension; import app.Page; import magnet.Instance; import magnet.Scope; @Instance(type = Page.class) class HomePageWithScope implements Page { HomePageWithScope(Scope scope) { } @Override public void show() { // nop } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/HomePageWithStaticConstructor.java ================================================ package app.extension; import java.util.List; import app.HomeRepository; import app.Page; public class HomePageWithStaticConstructor implements Page { public HomePageWithStaticConstructor( List repositories ) { } @Override public void show() { // nop } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/HomePageWithStaticConstructorDisabled.java ================================================ package app.extension.utils; import java.util.List; import app.HomeRepository; import app.Page; import app.extension.HomePageWithStaticConstructor; import magnet.Instance; import magnet.Scoping; public class HomePageWithStaticConstructorDisabled { @Instance( type = app.Page.class, scoping = Scoping.UNSCOPED, disabled = true ) public static Page create(List repositories) { return new HomePageWithStaticConstructor(repositories); } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/HomePageWithStaticConstructorSingle.java ================================================ package app.extension.utils; import java.util.List; import app.HomeRepository; import app.Page; import app.extension.HomePageWithStaticConstructor; import magnet.Instance; import magnet.Scoping; public class HomePageWithStaticConstructorSingle { @Instance( type = app.Page.class, scoping = Scoping.UNSCOPED ) public static Page create(List repositories) { return new HomePageWithStaticConstructor(repositories); } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/HomeRepository.java ================================================ package app; public interface HomeRepository { String[] getHomePageData(); } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Constructor_ManyParameter/UnderTest.java ================================================ package app; import kotlin.Lazy; import kotlin.Metadata; import magnet.Instance; import org.jetbrains.annotations.NotNull; import java.util.List; @Metadata( mv = {1, 1, 13}, bv = {1, 0, 3}, k = 1, d1 = {"\u0000\u001a\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\u0010 \n\u0002\u0010\u000e\n\u0002\b\u0002\b\u0007\u0018\u00002\u00020\u0001B\u0019\u0012\u0012\u0010\u0002\u001a\u000e\u0012\n\u0012\b\u0012\u0004\u0012\u00020\u00050\u00040\u0003¢\u0006\u0002\u0010\u0006¨\u0006\u0007"}, d2 = {"Lapp/UnderTest;", "", "dep", "Lkotlin/Lazy;", "", "", "(Lkotlin/Lazy;)V", "magnet-processor"} ) @Instance(type = UnderTest.class) public class UnderTest { public UnderTest(@NotNull Lazy> dep) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Constructor_ManyParameter/expected/UnderTestMagnetFactory.java ================================================ package app; import java.util.List; import kotlin.Lazy; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; import magnet.internal.ManyLazy; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { Lazy> dep = new ManyLazy(scope, String.class, ""); return new UnderTest(dep); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Constructor_ManyParameter_NullableGenericType/UnderTest.java ================================================ package app; import kotlin.Lazy; import kotlin.Metadata; import magnet.Instance; import org.jetbrains.annotations.NotNull; import java.util.List; @Metadata( mv = {1, 1, 13}, bv = {1, 0, 3}, k = 1, d1 = {"\u0000\u001a\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\u0010 \n\u0002\u0010\u000e\n\u0002\b\u0002\b\u0007\u0018\u00002\u00020\u0001B\u001b\u0012\u0014\u0010\u0002\u001a\u0010\u0012\f\u0012\n\u0012\u0006\u0012\u0004\u0018\u00010\u00050\u00040\u0003¢\u0006\u0002\u0010\u0006¨\u0006\u0007"}, d2 = {"Lapp/UnderTest;", "", "dep", "Lkotlin/Lazy;", "", "", "(Lkotlin/Lazy;)V", "de.halfbit.magnet-processor.main"} ) @Instance(type = UnderTest.class) public class UnderTest { public UnderTest(@NotNull Lazy> dep) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Constructor_ManyParameter_NullableListType/UnderTest.java ================================================ package app; import kotlin.Lazy; import kotlin.Metadata; import magnet.Instance; import org.jetbrains.annotations.NotNull; import java.util.List; @Metadata( mv = {1, 1, 13}, bv = {1, 0, 3}, k = 1, d1 = {"\u0000\u001a\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\u0010 \n\u0002\u0010\u000e\n\u0002\b\u0002\b\u0007\u0018\u00002\u00020\u0001B\u001b\u0012\u0014\u0010\u0002\u001a\u0010\u0012\f\u0012\n\u0012\u0004\u0012\u00020\u0005\u0018\u00010\u00040\u0003¢\u0006\u0002\u0010\u0006¨\u0006\u0007"}, d2 = {"Lapp/UnderTest;", "", "dep", "Lkotlin/Lazy;", "", "", "(Lkotlin/Lazy;)V", "de.halfbit.magnet-processor.main"} ) @Instance(type = UnderTest.class) public class UnderTest { public UnderTest(@NotNull Lazy> dep) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Constructor_ManyParameter_Wildcard/UnderTest.java ================================================ package app; import kotlin.Lazy; import kotlin.Metadata; import magnet.Instance; import org.jetbrains.annotations.NotNull; import java.util.List; @Metadata( mv = {1, 1, 15}, bv = {1, 0, 3}, k = 1, d1 = {"\u0000\u001a\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\u0010 \n\u0002\u0010\u000e\n\u0002\b\u0002\b\u0007\u0018\u00002\u00020\u0001B\u0019\u0012\u0012\u0010\u0002\u001a\u000e\u0012\n\u0012\b\u0012\u0004\u0012\u00020\u00050\u00040\u0003¢\u0006\u0002\u0010\u0006¨\u0006\u0007"}, d2 = {"Lapp/UnderTest;", "", "dep", "Lkotlin/Lazy;", "", "", "(Lkotlin/Lazy;)V", "magnet-processor"} ) @Instance(type = UnderTest.class) public class UnderTest { public UnderTest(@NotNull Lazy> dep) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Constructor_ManyParameter_Wildcard/expected/UnderTestMagnetFactory.java ================================================ package app; import java.util.List; import kotlin.Lazy; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; import magnet.internal.ManyLazy; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { Lazy> dep = new ManyLazy(scope, String.class, ""); return new UnderTest(dep); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Constructor_NoKotlinMetadata/UnderTest.java ================================================ package app; import kotlin.Lazy; import magnet.Instance; import org.jetbrains.annotations.NotNull; @Instance(type = UnderTest.class) public class UnderTest { public UnderTest(@NotNull Lazy dep) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Constructor_OptionalParameter/UnderTest.java ================================================ package app; import kotlin.Lazy; import kotlin.Metadata; import magnet.Instance; import org.jetbrains.annotations.NotNull; @Metadata( mv = {1, 1, 13}, bv = {1, 0, 3}, k = 1, d1 = {"\u0000\u0016\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\u0010\u000e\n\u0002\b\u0002\u0018\u00002\u00020\u0001B\u0015\u0012\u000e\u0010\u0002\u001a\n\u0012\u0006\u0012\u0004\u0018\u00010\u00040\u0003¢\u0006\u0002\u0010\u0005¨\u0006\u0006"}, d2 = {"Lapp/UnderTest;", "", "dep", "Lkotlin/Lazy;", "", "(Lkotlin/Lazy;)V", "magnet-processor"} ) @Instance(type = UnderTest.class) public class UnderTest { public UnderTest(@NotNull Lazy dep) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Constructor_OptionalParameter/expected/UnderTestMagnetFactory.java ================================================ package app; import kotlin.Lazy; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; import magnet.internal.OptionalLazy; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { Lazy dep = new OptionalLazy(scope, String.class, ""); return new UnderTest(dep); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Constructor_OptionalParameter_Wildcard/UnderTest.java ================================================ package app; import kotlin.Lazy; import kotlin.Metadata; import magnet.Instance; import org.jetbrains.annotations.NotNull; @Metadata( mv = {1, 1, 13}, bv = {1, 0, 3}, k = 1, d1 = {"\u0000\u0016\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\u0010\u000e\n\u0002\b\u0002\u0018\u00002\u00020\u0001B\u0015\u0012\u000e\u0010\u0002\u001a\n\u0012\u0006\u0012\u0004\u0018\u00010\u00040\u0003¢\u0006\u0002\u0010\u0005¨\u0006\u0006"}, d2 = {"Lapp/UnderTest;", "", "dep", "Lkotlin/Lazy;", "", "(Lkotlin/Lazy;)V", "magnet-processor"} ) @Instance(type = UnderTest.class) public class UnderTest { public UnderTest(@NotNull Lazy dep) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Constructor_OptionalParameter_Wildcard/expected/UnderTestMagnetFactory.java ================================================ package app; import kotlin.Lazy; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; import magnet.internal.OptionalLazy; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { Lazy dep = new OptionalLazy(scope, String.class, ""); return new UnderTest(dep); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Constructor_SingleParameter/UnderTest.java ================================================ package app; import kotlin.Lazy; import kotlin.Metadata; import magnet.Instance; import org.jetbrains.annotations.NotNull; @Metadata( mv = {1, 1, 13}, bv = {1, 0, 3}, k = 1, d1 = {"\u0000\u0016\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\u0010\u000e\n\u0002\b\u0002\u0018\u00002\u00020\u0001B\u0013\u0012\f\u0010\u0002\u001a\b\u0012\u0004\u0012\u00020\u00040\u0003¢\u0006\u0002\u0010\u0005¨\u0006\u0006"}, d2 = {"Lapp/UnderTest;", "", "dep", "Lkotlin/Lazy;", "", "(Lkotlin/Lazy;)V", "magnet-processor"} ) @Instance(type = UnderTest.class) public class UnderTest { public UnderTest(@NotNull Lazy dep) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Constructor_SingleParameter/expected/UnderTestMagnetFactory.java ================================================ package app; import kotlin.Lazy; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; import magnet.internal.SingleLazy; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { Lazy dep = new SingleLazy(scope, String.class, ""); return new UnderTest(dep); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Constructor_SingleParameter_ParameterizedType/Foo.java ================================================ package app; public class Foo { } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Constructor_SingleParameter_ParameterizedType/UnderTest.java ================================================ package app; import kotlin.Lazy; import kotlin.Metadata; import magnet.Instance; import org.jetbrains.annotations.NotNull; @Metadata( mv = {1, 1, 13}, bv = {1, 0, 3}, k = 1, d1 = {"\u0000\u001a\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\u0010\u000e\n\u0002\b\u0002\b\u0007\u0018\u00002\u00020\u0001B\u0019\u0012\u0012\u0010\u0002\u001a\u000e\u0012\n\u0012\b\u0012\u0004\u0012\u00020\u00050\u00040\u0003¢\u0006\u0002\u0010\u0006¨\u0006\u0007"}, d2 = {"Lapp/UnderTest;", "", "dep", "Lkotlin/Lazy;", "Lapp/Foo;", "", "(Lkotlin/Lazy;)V", "de.halfbit.magnet-processor.main"} ) @Instance(type = UnderTest.class) public class UnderTest { public UnderTest(@NotNull Lazy> dep) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Constructor_SingleParameter_ParameterizedType/expected/UnderTestMagnetFactory.java ================================================ package app; import kotlin.Lazy; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; import magnet.internal.SingleLazy; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { Lazy> dep = new SingleLazy(scope, Foo.class, ""); return new UnderTest(dep); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Constructor_SingleParameter_Wildcard/UnderTest.java ================================================ package app; import kotlin.Lazy; import kotlin.Metadata; import magnet.Instance; import org.jetbrains.annotations.NotNull; @Metadata( mv = {1, 1, 13}, bv = {1, 0, 3}, k = 1, d1 = {"\u0000\u0016\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\u0010\u000e\n\u0002\b\u0002\u0018\u00002\u00020\u0001B\u0013\u0012\f\u0010\u0002\u001a\b\u0012\u0004\u0012\u00020\u00040\u0003¢\u0006\u0002\u0010\u0005¨\u0006\u0006"}, d2 = {"Lapp/UnderTest;", "", "dep", "Lkotlin/Lazy;", "", "(Lkotlin/Lazy;)V", "magnet-processor"} ) @Instance(type = UnderTest.class) public class UnderTest { public UnderTest(@NotNull Lazy dep) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Constructor_SingleParameter_Wildcard/expected/UnderTestMagnetFactory.java ================================================ package app; import kotlin.Lazy; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; import magnet.internal.SingleLazy; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { Lazy dep = new SingleLazy(scope, String.class, ""); return new UnderTest(dep); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Method_NoKotlinMetadata/UnderTest.java ================================================ package app; import kotlin.Lazy; import magnet.Instance; import org.jetbrains.annotations.NotNull; public class UnderTest { @Instance(type = UnderTest.class) public static UnderTest provideUnderTest(@NotNull Lazy dep) { return UnderTest(); } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Method_OptionalParameter/UnderTest.java ================================================ package app; import kotlin.Lazy; import kotlin.Metadata; import magnet.Instance; import org.jetbrains.annotations.NotNull; @Metadata( mv = {1, 1, 13}, bv = {1, 0, 3}, k = 2, d1 = {"\u0000\u0012\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\u0010\u000e\n\u0000\u001a\u0018\u0010\u0000\u001a\u00020\u00012\u000e\u0010\u0002\u001a\n\u0012\u0006\u0012\u0004\u0018\u00010\u00040\u0003H\u0007¨\u0006\u0005"}, d2 = {"provideUnderTest", "Lapp/UnderTest;", "dep", "Lkotlin/Lazy;", "", "magnet-processor"} ) public class UnderTest { @Instance(type = UnderTest.class) public static UnderTest provideUnderTest(@NotNull Lazy dep) { return new UnderTest(); } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Method_OptionalParameter/expected/UnderTestProvideUnderTestDepMagnetFactory.java ================================================ package app; import kotlin.Lazy; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; import magnet.internal.OptionalLazy; @Generated public final class UnderTestProvideUnderTestDepMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { Lazy dep = new OptionalLazy(scope, String.class, ""); return UnderTest.provideUnderTest(dep); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Method_OptionalParameter_Wildcard/UnderTest.java ================================================ package app; import kotlin.Lazy; import kotlin.Metadata; import magnet.Instance; import org.jetbrains.annotations.NotNull; @Metadata( mv = {1, 1, 13}, bv = {1, 0, 3}, k = 2, d1 = {"\u0000\u0012\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\u0010\u000e\n\u0000\u001a\u0018\u0010\u0000\u001a\u00020\u00012\u000e\u0010\u0002\u001a\n\u0012\u0006\u0012\u0004\u0018\u00010\u00040\u0003H\u0007¨\u0006\u0005"}, d2 = {"provideUnderTest", "Lapp/UnderTest;", "dep", "Lkotlin/Lazy;", "", "magnet-processor"} ) public class UnderTest { @Instance(type = UnderTest.class) public static UnderTest provideUnderTest(@NotNull Lazy dep) { return new UnderTest(); } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Method_OptionalParameter_Wildcard/expected/UnderTestProvideUnderTestDepMagnetFactory.java ================================================ package app; import kotlin.Lazy; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; import magnet.internal.OptionalLazy; @Generated public final class UnderTestProvideUnderTestDepMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { Lazy dep = new OptionalLazy(scope, String.class, ""); return UnderTest.provideUnderTest(dep); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Method_SingleParameter/UnderTest.java ================================================ package app; import kotlin.Lazy; import kotlin.Metadata; import magnet.Instance; import org.jetbrains.annotations.NotNull; @Metadata( mv = {1, 1, 13}, bv = {1, 0, 3}, k = 2, d1 = {"\u0000\u0012\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\u0010\u000e\n\u0000\u001a\u0016\u0010\u0000\u001a\u00020\u00012\f\u0010\u0002\u001a\b\u0012\u0004\u0012\u00020\u00040\u0003H\u0007¨\u0006\u0005"}, d2 = {"provideUnderTest", "Lapp/UnderTest;", "dep", "Lkotlin/Lazy;", "", "magnet-processor"} ) public class UnderTest { @Instance(type = UnderTest.class) public static UnderTest provideUnderTest(@NotNull Lazy dep) { return new UnderTest(); } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Method_SingleParameter/expected/UnderTestProvideUnderTestDepMagnetFactory.java ================================================ package app; import kotlin.Lazy; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; import magnet.internal.SingleLazy; @Generated public final class UnderTestProvideUnderTestDepMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { Lazy dep = new SingleLazy(scope, String.class, ""); return UnderTest.provideUnderTest(dep); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Method_SingleParameter_Wildcard/UnderTest.java ================================================ package app; import kotlin.Lazy; import kotlin.Metadata; import magnet.Instance; import org.jetbrains.annotations.NotNull; @Metadata( mv = {1, 1, 13}, bv = {1, 0, 3}, k = 2, d1 = {"\u0000\u0012\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\u0010\u000e\n\u0000\u001a\u0016\u0010\u0000\u001a\u00020\u00012\f\u0010\u0002\u001a\b\u0012\u0004\u0012\u00020\u00040\u0003H\u0007¨\u0006\u0005"}, d2 = {"provideUnderTest", "Lapp/UnderTest;", "dep", "Lkotlin/Lazy;", "", "magnet-processor"} ) public class UnderTest { @Instance(type = UnderTest.class) public static UnderTest provideUnderTest(@NotNull Lazy dep) { return new UnderTest(); } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Lazy_Method_SingleParameter_Wildcard/expected/UnderTestProvideUnderTestDepMagnetFactory.java ================================================ package app; import kotlin.Lazy; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; import magnet.internal.SingleLazy; @Generated public final class UnderTestProvideUnderTestDepMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { Lazy dep = new SingleLazy(scope, String.class, ""); return UnderTest.provideUnderTest(dep); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Limit_Empty_NoGetter/UnderTest.java ================================================ package app; import magnet.Instance; import magnet.Scope; @Instance( type = UnderTest.class, limitedTo = "" ) public class UnderTest { public UnderTest(Scope scope) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Limit_Empty_NoGetter/expected/UnderTestMagnetFactory.java ================================================ package app; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { return new UnderTest(scope); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Limit_NotEmpty_HasGetter/UnderTest.java ================================================ package app; import magnet.Instance; import magnet.Scope; @Instance( type = UnderTest.class, limitedTo = "activity" ) public class UnderTest { public UnderTest(Scope scope) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Limit_NotEmpty_HasGetter/expected/UnderTestMagnetFactory.java ================================================ package app; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { return new UnderTest(scope); } @Override public String getLimit() { return "activity"; } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Limit_ReservedAsterisks_Fails/UnderTest.java ================================================ package app; import magnet.Instance; import magnet.Scope; @Instance( type = UnderTest.class, limitedTo = "*" ) public class UnderTest { public UnderTest(Scope scope) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Limit_ScopingDirect_GeneratesGetter/UnderTest.java ================================================ package app; import magnet.Instance; import magnet.Scope; import magnet.Scoping; @Instance( type = UnderTest.class, scoping = Scoping.DIRECT, limitedTo = "activity" ) public class UnderTest { public UnderTest(Scope scope) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Limit_ScopingDirect_GeneratesGetter/expected/UnderTestMagnetFactory.java ================================================ package app; import magnet.Scope; import magnet.Scoping; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { return new UnderTest(scope); } @Override public Scoping getScoping() { return Scoping.DIRECT; } @Override public String getLimit() { return "activity"; } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Limit_ScopingUnscoped_Fails/UnderTest.java ================================================ package app; import magnet.Instance; import magnet.Scope; import magnet.Scoping; @Instance( type = UnderTest.class, scoping = Scoping.UNSCOPED, limitedTo = "activity" ) public class UnderTest { public UnderTest(Scope scope) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/MenuItem.java ================================================ package app; public interface MenuItem { int getId(); } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Page.java ================================================ package app; public interface Page { void show(); } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/ScopeParameter_CustomName/UnderTest.java ================================================ package app; import magnet.Instance; import magnet.Scope; @Instance(type = UnderTest.class) public class UnderTest { public UnderTest(Scope parentScope) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/ScopeParameter_CustomName/expected/UnderTestMagnetFactory.java ================================================ package app; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { return new UnderTest(scope); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/ScopeParameter_CustomName_KotlinClass/UnderTest.java ================================================ package app; import kotlin.Metadata; import magnet.Instance; import magnet.Scope; import org.jetbrains.annotations.NotNull; @Metadata( mv = {1, 1, 15}, bv = {1, 0, 3}, k = 1, d1 = {"\u0000\u0012\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\b\u0007\u0018\u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u00020\u0003¢\u0006\u0002\u0010\u0004R\u000e\u0010\u0002\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\u0005"}, d2 = {"Lapp/UnderTest;", "", "parentScope", "Lmagnet/Scope;", "(Lmagnet/Scope;)V", "magnet-processor"} ) @Instance( type = UnderTest.class ) public final class UnderTest { public UnderTest(@NotNull Scope parentScope) { super(); } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/ScopeParameter_CustomName_KotlinClass/expected/UnderTestMagnetFactory.java ================================================ package app; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { return new UnderTest(scope); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/ScopeParameter_DefaultName/UnderTest.java ================================================ package app; import magnet.Instance; import magnet.Scope; @Instance(type = UnderTest.class) public class UnderTest { public UnderTest(Scope scope) { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/ScopeParameter_DefaultName/expected/UnderTestMagnetFactory.java ================================================ package app; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { return new UnderTest(scope); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/StaticMethodNeedsDependencyWithClassifier/Constants.java ================================================ package app; public final class Constants { public static final String APPLICATION = "application"; } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/StaticMethodNeedsDependencyWithClassifier/Input.java ================================================ package app; public interface Input { } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/StaticMethodNeedsDependencyWithClassifier/Output.java ================================================ package app; public class Output { } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/StaticMethodNeedsDependencyWithClassifier/StaticFunction.java ================================================ package app; import magnet.Classifier; import magnet.Instance; public class StaticFunction { @Instance(type = Output.class) public static Output provide(@Classifier(Constants.APPLICATION) Input input) { return new Output(); } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/StaticMethodNeedsDependencyWithClassifier/generated/StaticFunctionProvideInputMagnetFactory.java ================================================ package app; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class StaticFunctionProvideInputMagnetFactory extends InstanceFactory { @Override public Output create(Scope scope) { Input input = scope.getSingle(Input.class, "application"); return StaticFunction.provide(input); } public static Class getType() { return Output.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/StaticMethodProvidesInnerClass/PowerManager.java ================================================ package app; public class PowerManager { public static class WakeLock { public void aquire(Long timeout) {} public void release() {} } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/StaticMethodProvidesInnerClass/PowerManagerProvider.java ================================================ package app; import magnet.Instance; public class PowerManagerProvider { @Instance(type = PowerManager.WakeLock.class) public static PowerManager.WakeLock provideWakeLock() { return new PowerManager.WakeLock(); } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/StaticMethodProvidesInnerClass/expected/PowerManagerProviderProvideWakeLockMagnetFactory.java ================================================ package app; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class PowerManagerProviderProvideWakeLockMagnetFactory extends InstanceFactory { @Override public PowerManager.WakeLock create(Scope scope) { return PowerManagerProvider.provideWakeLock(); } public static Class getType() { return PowerManager.WakeLock.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/Tab.java ================================================ package app.extension; interface Tab {} ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/TypeAutoDetect_ExtendsObjectNoInterfaces/UnderTest.java ================================================ package app; import org.jetbrains.annotations.Nullable; import magnet.Instance; @Instance class UnderTest { public UnderTest() { } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/TypeAutoDetect_ExtendsObjectNoInterfaces/expected/UnderTestMagnetFactory.java ================================================ package app; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class UnderTestMagnetFactory extends InstanceFactory { @Override public UnderTest create(Scope scope) { return new UnderTest(); } public static Class getType() { return UnderTest.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/UnimplementedTab.java ================================================ package app.extension; import magnet.Instance; @Instance(type = Tab.class) class UnimplementedTypeTab {} ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/UserData.java ================================================ package app; public interface UserData { String getFullName(); String getEmail(); } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/UserPage.java ================================================ package app.extension; import app.Page; import magnet.Instance; import magnet.Scope; @Instance(type = Page.class) class UserPage implements Page { UserPage(Scope registry) { } @Override public void show() { // nop } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/UserPageMenuItem.java ================================================ package app.extension; import app.MenuItem; import magnet.Instance; import magnet.Scope; @Instance( type = MenuItem.class, classifier = "extended-menu" ) class UserPageMenuItem implements MenuItem { UserPageMenuItem(Scope registry) { } @Override public int getId() { return 0; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/WorkProcessor.java ================================================ package app; public interface WorkProcessor { void processWork(I processor); } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/generated/ForInterfaceWithGenericType_ExecutorMagnetFactory.java ================================================ package app.extension; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class ExecutorImplMagnetFactory extends InstanceFactory { @Override public Executor create(Scope scope) { return new ExecutorImpl(); } public static Class getType() { return Executor.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/generated/HomePageMagnetFactory.java ================================================ package app.extension; import app.HomeRepository; import app.Page; import app.UserData; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class HomePageMagnetFactory extends InstanceFactory { @Override public Page create(Scope scope) { HomeRepository homeRepository = scope.getSingle(HomeRepository.class, ""); UserData userData = scope.getSingle(UserData.class, ""); return new HomePage(homeRepository, userData, scope); } public static Class getType() { return Page.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/generated/HomePageNoParamsMagnetFactory.java ================================================ package app.extension; import app.Page; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class HomePageNoParamsMagnetFactory extends InstanceFactory { @Override public Page create(Scope scope) { return new HomePageNoParams(); } public static Class getType() { return Page.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/generated/HomePageWithClassifierParamsMagnetFactory.java ================================================ package app.extension; import app.HomeRepository; import app.Page; import app.UserData; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class HomePageWithClassifierParamsMagnetFactory extends InstanceFactory { @Override public Page create(Scope scope) { HomeRepository homeRepository = scope.getOptional(HomeRepository.class, "local"); UserData userData = scope.getSingle(UserData.class, "global"); return new HomePageWithClassifierParams(homeRepository, userData); } public static Class getType() { return Page.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/generated/HomePageWithManyParameterizedParamsMagnetFactory.java ================================================ package app.extension; import app.Page; import app.WorkProcessor; import java.util.List; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class HomePageWithManyParameterizedParamsMagnetFactory extends InstanceFactory { @Override @SuppressWarnings("unchecked") public Page create(Scope scope) { List variant1 = scope.getMany(WorkProcessor.class, ""); List variant2 = scope.getMany(WorkProcessor.class, "global"); List variant3 = scope.getMany(WorkProcessor.class, ""); List variant4 = scope.getMany(WorkProcessor.class, "global"); return new HomePageWithManyParameterizedParams(variant1, variant2, variant3, variant4); } public static Class getType() { return Page.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/generated/HomePageWithManyParameterizedWildcardInParamsMagnetFactory.java ================================================ package app.extension; import app.Page; import app.WorkProcessor; import java.util.List; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class HomePageWithManyParameterizedWildcardInParamsMagnetFactory extends InstanceFactory { @Override @SuppressWarnings("unchecked") public Page create(Scope scope) { List variant1 = scope.getMany(WorkProcessor.class, ""); List variant2 = scope.getMany(WorkProcessor.class, "global"); List variant3 = scope.getMany(WorkProcessor.class, ""); List variant4 = scope.getMany(WorkProcessor.class, "global"); return new HomePageWithManyParameterizedWildcardInParams(variant1, variant2, variant3, variant4); } public static Class getType() { return Page.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/generated/HomePageWithManyParameterizedWildcardKnownParamsMagnetFactory.java ================================================ package app.extension; import app.Page; import app.WorkProcessor; import java.util.List; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class HomePageWithManyParameterizedWildcardKnownParamsMagnetFactory extends InstanceFactory { @Override @SuppressWarnings("unchecked") public Page create(Scope scope) { List variant1 = scope.getMany(WorkProcessor.class, ""); List variant2 = scope.getMany(WorkProcessor.class, "global"); List variant3 = scope.getMany(WorkProcessor.class, ""); List variant4 = scope.getMany(WorkProcessor.class, "global"); return new HomePageWithManyParameterizedWildcardKnownParams(variant1, variant2, variant3, variant4); } public static Class getType() { return Page.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/generated/HomePageWithManyParameterizedWildcardOutParamsMagnetFactory.java ================================================ package app.extension; import app.Page; import app.WorkProcessor; import java.util.List; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class HomePageWithManyParameterizedWildcardOutParamsMagnetFactory extends InstanceFactory { @Override @SuppressWarnings("unchecked") public Page create(Scope scope) { List variant1 = scope.getMany(WorkProcessor.class, ""); List variant2 = scope.getMany(WorkProcessor.class, "global"); List variant3 = scope.getMany(WorkProcessor.class, ""); List variant4 = scope.getMany(WorkProcessor.class, "global"); return new HomePageWithManyParameterizedWildcardOutParams(variant1, variant2, variant3, variant4); } public static Class getType() { return Page.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/generated/HomePageWithManyParamsMagnetFactory.java ================================================ package app.extension; import app.HomeRepository; import app.Page; import java.util.List; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class HomePageWithManyParamsMagnetFactory extends InstanceFactory { @Override public Page create(Scope scope) { List variant1 = scope.getMany(HomeRepository.class, ""); List variant2 = scope.getMany(HomeRepository.class, "global"); List variant3 = scope.getMany(HomeRepository.class, ""); List variant4 = scope.getMany(HomeRepository.class, "global"); return new HomePageWithManyParams(variant1, variant2, variant3, variant4); } public static Class getType() { return Page.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/generated/HomePageWithManyWildcardParamsMagnetFactory.java ================================================ package app.extension; import app.HomeRepository; import app.Page; import java.util.List; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class HomePageWithManyWildcardParamsMagnetFactory extends InstanceFactory { @Override public Page create(Scope scope) { List repositories = scope.getMany(HomeRepository.class, ""); return new HomePageWithManyWildcardParams(repositories); } public static Class getType() { return Page.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/generated/HomePageWithParamsMagnetFactory.java ================================================ package app.extension; import app.HomeRepository; import app.Page; import app.UserData; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class HomePageWithParamsMagnetFactory extends InstanceFactory { @Override public Page create(Scope scope) { HomeRepository homeRepository = scope.getOptional(HomeRepository.class, ""); UserData userData = scope.getSingle(UserData.class, ""); return new HomePageWithParams(homeRepository, userData); } public static Class getType() { return Page.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/generated/HomePageWithScopeMagnetFactory.java ================================================ package app.extension; import app.Page; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class HomePageWithScopeMagnetFactory extends InstanceFactory { @Override public Page create(Scope scope) { return new HomePageWithScope(scope); } public static Class getType() { return Page.class; } } ================================================ FILE: magnet-processor/src/test/resources/MagnetProcessorTest/generated/HomePageWithStaticConstructorSingleCreateRepositoriesMagnetFactory.java ================================================ package app.extension.utils; import app.HomeRepository; import app.Page; import java.util.List; import magnet.Scope; import magnet.Scoping; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class HomePageWithStaticConstructorSingleCreateRepositoriesMagnetFactory extends InstanceFactory { @Override public Page create(Scope scope) { List repositories = scope.getMany(HomeRepository.class, ""); return HomePageWithStaticConstructorSingle.create(repositories); } @Override public Scoping getScoping() { return Scoping.UNSCOPED; } public static Class getType() { return Page.class; } } ================================================ FILE: magnet-processor/src/test/resources/SelectorInFactoryClassTest/Implementation1.java ================================================ package selector; import magnet.Instance; @Instance( types = Interface.class, selector = "" ) class Implementation1 implements Interface {} ================================================ FILE: magnet-processor/src/test/resources/SelectorInFactoryClassTest/Implementation10.java ================================================ package selector; import magnet.Instance; @Instance( types = Interface.class, selector = "android.api !in 5..10" ) class Implementation10 implements Interface {} ================================================ FILE: magnet-processor/src/test/resources/SelectorInFactoryClassTest/Implementation2.java ================================================ package selector; import magnet.Instance; @Instance( types = Interface.class, selector = "kaboom" ) class Implementation2 implements Interface {} ================================================ FILE: magnet-processor/src/test/resources/SelectorInFactoryClassTest/Implementation3.java ================================================ package selector; import magnet.Instance; @Instance( types = Interface.class, selector = ".api > 25" ) class Implementation3 implements Interface {} ================================================ FILE: magnet-processor/src/test/resources/SelectorInFactoryClassTest/Implementation4.java ================================================ package selector; import magnet.Instance; @Instance( types = Interface.class, selector = "android. > 25" ) class Implementation4 implements Interface {} ================================================ FILE: magnet-processor/src/test/resources/SelectorInFactoryClassTest/Implementation5.java ================================================ package selector; import magnet.Instance; @Instance( types = Interface.class, selector = "android.api kaboom 25" ) class Implementation5 implements Interface {} ================================================ FILE: magnet-processor/src/test/resources/SelectorInFactoryClassTest/Implementation6.java ================================================ package selector; import magnet.Instance; @Instance( types = Interface.class, selector = "android.api >" ) class Implementation6 implements Interface {} ================================================ FILE: magnet-processor/src/test/resources/SelectorInFactoryClassTest/Implementation7.java ================================================ package selector; import magnet.Instance; @Instance( types = Interface.class, selector = "android.api >= 28" ) class Implementation7 implements Interface {} ================================================ FILE: magnet-processor/src/test/resources/SelectorInFactoryClassTest/Implementation8.java ================================================ package selector; import magnet.Instance; @Instance( types = Interface.class, selector = "android.api in 1..19" ) class Implementation8 implements Interface {} ================================================ FILE: magnet-processor/src/test/resources/SelectorInFactoryClassTest/Implementation9.java ================================================ package selector; import magnet.Instance; @Instance( types = Interface.class, selector = "android.api != 19" ) class Implementation9 implements Interface {} ================================================ FILE: magnet-processor/src/test/resources/SelectorInFactoryClassTest/Interface.java ================================================ package selector; interface Interface {} ================================================ FILE: magnet-processor/src/test/resources/SelectorInFactoryClassTest/generated/Implementation10MagnetFactory.java ================================================ package selector; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class Implementation10MagnetFactory extends InstanceFactory { private static String[] SELECTOR = { "android", "api", "!in", "5", "10" }; @Override public Interface create(Scope scope) { return new Implementation10(); } @Override public String[] getSelector() { return SELECTOR; } public static Class getType() { return Interface.class; } } ================================================ FILE: magnet-processor/src/test/resources/SelectorInFactoryClassTest/generated/Implementation1MagnetFactory.java ================================================ package selector; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class Implementation1MagnetFactory extends InstanceFactory { @Override public Interface create(Scope scope) { return new Implementation1(); } public static Class getType() { return Interface.class; } } ================================================ FILE: magnet-processor/src/test/resources/SelectorInFactoryClassTest/generated/Implementation7MagnetFactory.java ================================================ package selector; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class Implementation7MagnetFactory extends InstanceFactory { private static String[] SELECTOR = { "android", "api", ">=", "28" }; @Override public Interface create(Scope scope) { return new Implementation7(); } @Override public String[] getSelector() { return SELECTOR; } public static Class getType() { return Interface.class; } } ================================================ FILE: magnet-processor/src/test/resources/SelectorInFactoryClassTest/generated/Implementation8MagnetFactory.java ================================================ package selector; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class Implementation8MagnetFactory extends InstanceFactory { private static String[] SELECTOR = { "android", "api", "in", "1", "19" }; @Override public Interface create(Scope scope) { return new Implementation8(); } @Override public String[] getSelector() { return SELECTOR; } public static Class getType() { return Interface.class; } } ================================================ FILE: magnet-processor/src/test/resources/SelectorInFactoryClassTest/generated/Implementation9MagnetFactory.java ================================================ package selector; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class Implementation9MagnetFactory extends InstanceFactory { private static String[] SELECTOR = { "android", "api", "!=", "19" }; @Override public Interface create(Scope scope) { return new Implementation9(); } @Override public String[] getSelector() { return SELECTOR; } public static Class getType() { return Interface.class; } } ================================================ FILE: magnet-processor/src/test/resources/SiblingTypesTest/Implementation1.java ================================================ package siblings; import magnet.Instance; @Instance() class Implementation1 implements Interface1, Interface2 {} ================================================ FILE: magnet-processor/src/test/resources/SiblingTypesTest/Implementation2.java ================================================ package siblings; import magnet.Instance; @Instance(type = Interface1.class, types = {Interface1.class, Interface2.class}) class Implementation1 implements Interface1, Interface2 {} ================================================ FILE: magnet-processor/src/test/resources/SiblingTypesTest/Implementation3.java ================================================ package siblings; import magnet.Instance; @Instance(type = Interface1.class, types = {Interface1.class, Interface2.class}) class Implementation1 implements Interface1 {} ================================================ FILE: magnet-processor/src/test/resources/SiblingTypesTest/Implementation4.java ================================================ package siblings; import magnet.Instance; @Instance(types = {Interface1.class, Interface2.class}) class Implementation4 implements Interface1, Interface2 {} ================================================ FILE: magnet-processor/src/test/resources/SiblingTypesTest/Implementation5.java ================================================ package siblings; import magnet.Instance; import magnet.Scoping; @Instance(types = {Interface1.class, Interface2.class}, scoping = Scoping.UNSCOPED) class Implementation5 implements Interface1, Interface2 {} ================================================ FILE: magnet-processor/src/test/resources/SiblingTypesTest/Interface1.java ================================================ package siblings; interface Interface1 {} ================================================ FILE: magnet-processor/src/test/resources/SiblingTypesTest/Interface2.java ================================================ package siblings; interface Interface2 {} ================================================ FILE: magnet-processor/src/test/resources/SiblingTypesTest/generated/Implementation4Interface1MagnetFactory.java ================================================ package siblings; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class Implementation4Interface1MagnetFactory extends InstanceFactory { private static Class[] SIBLING_TYPES = {Interface2.class, Implementation4Interface2MagnetFactory.class}; @Override public Interface1 create(Scope scope) { return new Implementation4(); } @Override public Class[] getSiblingTypes() { return SIBLING_TYPES } public static Class getType() { return Interface1.class; } } ================================================ FILE: magnet-processor/src/test/resources/SiblingTypesTest/generated/Implementation4Interface2MagnetFactory.java ================================================ package siblings; import magnet.Scope; import magnet.internal.Generated; import magnet.internal.InstanceFactory; @Generated public final class Implementation4Interface2MagnetFactory extends InstanceFactory { private static Class[] SIBLING_TYPES = {Interface1.class, Implementation4Interface1MagnetFactory.class}; @Override public Interface2 create(Scope scope) { return new Implementation4(); } @Override public Class[] getSiblingTypes() { return SIBLING_TYPES; } public static Class getType() { return Interface2.class; } } ================================================ FILE: magnetx-app/build.gradle ================================================ plugins { id 'kotlin' id 'kotlin-kapt' id 'com.vanniktech.maven.publish' } compileKotlin { kotlinOptions { jvmTarget = javaVersion } } compileTestKotlin { kotlinOptions { jvmTarget = javaVersion } } dependencies { implementation deps.kotlinjdk api project(':magnet') kapt project(':magnet-processor') } ================================================ FILE: magnetx-app/gradle.properties ================================================ POM_NAME=Application Extension API for Magnet POM_ARTIFACT_ID=magnetx-app POM_PACKAGING=jar ================================================ FILE: magnetx-app/src/main/kotlin/magnetx/AppExtension.kt ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnetx import magnet.Instance /** * Interface to be implemented by extensions of `android.app.Application` class. * Application implementation class should query the extensions using `getMany()` * operator with `AppExtension` type from the application scope. * * ### Scope dependencies * `application: Application` * * ### Scoping * any scoping * * ### Usage example * ```kotlin * @Instance( * type = AppExtension::class, * scoping = Scoping.UNSCOPED * ) * internal class LeakCanaryAppExtension( * private val app: Application * ) : AppExtension { * override fun onCreate() { * if (LeakCanary.isInAnalyzerProcess(context)) { * return * } * LeakCanary.install(context as Application) * } * } * ``` */ interface AppExtension { /** * Called on each registered extension when `Application.onCreate()` is called. * * For more information see * [https://developer.android.com/reference/android/app/Application.html#onCreate()] */ fun onCreate() /** * Called on each registered extension when `Application.onTrimMemory()` is called. * * For more information see * [https://developer.android.com/reference/android/app/Application.html#onTrimMemory(int)] */ fun onTrimMemory(level: Int) {} /** * Delegate to be used in Application for dispatching application events to * all registered application extensions. */ @Instance(type = AppExtension.Delegate::class) class Delegate(private val appExtensions: List) { fun onCreate() { for (appExtension in appExtensions) { appExtension.onCreate() } } fun onTrimMemory(level: Int) { for (appExtension in appExtensions) { appExtension.onTrimMemory(level) } } } } ================================================ FILE: magnetx-app-rx3android/build.gradle ================================================ plugins { id 'com.android.library' id 'kotlin-android' id 'kotlin-kapt' id 'com.vanniktech.maven.publish' } repositories { maven { url 'https://maven.google.com' } } android { compileSdkVersion 32 defaultConfig { minSdkVersion 15 targetSdkVersion 32 } } dependencies { compileOnly deps.rx3android implementation deps.kotlinjdk implementation project(':magnet') kapt project(':magnet-processor') api project(':magnetx-app') } tasks.withType(Javadoc).all { excludes = ['**/*.kt'] } ================================================ FILE: magnetx-app-rx3android/gradle.properties ================================================ POM_NAME=Application magnet registering MainThreadSchedulerHandler at RxAndroidPlugins (RxJava3) POM_ARTIFACT_ID=magnetx-app-rx3android POM_PACKAGING=jar ================================================ FILE: magnetx-app-rx3android/src/main/AndroidManifest.xml ================================================ ================================================ FILE: magnetx-app-rx3android/src/main/java/magnetx/RxAndroidAppExtension.kt ================================================ /* * Copyright (C) 2020 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnetx import android.os.Looper import io.reactivex.rxjava3.android.plugins.RxAndroidPlugins import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import magnet.Instance import magnet.Scoping @Instance( type = AppExtension::class, scoping = Scoping.UNSCOPED ) class RxAndroidAppExtension : AppExtension { override fun onCreate() { RxAndroidPlugins.setMainThreadSchedulerHandler { AndroidSchedulers.from(Looper.getMainLooper(), true) } } } ================================================ FILE: magnetx-app-rxandroid/build.gradle ================================================ plugins { id 'com.android.library' id 'kotlin-android' id 'kotlin-kapt' id 'com.vanniktech.maven.publish' } repositories { maven { url 'https://maven.google.com' } } android { compileSdkVersion 32 defaultConfig { minSdkVersion 15 targetSdkVersion 32 versionCode 0 versionName "0" } } dependencies { compileOnly deps.rxandroid implementation deps.kotlinjdk implementation project(':magnet') kapt project(':magnet-processor') api project(':magnetx-app') } tasks.withType(Javadoc).all { excludes = ['**/*.kt'] } ================================================ FILE: magnetx-app-rxandroid/gradle.properties ================================================ POM_NAME=Application magnet registering MainThreadSchedulerHandler at RxAndroidPlugins POM_ARTIFACT_ID=magnetx-app-rxandroid POM_PACKAGING=jar ================================================ FILE: magnetx-app-rxandroid/src/main/AndroidManifest.xml ================================================ ================================================ FILE: magnetx-app-rxandroid/src/main/java/magnetx/RxAndroidAppExtension.kt ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnetx import android.os.Looper import io.reactivex.android.plugins.RxAndroidPlugins import io.reactivex.android.schedulers.AndroidSchedulers import magnet.Instance import magnet.Scoping @Instance( type = AppExtension::class, scoping = Scoping.UNSCOPED ) class RxAndroidAppExtension : AppExtension { override fun onCreate() { RxAndroidPlugins.setMainThreadSchedulerHandler { AndroidSchedulers.from(Looper.getMainLooper(), true) } } } ================================================ FILE: magnetx-app-stetho/build.gradle ================================================ plugins { id 'com.android.library' id 'kotlin-android' id 'kotlin-kapt' id 'com.vanniktech.maven.publish' } repositories { maven { url 'https://maven.google.com' } } android { compileSdkVersion 31 defaultConfig { minSdkVersion 15 targetSdkVersion 31 versionCode 0 versionName "0" } } dependencies { api project(':magnetx-app') compileOnly deps.stetho implementation deps.kotlinjdk kapt project(':magnet-processor') } tasks.withType(Javadoc).all { excludes = ['**/*.kt'] } ================================================ FILE: magnetx-app-stetho/gradle.properties ================================================ POM_NAME=Magnet application extension for Stetho POM_ARTIFACT_ID=magnetx-app-stetho POM_PACKAGING=jar ================================================ FILE: magnetx-app-stetho/src/main/AndroidManifest.xml ================================================ ================================================ FILE: magnetx-app-stetho/src/main/java/magnetx/app/stetho/StethoAppExtension.kt ================================================ /* * Copyright (C) 2019 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnetx.app.stetho import android.app.Application import android.content.Context import com.facebook.stetho.Stetho import magnet.Instance import magnet.Scoping import magnetx.AppExtension @Instance( type = AppExtension::class, scoping = Scoping.UNSCOPED ) class StethoAppExtension( private val application: Application, private val stethoInitializers: List ) : AppExtension { override fun onCreate() { Stetho.initialize( Stetho .newInitializerBuilder(application) .also { for (stethoInitializer in stethoInitializers) { stethoInitializer.initialize(it, application) } } .build() ) } /** * Implement this interface and annotate in with unscoped {@link Instance} * to participate on initializing Stetho on application launch. */ interface Initializer { fun initialize(builder: Stetho.InitializerBuilder, context: Context) } } ================================================ FILE: magnetx-app-stetho-scope/build.gradle ================================================ plugins { id 'com.android.library' id 'kotlin-android' id 'kotlin-kapt' id 'com.vanniktech.maven.publish' } repositories { maven { url 'https://maven.google.com' } } android { compileSdkVersion 32 defaultConfig { minSdkVersion 15 targetSdkVersion 32 versionCode 0 versionName "0" } } dependencies { api project(':magnetx-app-stetho') compileOnly deps.stetho implementation deps.kotlinjdk kapt project(':magnet-processor') } tasks.withType(Javadoc).all { excludes = ['**/*.kt'] } ================================================ FILE: magnetx-app-stetho-scope/gradle.properties ================================================ POM_NAME=Magnet application extension providing a Stetho scope dumper POM_ARTIFACT_ID=magnetx-app-stetho-scope POM_PACKAGING=jar ================================================ FILE: magnetx-app-stetho-scope/src/main/AndroidManifest.xml ================================================ ================================================ FILE: magnetx-app-stetho-scope/src/main/java/magnetx/app/stetho/scope/ScopeDumper.kt ================================================ /* * Copyright (C) 2019 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnetx.app.stetho.scope import magnet.Classifier import magnet.Visitor import magnet.Visitor.Instance import magnet.Visitor.Scope import java.io.PrintStream internal class ScopeDumper(private val writer: PrintStream) : Visitor { private val instances = mutableListOf() private var currentScope: Scope? = null private var level = 0 override fun onEnterScope(scope: Scope, parent: Scope?): Boolean { currentScope?.let { instances.dump(it, level, writer) } currentScope = scope level++ return true } override fun onInstance(instance: Instance): Boolean { instances.add(instance) return true } override fun onExitScope(scope: Scope) { currentScope?.let { instances.dump(it, level, writer) } currentScope = null level-- } } private fun MutableList.dump(scope: Scope, level: Int, writer: PrintStream) { val indentScope = " ".repeat(level - 1) val limits = scope.limits?.let { "<${it.joinToString(", ")}> " } ?: "" writer.println("$indentScope [$level] $limits$scope") sortWith(compareBy({ it.provision }, { it.scoping }, { it.limit }, { it.type.name })) val indentInstance = " ".repeat(level) for (instance in this) { val line = when (instance.provision) { Visitor.Provision.BOUND -> instance.renderBound() Visitor.Provision.INJECTED -> instance.renderInjected() } writer.println("$indentInstance $line") } clear() } private fun Instance.renderBound(): String = if (classifier == Classifier.NONE) "$provision ${type.simpleName} $value" else "$provision ${type.name}@$classifier $value" private fun Instance.renderInjected(): String { val renderedScoping = if (limit.isNotEmpty()) "<$limit>" else scoping.toString() return if (classifier == Classifier.NONE) "$renderedScoping ${type.simpleName} $value" else "$renderedScoping ${type.simpleName}@$classifier $value" } ================================================ FILE: magnetx-app-stetho-scope/src/main/java/magnetx/app/stetho/scope/StethoScopeDumpPlugin.kt ================================================ /* * Copyright (C) 2019 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnetx.app.stetho.scope import com.facebook.stetho.dumpapp.ArgsHelper import com.facebook.stetho.dumpapp.DumpUsageException import com.facebook.stetho.dumpapp.DumperContext import com.facebook.stetho.dumpapp.DumperPlugin import magnet.Scope import java.io.PrintStream internal class StethoScopeDumpPlugin( private val scope: Scope ) : DumperPlugin { override fun getName(): String = NAME_MAGNET override fun dump(dumpContext: DumperContext) { val writer: PrintStream = dumpContext.stdout val args: Iterator = dumpContext.argsAsList.iterator() val command: String? = ArgsHelper.nextOptionalArg(args, null) when (command) { CMD_SCOPE -> doDumpScopes(args, writer) else -> { doUsage(writer) if (command != null) { throw DumpUsageException("Unknown command: $command") } } } } private fun doDumpScopes(args: Iterator, writer: PrintStream) { val depth = ArgsHelper.nextOptionalArg(args, null)?.toInt() ?: Integer.MAX_VALUE scope.accept(ScopeDumper(writer), depth) } private fun doUsage(writer: PrintStream) { with(writer) { println("Usage: ./dumpapp magnet scope []") println() } } } private const val NAME_MAGNET = "magnet" private const val CMD_SCOPE = "scope" ================================================ FILE: magnetx-app-stetho-scope/src/main/java/magnetx/app/stetho/scope/StethoScopeInitializer.kt ================================================ /* * Copyright (C) 2019 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnetx.app.stetho.scope import android.content.Context import com.facebook.stetho.Stetho import magnet.Instance import magnet.Scope import magnet.Scoping import magnetx.app.stetho.StethoAppExtension @Instance( type = StethoAppExtension.Initializer::class, scoping = Scoping.UNSCOPED ) internal class StethoScopeInitializer( private val scope: Scope ) : StethoAppExtension.Initializer { override fun initialize(builder: Stetho.InitializerBuilder, context: Context) { builder.enableDumpapp { Stetho.DefaultDumperPluginsBuilder(context) .provide(StethoScopeDumpPlugin(scope)) .finish() } } } ================================================ FILE: magnetx-selector-android/build.gradle ================================================ plugins { id 'kotlin' id 'kotlin-kapt' id 'com.vanniktech.maven.publish' } repositories { maven { url 'https://maven.google.com' } } compileKotlin { kotlinOptions { jvmTarget = javaVersion } } compileTestKotlin { kotlinOptions { jvmTarget = javaVersion } } dependencies { compileOnly deps.android implementation deps.kotlinjdk implementation project(':magnet') kapt project(':magnet-processor') } ================================================ FILE: magnetx-selector-android/gradle.properties ================================================ POM_NAME=Magner selection filter evaluating Android API level field POM_ARTIFACT_ID=magnetx-selector-android POM_PACKAGING=jar ================================================ FILE: magnetx-selector-android/src/main/kotlin/magnetx/AndroidSelectorFilter.kt ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnetx import android.os.Build import magnet.Instance import magnet.SelectorFilter @Instance( type = SelectorFilter::class, classifier = "android" ) internal class AndroidSelectorFilter : SelectorFilter() { override fun filter(selector: Array): Boolean { check(selector.size >= 4) { "Unexpected selector length: ${selector.size}. $VERSION_ERROR" } check(selector[1] == "api") { "Unexpected selector key ${selector[1]}. $VERSION_ERROR" } val operator = when (selector[2]) { ">=" -> Operator.GreatOrEqual "<=" -> Operator.LessOrEqual ">" -> Operator.Great "<" -> Operator.Less "==" -> Operator.Equal "!=" -> Operator.NotEqual "in" -> Operator.In "!in" -> Operator.NotIn else -> error("Unsupported operator ${selector[2]}. $VERSION_ERROR") } return when (operator) { is Operator.OneOperand -> operator.apply(operand = selector[3].toInt()) is Operator.TwoOperands -> { check(selector.size > 4) { "Unexpected selector length ${selector.size} for $operator. $VERSION_ERROR" } operator.apply(operand1 = selector[3].toInt(), operand2 = selector[4].toInt()) } else -> error("Unsupported operator type: $operator") } } companion object { private const val VERSION_ERROR = "Make sure to use same versions of 'magnet' and 'magnetx' packages." } } private sealed class Operator { interface OneOperand { fun apply(operand: Int): Boolean } interface TwoOperands { fun apply(operand1: Int, operand2: Int): Boolean } object NotIn : Operator(), TwoOperands { override fun apply(operand1: Int, operand2: Int): Boolean = Build.VERSION.SDK_INT !in operand1..operand2 } object In : Operator(), TwoOperands { override fun apply(operand1: Int, operand2: Int): Boolean = Build.VERSION.SDK_INT in operand1..operand2 } object Less : Operator(), OneOperand { override fun apply(operand: Int) = Build.VERSION.SDK_INT < operand } object Great : Operator(), OneOperand { override fun apply(operand: Int) = Build.VERSION.SDK_INT > operand } object LessOrEqual : Operator(), OneOperand { override fun apply(operand: Int) = Build.VERSION.SDK_INT <= operand } object GreatOrEqual : Operator(), OneOperand { override fun apply(operand: Int) = Build.VERSION.SDK_INT >= operand } object Equal : Operator(), OneOperand { override fun apply(operand: Int) = Build.VERSION.SDK_INT == operand } object NotEqual : Operator(), OneOperand { override fun apply(operand: Int) = Build.VERSION.SDK_INT != operand } } ================================================ FILE: magnetx-selector-features/build.gradle ================================================ plugins { id 'kotlin' id 'kotlin-kapt' id 'com.vanniktech.maven.publish' } repositories { maven { url 'https://maven.google.com' } } compileKotlin { kotlinOptions { jvmTarget = javaVersion } } compileTestKotlin { kotlinOptions { jvmTarget = javaVersion } } dependencies { compileOnly deps.android implementation deps.kotlinjdk implementation project(':magnet') kapt project(':magnet-processor') } ================================================ FILE: magnetx-selector-features/gradle.properties ================================================ POM_NAME=Magner selection filter evaluating feature flags stored in provided SharedPreferences POM_ARTIFACT_ID=magnetx-selector-features POM_PACKAGING=jar ================================================ FILE: magnetx-selector-features/src/main/AndroidManifest.xml ================================================ ================================================ FILE: magnetx-selector-features/src/main/java/magnetx/FeaturesSelectorFilter.kt ================================================ /* * Copyright (C) 2018 Sergej Shafarenka, www.halfbit.de * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package magnetx import android.content.SharedPreferences import magnet.Classifier import magnet.Instance import magnet.SelectorFilter const val FEATURE_SELECTOR = "features" /** * This class enables selective injection based on boolean values stored in * provided shared preferences. Add the library containing this class to your * app module to enable feature selector. * * * ### Scope dependencies * `@Classifier(FEATURE_SELECTOR) preferences: SharedPreferences` * * ### Scoping * any scoping */ @Instance( type = SelectorFilter::class, classifier = FEATURE_SELECTOR ) internal class FeaturesSelectorFilter( @Classifier(FEATURE_SELECTOR) private val preferences: SharedPreferences ) : SelectorFilter() { override fun filter(selector: Array): Boolean { check(selector.size == 4) { "Expected selector length 4, actual: ${selector.size}." } val operator = when (selector[2]) { "==" -> Operator.Equal "!=" -> Operator.NotEqual else -> error("Supported operators == and !=, actual: ${selector[2]}") } return operator.apply( key = selector[1], operand = selector[3].toBoolean(), preferences = preferences ) } } private sealed class Operator { abstract fun apply(key: String, operand: Boolean, preferences: SharedPreferences): Boolean object Equal : Operator() { override fun apply(key: String, operand: Boolean, preferences: SharedPreferences) = preferences.getBoolean(key, false) == operand } object NotEqual : Operator() { override fun apply(key: String, operand: Boolean, preferences: SharedPreferences) = preferences.getBoolean(key, false) != operand } } ================================================ FILE: settings.gradle ================================================ include ':magnet', ':magnet-kotlin', ':magnet-processor', ':magnetx-app', ':magnetx-app-stetho', ':magnetx-app-stetho-scope', ':magnetx-app-rxandroid', ':magnetx-app-rx3android', ':magnetx-selector-android', ':magnetx-selector-features'