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

org.cqfn.diktat.ruleset.rules.chapter1.FileNaming.kt Maven / Gradle / Ivy

There is a newer version: 1.2.5
Show newest version
package org.cqfn.diktat.ruleset.rules.chapter1

import org.cqfn.diktat.common.config.rules.RulesConfig
import org.cqfn.diktat.ruleset.constants.Warnings.FILE_NAME_INCORRECT
import org.cqfn.diktat.ruleset.constants.Warnings.FILE_NAME_MATCH_CLASS
import org.cqfn.diktat.ruleset.rules.DiktatRule
import org.cqfn.diktat.ruleset.utils.getAllChildrenWithType
import org.cqfn.diktat.ruleset.utils.getFilePath
import org.cqfn.diktat.ruleset.utils.getFirstChildWithType
import org.cqfn.diktat.ruleset.utils.isKotlinScript
import org.cqfn.diktat.ruleset.utils.isPascalCase

import com.pinterest.ktlint.core.ast.ElementType.CLASS
import com.pinterest.ktlint.core.ast.ElementType.FILE
import com.pinterest.ktlint.core.ast.ElementType.IDENTIFIER
import org.jetbrains.kotlin.com.intellij.lang.ASTNode

import java.io.File

/**
 * This visitor covers rule 1.2 of Huawei code style. It covers following rules related to a file naming:
 * 1) File must have ".kt" extension
 * 2) File must be in camel case
 * 3) File name must start with capital letter (PascalCase)
 *
 * Aggressive: In case file contains only one class on upper level - it should be named with the same name
 */
@Suppress("ForbiddenComment")
class FileNaming(configRules: List) : DiktatRule(
    NAME_ID,
    configRules,
    listOf(FILE_NAME_INCORRECT, FILE_NAME_MATCH_CLASS)
) {
    private lateinit var filePath: String

    override fun logic(node: ASTNode) {
        if (node.elementType == FILE) {
            filePath = node.getFilePath()
            if (!filePath.isKotlinScript()) {
                checkFileNaming(node)
                checkClassNameMatchesWithFile(node)
            }
        }
    }

    private fun checkFileNaming(node: ASTNode) {
        val (name, extension) = getFileParts(filePath)
        if (!name.isPascalCase() || !validExtensions.contains(extension)) {
            FILE_NAME_INCORRECT.warnAndFix(configRules, emitWarn, isFixMode, "$name$extension", 0, node) {
                // FixMe: we can add an autocorrect here in future, but is there any purpose to change file or class name?
            }
        }
    }

    @Suppress("UnsafeCallOnNullableType", "ControlFlowWithEmptyBody")
    private fun checkClassNameMatchesWithFile(fileLevelNode: ASTNode) {
        val (fileNameWithoutSuffix, fileNameSuffix) = getFileParts(filePath)
        val classes = fileLevelNode.getAllChildrenWithType(CLASS)
        if (classes.size == 1) {
            val className = classes[0].getFirstChildWithType(IDENTIFIER)!!.text
            if (className != fileNameWithoutSuffix) {
                FILE_NAME_MATCH_CLASS.warnAndFix(configRules, emitWarn, isFixMode, "$fileNameWithoutSuffix$fileNameSuffix vs $className", 0, fileLevelNode) {
                    // FixMe: we can add an autocorrect here in future, but is there any purpose to change file name?
                }
            }
        } else {
            // FixMe: need to check that if there are several classes - at least one of them should match
        }
    }

    private fun getFileParts(fileName: String): Pair {
        val file = File(fileName)
        val fileNameWithoutSuffix = file.name.replace(Regex("\\..*"), "")
        val fileNameSuffix = file.name.replace(fileNameWithoutSuffix, "")
        return Pair(fileNameWithoutSuffix, fileNameSuffix)
    }

    companion object {
        // FixMe: should be moved to properties
        const val NAME_ID = "aag-file-naming"
        val validExtensions = listOf(".kt", ".kts")
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy