All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.williamwebb.auto.stub.StubGenerator.kt Maven / Gradle / Ivy

The newest version!
package io.williamwebb.auto.stub

import com.squareup.javapoet.*
import java.lang.reflect.Constructor
import java.lang.reflect.Executable
import java.lang.reflect.Method
import java.lang.reflect.Type
import java.util.HashSet
import javax.lang.model.element.Modifier

/**
 * Created by williamwebb on 3/6/16.
 */
class StubGenerator(internal val model: StubModel) {

  final val INSTANCES = HashSet()

  fun brewJava(): JavaFile {

    val classBuilder = createClass(model)
    classBuilder.addModifiers(*model.modifiers)

    if(INSTANCES.isNotEmpty()) {
      classBuilder.addStaticBlock(createStaticInstances(classBuilder))
    }

    return JavaFile.builder(model.packageName, classBuilder.build()).build()
  }

  private fun createClass(model: StubModel): TypeSpec.Builder {
    println("Brewing: " + model.packageName + "." + model.name)

    val builder = TypeSpec.classBuilder(model.name)
    builder.addModifiers(*model.modifiers)

    model.constructors.forEach {
      builder.addMethod(createConstructor(it).build())
    }

    model.methods.forEach {
      builder.addMethod(createMethod(it).build())
    }

    model.innerClasses.forEach {
      builder.addType(createClass(it).build())
    }

    return builder
  }

  private fun createConstructor(constructor: Constructor<*>): MethodSpec.Builder {
    return createExecutable(MethodSpec.constructorBuilder(), constructor)
  }

  private fun createMethod(method: Method): MethodSpec.Builder {
    val spec = createExecutable(MethodSpec.methodBuilder(method.name), method)

    if(!(method.returnType.equals(Void.TYPE))) {
      spec.returns(method.returnType)
      spec.addStatement("return ${formatType(method.returnType)}")

      if(!INSTANCES.contains(method.returnType)) {
        INSTANCES.add(method.returnType)
      }
    }

    return spec
  }

  private fun createExecutable(builder: MethodSpec.Builder, exec: Executable): MethodSpec.Builder {
    builder.addModifiers(exec.modifiers())

    exec.annotations.forEach {
      builder.addAnnotation(ClassName.get(it.javaClass))
    }

    exec.parameters.forEach {
      builder.addParameter(it.type, it.name)
    }

    exec.exceptionTypes.forEach {
      builder.addException(it)
    }

    return builder
  }

  private fun createStaticInstances(typeSpec: TypeSpec.Builder): CodeBlock {
    val codeBlock = CodeBlock.builder()

    tryCatch(codeBlock, "Exception", {
      INSTANCES.forEach {
        val type = formatType(it)
        typeSpec.addField(it, type, Modifier.STATIC, Modifier.PRIVATE)
        codeBlock.addStatement("  $type = ${it.typeName}.class.newInstance()")
      }
    })

    return codeBlock.build()
  }

  private inline fun tryCatch(codeBlock : CodeBlock.Builder, exception: String, body: () -> Unit) {
    codeBlock.add("try {").add("\n")
    body()
    codeBlock.add("} catch($exception noop) {}").add("\n")
  }

  private fun formatType(type: Type) = "${type.typeName.replace(".","_")}_INSTANCE"
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy