Skip to content

Commit

Permalink
Merge pull request #2 from Sagacify/staging
Browse files Browse the repository at this point in the history
Adding scalastyle support
  • Loading branch information
Augustin Borsu committed Apr 30, 2016
2 parents 85f7b9a + a7979fe commit 72b21c6
Show file tree
Hide file tree
Showing 21 changed files with 1,496 additions and 5 deletions.
14 changes: 12 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
*.log

# sbt specific
.cache
.history
.cache/
.history/
.lib/
dist/*
target/
Expand All @@ -15,3 +15,13 @@ project/plugins/project/
# Scala-IDE specific
.scala_dependencies
.worksheet
.classpath
.project
.settings
.cache

# Intellij IDEA Specific
.idea/*
*.iml
*.iws
*.ipr
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ Here is a list of the main ones.
[SonarSource/sonar-examples](https://github.com/SonarSource/sonar-examples)

[NCR-CoDE/sonar-scalastyle](https://github.com/NCR-CoDE/sonar-scalastyle)

# Integration
Sonar-scala integrates the latest code from the [Sonar Scalastyle Plugin](https://github.com/NCR-CoDE/sonar-scalastyle). As both project are licensed under LGPL3 this shouldn't be a problem.
21 changes: 20 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<artifactId>sonar-scala-plugin</artifactId>
<packaging>sonar-plugin</packaging>
<version>0.0.1-SNAPSHOT</version>
<version>0.0.2-SNAPSHOT</version>

<name>Sonar Scala Plugin</name>
<description>Enables analysis of Scala projects into Sonar.</description>
Expand Down Expand Up @@ -111,6 +111,25 @@
<version>${sonar.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.scalastyle</groupId>
<artifactId>scalastyle_${scala.major.version}</artifactId>
<version>0.8.0</version>
<exclusions>
<exclusion>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.sonarsource.sonarqube</groupId>
<artifactId>sonar-core</artifactId>
<version>${sonar.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
2 changes: 1 addition & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Required metadata
sonar.projectName=Sonar Scala Plugin
sonar.projectVersion=0.0.1
sonar.projectVersion=0.0.2

# Comma-separated paths to directories with sources (required)
sonar.sources=src
Expand Down
29 changes: 29 additions & 0 deletions src/main/scala/com/ncredinburgh/sonar/scalastyle/Constants.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Sonar Scalastyle Plugin
* Copyright (C) 2014 All contributors
*
* 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 02
*/
package com.ncredinburgh.sonar.scalastyle

object Constants {
val ScalaKey = "scala"
val RepositoryKey = "Scalastyle"
val RepositoryName = "Scalastyle Rules"
val ProfileName = "Scalastyle"

/** the class of the checker that should be executed by the sonar rule */
val ClazzParam = "scalastyle-checker"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Sonar Scalastyle Plugin
* Copyright (C) 2014 All contributors
*
* 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 02
*/
package com.ncredinburgh.sonar.scalastyle

import org.sonar.api.server.rule.RuleParamType

case class RepositoryRule(clazz : String, id : String, description : String, params : List[Param])

case class Param(name: String, `type`: RuleParamType, desc: String, defaultVal: String)




Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Sonar Scalastyle Plugin
* Copyright (C) 2014 All contributors
*
* 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 02
*/
package com.ncredinburgh.sonar.scalastyle

import org.slf4j.LoggerFactory
import org.sonar.api.profiles.{ProfileDefinition, RulesProfile}
import org.sonar.api.rules.{RuleFinder, ActiveRule}
import org.sonar.api.utils.ValidationMessages
import org.scalastyle.ScalastyleError
import scala.xml.XML
import collection.JavaConversions._
import org.sonar.api.rules.RuleQuery
import org.sonar.api.rules.Rule

/**
* This class creates the default "Scalastyle" quality profile from Scalastyle's default_config.xml
*/
class ScalastyleQualityProfile(ruleFinder: RuleFinder) extends ProfileDefinition {

private val log = LoggerFactory.getLogger(classOf[ScalastyleQualityProfile])
private val defaultConfigRules = xmlFromClassPath("/default_config.xml") \\ "scalastyle" \ "check"

override def createProfile(validation: ValidationMessages): RulesProfile = {
val profile = RulesProfile.create(Constants.ProfileName, Constants.ScalaKey)
val enabledRules = defaultConfigRules filter (x => (x \ "@enabled").text.equals("true"))
val defaultRuleClasses = enabledRules map (x => (x \ "@class").text)

// currently findAll is buggy (sonar 4.5-5.1 https://jira.sonarsource.com/browse/SONAR-6390)
// will still work but won't add all possible rule to the default profile
val query = RuleQuery.create().withRepositoryKey(Constants.RepositoryKey)
val repoRules = ruleFinder.findAll(query)

for {clazz <- defaultRuleClasses} {
val ruleOption = repoRules.find(clazzMatch(_, clazz))

ruleOption match {
case None => validation.addWarningText(s"Rule for $clazz not found in ${Constants.RepositoryKey} repository! Rule won't be activated.")
case Some(rule) => {
if (!rule.isTemplate()) {
val activated = profile.activateRule(rule, rule.getSeverity)
setParameters(activated, clazz)
}
}
}
}

profile
}

def setParameters(activeRule: ActiveRule, clazz: String) {
// set parameters
defaultConfigRules.find(x => (x \ "@class").text.equals(clazz)) match {
case Some(rule) => {
val params = (rule \ "parameters" \ "parameter").map(n => ((n \ "@name").text, n.text)).toMap
params foreach { case (key, value) => activeRule.setParameter(key, value) }
}
case _ => log.warn("Default rule with key " + activeRule.getRuleKey + " could not found in default_config.xml")
}

// set synthetic parameter
activeRule.setParameter(Constants.ClazzParam, clazz)
}

private def clazzMatch(rule: Rule, clazz: String): Boolean = {
Option(rule.getParam(Constants.ClazzParam)) match {
case Some(param) => {
param.getDefaultValue.equals(clazz)
}
case None => {
log.warn(s"Could not find required parameter ${Constants.ClazzParam}, rule for $clazz cannot be activated")
false
}
}
}

private def xmlFromClassPath(s: String) = XML.load(classOf[ScalastyleError].getResourceAsStream(s))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Sonar Scalastyle Plugin
* Copyright (C) 2014 All contributors
*
* 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 02
*/
package com.ncredinburgh.sonar.scalastyle

import org.sonar.api.rule.Severity
import org.sonar.api.server.rule.RulesDefinition
import org.sonar.api.server.rule.RuleParamType
import org.slf4j.LoggerFactory
import org.sonar.api.server.rule.RulesDefinition.NewRepository
import com.ncredinburgh.sonar.scalastyle.ScalastyleRepository.getStandardKey
import scala.annotation.tailrec

object ScalastyleRepository {

def getStandardKey(clazz: String) = {
val simpleClazz = clazz.reverse.takeWhile(_ != '.').reverse
s"scalastyle_${simpleClazz}"
}
}

/**
* Scalastyle rules repository - creates a rule for each checker shipped with Scalastyle based
* on the scalastyle_definition.xml file that ships with the Scalastyle jar.
*/
class ScalastyleRepository extends RulesDefinition {

override def define(context: RulesDefinition.Context): Unit = {
val repository = context
.createRepository(Constants.RepositoryKey, Constants.ScalaKey)
.setName(Constants.RepositoryName)

ScalastyleResources.allDefinedRules foreach {
repoRule =>
{
val ruleKey = determineFreeRuleKey(repoRule.clazz, repository)

// define the rule
val rule = repository.createRule(ruleKey)
rule.setName(ScalastyleResources.label(repoRule.id))
rule.setHtmlDescription(repoRule.description)

// currently all rules comes with "warning" default level so we can treat with major severity
rule.setSeverity(Severity.MAJOR)

// add parameters
repoRule.params foreach {
param =>
{
rule
.createParam(param.name)
.setDefaultValue(param.defaultVal)
.setType(param.`type`)
.setDescription(param.desc)
}
}

// add synthetic parameter as reference to the class
rule.createParam(Constants.ClazzParam)
.setDefaultValue(repoRule.clazz)
.setType(RuleParamType.STRING)
.setDescription("Scalastyle checker that validates the rule.")

// if a rule has at least one real parameter make it a template
rule.setTemplate(repoRule.params.size > 0)

}
}

repository.done()
}

/**
* determines a free rule key in the repo, in case the key scalastyle-<simple class name> is already
* in use the name scalastyle_<simple class name>_<i> is tried i = 1, 2, ....
*/
private def determineFreeRuleKey(clazz: String, repo: NewRepository): String = {
@tailrec
def getFreeRuleKey(key: String, count: Int, repo: NewRepository): String = {
val testee = if (count == 0) key else "$key_$count"
if (repo.rule(testee) == null) {
testee
} else {
getFreeRuleKey(key, (count + 1), repo)
}
}

getFreeRuleKey(getStandardKey(clazz), 0, repo)
}

}
Loading

0 comments on commit 72b21c6

Please sign in to comment.