Skip to content

Commit

Permalink
feat: import badsmell from coca
Browse files Browse the repository at this point in the history
  • Loading branch information
phodal committed Dec 4, 2023
1 parent 983596a commit 399d4e0
Show file tree
Hide file tree
Showing 14 changed files with 732 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.unimesh.eval.picker.bs

class BadSmellGraphCall {
companion object {
var totalPath: MutableList<String> = mutableListOf()
}

fun analysisGraphCallPath(nodes: Map<String, List<String>>): List<String> {
for (k in nodes.keys) {
getConnectedPath(k, nodes)
}
return totalPath
}

private fun getConnectedPath(startNode: String, nodes: Map<String, List<String>>) {
val relatedNodes = nodes[startNode] ?: emptyList()
val currentPath: MutableList<String> = mutableListOf()
for (i in relatedNodes.indices) {
for (j in i + 1 until relatedNodes.size) {
getPath(startNode, nodes, currentPath, relatedNodes[i], relatedNodes[j])
getPath(startNode, nodes, currentPath, relatedNodes[j], relatedNodes[i])
}
}
}

private fun getPath(startNode: String, nodes: Map<String, List<String>>, currentPath: List<String>, currentNode: String, endNode: String) {
val nextNodes = nodes[currentNode] ?: emptyList()
if (nextNodes.isEmpty() || currentNode == startNode || currentNode == endNode) {
return
}
if (nextNodes.contains(endNode)) {
val path = listOf(startNode) + currentPath + listOf(currentNode, endNode)
totalPath.add(path.joinToString("->") + ";$startNode->$endNode")
}
for (node in nextNodes) {
if (currentPath.contains(node)) {
continue
}
getPath(startNode, nodes, currentPath + currentNode, node, endNode)
}
}

private fun contains(list: List<String>, element: String): Boolean {
return list.contains(element)
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,221 @@
package org.unimesh.eval.picker.bs

import chapi.domain.core.CodeDataStruct
import chapi.domain.core.CodeFunction
import chapi.domain.core.CodePosition
import chapi.domain.core.DataStructType

class BadsmellAnalyser(val data: List<CodeDataStruct>) {
fun check() {
private val CodeFunction.IfSize: Int get() = 0
private val CodeFunction.SwitchSize: Int get() = 0
private val CodeFunction.IfInfo: CodePosition
get() {
return CodePosition(StartLine = this.Position.StartLine, StopLine = this.Position.StopLine)
}

data class BsConfig(
val bsLongParasLength: Int = 5,
val bsIfSwitchLength: Int = 8,
val bsLargeLength: Int = 20,
val bsMethodLength: Int = 30,
val bsIfLinesLength: Int = 3,
)

enum class SmellType {
SMELL_GARPH_CONNECTED_CALL,
SMELL_LAZY_ELEMENT,
SMELL_LONG_METHOD,
SMELL_DATA_CLASS,
SMELL_REFUSED_BEQUEST,
SMELL_LARGE_CLASS,
SMELL_COMPLEX_CONDITION,
SMELL_REPEATED_SWITCHES,
SMELL_LONG_PARAMETER_LIST
}

data class BadSmellModel(
val file: String? = null,
val line: String? = null,
val bs: SmellType? = null,
val description: String? = null,
val size: Int? = null,
)


class BadsmellAnalyser(val data: List<CodeDataStruct>, val bsConfig: BsConfig = BsConfig()) {
fun analysis(): MutableList<BadSmellModel> {
val badSmellList = mutableListOf<BadSmellModel>()
for (node in data) {
checkLazyElement(node, badSmellList)

var onlyHaveGetterAndSetter = true
for (method in node.Functions) {
checkLongMethod(method, node, badSmellList)

if (!(method.isGetterSetter())) {
onlyHaveGetterAndSetter = false
}

checkLongParameterList(method, node, badSmellList)
checkRepeatedSwitches(method, node, badSmellList)
checkComplexIf(method, node, badSmellList)
}

checkDataClass(onlyHaveGetterAndSetter, node, badSmellList)
checkRefusedBequest(node, badSmellList)
checkLargeClass(node, badSmellList)
}

checkConnectedGraphCall(data, badSmellList)
return badSmellList
}

fun checkConnectedGraphCall(nodes: List<CodeDataStruct>, badSmellList: MutableList<BadSmellModel>) {
val classNodes = mutableMapOf<String, List<String>>()
val classNodeMaps = mutableMapOf<String, Boolean>()
for (node in nodes) {
classNodeMaps[node.getClassFullName()] = true
}
for (node in nodes) {
classNodes[node.getClassFullName()] = node.getCalledClasses(classNodeMaps)
}
val badSmellGraphCall = BadSmellGraphCall()
val descriptions = badSmellGraphCall.analysisGraphCallPath(classNodes)
for (description in descriptions) {
badSmellList.add(BadSmellModel(bs = SmellType.SMELL_GARPH_CONNECTED_CALL, description = description))
}
}

fun CodeDataStruct.getCalledClasses(maps: Map<String, Boolean>): List<String> {
val calledClassesMap = mutableMapOf<String, Boolean>()
val calledClasses = mutableListOf<String>()
for (methodCalled in this.FunctionCalls) {
if (methodCalled.NodeName == "" || !maps[methodCalled.buildClassFullName()]!! || this.getClassFullName() == methodCalled.buildClassFullName()) {
continue
}
calledClassesMap[methodCalled.buildClassFullName()] = true
}
for (key in calledClassesMap.keys) {
calledClasses.add(key)
}

return calledClasses
}


fun checkLazyElement(node: CodeDataStruct, badSmellList: MutableList<BadSmellModel>) {
if (node.Type == DataStructType.CLASS && node.Functions.isEmpty()) {
badSmellList.add(BadSmellModel(file = node.FilePath, bs = SmellType.SMELL_LAZY_ELEMENT))
}
}

fun checkLongMethod(method: CodeFunction, node: CodeDataStruct, badSmellList: MutableList<BadSmellModel>) {
val methodLength = method.Position.StopLine - method.Position.StartLine

if (methodLength > bsConfig.bsMethodLength) {
val description = "method length: $methodLength"
val longMethod = BadSmellModel(
file = node.FilePath,
line = method.Position.StartLine.toString(),
bs = SmellType.SMELL_LONG_METHOD,
description = description,
size = methodLength
)
badSmellList.add(longMethod)
}
}

fun checkDataClass(
onlyHaveGetterAndSetter: Boolean,
node: CodeDataStruct,
badSmellList: MutableList<BadSmellModel>,
) {
if (onlyHaveGetterAndSetter && node.Type == DataStructType.CLASS && node.Functions.isNotEmpty()) {
val dataClass =
BadSmellModel(file = node.FilePath, bs = SmellType.SMELL_DATA_CLASS, size = node.Functions.size)
badSmellList.add(dataClass)
}
}

fun checkRefusedBequest(node: CodeDataStruct, badSmellList: MutableList<BadSmellModel>) {
if (node.Extend != "" && node.HasCallSuper()) {
badSmellList.add(BadSmellModel(file = node.FilePath, bs = SmellType.SMELL_REFUSED_BEQUEST))
}
}

fun CodeDataStruct.HasCallSuper(): Boolean {
var hasCallSuperMethod = false
for (methodCall in this.FunctionCalls) {
if (methodCall.NodeName == this.Extend) {
hasCallSuperMethod = true
}
}

return hasCallSuperMethod
}

fun checkLargeClass(node: CodeDataStruct, badSmellList: MutableList<BadSmellModel>) {
val normalClassLength = node.Functions.filter { !it.isGetterSetter() }.size
if (node.Type == DataStructType.CLASS && normalClassLength >= bsConfig.bsLargeLength) {
val description = "methods number (without getter/setter): $normalClassLength"
badSmellList.add(
BadSmellModel(
file = node.FilePath,
bs = SmellType.SMELL_LARGE_CLASS,
description = description,
size = normalClassLength
)
)
}
}

fun checkComplexIf(method: CodeFunction, node: CodeDataStruct, badSmellList: MutableList<BadSmellModel>) {
val info = method.IfInfo
if (info.StopLine - info.StartLine >= bsConfig.bsIfLinesLength) {
val longParams = BadSmellModel(
file = node.FilePath,
line = info.StartLine.toString(),
bs = SmellType.SMELL_COMPLEX_CONDITION,
description = SmellType.SMELL_COMPLEX_CONDITION.name
)
badSmellList.add(longParams)
}
}

fun checkRepeatedSwitches(method: CodeFunction, node: CodeDataStruct, badSmellList: MutableList<BadSmellModel>) {
if (method.IfSize >= bsConfig.bsIfSwitchLength) {
val longParams = BadSmellModel(
file = node.FilePath,
line = method.Position.StartLine.toString(),
bs = SmellType.SMELL_REPEATED_SWITCHES,
description = "ifSize",
size = method.IfSize
)
badSmellList.add(longParams)
}

if (method.SwitchSize >= bsConfig.bsIfSwitchLength) {
val longParams = BadSmellModel(
file = node.FilePath,
line = method.Position.StartLine.toString(),
bs = SmellType.SMELL_REPEATED_SWITCHES,
description = "switchSize",
size = method.SwitchSize
)
badSmellList.add(longParams)
}
}

fun checkLongParameterList(method: CodeFunction, node: CodeDataStruct, badSmellList: MutableList<BadSmellModel>) {
if (method.Parameters.size > bsConfig.bsLongParasLength) {
val paramsJson = method.Parameters.joinToString(", ")
val longParams = BadSmellModel(
file = node.FilePath,
line = method.Position.StartLine.toString(),
bs = SmellType.SMELL_LONG_PARAMETER_LIST,
description = paramsJson,
size = method.Parameters.size
)
badSmellList.add(longParams)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.unimesh.eval.picker.bs

import chapi.ast.javaast.JavaAnalyser
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import java.io.File
import java.nio.file.Paths

class BsAnalyserTest {
private fun getAbsolutePath(path: String): String {
val resource = this.javaClass.classLoader.getResource(path)
return Paths.get(resource!!.toURI()).toFile().absolutePath
}

@Test
internal fun shouldIdentifyJavaEmptyTest() {
val path = getAbsolutePath("bs/LazyClass.java")
val data = JavaAnalyser().analysis(File(path).readText(), "LazyClass.java").DataStructures
val results = BadsmellAnalyser(data).analysis()

assertEquals(1, results.size)
assertEquals("LazyClass.java", results[0].file)
assertEquals(SmellType.SMELL_LAZY_ELEMENT, results[0].bs)
}

@Test
fun shouldIdentifyLongParameters() {
val path = getAbsolutePath("bs/LongParameter.java")
val data = JavaAnalyser().analysis(File(path).readText(), "LongParameter.java").DataStructures
val results = BadsmellAnalyser(data).analysis()

assertEquals(1, results.size)
assertEquals("LongParameter.java", results[0].file)
assertEquals(SmellType.SMELL_LONG_PARAMETER_LIST, results[0].bs)
}

@Test
fun shouldIdentifyMultipleIf() {
val path = getAbsolutePath("bs/MultipleIf.java")
val data = JavaAnalyser().analysis(File(path).readText(), "MultipleIf.java").DataStructures
val results = BadsmellAnalyser(data).analysis()

assertEquals(1, results.size)
assertEquals("MultipleIf.java", results[0].file)
assertEquals(SmellType.SMELL_COMPLEX_CONDITION, results[0].bs)
}
}
70 changes: 70 additions & 0 deletions unit-picker/src/test/resources/bs/ComplexIf.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.zheng.upms.server.controller.manage;

import com.baidu.unbiz.fluentvalidator.ComplexResult;
import com.baidu.unbiz.fluentvalidator.FluentValidator;
import com.baidu.unbiz.fluentvalidator.ResultCollectors;
import com.zheng.common.base.BaseController;
import com.zheng.common.validator.LengthValidator;
import com.zheng.upms.common.constant.UpmsResult;
import com.zheng.upms.common.constant.UpmsResultConstant;
import com.zheng.upms.dao.model.UpmsSystem;
import com.zheng.upms.dao.model.UpmsSystemExample;
import com.zheng.upms.rpc.api.UpmsSystemService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* 系统controller
* Created by shuzheng on 2016/12/18.
*/
@Controller
@Api(value = "系统管理", description = "系统管理")
@RequestMapping("/manage/system")
public class UpmsSystemController extends BaseController {

private static final Logger LOGGER = LoggerFactory.getLogger(UpmsSystemController.class);

@Autowired
private UpmsSystemService upmsSystemService;

@ApiOperation(value = "系统列表")
@RequiresPermissions("upms:system:read")
@RequestMapping(value = "/list", method = RequestMethod.GET)
@ResponseBody
public Object list(
@RequestParam(required = false, defaultValue = "0", value = "offset") int offset,
@RequestParam(required = false, defaultValue = "10", value = "limit") int limit,
@RequestParam(required = false, defaultValue = "", value = "search") String search,
@RequestParam(required = false, value = "sort") String sort,
@RequestParam(required = false, value = "order") String order) {
UpmsSystemExample upmsSystemExample = new UpmsSystemExample();
if (!StringUtils.isBlank(sort)
&& !StringUtils.isBlank(order)

) {
upmsSystemExample.setOrderByClause(sort + " " + order);
}
if (StringUtils.isNotBlank(search)) {
upmsSystemExample.or()
.andTitleLike("%" + search + "%");
}
List<UpmsSystem> rows = upmsSystemService.selectByExampleForOffsetPage(upmsSystemExample, offset, limit);
long total = upmsSystemService.countByExample(upmsSystemExample);
Map<String, Object> result = new HashMap<>();
result.put("rows", rows);
result.put("total", total);
return result;
}
}
Loading

0 comments on commit 399d4e0

Please sign in to comment.