<commit_before>/*
 * Copyright 2014 JetBrains s.r.o.
 *
 * 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 org.jetbrains.android.dsl

import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.MethodNode
import java.util.ArrayList
import org.jetbrains.android.dsl.utils.Buffer
import org.jetbrains.android.dsl.KoanFile.*
import org.jetbrains.android.dsl.ConfigurationTune.*
import org.objectweb.asm.Type

class Renderer(private val generator: Generator) : Configurable(generator.config) {

    private fun buffer(init: Buffer.() -> Unit) = Buffer(config.indent, 0, init)
    private fun buffer(indent: Int, init: Buffer.() -> Unit) = Buffer(config.indent, indent, init)

    val views = generate(VIEWS) {
        generateViews(generator.viewClasses) { cleanInternalName(it) }
    }

    val viewGroups = generate(VIEWS) {
        generateViews(generator.viewGroupClasses) { "_" + stripClassName(cleanInternalName(it)) }
    }

    val helperConstructors = generate(HELPER_CONSTRUCTORS) {
        genHelperConstructors()
    }

    // Generate View extension properties (with "best" setter)
    val properties = generate(PROPERTIES) {
        generator.properties.map {
            val getter = it.getter
            val className = cleanInternalName(getter.clazz.name)
            val propertyName = it.name
            val fullPropertyName = "$className.$propertyName"
            val bestSetter = it.setters.head
            val mutability = if (bestSetter != null) "var" else "val"
            val returnType = getter.method.renderReturnType()
            val otherSetters = if (it.setters.size > 1) it.setters.tail else listOf()

            buffer {
                line("public $mutability $fullPropertyName: $returnType")
                indent.line("get() = ${getter.method.name}()")
                if (bestSetter != null) {
                    val arg = if (returnType.endsWith("?")) "v!!" else "v"
                    indent.line("set(v) = ${bestSetter.method.name}($arg)")
                }

                renderResourceProperty(otherSetters, fullPropertyName, returnType)
            }.toString()
        }
    }

    private fun Buffer.renderResourceProperty(
        otherSetters: List<MethodNodeWithClass>,
        fullPropertyName: String,
        returnType: String)
    {
        if (otherSetters.notEmpty && supportsResourceSetter(returnType)) {
            val resourceSetter = otherSetters.firstOrNull {
                (it.method.arguments?.size() ?: 0) == 1 &&
                    (it.method.arguments?.get(0)?.getClassName() == "int")
            }
            if (resourceSetter != null) {
                line("public var ${fullPropertyName}Resource: Int")
                indent.line("get() = throw KoanException(\"'${fullPropertyName}Resource' property doesn't have a getter\")")
                indent.line("set(v) = ${resourceSetter.method.name}(v)")
            }
        }
    }

    // Render simple listeners (interfaces with one method)
    val simpleListeners = generate(SIMPLE_LISTENERS) {
        generator.listeners
            .filter { it is SimpleListener }
            .map { renderSimpleListener(it as SimpleListener) }
    }

    // Render complex listeners (interfaces with more than one method)
    private val complexListeners = generate(COMPLEX_LISTENER_CLASSES, COMPLEX_LISTENER_SETTERS) {
        generator.listeners.filter { it is ComplexListener }
    }

    val complexListenerSetters = generate(COMPLEX_LISTENER_SETTERS) {
        complexListeners.map { renderComplexListenerSetters(it as ComplexListener) }
    }

    val complexListenerClasses = generate(COMPLEX_LISTENER_CLASSES) {
        complexListeners.map { renderComplexListenerClass(it as ComplexListener) }
    }

    //generated layout classes with custom LayoutParams
    val layouts = generate(LAYOUTS) {
        generator.layouts.map { renderLayout(it) }
    }

    val services = generator.services.map {
        val propertyName = it.second!!.data.cleanNameDecap()
        val className = it.second!!.data.cleanInternalName()
        "public val Context.$propertyName: $className\n"+
                "${config.indent}get() = getSystemService(Context.${it.first}) as $className"
    }

    val sqLiteParserHelpers = generate(SQL_PARSER_HELPERS) {
        val list = arrayListOf<String>()
        for (i in 1..22) {
            val types = (1..i).map { "T$it" }.joinToString(", ")
            val args = (1..i).map { "columns[$it] as T$it" }.joinToString(", ")
            list.add(buffer {
                line("public fun <$types, R> rowParser(parser: ($types) -> R): RowParser<R> {")
                    line("return object : RowParser<R> {")
                        line("override fun parseRow(columns: Array<Any>): R {")
                        line("if (columns.size() != $i)")
                        val s = if (i == 1) "" else "s"
                        indent.line("throw SQLiteException(\"Invalid row: $i column$s required\")")
                        line("[suppress(\"UNCHECKED_CAST\")]")
                        line("return parser($args)")
                        line("}")
                    line("}")
                line("}")
            }.toString())
        }
        list
    }

    val interfaceWorkarounds = if (!config[INTERFACE_WORKAROUNDS]) "" else
        generator.interfaceWorkarounds.map {
            val (mainClass, ancestor, innerClass) = it
            val probInterfaceName = innerClass!!.innerName
            val conflict = generator.interfaceWorkarounds.count { it.third!!.innerName == probInterfaceName } > 1
            val interfaceName = (
                if (conflict) stripClassName(cleanInternalName(innerClass.outerName)) + "_" else "") + probInterfaceName
            val ancestorName = cleanInternalName(ancestor!!.name)
            buffer(1) {
                line("public static interface $interfaceName {")
                for (field in mainClass.fields.filter { it.isPublic && it.isStatic && it.isFinal }) {
                    val name = field.name
                    val type = Type.getType(field.desc).toJavaStr()<commit_msg>Fix OutOfBounds in Cursor parsing<commit_after>/*
 * Copyright 2014 JetBrains s.r.o.
 *
 * 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 org.jetbrains.android.dsl

import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.MethodNode
import java.util.ArrayList
import org.jetbrains.android.dsl.utils.Buffer
import org.jetbrains.android.dsl.KoanFile.*
import org.jetbrains.android.dsl.ConfigurationTune.*
import org.objectweb.asm.Type

class Renderer(private val generator: Generator) : Configurable(generator.config) {

    private fun buffer(init: Buffer.() -> Unit) = Buffer(config.indent, 0, init)
    private fun buffer(indent: Int, init: Buffer.() -> Unit) = Buffer(config.indent, indent, init)

    val views = generate(VIEWS) {
        generateViews(generator.viewClasses) { cleanInternalName(it) }
    }

    val viewGroups = generate(VIEWS) {
        generateViews(generator.viewGroupClasses) { "_" + stripClassName(cleanInternalName(it)) }
    }

    val helperConstructors = generate(HELPER_CONSTRUCTORS) {
        genHelperConstructors()
    }

    // Generate View extension properties (with "best" setter)
    val properties = generate(PROPERTIES) {
        generator.properties.map {
            val getter = it.getter
            val className = cleanInternalName(getter.clazz.name)
            val propertyName = it.name
            val fullPropertyName = "$className.$propertyName"
            val bestSetter = it.setters.head
            val mutability = if (bestSetter != null) "var" else "val"
            val returnType = getter.method.renderReturnType()
            val otherSetters = if (it.setters.size > 1) it.setters.tail else listOf()

            buffer {
                line("public $mutability $fullPropertyName: $returnType")
                indent.line("get() = ${getter.method.name}()")
                if (bestSetter != null) {
                    val arg = if (returnType.endsWith("?")) "v!!" else "v"
                    indent.line("set(v) = ${bestSetter.method.name}($arg)")
                }

                renderResourceProperty(otherSetters, fullPropertyName, returnType)
            }.toString()
        }
    }

    private fun Buffer.renderResourceProperty(
        otherSetters: List<MethodNodeWithClass>,
        fullPropertyName: String,
        returnType: String)
    {
        if (otherSetters.notEmpty && supportsResourceSetter(returnType)) {
            val resourceSetter = otherSetters.firstOrNull {
                (it.method.arguments?.size() ?: 0) == 1 &&
                    (it.method.arguments?.get(0)?.getClassName() == "int")
            }
            if (resourceSetter != null) {
                line("public var ${fullPropertyName}Resource: Int")
                indent.line("get() = throw KoanException(\"'${fullPropertyName}Resource' property doesn't have a getter\")")
                indent.line("set(v) = ${resourceSetter.method.name}(v)")
            }
        }
    }

    // Render simple listeners (interfaces with one method)
    val simpleListeners = generate(SIMPLE_LISTENERS) {
        generator.listeners
            .filter { it is SimpleListener }
            .map { renderSimpleListener(it as SimpleListener) }
    }

    // Render complex listeners (interfaces with more than one method)
    private val complexListeners = generate(COMPLEX_LISTENER_CLASSES, COMPLEX_LISTENER_SETTERS) {
        generator.listeners.filter { it is ComplexListener }
    }

    val complexListenerSetters = generate(COMPLEX_LISTENER_SETTERS) {
        complexListeners.map { renderComplexListenerSetters(it as ComplexListener) }
    }

    val complexListenerClasses = generate(COMPLEX_LISTENER_CLASSES) {
        complexListeners.map { renderComplexListenerClass(it as ComplexListener) }
    }

    //generated layout classes with custom LayoutParams
    val layouts = generate(LAYOUTS) {
        generator.layouts.map { renderLayout(it) }
    }

    val services = generator.services.map {
        val propertyName = it.second!!.data.cleanNameDecap()
        val className = it.second!!.data.cleanInternalName()
        "public val Context.$propertyName: $className\n"+
                "${config.indent}get() = getSystemService(Context.${it.first}) as $className"
    }

    val sqLiteParserHelpers = generate(SQL_PARSER_HELPERS) {
        val list = arrayListOf<String>()
        for (i in 1..22) {
            val types = (1..i).map { "T$it" }.joinToString(", ")
            val args = (1..i).map { "columns[${it - 1}] as T$it" }.joinToString(", ")
            list.add(buffer {
                line("public fun <$types, R> rowParser(parser: ($types) -> R): RowParser<R> {")
                    line("return object : RowParser<R> {")
                        line("override fun parseRow(columns: Array<Any>): R {")
                        line("if (columns.size() != $i)")
                        val s = if (i == 1) "" else "s"
                        indent.line("throw SQLiteException(\"Invalid row: $i column$s required\")")
                        line("[suppress(\"UNCHECKED_CAST\")]")
                        line("return parser($args)")
                        line("}")
                    line("}")
                line("}")
            }.toString())
        }
        list
    }

    val interfaceWorkarounds = if (!config[INTERFACE_WORKAROUNDS]) "" else
        generator.interfaceWorkarounds.map {
            val (mainClass, ancestor, innerClass) = it
            val probInterfaceName = innerClass!!.innerName
            val conflict = generator.interfaceWorkarounds.count { it.third!!.innerName == probInterfaceName } > 1
            val interfaceName = (
                if (conflict) stripClassName(cleanInternalName(innerClass.outerName)) + "_" else "") + probInterfaceName
            val ancestorName = cleanInternalName(ancestor!!.name)
            buffer(1) {
                line("public static interface $interfaceName {")
                for (field in mainClass.fields.filter { it.isPublic && it.isStatic && it.isFinal }) {
                    val name = field.name
                    val type = Type.getType(field.desc).toJavaStr()
{'fraction_non_alphanumeric': 0.11327713382507903, 'fraction_numerical': 0.003236489537859401, 'mean_word_length': 2.387812340642529, 'pattern_counts': {'":': 0, '<': 15, '<?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': '2222568', 'n_tokens_mistral': 3695, 'n_tokens_neox': 3548, 'n_words': 868}