<commit_before>/*
 * Copyright 2016 <NAME>
 *
 * 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 io.michaelrocks.lightsaber.processor.generation

import io.michaelrocks.grip.ClassRegistry
import io.michaelrocks.grip.mirrors.isPublic
import io.michaelrocks.lightsaber.processor.ErrorReporter
import io.michaelrocks.lightsaber.processor.annotations.proxy.AnnotationCreator
import io.michaelrocks.lightsaber.processor.commons.*
import io.michaelrocks.lightsaber.processor.descriptors.FieldDescriptor
import io.michaelrocks.lightsaber.processor.generation.model.*
import io.michaelrocks.lightsaber.processor.io.FileSink
import io.michaelrocks.lightsaber.processor.model.Component
import io.michaelrocks.lightsaber.processor.model.InjectionContext
import org.objectweb.asm.Type
import java.util.*

class Generator(
    private val classRegistry: ClassRegistry,
    private val errorReporter: ErrorReporter,
    fileSink: FileSink
) {
  private val classProducer = ProcessorClassProducer(fileSink, errorReporter)
  private val annotationCreator = AnnotationCreator(classProducer, classRegistry)

  fun generate(injectionContext: InjectionContext) {
    val generationContext = composeGeneratorModel(injectionContext)
    generateProviders(injectionContext, generationContext)
    generateLightsaberConfigurator(generationContext)
    generateInjectorConfigurators(generationContext)
    generateInjectors(generationContext)
    generatePackageInvaders(generationContext)
    generateKeyRegistry(generationContext)
  }

  private fun composeGeneratorModel(context: InjectionContext) =
      GenerationContext(
          composePackageInjectorConfigurator(context),
          composeInjectorConfigurators(context),
          composeMembersInjectors(context),
          composePackageInvaders(context),
          composeKeyRegistry(context)
      )

  private fun composePackageInjectorConfigurator(context: InjectionContext): InjectorConfigurator {
    val configuratorType = composeConfiguratorType(context.packageComponent)
    return InjectorConfigurator(configuratorType, context.packageComponent)
  }

  private fun composeInjectorConfigurators(context: InjectionContext): Collection<InjectorConfigurator> {
    return context.components
        .map { component ->
          val configuratorType = composeConfiguratorType(component)
          InjectorConfigurator(configuratorType, component)
        }
  }

  private fun composeConfiguratorType(component: Component): Type {
    val componentNameWithDollars = component.type.internalName.replace('/', '$')
    return Type.getObjectType("io/michaelrocks/lightsaber/InjectorConfigurator\$$componentNameWithDollars")
  }

  private fun composeMembersInjectors(context: InjectionContext) =
      context.injectableTargets.map { injectableTarget ->
        val injectorType = Type.getObjectType(injectableTarget.type.internalName + "\$MembersInjector")
        MembersInjector(injectorType, injectableTarget)
      }

  private fun composePackageInvaders(context: InjectionContext): Collection<PackageInvader> =
      context.allComponents.asSequence()
          .flatMap { it.modules.asSequence() }
          .flatMap { it.providers.asSequence() }
          .asIterable()
          .groupNotNullByTo(
              HashMap(),
              { provider -> Types.getPackageName(provider.moduleType) },
              { provider ->
                val type = provider.dependency.type.rawType.box()
                given (!classRegistry.getClassMirror(type).isPublic) { type }
              }
          )
          .map {
            val (packageName, types) = it
            val type = Type.getObjectType("$packageName/Lightsaber\$PackageInvader")
            val fields = types.associateByIndexedTo(HashMap(),
                { index, type -> type.box() },
                { index, type -> FieldDescriptor("class$index", Types.CLASS_TYPE) }
            )
            PackageInvader(type, packageName, fields)
          }

  private fun composeKeyRegistry(context: InjectionContext): KeyRegistry {
    val type = Type.getObjectType("io/michaelrocks/lightsaber/KeyRegistry")
    val fields = context.allComponents.asSequence()
        .flatMap { it.modules.asSequence() }<commit_msg>Create package invaders for components<commit_after>/*
 * Copyright 2016 <NAME>
 *
 * 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 io.michaelrocks.lightsaber.processor.generation

import io.michaelrocks.grip.ClassRegistry
import io.michaelrocks.grip.mirrors.isPublic
import io.michaelrocks.lightsaber.processor.ErrorReporter
import io.michaelrocks.lightsaber.processor.annotations.proxy.AnnotationCreator
import io.michaelrocks.lightsaber.processor.commons.*
import io.michaelrocks.lightsaber.processor.descriptors.FieldDescriptor
import io.michaelrocks.lightsaber.processor.generation.model.*
import io.michaelrocks.lightsaber.processor.io.FileSink
import io.michaelrocks.lightsaber.processor.model.Component
import io.michaelrocks.lightsaber.processor.model.InjectionContext
import org.objectweb.asm.Type
import java.util.*

class Generator(
    private val classRegistry: ClassRegistry,
    private val errorReporter: ErrorReporter,
    fileSink: FileSink
) {
  private val classProducer = ProcessorClassProducer(fileSink, errorReporter)
  private val annotationCreator = AnnotationCreator(classProducer, classRegistry)

  fun generate(injectionContext: InjectionContext) {
    val generationContext = composeGeneratorModel(injectionContext)
    generateProviders(injectionContext, generationContext)
    generateLightsaberConfigurator(generationContext)
    generateInjectorConfigurators(generationContext)
    generateInjectors(generationContext)
    generatePackageInvaders(generationContext)
    generateKeyRegistry(generationContext)
  }

  private fun composeGeneratorModel(context: InjectionContext) =
      GenerationContext(
          composePackageInjectorConfigurator(context),
          composeInjectorConfigurators(context),
          composeMembersInjectors(context),
          composePackageInvaders(context),
          composeKeyRegistry(context)
      )

  private fun composePackageInjectorConfigurator(context: InjectionContext): InjectorConfigurator {
    val configuratorType = composeConfiguratorType(context.packageComponent)
    return InjectorConfigurator(configuratorType, context.packageComponent)
  }

  private fun composeInjectorConfigurators(context: InjectionContext): Collection<InjectorConfigurator> {
    return context.components
        .map { component ->
          val configuratorType = composeConfiguratorType(component)
          InjectorConfigurator(configuratorType, component)
        }
  }

  private fun composeConfiguratorType(component: Component): Type {
    val componentNameWithDollars = component.type.internalName.replace('/', '$')
    return Type.getObjectType("io/michaelrocks/lightsaber/InjectorConfigurator\$$componentNameWithDollars")
  }

  private fun composeMembersInjectors(context: InjectionContext) =
      context.injectableTargets.map { injectableTarget ->
        val injectorType = Type.getObjectType(injectableTarget.type.internalName + "\$MembersInjector")
        MembersInjector(injectorType, injectableTarget)
      }

  private fun composePackageInvaders(context: InjectionContext): Collection<PackageInvader> =
      context.allComponents.asSequence()
          .flatMap { it.modules.asSequence() }
          .flatMap { it.providers.asSequence() }
          .asIterable()
          .groupNotNullByTo(
              HashMap(),
              { provider -> Types.getPackageName(provider.moduleType) },
              { provider ->
                val type = provider.dependency.type.rawType.box()
                given (!classRegistry.getClassMirror(type).isPublic) { type }
              }
          )
          .mergeWith(
              context.components.groupNotNullByTo(
                  HashMap<String, MutableList<Type>>(),
                  { component -> Types.getPackageName(component.type) },
                  { component ->
                    given (!classRegistry.getClassMirror(component.type).isPublic) { component.type }
                  }
              )
          )
          .map {
            val (packageName, types) = it
            val type = Type.getObjectType("$packageName/Lightsaber\$PackageInvader")
            val fields = types.associateByIndexedTo(HashMap(),
                { index, type -> type.box() },
                { index, type -> FieldDescriptor("class$index", Types.CLASS_TYPE) }
            )
            PackageInvader(type, packageName, fields)
          }

  private fun composeKeyRegistry(context: InjectionContext): KeyRegistry {
    val type = Type.getObjectType("io/michaelrocks/lightsaber/KeyRegistry")
    val fields = context.allComponents.asSequence()
        .flatMap { it.modules.asSequence() }
{'fraction_non_alphanumeric': 0.08262540453074434, 'fraction_numerical': 0.0016181229773462784, 'mean_word_length': 3.6558380414312617, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '380516', 'n_tokens_mistral': 2640, 'n_tokens_neox': 2567, 'n_words': 526}