-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
732 additions
and
2 deletions.
There are no files selected for viewing
46 changes: 46 additions & 0 deletions
46
unit-picker/src/main/kotlin/org/unimesh/eval/picker/bs/BadSmellGraphCall.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
216 changes: 214 additions & 2 deletions
216
unit-picker/src/main/kotlin/org/unimesh/eval/picker/bs/BadsmellAnalyser.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
unit-picker/src/test/kotlin/org/unimesh/eval/picker/bs/BsAnalyser.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
Oops, something went wrong.