org.sonar.plsqlopen.CustomAnnotationBasedRulesDefinition.kt Maven / Gradle / Ivy
/**
* Z PL/SQL Analyzer
* Copyright (C) 2015-2024 Felipe Zorzo
* mailto:felipe AT felipezorzo DOT com DOT br
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.plsqlopen
import org.sonar.plsqlopen.rules.RuleMetadataLoader
import org.sonar.plsqlopen.rules.RulesDefinitionAnnotationLoader
import org.sonar.plsqlopen.rules.ZpaRepository
import org.sonar.plsqlopen.rules.ZpaRule
import org.sonar.plsqlopen.utils.getAnnotation
import org.sonar.plugins.plsqlopen.api.annotations.ActivatedByDefault
import org.sonar.plugins.plsqlopen.api.annotations.ConstantRemediation
import org.sonar.plugins.plsqlopen.api.annotations.RuleInfo
import org.sonar.plugins.plsqlopen.api.annotations.RuleTemplate
import java.io.IOException
import java.net.URL
import java.util.*
class CustomAnnotationBasedRulesDefinition(private val repository: ZpaRepository,
private val languageKey: String,
private val ruleMetadataLoader: RuleMetadataLoader) {
private val locale: Locale = Locale.getDefault()
private val externalDescriptionBasePath: String = String.format("%s/rules/plsql",
getLocalizedFolderName(String.format("/org/sonar/l10n/%s", languageKey), locale))
fun addRuleClasses(ruleClasses: Iterable>) {
val loader = RulesDefinitionAnnotationLoader(ruleMetadataLoader)
for (annotatedClass in ruleClasses) {
loader.load(repository, annotatedClass)
}
val newRules = ArrayList()
for (ruleClass in ruleClasses) {
val rule = newRule(ruleClass)
addHtmlDescription(rule)
rule.template = getAnnotation(ruleClass, RuleTemplate::class.java) != null
try {
val constant = getAnnotation(ruleClass, ConstantRemediation::class.java)
if (constant != null) {
rule.remediationConstant = constant.value
}
} catch (e: RuntimeException) {
throw IllegalArgumentException("Invalid remediation constant on $ruleClass", e)
}
val ruleInfo = getAnnotation(ruleClass, RuleInfo::class.java)
if (ruleInfo != null) {
rule.scope = ruleInfo.scope
}
val activatedByDefault = getAnnotation(ruleClass, ActivatedByDefault::class.java)
if (activatedByDefault != null) {
rule.isActivatedByDefault = true
}
newRules.add(rule)
}
setupExternalNames(newRules)
}
private fun addHtmlDescription(rule: ZpaRule) {
val resource = CustomAnnotationBasedRulesDefinition::class.java.getResource("$externalDescriptionBasePath/${rule.key}.html")
if (resource != null) {
addHtmlDescription(rule, resource)
}
}
private fun addHtmlDescription(rule: ZpaRule, resource: URL) {
try {
rule.htmlDescription = resource.readText()
} catch (e: IOException) {
throw IllegalStateException("Failed to read: $resource", e)
}
}
private fun newRule(ruleClass: Class<*>): ZpaRule {
val ruleKey = getRuleKey(ruleMetadataLoader, ruleClass)
return repository.rule(ruleKey) ?: throw IllegalStateException("Rule $ruleKey was not created")
}
private fun setupExternalNames(rules: Collection) {
val bundle: ResourceBundle
try {
bundle = ResourceBundle.getBundle("org.sonar.l10n.$languageKey", locale)
} catch (e: MissingResourceException) {
return
}
for (rule in rules) {
val baseKey = rule.key
val nameKey = "$baseKey.name"
if (bundle.containsKey(nameKey)) {
rule.name = bundle.getString(nameKey)
}
for (param in rule.params) {
val paramDescriptionKey = baseKey + ".param." + param.key
if (bundle.containsKey(paramDescriptionKey)) {
param.description = bundle.getString(paramDescriptionKey)
}
}
}
}
companion object {
/**
* Adds annotated rule classes to an instance of NewRepository. Fails if one of
* the classes has no SQALE annotation.
* @param repository repository of rules
* @param languageKey language identifier
* @param ruleClasses classes to add
*/
fun load(repository: ZpaRepository, languageKey: String, ruleClasses: Iterable>, ruleMetadataLoader: RuleMetadataLoader) {
CustomAnnotationBasedRulesDefinition(repository, languageKey, ruleMetadataLoader).addRuleClasses(ruleClasses)
}
fun getLocalizedFolderName(baseName: String, locale: Locale): String {
val control = ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_DEFAULT)
var path = control.toBundleName(baseName, locale)
var url: URL? = CustomAnnotationBasedRulesDefinition::class.java.getResource(path)
if (url == null) {
val localeWithoutCountry = if (locale.country == null) locale else Locale(locale.language)
path = control.toBundleName(baseName, localeWithoutCountry)
url = CustomAnnotationBasedRulesDefinition::class.java.getResource(path)
if (url == null) {
path = baseName
CustomAnnotationBasedRulesDefinition::class.java.getResource(path)
}
}
return path
}
fun convertCheckClassName(ruleClass: Class<*>): String {
var name = ruleClass.simpleName
if (name.endsWith("Check")) {
name = name.substring(0, name.length - 5)
}
return name
}
fun getRuleKey(ruleMetadataLoader: RuleMetadataLoader, ruleClass: Class<*>): String {
val ruleAnnotation = ruleMetadataLoader.getRuleAnnotation(ruleClass)
?: throw IllegalArgumentException("No Rule annotation was found on $ruleClass")
return ruleAnnotation.key.ifEmpty { convertCheckClassName(ruleClass) }
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy